io.odysz.semantic.DA.Connects Maven / Gradle / Ivy
package io.odysz.semantic.DA;
import static io.odysz.common.LangExt.isblank;
import static io.odysz.common.LangExt.len;
import java.io.File;
import java.io.IOException;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.naming.NamingException;
import org.apache.commons.io_odysz.FilenameUtils;
import org.xml.sax.SAXException;
import io.odysz.common.dbtype;
import io.odysz.common.LangExt;
import io.odysz.common.Regex;
import io.odysz.common.Utils;
import io.odysz.module.rs.AnResultset;
import io.odysz.module.xtable.ILogger;
import io.odysz.module.xtable.IXMLStruct;
import io.odysz.module.xtable.Log4jWrapper;
import io.odysz.module.xtable.XMLDataFactory;
import io.odysz.module.xtable.XMLDataFactoryEx;
import io.odysz.module.xtable.XMLTable;
import io.odysz.module.xtable.XMLTable.IMapValue;
import io.odysz.semantic.util.LogFlags;
import io.odysz.semantics.IUser;
import io.odysz.semantics.meta.TableMeta;
import io.odysz.semantics.x.SemanticException;
import io.odysz.transact.x.TransException;
/**
* Connection configurations and DB table meta data manager.
*
* @author [email protected]
*/
public class Connects {
/** nothing special for commit */
public static final int flag_nothing = 0;
public static final int flag_printSql = 1;
public static final int flag_disableSql = 2;
/**Convert names like "sqlit" to {@link dbtype}.
* @param type
* @return db type
* @throws SemanticException
*/
public static dbtype parseDrvType(String type) throws SemanticException {
if (type == null || type.trim().length() == 0)
throw new SemanticException("Drived type not suppored: %s", type);
type = type.trim().toLowerCase();
if (type.equals("mysql"))
return dbtype.mysql;
else if (type.equals("mssql2k") || type.equals("ms2k"))
return dbtype.ms2k;
else if (type.equals("oracle") || type.equals("orcl"))
return dbtype.oracle;
else if (type.startsWith("sqlit_queue"))
return dbtype.sqlite_queue;
else if (type.startsWith("sqlit"))
return dbtype.sqlite;
else
throw new SemanticException("Driver type not suppored yet: %s", type);
}
/** Connection (data sources) */
private static HashMap>> srcs;
public static Set getAllConnIds() {
return srcs == null ? null : srcs.keySet();
}
/** Component URI - connection mappings */
private static LinkedHashMap conn_uri;
private static String defltConn;
private static String workingDir;
public static String defltConn() { return defltConn; }
private static final int DmConn = 1;
private static final int CpConn = 2;
/**
* parse connects.xml, setup connections configured in table "drvmnger", for JDBC DriverManger,
* and "dbcp", for JDBC connection-pooled connection managed by container.
*
* @param xmlDir configure file folder
*/
public static void init(String xmlDir) {
Utils.logi("Initializing connects with path to %s", xmlDir);
workingDir = xmlDir;
if (srcs != null) return;
srcs = new HashMap>>();
try{
ILogger logger = new Log4jWrapper("xtabl");
srcs = loadConnects(srcs, "drvmnger", DmConn, logger, xmlDir);
srcs = loadConnects(srcs, "dbcp", CpConn, logger, xmlDir);
conn_uri = loadConnUri("conn-uri", logger, xmlDir);
if (srcs != null && srcs.size() > 0 && !srcs.containsKey(defltConn))
throw new SQLException("Found connection configruations, but initialization has failed. DB source must be configured with a default source.");
if (LogFlags.Semantic.connects)
Utils.logi("INFO - JDBC initialized using %s (%s) as default connection.",
defltConn, srcs != null && srcs.size() > 0 ? srcs.get(defltConn).driverType() : "empty");
}
catch (Exception ex) {
System.err.println("FATAL - Connection initializing failed! !!\n");
ex.printStackTrace();
return;
}
}
static HashMap>> loadConnects(
HashMap>> srcs,
String tablId, int dmCp, ILogger logger, String xmlDir) throws SAXException {
if (srcs == null)
srcs = new HashMap>>();
String absPath = FilenameUtils.concat(xmlDir, "connects.xml");
Utils.logi(new File(absPath).getAbsolutePath());
XMLTable conn = XMLDataFactory.getTable(logger , tablId, absPath, // xmlDir + "/connects.xml",
new IXMLStruct() {
@Override public String rootTag() { return "conns"; }
@Override public String tableTag() { return "t"; }
@Override public String recordTag() { return "c"; }});
conn.beforeFirst();
while (conn.next()) {
try {
// columns="type,id,isdef,conn,usr,pswd,dbg"
dbtype type = parseDrvType(conn.getString("type"));
String id = conn.getString("id");
// boolean log = conn.getBool("log", false);
if (dmCp == DmConn)
if (srcs.get(id) != null)
;
else
srcs.put(id, AbsConnect.initDmConnect(xmlDir, type, conn.getString("src"),
conn.getString("usr"), conn.getString("pswd"),
conn.getBool("dbg", false), conn.getBool("log", false))
.prop("smtcs", conn.getString("smtcs")));
else
srcs.put(id, AbsConnect.initPooledConnect(xmlDir, type, conn.getString("src"),
conn.getString("usr"), conn.getString("pswd"),
conn.getBool("dbg", false), conn.getBool("log", false))
.prop("smtcs", conn.getString("smtcs")));
if (conn.getBool("isdef", false)) {
if (defltConn != null)
Utils.warn("WARN - duplicate default ids found, the previous defined source been ignored: " + defltConn);
defltConn = id;
}
} catch (Exception e) {
Utils.warn("ERROR: Connection intiialization failed: %s. (default connection id can be null.)",
conn.getString("type"));
e.printStackTrace();
continue;
}
}
return srcs;
}
private static LinkedHashMap loadConnUri(String tablId, ILogger logger, String xmlDir)
throws SAXException, SemanticException {
if (conn_uri == null)
conn_uri = new LinkedHashMap();
String absPath = FilenameUtils.concat(xmlDir, "connects.xml");
Utils.logi(new File(absPath).getAbsolutePath());
XMLTable conn = XMLDataFactory.getTable(logger, tablId, absPath, //xmlDir + "/connects.xml",
new IXMLStruct() {
@Override public String rootTag() { return "conns"; }
@Override public String tableTag() { return "t"; }
@Override public String recordTag() { return "c"; }});
if (conn == null)
throw new SemanticException("Since v1.3.0, connects.xml/t[id='conn-uri' is necessary.");
conn.beforeFirst();
while (conn.next()) {
try {
String uriReg = conn.getString("uri");
String connId = conn.getString("conn");
conn_uri.put(new Regex(uriReg), connId);
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
return conn_uri;
}
public static void close() {
if (srcs != null)
for (AbsConnect> c : srcs.values())
try {
c.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/////////////////////////////// common helper /////////////////////////////
/** If printSql is true or if asking enable,
* then print sqls.
* @param asking
* @param flag
* @param sqls
*/
public static void printSql(boolean asking, int flag, ArrayList sqls) {
if ((flag & flag_printSql) == flag_printSql
|| asking && (flag & flag_disableSql) != flag_disableSql)
Utils.logi(sqls);
}
public static void printSql(boolean asking, int flag, String sql) {
if ((flag & flag_printSql) == flag_printSql
|| asking && (flag & flag_disableSql) != flag_disableSql)
Utils.logi(sql);
}
///////////////////////////////////// select ///////////////////////////////
public static AnResultset select(String conn, String sql, int... flags) throws SQLException {
// This is probably because of wrong configuration in connects.xml.
if (flags != null && flags.length > 0 && flags[0] == flag_printSql )
if (conn != null && !srcs.containsKey(conn))
throw new SQLException("Can't find connection: " + conn);
String connId = conn == null ? defltConn : conn;
try {
return srcs.get(connId)
.select(sql, flags == null || flags.length <= 0 ? flag_nothing : flags[0]);
} catch (NamingException e) {
throw new SQLException("Can't find connection, id=" + connId);
}
}
public static AnResultset select(String sql, int... flags) throws SQLException {
return select(null, sql, flags);
}
/**compose paged sql, e.g. for Oracle:
* select * from (sql) t where rownum > 0 and row num < 14
* @param sql
* @param page
* @param size
* @return sql
* @throws TransException
*/
public static String pagingSql(String conn, String sql, int page, int size)
throws TransException {
conn = conn == null ? defltConn : conn;
dbtype driverType = srcs.get(conn).driverType();
return pagingSql(driverType, sql, page, size);
}
public static String pagingSql(dbtype dt, String sql, int pageIx, int pgSize)
throws TransException {
if (pageIx < 0 || pgSize <= 0)
return sql;
long i1 = pageIx * pgSize;
String r2 = String.valueOf(i1 + pgSize);
String r1 = String.valueOf(i1);
Stream s;
if (dt == dbtype.oracle)
s = Stream.of("select * from (select t.*, rownum r_n_ from (", sql,
") t order by rownum) t where r_n_ > ", r1, " and r_n_ <= ", r2);
else if (dt == dbtype.ms2k)
s = Stream.of("select * from (SELECT ROW_NUMBER() OVER(ORDER BY (select NULL as noorder)) AS RowNum, * from (", sql,
") t) t where rownum > ", r1, " and rownum <= %s", r2);// FIXME "%s" is tested?
else if (dt == dbtype.sqlite)
// https://stackoverflow.com/a/51380906
s = Stream.of("select * from (", sql, ") limit ", String.valueOf(pgSize), " offset ", r1);
else // mysql
s = Stream.of("select * from (select t.*, @ic_num := @ic_num + 1 as rnum from (", sql,
") t, (select @ic_num := 0) ic_t) t1 where rnum > ", r1, " and rnum <= ", r2);
return s.collect(Collectors.joining(" "));
}
/////////////////////////////////// update /////////////////////////////
/**
* Commit to default connection.
* @param usr
* @param sqls
* @param flags
* @return affected row count
* @throws SQLException
* @throws TransException
*/
public static int[] commit(IUser usr, ArrayList sqls, int... flags) throws SQLException, TransException {
try {
return srcs.get(defltConn).commit(usr, sqls, flags.length > 0 ? flags[0] : flag_nothing);
} catch (NamingException e) {
throw new TransException("Can't find connection, id=" + defltConn);
}
}
public static int[] commit(IUser usr, ArrayList sqls, ArrayList lobs, int... flags) throws SQLException {
return srcs.get(defltConn).commit(usr, sqls, lobs, flags.length > 0 ? flags[0] : flag_nothing);
}
@SuppressWarnings("serial")
public static int[] commit(String conn, IUser usr, String sql, int... flags) throws SQLException, TransException {
return commit(conn, usr, new ArrayList() { {add(sql);} }, flags.length > 0 ? flags[0] : flag_nothing);
}
public static int[] commit(String conn, IUser usr, ArrayList sqls, int... flags)
throws SQLException, TransException {
if (srcs == null || !srcs.containsKey(conn))
throw new SemanticException("Can't find connection %s.", conn);
try {
return srcs.get(conn).commit(usr, sqls, flags.length > 0 ? flags[0] : flag_nothing);
} catch (NamingException e) {
throw new TransException("Can't find connection, id=" + defltConn);
}
}
@SuppressWarnings("serial")
public static int[] commit(IUser usr, final String sql) throws SQLException, TransException {
return commit(usr, new ArrayList () { {add(sql);} });
}
public static dbtype driverType(String conn) {
conn = conn == null ? defltConn : conn;
if (!srcs.containsKey(conn))
throw new NullPointerException("Can't find datasourse: " + conn);
return srcs.get(conn).driverType();
}
public static Set connIds() {
return srcs == null ? null : srcs.keySet();
}
/**
* Build database tables' meta.
*
* @param conn
* @return metas
* @throws IOException
* @throws SQLException
* @throws Exception
*/
public static HashMap loadMeta(String conn)
throws SemanticException, SQLException {
dbtype dt = driverType(conn);
HashMap metas = new HashMap();
if (dt == null)
throw new SemanticException("Drived type not suppored: ", conn);
if (dt == dbtype.mysql)
metas = MetaBuilder.buildMysql(conn);
else if (dt == dbtype.ms2k)
metas = MetaBuilder.buildMs2k(conn);
else if (dt == dbtype.oracle)
metas = MetaBuilder.buildOrcl(conn);
else if (dt == dbtype.sqlite)
metas = MetaBuilder.buildSqlite(conn);
else
throw new SemanticException("Drived type not suppored: %s", dt.name());
replaceSemantics(conn, metas);
return metas;
}
/**
* Replace DB metas with classes defined in semantics.xml/t[id=metas]
*
* @since 2.0.0
* @param metas
* @throws IOException
* @throws SAXException
* @throws Exception
*/
static void replaceSemantics(String connId, HashMap metas) throws SemanticException {
if (len(metas) > 0) {
String fpath = Connects.getSmtcsPath(connId);
LinkedHashMap xtabs;
try {
xtabs = XMLDataFactoryEx.getXtables(
new Log4jWrapper("").setDebugMode(false), fpath, new IXMLStruct() {
@Override public String rootTag() { return "semantics"; }
@Override public String tableTag() { return "t"; }
@Override public String recordTag() { return "s"; }});
} catch (SAXException | IOException e1) {
e1.printStackTrace();
throw new SemanticException(e1.getMessage());
}
XMLTable xmetas = xtabs.get("metas");
if (xmetas != null) {
HashMap semetas;
try {
semetas = xmetas.map(
(XMLTable t) -> {
String tabl = xmetas.getString("tabl");
String clzz = xmetas.getString("semanticlass");
return (IMapValue) Class.forName(clzz)
.getConstructor(String.class, String.class)
.newInstance(tabl, connId);
});
} catch (Exception e) {
e.printStackTrace();
throw new SemanticException(e.getMessage());
}
for (String sm : semetas.keySet())
metas.put(sm, (TableMeta) semetas.remove(sm));
}
}
}
protected static XMLTable loadMetaCfgs(String connId) throws SAXException, IOException, SemanticException {
String fpath = Connects.getSmtcsPath(connId);
if (isblank(fpath, "\\."))
throw new SemanticException(
"Trying to find semantics of conn %1$s, but the configuration path is empty.\n" +
"No 'smtcs' configured in connects.xml for connection \"%1$s\"?\n" +
"Looking in path: %2$s", connId, fpath);
LinkedHashMap xtabs = XMLDataFactoryEx.getXtables(
new Log4jWrapper("").setDebugMode(false), fpath, new IXMLStruct() {
@Override public String rootTag() { return "semantics"; }
@Override public String tableTag() { return "t"; }
@Override public String recordTag() { return "s"; }});
XMLTable xconn = xtabs.get("semantics");
if (xconn == null)
throw new SemanticException("Xml structure error (no semantics table) in\n%s", fpath);
return xconn;
}
protected static HashMap> metas;
public static HashMap getMeta(String connId)
throws SemanticException, SQLException {
if (metas == null)
metas = new HashMap>(srcs.size());
if (connId == null)
connId = defltConn;
if (!metas.containsKey(connId))
metas.put(connId, loadMeta(connId));
if (!metas.containsKey(connId))
metas.put(connId, new HashMap(0));
return metas.get(connId);
}
/**
* @since 1.4.25
* @param connId
* @param tbl
* @return table meta
* @throws SemanticException
* @throws SQLException
* @throws IOException
*/
public static TableMeta getMeta(String connId, String tbl)
throws SemanticException, SQLException {
return getMeta(connId).get(tbl);
}
/**
* Set table meta (providing a chance of extending table's semantics and keep a single copy for DB's meta).
* @since 1.4.25
* @param connId
* @param m
* @throws SemanticException
* @throws SQLException
* @throws IOException
* @throws SAXException
*/
public static void setMeta(String connId, TableMeta m) throws SemanticException, SQLException {
if (m == null || !m.typesInited())
throw new SemanticException("Arg or arg's types are null, TableMeta is not loaded from DB? Call getMeta() and extend it.");
TableMeta mdb = getMeta(connId, m.tbl);
if (mdb == null)
throw new SemanticException("Table %s deson't exists in connect %s.", m.tbl, connId);
getMeta(connId).put(m.tbl, m);
}
/**
* Get the smtcs file path configured in connects.xml.
*
* @param conn
* @return smtcs (e.g. semantics.xml)
*/
public static String getSmtcsPath(String conn) {
if (conn == null)
conn = defltConn;
return FilenameUtils.concat(workingDir,
srcs == null || !srcs.containsKey(conn) ? null
: srcs.get(conn).prop("smtcs"));
}
public static boolean getDebug(String conn) {
if (conn == null)
conn = defltConn;
return srcs.get(conn).enableSystemout;
}
public static void setDebug(String conn, boolean debug) {
if (conn == null)
conn = defltConn;
srcs.get(conn).enableSystemout = debug;
}
/**
* Mapping client function id to data connection according to configuration.
*
* Uri pattern is matched with prefix of strings' in db config.xml.
*
* @param uri
* @return
* @throws SemanticException uri is null
*/
public static String uri2conn(String uri) throws SemanticException {
if (LangExt.isblank(uri))
throw new SemanticException("Function's uri cannot be null! Which is used for connecting datasource.");
for (Regex reg : conn_uri.keySet())
if (reg.match(uri))
return conn_uri.get(reg);
return defltConn;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy