/*
* (C) Copyright IBM Corp. 2008
*
* LICENSE: Eclipse Public License v1.0
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.ibm.gaiandb;
import java.io.File;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.SQLBit;
import org.apache.derby.iapi.types.SQLChar;
import org.apache.derby.iapi.types.SQLInteger;
import org.apache.derby.iapi.types.SQLLongint;
import org.apache.derby.iapi.types.SQLTimestamp;
import com.ibm.db2j.FileImport;
import com.ibm.db2j.GExcel;
import com.ibm.db2j.GaianTable;
import com.ibm.db2j.GenericWS;
import com.ibm.gaiandb.DataSourcesManager.RDBProvider;
import com.ibm.gaiandb.DataSourcesManager.RecallingStack;
import com.ibm.gaiandb.diags.GDBMessages;
import com.ibm.gaiandb.webservices.parser.data.GenericWsLogicalTableGenerator;
import com.ibm.gaiandb.webservices.scanner.FormatSpecifierInputStream;
import com.ibm.gaiandb.webservices.scanner.WsDataFormat;
import com.ibm.gaiandb.webservices.ws.PostRestWS;
import com.ibm.gaiandb.webservices.ws.RestWS;
import com.ibm.gaiandb.webservices.ws.SoapWS;
import com.ibm.gaiandb.webservices.ws.WebService;
/**
* @author DavidVyvyan
*/
public class GaianDBConfigProcedures extends GaianDBProcedureUtils {
// Use PROPRIETARY notice if class contains a main() method, otherwise use COPYRIGHT notice.
public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2008";
private static final Logger logger = new Logger( "GaianDBConfigProcedures", 30 );
// Utility ResultSet request identifiers for API procs and funcs
private static final String LTDEFS = "LTDEFS";
private static final String RDBCONNECTIONS = "RDBCONNECTIONS";
private static final String DATASOURCES = "DATASOURCES";
private static final String FULLCONFIG = "FULLCONFIG";
private static final String LISTQUERIES = "LISTQUERIES";
private static final String USERWARNING = "USERWARNING";
private static final String API = "API";
// Config requests' ResultSet column names
private static final String LTNAME = "LTNAME";
private static final String LTDEF = "LTDEF";
private static final String CID = "CID";
private static final String CNODEID = "CNODEID";
private static final String CPOOLSIZE = "CPOOLSIZE";
private static final String CDRIVER = "CDRIVER";
private static final String CURL = "CURL";
private static final String CUSR = "CUSR";
private static final String DSID = "DSID";
private static final String DSTYPE = "DSTYPE";
private static final String DSWRAPPER = "DSWRAPPER";
private static final String DSHANDLE = "DSHANDLE";
private static final String DSOPTIONS = "DSOPTIONS";
private static final String DSCID = "DSCID";
private static final String PROPID = "PROPID";
private static final String PROPDEF = "PROPDEF";
private static final String TSTAMP = "TSTAMP";
private static final String QUERYID = "QUERYID";
private static final String STATE = "STATE";
private static final String NBROWS = "NBROWS";
private static final String START_TS = "START_TS";
private static final String DEPTH = "DEPTH";
private static final String QUERYHASH = "QUERYHASH";
private static final String GDB_WID = "GDB_WID";
private static final String QUERY_MS = "QUERY_MS";
private static final String WARNING = "WARNING";
private static final String TSTAMPFIRST = "TSTAMPFIRST";
private static final String REPEATCOUNT = "REPEATCOUNT";
private static final String APISETUPSQL = "APISETUPSQL";
// Config requests' ResultSet definitions
private static final String[] CONFIG_REQUESTS = { LTDEFS, RDBCONNECTIONS, DATASOURCES, FULLCONFIG, LISTQUERIES, USERWARNING, API, FILESTATS };
private static final String[] CONFIG_REQUEST_RESULT_DEFS = {
LTNAME + " " + TSTR + ", " + LTDEF + " " + MSTR, // use medium string as this is just the display width
CID + " " + TSTR + ", " + CNODEID + " " + TSTR + ", " + CPOOLSIZE + " INT, " +
CDRIVER + " " + SSTR + ", " + CURL + " " + MSTR + ", " + CUSR + " " + TSTR,
// DSTYPE is R, V or G for: RDB, VTI and Gaian, DSWRAPPER is e.g. "FileImport" or "C1",
// DSHANDLE is e.g. filename or rdb tablename, DSOPTIONS is e.g. "InMemory"
DSID + " " + TSTR + ", DSTYPE CHAR, " + DSWRAPPER + " " + TSTR + ", " + DSHANDLE + " " + MSTR +
", " + DSOPTIONS + " " + MSTR + ", " + DSCID + " " + TSTR,
PROPID + " " + SSTR + ", " + PROPDEF + " " + MSTR,
QUERYID + " " + SSTR + ", " + DEPTH + " INT, " + STATE + " " + TSTR + ", " + LTNAME + " " + TSTR + ", " + QUERYHASH + " " + TSTR + ", "
+ START_TS + " TIMESTAMP, " + QUERY_MS + " BIGINT, " + NBROWS + " BIGINT, " + GDB_WID + " " + TSTR, // This TSTR is not an enforced max size
TSTAMP + " TIMESTAMP, " + WARNING + ' ' + MSTR + ", " + TSTAMPFIRST + " TIMESTAMP, " + REPEATCOUNT + " BIGINT",
APISETUPSQL + " " + MSTR,
"FNAME " + MSTR + ", MODIFIED BIGINT, SIZE BIGINT, CHECKSUM CHAR(20) FOR BIT DATA"
};
// A Hashtable for looking up the resulting table defs from a config request string
public static final Hashtable<String, GaianResultSetMetaData> configRequestResultDefs = new Hashtable<String, GaianResultSetMetaData>();
static {
for (int i=0; i<CONFIG_REQUESTS.length; i++)
try { configRequestResultDefs.put(CONFIG_REQUESTS[i], new GaianResultSetMetaData(CONFIG_REQUEST_RESULT_DEFS[i])); }
catch (Exception e) {
String msg = "Error loading config request meta data for " + CONFIG_REQUESTS[i] + ": " + e;
System.out.println(msg);
GaianNode.stop(msg);
}
}
//private static final GaianResultSetMetaData[] CR_MD = { configRequestResultDefs.get( LTDEFS ) };
// In the unlikely case where it is necessary to change the system schema from the default 'gaiandb', use setconfigproperty().
// Further, to toggle enablement of authentication and change the gaiandb pwd, a user must edit derby.properties
// private static final String setgaiancred = "setgaiancred";
static final String GAIANDB_API = ""
+ "!DROP PROCEDURE "+listspfs+";!CREATE PROCEDURE "+listspfs+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listStoredProceduresAndFunctions'"
+ ";"
+ "!DROP PROCEDURE "+listapi+";!CREATE PROCEDURE "+listapi+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listAPI'"
+ ";"
+ "!DROP PROCEDURE "+listconfig+";!CREATE PROCEDURE "+listconfig+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listConfig'"
+ ";"
+ "!DROP PROCEDURE "+listwarnings+";!CREATE PROCEDURE "+listwarnings+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listWarnings'"
+ ";"
+ "!DROP PROCEDURE "+listwarningsx+";!CREATE PROCEDURE "+listwarningsx+"(DEPTH INT)"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listWarningsToDepth'"
+ ";"
+ "!DROP PROCEDURE "+listnodes+";!CREATE PROCEDURE "+listnodes+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listGaianNodes'"
+ ";"
+ "!DROP PROCEDURE "+listrdbc+";!CREATE PROCEDURE "+listrdbc+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listRDBConnections'"
+ ";"
+ "!DROP PROCEDURE "+listlts+";!CREATE PROCEDURE "+listlts+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listLogicalTables'"
+ ";"
+ "!DROP PROCEDURE "+listltmatches+";!CREATE PROCEDURE "+listltmatches+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listLogicalTableMatches'"
+ ";"
+ "!DROP PROCEDURE "+listds+";!CREATE PROCEDURE "+listds+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listDataSources'"
+ ";"
+ "!DROP PROCEDURE "+listqueries+";!CREATE PROCEDURE "+listqueries+"(DEPTH INT)"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listQueries'"
+ ";"
+ "!DROP PROCEDURE "+listderbytables+";!CREATE PROCEDURE "+listderbytables+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listDerbyTables'"
+ ";"
+ "!DROP PROCEDURE "+listexplain+";!CREATE PROCEDURE "+listexplain+"(LTNAME_OR_GSQL "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listExplainGaianTableOrQuery'"
+ ";"
+ "!DROP PROCEDURE "+listflood+";!CREATE PROCEDURE "+listflood+"()"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.listFlood'"
// + ";"
// + "!DROP FUNCTION GEXPLAIN;"
// + " !CREATE FUNCTION GEXPLAIN(GSQL "+XSTR+") RETURNS TABLE(" + EXPLAIN_COLDEFS + ")"
// + " PARAMETER STYLE DERBY_JDBC_RESULT_SET LANGUAGE JAVA READS SQL DATA"
// + " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.getExplainGaianTableOrQuery'"
+ ";"
+ "!DROP FUNCTION "+getconfigproperty+";!CREATE FUNCTION "+getconfigproperty+"(PROPERTY_NAME_SQLPATTERN "+XSTR+")"
+ " RETURNS " + XSTR
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.getConfigProperty'"
+ ";"
+ "!DROP FUNCTION "+gdb_node+";!CREATE FUNCTION "+gdb_node+"() RETURNS " + XSTR
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfig.getGaianNodeID'"
+ ";"
+ "!DROP FUNCTION "+getlts+";"
+ " !CREATE FUNCTION "+getlts+"() RETURNS TABLE(NODEID " + PROVENANCE_COLS[0].split(" ")[1] +
", " + LTNAME + " " + XSTR + ", " + LTDEF + " " + XSTR + ")"
+ " PARAMETER STYLE DERBY_JDBC_RESULT_SET LANGUAGE JAVA READS SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.getLogicalTables'"
+ ";"
+ "!DROP FUNCTION " + getnodes + ";"
+ " !CREATE FUNCTION " + getnodes + "() RETURNS TABLE(NODEID "+SSTR+")"
+ " PARAMETER STYLE DERBY_JDBC_RESULT_SET LANGUAGE JAVA READS SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.getGaianNodes'"
// + ";" // Example Table Function equivalent of a GaianTable VTI call against LT0 (with limitations)
// + "!DROP FUNCTION LT0;!CREATE FUNCTION LT0(TABNAME "+XSTR+") RETURNS TABLE(LOCATION "+TSTR+", NUMBER INTEGER, MISC "+TSTR+", BF2_PLAYER INTEGER)"
// + " PARAMETER STYLE DERBY_JDBC_RESULT_SET LANGUAGE JAVA NOT DETERMINISTIC READS SQL DATA EXTERNAL NAME 'com.ibm.db2j.GaianTable.queryGaianTable'"
+ ";" // Procedure to get the last N lines from the log files of a set of GaianDB nodes
+ "!DROP PROCEDURE LOGTAIL;!CREATE PROCEDURE LOGTAIL(NODELIST "+XSTR+", NUMLINES INT)"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA DYNAMIC RESULT SETS 1"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.logTail'"
// + ";"
// + "!DROP PROCEDURE "+setuser+";!CREATE PROCEDURE"
// + " "+setuser+"(usr "+XSTR+", affiliation "+XSTR+", clearance "+XSTR+", pwd "+XSTR+")"
// + " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setUser'"
// + ";"
// + "!DROP PROCEDURE "+removeuser+";!CREATE PROCEDURE "+removeuser+"(usr "+XSTR+")"
// + " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.removeUser'"
// + ";" // Function that gets the current public key for this server
// + "!DROP FUNCTION "+gpublickey+";!CREATE FUNCTION "+gpublickey+"() RETURNS VARCHAR(1024) FOR BIT DATA PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL"
// + " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.getPublicKey'"
+ ";"
+ "!DROP PROCEDURE "+addgateway+";!CREATE PROCEDURE "+addgateway+"(ipaddress "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.addGateway'"
+ ";"
+ "!DROP PROCEDURE "+removegateway+";!CREATE PROCEDURE "+removegateway+"(ipaddress "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.removeGateway'"
+ ";"
+ "!DROP PROCEDURE "+setrdbc+";!CREATE PROCEDURE"
+ " "+setrdbc+"(connectionID "+XSTR+", driver "+XSTR+", url "+XSTR+", usr "+XSTR+", pwd "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setRDBConnection'"
+ ";"
+ "!DROP PROCEDURE "+setlt+";!CREATE PROCEDURE "+setlt+"(LTNAME "+XSTR+", DEFINITION "+XSTR+", CONSTANTS "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setLogicalTable'"
+ ";"
+ "!DROP PROCEDURE "+setltforrdbtable+";!CREATE PROCEDURE "+setltforrdbtable+"(LTNAME "+XSTR+", CID "+XSTR+", rdbTableOrFromExpression "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setLogicalTableForRDBTable'"
+ ";"
+ "!DROP PROCEDURE "+setltforfile+";!CREATE PROCEDURE "+setltforfile+"(LTNAME "+XSTR+", FILENAME "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setLogicalTableForFile'"
+ ";"
// Denis : creation of the procedure for setltforexcel
+ "!DROP PROCEDURE "+setltforexcel+";!CREATE PROCEDURE "+setltforexcel+"(LTNAME "+XSTR+", SPREADSHEETLOCATION "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setLogicalTableForExcel'"
+ ";"
// // Creation of the procedure setltforws
// + "!DROP PROCEDURE "+setltforws+";!CREATE PROCEDURE "+setltforws +"(LTNAME "+XSTR+", URL "+XSTR+", WSDLLOCATION "+XSTR+", POSTDATA "+XSTR+")"
// + " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA"
// + " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setLogicalTableForWS'"
// + ";"
// + "!DROP PROCEDURE "+setltformongodb+";!CREATE PROCEDURE "+setltformongodb +"(LTNAME "+XSTR+", URL "+XSTR+", FIELDS "+XSTR+")"
// + " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA"
// + " EXTERNAL NAME 'com.ibm.db2j.MongoDB.setLogicalTableForMongoDB'"
// + ";"
+ "!DROP PROCEDURE "+setltfornode+";!CREATE PROCEDURE "+setltfornode+"(LTNAME "+XSTR+", NODEID "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setLogicalTableForNode'"
+ ";"
+ "!DROP PROCEDURE "+setltconstants+";!CREATE PROCEDURE "+setltconstants+"(LTNAME "+XSTR+", CONSTANTS "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setLogicalTableConstants'"
+ ";"
+ "!DROP PROCEDURE "+setnodeconstants+";!CREATE PROCEDURE "+setnodeconstants+"(nodeConstants "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setNodeConstants'"
+ ";"
+ "!DROP PROCEDURE "+setdsvti+";!CREATE PROCEDURE"
+ " "+setdsvti+"(ltName "+XSTR+", dsName "+XSTR+", vtiClass "+XSTR+", args "+XSTR+", options "+XSTR+", columnsCSV "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setDataSourceVTI'"
+ ";"
+ "!DROP PROCEDURE "+setdsexcel+";!CREATE PROCEDURE"
+ " "+setdsexcel+"(ltName "+XSTR+", dsName "+XSTR+", fileName "+XSTR+", options "+XSTR+", columns "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setDataSourceExcel'"
+ ";"
+ "!DROP PROCEDURE "+setdsfile+";!CREATE PROCEDURE"
+ " "+setdsfile+"(ltName "+XSTR+", dsName "+XSTR+", fileName "+XSTR+", options "+XSTR+", columnsCSV "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setDataSourceFile'"
+ ";"
+ "!DROP PROCEDURE "+setdsrdbtable+";!CREATE PROCEDURE"
+ " "+setdsrdbtable+"(ltName "+XSTR+", dsName "+XSTR+", connectionID "+XSTR+", rdbTable "+XSTR+", options "+XSTR+", columnsCSV "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setDataSourceRDBTable'"
+ ";"
+ "!DROP PROCEDURE "+setdslocalderby+";!CREATE PROCEDURE"
+ " "+setdslocalderby+"(ltName "+XSTR+", dsName "+XSTR+", rdbTable "+XSTR+", options "+XSTR+", columnsCSV "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setDataSourceRDBTable'"
+ ";" // Function that evaluates whether 2 logical table defs match (i.e. each column name shd have the same type in both defs or not exist)
+ "!DROP FUNCTION " + ltmatch + ";!CREATE FUNCTION " + ltmatch + "(LTDEF1 "+XSTR+", LTDEF2 "+XSTR+")"
+ " RETURNS SMALLINT PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.matchTableDefinitions'"
+ ";" // Get count of GaianDB nodes
+ "!DROP FUNCTION " + getnodecount + ";!CREATE FUNCTION " + getnodecount + "() RETURNS BIGINT PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.getNodeCount'"
+ ";"
+ "!DROP PROCEDURE "+removerdbc+";!CREATE PROCEDURE "+removerdbc+"(connectionID "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.removeRDBConnection'"
+ ";"
+ "!DROP PROCEDURE "+removelt+";!CREATE PROCEDURE "+removelt+"(LTNAME "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA MODIFIES SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.removeLogicalTable'"
+ ";"
+ "!DROP PROCEDURE "+removeds+";!CREATE PROCEDURE "+removeds+"(dsID "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.removeDataSource'"
+ ";"
+ "!DROP PROCEDURE "+gconnect+";!CREATE PROCEDURE "+gconnect+"(connectionID "+XSTR+", host "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.gaianConnect'"
+ ";"
+ "!DROP PROCEDURE "+gconnectx+";!CREATE PROCEDURE"
+ " "+gconnectx+"(connectionID "+XSTR+", host "+XSTR+", port int, usr "+XSTR+", pwd "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.gaianConnect'"
+ ";"
+ "!DROP PROCEDURE "+gdisconnect+";!CREATE PROCEDURE "+gdisconnect+"(connectionID "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.gaianDisconnect'"
// + ";"
// + "!DROP PROCEDURE "+setgaiancred+";!CREATE PROCEDURE "+setgaiancred+"(usr "+XSTR+", pwd "+XSTR+")"
// + " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setGaianCred'"
+ ";"
+ "!DROP PROCEDURE "+setminconnections+";!CREATE PROCEDURE "+setminconnections+"(numConnections int)"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setNumConnectionsSought'"
+ ";"
+ "!DROP PROCEDURE "+setaccessclusters+";!CREATE PROCEDURE "+setaccessclusters+"(clustersCSV "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setAccessClusters'"
+ ";"
+ "!DROP PROCEDURE "+setdiscoveryhosts+";!CREATE PROCEDURE "+setdiscoveryhosts+"(hostsCSV "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setDiscoveryHosts'"
+ ";"
+ "!DROP PROCEDURE "+setdiscoveryip+";!CREATE PROCEDURE "+setdiscoveryip+"(ipAddress "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setDiscoveryIP'"
+ ";"
+ "!DROP PROCEDURE "+setmaxpropagation+";!CREATE PROCEDURE "+setmaxpropagation+"(depth int)"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setMaxPropagation'"
+ ";"
+ "!DROP PROCEDURE "+setmaxpoolsizes+";!CREATE PROCEDURE "+setmaxpoolsizes+"(size int)"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setMaxPoolsizes'"
+ ";"
+ "!DROP PROCEDURE "+setloglevel+";!CREATE PROCEDURE "+setloglevel+"(level "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setLogLevel'"
+ ";"
+ "!DROP PROCEDURE "+setsourcelist+";!CREATE PROCEDURE "+setsourcelist+"(listID "+XSTR+", sourcesCSV "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setSourceList'"
+ ";"
+ "!DROP PROCEDURE "+setmsgbrokerdetails+";!CREATE PROCEDURE "+setmsgbrokerdetails+"(host "+XSTR+", port int, topic "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setMsgBrokerDetails'"
+ ";"
+ "!DROP PROCEDURE "+setconfigproperty+";!CREATE PROCEDURE "+setconfigproperty+"(propertyName "+XSTR+", value "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setConfigProperty'"
+ ";"
+ "!DROP PROCEDURE "+setConfigProperties+";!CREATE PROCEDURE "+setConfigProperties+"(SQLQUERY_RETURNING_PROPERTY_KEYS_AND_VALUES "+XSTR+")"
+ " PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setConfigProperties'"
+ ";"
+ "!DROP FUNCTION INTERNALDIAGS;!CREATE FUNCTION INTERNALDIAGS(VarName "+XSTR+") RETURNS " + XSTR
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.internalDiags'"
+ ";" // Cancel query
+ "!DROP PROCEDURE "+cancelquery+";!CREATE PROCEDURE "+cancelquery+"(QUERYID "+XSTR+") PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.cancelQuery'"
+ ";" // Kill this GaianDB node
+ "!DROP FUNCTION "+gkill+";!CREATE FUNCTION "+gkill+"(CODE INT) RETURNS INT PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianNode.stop'"
+ ";" // Kill a set of GaianDB nodes
+ "!DROP PROCEDURE "+gkillnodes+";!CREATE PROCEDURE "+gkillnodes+"(NODELIST " + XSTR + ") PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.killNodes'"
+ ";" // Kill all GaianDB nodes in network
+ "!DROP PROCEDURE "+gkillall+";!CREATE PROCEDURE "+gkillall+"(CODE INT) PARAMETER STYLE JAVA LANGUAGE JAVA READS SQL DATA"
+ " EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.killAll'"
+ ";" // Get the run status of the node
+ "!DROP FUNCTION GDBRUNSTATUS;!CREATE FUNCTION GDBRUNSTATUS() RETURNS " + SSTR
+ " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.getNodeRunStatus'"
;
// Incomplete batch-update implementation - Using setConfigProperties() instead for now...
// + ";" // Enable auto-commit of APIs
// + "!DROP PROCEDURE SETCOMMITAPIS_ON;!CREATE PROCEDURE SETCOMMITAPIS_ON()"
// + " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setCommitAPIsOn'"
// + ";" // Disable auto-commit of APIs
// + "!DROP PROCEDURE SETCOMMITAPIS_OFF;!CREATE PROCEDURE SETCOMMITAPIS_OFF()"
// + " PARAMETER STYLE JAVA LANGUAGE JAVA NO SQL EXTERNAL NAME 'com.ibm.gaiandb.GaianDBConfigProcedures.setCommitAPIsOff'"
// ;
/**
* Returns result rows for any config request string (e.g. LTDEFS), according to corresponding table def schema.
*
* @param request
* @return
* @throws Exception
*/
public static DataValueDescriptor[][] getResultSetForUtilityRequest( String request ) throws Exception {
return getResultSetForUtilityRequest(request, null);
}
/**
* Returns result rows for any config request string (e.g. LTDEFS), according to corresponding table def schema.
*
* @param request
* @param additionalParams - String - CSV list of additional parameters for the request. Null if none.
* @return
* @throws Exception
*/
public static DataValueDescriptor[][] getResultSetForUtilityRequest( String request, String additionalParams ) throws Exception {
DataValueDescriptor[][] dvdrs = null;
if ( LTDEFS.equals(request) ) {
/*
* Check the parameters to see if constant values should be included.
*/
boolean includeConstantValues = false;
String[] splitParams = Util.splitByCommas(additionalParams);
if(splitParams.length > 0) {
includeConstantValues = Boolean.parseBoolean(splitParams[0]);
}
synchronized ( DataSourcesManager.class ) {
Set<String> lts = DataSourcesManager.getLogicalTableNamesLoaded();
int len = lts.size();
dvdrs = new DataValueDescriptor[len][2];
// System.out.println("lt names: " + lts);
Iterator<String> iter = lts.iterator();
for ( int i=0; iter.hasNext(); i++ ) {
String ltname = (String) iter.next();
dvdrs[i][0] = new SQLChar( ltname );
GaianResultSetMetaData grsmd = DataSourcesManager.getLogicalTableRSMD(ltname);
if(includeConstantValues) {
dvdrs[i][1] = new SQLChar( null==grsmd ? null : grsmd.getColumnsDefinitionExcludingHiddenOnes() );
}
else {
dvdrs[i][1] = new SQLChar( null==grsmd ? null : grsmd.getColumnsDefinitionExcludingHiddenOnesAndConstantValues() );
}
}
}
} else if ( RDBCONNECTIONS.equals(request) ) {
// "CID " + TSTR + ", CNODEID " + TSTR + ", POOLSIZE " + INT + ", CUSR " + TSTR + ", CURL " + MSTR + ", CDRIVER " + SSTR,
synchronized ( DataSourcesManager.class ) {
// Set<String> allReferencedJDBCConnectionIDs = new HashSet<String>( getAllSourceListsConnectionsIDs() );
// allReferencedJDBCConnectionIDs.addAll( getGaianConnectionsAsSet() );
// String[] allcs = getAllUserPropertyValuesForKeysWithSuffix(CONNECTION_SUFFIX);
// for (int i=0; i<allcs.length; i++) allcs[i] = allcs[i].split(" ")[0];
// allReferencedJDBCConnectionIDs.addAll( Arrays.asList( allcs ) );
// allReferencedJDBCConnectionIDs.add( LOCALDERBY_CONNECTION );
//
// Set<String> loadedConnections = DataSourcesManager.getLoadedRDBSourceHandles();
//// System.out.println("Getting cdetails for: " + loadedConnections );
Set<String> allReferencedJDBCConnectionIDs = new HashSet<String>( lookupAllRegisteredCIDs().values() );
allReferencedJDBCConnectionIDs.add( LOCALDERBY_CONNECTION );
ArrayList<DataValueDescriptor[]> rdbcs = new ArrayList<DataValueDescriptor[]>();
Iterator<String> iter = allReferencedJDBCConnectionIDs.iterator();
while ( iter.hasNext() ) {
for ( String cid : Util.splitByCommas( iter.next() ) ) {
String cdetails = null;
try { cdetails = getRDBConnectionDetailsAsString(cid); }
catch (Exception e) { continue; } // ignore/skip - warning is logged at load time
// if ( loadedConnections.contains(cdetails) ) {
DataValueDescriptor[] dvdr = new DataValueDescriptor[6];
String[] ctokens = getConnectionTokens(cdetails);
dvdr[0] = new SQLChar( cid );
dvdr[1] = new SQLChar( getDiscoveredNodeID(cid) );
dvdr[2] = new SQLInteger( ((RecallingStack<Object>)DataSourcesManager.getSourceHandlesPool( cdetails )).getMaxSize() );
dvdr[3] = new SQLChar( ctokens[0] );
dvdr[4] = new SQLChar( ctokens[1] );
dvdr[5] = new SQLChar( ctokens[2] );
// System.out.println("Returning row: " + Arrays.asList( dvdr ) );
rdbcs.add(dvdr);
// }
}
}
dvdrs = rdbcs.toArray( new DataValueDescriptor[0][] );
}
} else if ( DATASOURCES.equals(request) ) {
// "DSID " + TSTR + ", DSTYPE CHAR, DSWRAPPER " + TSTR + ", DSHANDLE " + TSTR + ", DSOPTIONS " + SSTR,
// DSTYPE is R, V or G for: RDB, VTI and Gaian, DSWRAPPER is e.g. "FileImport" or "C1",
// DSHANDLE is e.g. filename or rdb url and tablename, DSOPTIONS is e.g. "InMemory"
synchronized ( DataSourcesManager.class ) {
Set<String> lts = DataSourcesManager.getLogicalTableNamesLoaded();
ArrayList<DataValueDescriptor[]> dataSources = new ArrayList<DataValueDescriptor[]>();
Iterator<String> iter = lts.iterator();
while( iter.hasNext() ) {
String ltName = (String) iter.next();
VTIWrapper[] vtis = DataSourcesManager.getDataSources(ltName);
for ( int i=0; i<vtis.length; i++ ) {
DataValueDescriptor[] dvdr = new DataValueDescriptor[6];
VTIWrapper vti = vtis[i];
if ( null == vti ) {
logger.logWarning(GDBMessages.CONFIG_LT_DS_NULL,
"Detected (and ignoring) null data source for LT: " + ltName);
continue;
}
// No need to show a gaian data source for every table - these can be deduced with listrdbc()
if ( vti.isGaianNode() ) continue;
boolean isRDB = vti instanceof VTIRDBResult;
dvdr[0] = new SQLChar( vti.nodeDefName );
dvdr[1] = new SQLChar( isRDB ? vti.isGaianNode() ? "G" : "R" : "V" );
dvdr[2] = new SQLChar( vti.getSourceHandlesSnapshotInfo() );
dvdr[3] = new SQLChar( vti.getSourceDescription(null) ); // Use 'null' to report argument in config (which may contain a wildcard)
dvdr[4] = new SQLChar( getUserProperty( vti.nodeDefName + OPTIONS_SUFFIX ) );
dvdr[5] = new SQLChar( getNodeDefRDBMSConnectionID( vti.nodeDefName ) );
dataSources.add( dvdr );
}
}
dvdrs = dataSources.toArray( new DataValueDescriptor[0][] );
}
} else if ( FULLCONFIG.equals(request) ) {
Properties allProps = new Properties();
synchronized ( DataSourcesManager.class ) {
allProps.putAll( upr );
allProps.putAll( spr );
}
for ( String[] propMap : getAllDefaultProperties() )
if ( !allProps.containsKey(propMap[0]) )
allProps.put("#" + propMap[0], propMap[1]); // put this in as a commented property to indicate it is the default value
int len = allProps.size();
dvdrs = new DataValueDescriptor[len][2];
Iterator<Object> iter = allProps.keySet().iterator();
for ( int i=0; iter.hasNext(); i++ ) {
String pr = (String) iter.next();
dvdrs[i][0] = new SQLChar( pr );
dvdrs[i][1] = new SQLChar( pr.toUpperCase().endsWith("_PWD") ? "<pwd>" : (String) allProps.get(pr) );
}
} else if ( LISTQUERIES.equals(request) ) {
// returns: QUERYID SSTR, DEPTH INT, STATE TSTR, LTNAME TSTR, QUERYHASH TSTR, START_TS TIMESTAMP, QUERY_MS BIGINT, NBROWS BIGINT, GDB_WID SSTR
Set<GaianResult> gResults = GaianTable.getGresults();
synchronized( gResults ) {
dvdrs = new DataValueDescriptor[gResults.size()][9];
// EXECUTE, FETCH, RE-FETCH, COMPLETE
// if queryTime is set, state is re-fetch or complete
// if fetchStartTime is not set, state is execute
// if re-fetching, re-fetch iteration is > 0 in GaianTable
Iterator<GaianResult> iter = gResults.iterator();
for ( int i=0; iter.hasNext(); i++ ) {
GaianResult gr = iter.next();
long startTime = gr.getQueryStartTime();
long queryTime = gr.getQueryTime();
boolean isActive = -1==queryTime;
boolean isAwaitingRefetch = gr.isAwaitingRefetch();
dvdrs[i][0] = new SQLChar( gr.getQueryID() );
dvdrs[i][1] = new SQLInteger( gr.getQueryDepth() );
dvdrs[i][2] = new SQLChar( isActive ?
gr.isExecuting() ? "EXECUTE" : "FETCH" : isAwaitingRefetch ? "RE-FETCH" : "CACHED" );
dvdrs[i][3] = new SQLChar( gr.getLTName() );
dvdrs[i][4] = new SQLChar( gr.getQueryHash() );
dvdrs[i][5] = new SQLTimestamp( new Timestamp(startTime) );
dvdrs[i][6] = new SQLLongint( isActive || isAwaitingRefetch ?
System.currentTimeMillis() - startTime : gr.getQueryTimeIncludingRefetch() );
dvdrs[i][7] = new SQLLongint( gr.getRowCount() );
dvdrs[i][8] = new SQLChar( gr.getWID() );
}
}
} else if ( USERWARNING.equals(request) ) {
// returns: TSTAMP TIMESTAMP, WARNING MSTR, TSTAMPFIRST TIMESTAMP, REPEAT_COUNT BIGINT
String[] warnings = Logger.getLatestWarnings();
dvdrs = new DataValueDescriptor[warnings.length][4];
for ( int i=0; i<warnings.length; i++ ) {
String w = warnings[i];
// int idx = w.indexOf('#');
// dvdrs[i][0] = new SQLChar( w.substring(0, idx) );
// dvdrs[i][1] = new SQLChar( w.substring(idx+1) );
int idx1 = w.indexOf('x'), idx2 = w.indexOf('~'), idx3 = w.indexOf('#');
final String mostRecentTs = w.substring(0, idx1);
final int repeatCount = Integer.parseInt( w.substring(idx1+1, idx2) );
final String firstOccurenceTs = w.substring(idx2+1, idx3);
final String msg = w.substring(idx3+1);
dvdrs[i][0] = new SQLChar( mostRecentTs );
dvdrs[i][1] = new SQLChar( msg );
dvdrs[i][2] = new SQLChar( firstOccurenceTs );
dvdrs[i][3] = new SQLLongint( repeatCount );
}
} else if ( API.equals(request) ) {
String[] apicmds = Util.splitByTrimmedDelimiter( GAIANDB_API, ';' );
dvdrs = new DataValueDescriptor[apicmds.length][1];
for ( int i=0; i<apicmds.length; i++ )
dvdrs[i][0] = new SQLChar(apicmds[i]);
// } else if ( PUBLICKEY.equals(request) ) {
//
// String[] apicmds = Util.splitByTrimmedDelimiter( GAIANDB_API, ';' );
// dvdrs = new DataValueDescriptor[apicmds.length][1];
//
// for ( int i=0; i<apicmds.length; i++ )
// dvdrs[i][0] = new SQLChar(apicmds[i]);
} else if ( request.startsWith(FILESTATS) ) {
// Returns: Name, Modified, Size, Checksum
dvdrs = new DataValueDescriptor[1][4];
String[] toks = Util.splitByCommas(request);
if ( 1 > toks.length ) throw new Exception("File path argument not found, required syntax is: '" + FILESTATS + ",<file path>'");
File f = new File( toks[1] );
dvdrs[0][0] = new SQLChar( f.getName() );
dvdrs[0][1] = new SQLLongint( f.lastModified() ); //new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format( new Date(f.lastModified()) ) );
dvdrs[0][2] = new SQLLongint( f.length() );
dvdrs[0][3] = new SQLBit( SecurityManager.getChecksumSHA1( readAndZipFileBytes(f) ) );
}
return dvdrs;
}
/**
* Stored function used to check if a connection is bi-directional.
* If not, we try to re-establish the link.
* This is the old definition for backwards compatibility.
* It does not have the extraInfo which passes information about the foreign node and helps to indicate the expected structure
* of the return message.
*
* @param foreignNode
* @return null if the connection is established, string indicating reason otherwise
*/
public static String maintainConnection( String senderNodeID, String usr, String scrambledpwd ) {
return maintainConnection2( senderNodeID, usr, scrambledpwd, null );
}
/**
* New version of maintainConnection() - for V1.04 and V2.0 and above.
*
* @param senderNodeID
* @param usr
* @param scrambledpwd
* @param extraInfo
* @return message indicating success or error info - If the version is null then success is indicated using a null response
*/
public static String maintainConnection2( String senderNodeID, String usr, String scrambledpwd, String extraInfo ) {
return GaianNodeSeeker.maintainConnection(senderNodeID, usr, scrambledpwd, extraInfo);
}
private static final Set<String> triggerEvents = new HashSet<String>();
public static int setTriggerEvent( String evt ) {
System.out.println("GDB Trigger event: " + evt);
triggerEvents.add(evt);
return 1;
}
public static boolean checkAndClearTriggerEvent( String evt ) {
return triggerEvents.remove(evt);
}
private static String constructWhereClauseForNodes( String nodelist ) {
Set<String> nodes = new HashSet<String> ( Arrays.asList( Util.splitByCommas(nodelist) ));
if ( nodes.isEmpty() ) return "";
StringBuffer nlist = new StringBuffer("where GDB_NODE<'!'");
for ( String s : nodes ) nlist.append(" OR GDB_NODE='"+s+"'");
return nlist.toString();
}
public static void cancelQuery( String queryID ) throws Exception {
apiStart(cancelquery, Arrays.asList(queryID));
if ( null == queryID || 0 == queryID.length() ) return;
boolean isPropagated = '!' == queryID.charAt(0);
if ( isPropagated ) {
queryID = queryID.substring(1);
boolean isFoundAndCancelled = GaianTable.cancelQuery(queryID);
logger.logInfo("Cancel request for queryID: " + queryID + ", foundAndCancelled = " + isFoundAndCancelled);
return;
}
// distribute the cancel instruction
ResultSet rs;
rs = getResultSetFromQueryAgainstDefaultConnection(
"select * from new com.ibm.db2j.GaianQuery('call "+cancelquery+"(''!"+queryID+"'')') GT" );
while( rs.next() ); // wait until all queries have been completely processed before returning // $codepro.audit.disable emptyStatement
rs.getStatement().getConnection().close();
}
public static void killNodes( String nodelist ) throws Exception {
apiStart(gkill, Arrays.asList(nodelist));
ResultSet rs;
rs = getResultSetFromQueryAgainstDefaultConnection(
"select 1 from new com.ibm.db2j.GaianQuery('select GKILL() from sysibm.sysdummy1', " +
"'with_provenance, maxDepth=-1') GT " + constructWhereClauseForNodes(nodelist) );
while( rs.next() ); // wait until all Kill commands have been completely processed before returning // $codepro.audit.disable emptyStatement
rs.getStatement().getConnection().close();
}
public static void killAll(int code) throws Exception {
apiStart(gkillall, Arrays.asList(code+""));
ResultSet rs;
rs = getResultSetFromQueryAgainstDefaultConnection(
"select 1 from new com.ibm.db2j.GaianQuery('select GKILL("+code+") from sysibm.sysdummy1', 'maxDepth=-1') GT");
while( rs.next() );// wait until all "GKILL" queries have been completely processed before returning
rs.getStatement().getConnection().close();
}
public static short matchTableDefinitions( String def1, String def2 ) throws Exception {
return (short) ( new GaianResultSetMetaData(def1).matchupWithTableDefinition(def2, false) ? 1 : 0 );
}
// GAIANDB config procedures
public static void listStoredProceduresAndFunctions( ResultSet[] storedProcFuncNames ) throws Exception {
apiStart(listspfs);
storedProcFuncNames[0] = getResultSetFromQueryAgainstDefaultConnection(
"select cast(alias as " + TSTR + ") alias, aliastype, jreplace( cast(aliasinfo as " + LSTR + "), ' LANGUAGE JAVA .*', '' ) aliasinfo from " +
"sys.sysaliases a, sys.sysschemas s where a.schemaid=s.schemaid and s.schemaname = upper('" + getGaianNodeUser() + "') order by alias"
);
storedProcFuncNames[0].getStatement().getConnection().close();
}
public static void listExplainGaianTableOrQuery( String sql, ResultSet[] tables ) throws Exception {
apiStart(listexplain);
tables[0] = getExplainGaianTableOrQuery( sql );
tables[0].getStatement().getConnection().close();
}
public static ResultSet getExplainGaianTableOrQuery( String sql ) throws Exception {
// Pick off the first 7 chars to see if the sql starts with 'SELECT', otherwise it is a logical table name.
String first7chars = sql.length() > 7 ? sql.trim().substring(0, 7).toUpperCase().replaceAll("\\s", " ") : null;
if ( null == first7chars || ( ! first7chars.equals("SELECT ") && ! first7chars.equals("VALUES ") ))
return getResultSetFromQueryAgainstDefaultConnection(
"select " + EXPLAIN_FROM + ", " + EXPLAIN_TO + ", " + EXPLAIN_DEPTH + ", " + EXPLAIN_PRECEDENCE + ", " + EXPLAIN_COUNT +
" from new com.ibm.db2j.GaianTable('" + sql + "', 'explain in graph.dot') GT"
);
return getResultSetFromQueryAgainstDefaultConnection(
"select " + EXPLAIN_FROM + ", " + EXPLAIN_TO + ", " + EXPLAIN_DEPTH + ", " + EXPLAIN_PRECEDENCE + ", " + EXPLAIN_COUNT +
" from new com.ibm.db2j.GaianQuery('" + Util.escapeSingleQuotes(sql) + "', 'explain in graph.dot') GQ"
);
}
public static ResultSet getGaianNodes() throws Exception {
apiStart(getnodes);
return getResultSetFromQueryAgainstDefaultConnection(
"SELECT DISTINCT gdbx_to_node node FROM new com.ibm.db2j.GaianTable('gdb_ltnull', 'explain') T order by node"
);
}
public static void listGaianNodes( ResultSet[] tables ) throws Exception {
apiStart(listnodes);
tables[0] = getGaianNodes();
tables[0].getStatement().getConnection().close();
}
public static long getNodeCount() throws Exception {
apiStart(getnodecount);
ResultSet rs = getResultSetFromQueryAgainstDefaultConnection(
"SELECT count(distinct gdbx_to_node) FROM new com.ibm.db2j.GaianTable('gdb_ltnull', 'explain') T"
);
if ( !rs.next() )
throw new Exception("Unable to compute node count, ResultSet empty!");
long rc = rs.getLong(1); rs.getStatement().getConnection().close(); return rc;
}
public static void listFlood( ResultSet[] tables ) throws Exception {
apiStart(listflood);
tables[0] = getResultSetFromQueryAgainstDefaultConnection(
"select " + EXPLAIN_FROM + ", " + EXPLAIN_TO + ", " + EXPLAIN_DEPTH + ", " + EXPLAIN_PRECEDENCE +
" from new com.ibm.db2j.GaianTable('gdb_ltnull', 'explain, maxDepth=-1') GT" // where " + EXPLAIN_DEPTH + ">0"
);
tables[0].getStatement().getConnection().close();
}
public static void logTail( String nodelist, int numlines, ResultSet[] tables ) throws Exception {
apiStart(logtail);
String wc = "*".equals(nodelist) ? "*" : constructWhereClauseForNodes(nodelist);
tables[0] = getResultSetFromQueryAgainstDefaultConnection(
"select " + GDB_NODE + ", line, log from new com.ibm.db2j.GaianQuery(" +
"'select * from ( select row_number() over () line, column1 log from new com.ibm.db2j.GaianTable(''GDB_LTLOG'', ''maxDepth=0'') GT ) SQ', " +
"'with_provenance, order by line desc fetch first " + numlines + " rows only" + (0==wc.length()?", maxDepth=0":"") + "') GQ" +
(wc.equals("*") ? "" : " "+wc) + " order by gdb_node, line"
);
// Alternative query:
// select * from ( select row_number() over () line, column1 log from gdb_ltlog_0 ) S
// where line+51 > (
// select count(1) from new com.ibm.db2j.GaianTable('gdb_ltlog','maxDepth=0') T
// )
tables[0].getStatement().getConnection().close();
}
public static void listLogicalTables( ResultSet[] tables ) throws Exception {
apiStart(listlts);
tables[0] = getLogicalTables();
tables[0].getStatement().getConnection().close();
}
// Really what we want is listmatchinglts, with counts for all that loosely match the local ones (i.e. they may possibly have more or less cols)
// and listdistinctlts, which show all definitions which are not *exactly* the same (apart from col ordering, which may always be different)
public static void listLogicalTableMatches( ResultSet[] tables ) throws Exception {
apiStart(listltmatches);
tables[0] = getResultSetFromQueryAgainstDefaultConnection(
// "select ltname, '" + getGaianNodeID() + "' nodeid, ltdef from new com.ibm.db2j.GaianConfig('LTDEFS') t UNION ALL " +
// "select distinct b.ltname, b.nodeid, b.ltdef from new com.ibm.db2j.GaianConfig('LTDEFS') a, table(getlts()) b where " +
// "b.ltname not in ( select ltname from new com.ibm.db2j.GaianConfig('LTDEFS') t ) OR " +
// "a.ltname=b.ltname AND LTMATCH(a.ltdef, b.ltdef) = 0 order by ltname, nodeid"
// "select * from ( " +
// "select ltname, 1 count, '" + getGaianNodeID() + "' nodeid, ltdef from new com.ibm.db2j.GaianConfig('LTDEFS') t1 UNION ALL " +
// "select ltname, 1 count, nodeid, ltdef from table(getlts()) t2" +
// ") s1 where ltname not in ( " +
// "select ltname from new com.ibm.db2j.GaianConfig('LTDEFS') t3 INTERSECT " +
// "select ltname from table(getlts()) t4) " +
// "UNION ALL " +
// "select distinct b.ltname, 1 count, b.nodeid, b.ltdef from new com.ibm.db2j.GaianConfig('LTDEFS') a, table(getlts()) b where " +
// "a.ltname=b.ltname AND LTMATCH(a.ltdef, b.ltdef) = 0 " +
// "UNION ALL " +
// "select distinct c.ltname, count(1) count, max('" + getGaianNodeID() + "') nodeid, c.ltdef from " +
// "new com.ibm.db2j.GaianConfig('LTDEFS') c, table(getlts()) d where " +
// "c.ltname=d.ltname AND LTMATCH(c.ltdef, d.ltdef) = 1 group by c.ltname, c.ltdef order by ltname, nodeid"
"select a.ltname, count(1) matches, a.nodeid, a.ltdef from table(getlts()) a, table(getlts()) b " +
"where a.ltname = b.ltname and LTMATCH( a.ltdef, b.ltdef ) = 1 " +
"group by a.ltname, a.nodeid, a.ltdef order by ltname, matches, nodeid"
);
tables[0].getStatement().getConnection().close();
}
// Get the number of logical tables for this Gaian Node
// private static int countLogicalTables() throws Exception {
// int count = 0;
// ResultSet rs = getResultSetFromQueryAgainstDefaultConnection("select count(*) from new com.ibm.db2j.GaianQuery(" +
// "'select * from new com.ibm.db2j.GaianConfig(''" + LTDEFS + "'') GC') GQ ");
// while (rs.next()) {
// count = rs.getInt(1);
// }
// return count;
// }
//
// private static int countLogicalTables(String nodeid) throws Exception {
// int count = 0;
// ResultSet rs = getResultSetFromQueryAgainstDefaultConnection("select count(*) from new com.ibm.db2j.GaianQuery(" +
// "'select * from new com.ibm.db2j.GaianConfig(''" + LTDEFS + "'') GC') GQ where " + GDB_NODE + "='" + nodeid + "'");
// while (rs.next()) {
// count = rs.getInt(1);
// }
// return count;
// }
public static ResultSet getLogicalTables() throws Exception {
return getResultSetFromQueryAgainstDefaultConnection(
"select " + GDB_NODE + ", " + LTNAME + ", " + LTDEF + " from new com.ibm.db2j.GaianQuery(" +
"'select * from new com.ibm.db2j.GaianConfig(''" + LTDEFS + "'') GC', 'with_provenance') GQ " +
"order by " + GDB_NODE + ", " + LTNAME
);
}
public static ResultSet getLogicalTables( String tname ) throws Exception {
return getResultSetFromQueryAgainstDefaultConnection(
"select " + GDB_NODE + ", " + LTDEF + " from new com.ibm.db2j.GaianQuery(" +
"'select " + LTDEF + " from new com.ibm.db2j.GaianConfig(''" + LTDEFS + "'') GC where " + LTNAME + "=''" + tname + "''', 'with_provenance') GQ " +
"order by " + GDB_NODE
);
}
public static ResultSet getLogicalTable( String tname, String nodeid ) throws Exception {
return getResultSetFromQueryAgainstDefaultConnection(
"select " + LTDEF + " from new com.ibm.db2j.GaianQuery(" +
"'select " + LTDEF + " from new com.ibm.db2j.GaianConfig(''" + LTDEFS + "'') GC where " + LTNAME + "=''" + tname + "''', 'with_provenance') GQ " +
"where " + GDB_NODE + "='" + nodeid + "'"
);
}
public static ResultSet getLogicalTable( String tname, String nodeid, String additionalParams ) throws Exception {
return getResultSetFromQueryAgainstDefaultConnection(
"select " + LTDEF + " from new com.ibm.db2j.GaianQuery(" +
"'select " + LTDEF + " from new com.ibm.db2j.GaianConfig(''" + LTDEFS + "'', ''" + additionalParams + "'') GC where " + LTNAME + "=''" + tname + "''', 'with_provenance') GQ " +
"where " + GDB_NODE + "='" + nodeid + "'"
);
}
public static void listDerbyTables( ResultSet[] tables ) throws Exception {
apiStart(listderbytables);
tables[0] = getResultSetFromQueryAgainstDefaultConnection(
// "select tablename from sys.systables"
"select distinct " + GDB_NODE + ", tabname derbytable from " +
"new com.ibm.db2j.GaianTable('DERBY_TABLES', 'with_provenance') T where tabtype = 'T'" +
"order by " + GDB_NODE + ", derbytable"
);
tables[0].getStatement().getConnection().close();
}
public static void listRDBConnections( ResultSet[] rs ) throws Exception {
apiStart(listrdbc);
rs[0] = getResultSetFromQueryAgainstDefaultConnection(
"select " + GDB_NODE + ", " + CID + ", " + CNODEID + ", " + CPOOLSIZE + ", " + CDRIVER + ", " + CURL + ", " + CUSR +
" from new com.ibm.db2j.GaianQuery(" +
"'select * from new com.ibm.db2j.GaianConfig(''" + RDBCONNECTIONS + "'') GC', 'with_provenance') GQ " +
" order by " + GDB_NODE + ", " + CNODEID + ", " + CID
);
rs[0].getStatement().getConnection().close();
}
public static void listDataSources( ResultSet[] rs ) throws Exception {
apiStart(listds);
rs[0] = getResultSetFromQueryAgainstDefaultConnection(
"select " + GDB_NODE + ", " + DSID + ", " + DSTYPE + ", " + DSWRAPPER + ", " +
DSHANDLE + ", " + DSOPTIONS + ", " + DSCID + " from new com.ibm.db2j.GaianQuery(" +
"'select * from new com.ibm.db2j.GaianConfig(''" + DATASOURCES + "'') GC', 'with_provenance') GQ" +
" order by " + GDB_NODE + ", " + DSID
);
rs[0].getStatement().getConnection().close();
}
public static void listQueries( int maxDepth, ResultSet[] rs ) throws Exception {
apiStart(listqueries, Arrays.asList(maxDepth+""));
rs[0] = getResultSetFromQueryAgainstDefaultConnection(
"select " + GDB_NODE + ", " + QUERYID + ", " + DEPTH + ", " + STATE + ", " + LTNAME + ", " + QUERYHASH
+ ", " + START_TS + ", " + QUERY_MS + ", " + NBROWS + ", " + GDB_WID + " from new com.ibm.db2j.GaianQuery(" +
"'select * from new com.ibm.db2j.GaianConfig(''" + LISTQUERIES + "'') GC', 'with_provenance, maxDepth="+maxDepth+"') GQ" +
// " order by START_TS desc"
" order by jreplace("+QUERYID+",'.*:([\\d]+:[\\d]+)','$1') desc, " + DEPTH
// Note ordering by the timestamp+seqID of the queryid itself means that exec units on different nodes for the same query are grouped together
);
rs[0].getStatement().getConnection().close();
}
public static void listConfig( ResultSet[] rs ) throws Exception {
apiStart(listconfig);
rs[0] = getResultSetFromQueryAgainstDefaultConnection(
"select " + GDB_NODE + ", " + PROPID + ", " + PROPDEF + " from new com.ibm.db2j.GaianQuery(" +
"'select * from new com.ibm.db2j.GaianConfig(''" + FULLCONFIG + "'') GC', 'with_provenance') GQ" +
" order by " + GDB_NODE + ", " + PROPID
);
rs[0].getStatement().getConnection().close();
}
/**
* Retrieves a config property from GaianDB.
* Note:
* If the 1st character of the propertyKey passed in is an '=' sign then this character is removed to constitute the key
* and then if this shortened key property is not set to a value in GaianDB, then this function will attempt to retrieve the default
* value for this property - which should be the "active" value of the property.
*
* @param propertyKey: Any GaianDB config property. This may be preceded by an '=' sign to resolve a default value in case it was not set.
* @return
* @throws Exception
*/
public static String getConfigProperty( String propertyKey ) throws Exception {
boolean isResolve = null != propertyKey && 0 < propertyKey.length() && '=' == propertyKey.charAt(0);
if ( isResolve ) propertyKey = propertyKey.substring(1);
String result = GaianDBConfig.getCrossOverProperty( propertyKey );
if ( !isResolve || null != result ) return result;
// result is null and we want to resolve a default value
for ( String[] propMap : getAllDefaultProperties() )
if ( propMap[0].equals( propertyKey ) ) {
result = propMap[1];
return '<' == result.charAt(0) ? null : result;
}
return null;
}
public static byte[] getPublicKey() throws Exception {
apiStart(gpublickey);
return SecurityManager.getPublicKey();
}
public static Map<String, String> prepareLogicalTable( String ltName, String ltDef, String ltConstantColsDef ) throws Exception {
// Don't worry about possible conflicting properties - there shouldn't be any possible conflicts...
// (however any existing properties ending ltName+_DS... will be deleted) .. users should not be creating such properties anyway.
// // Check if the string tableName is already a prefix of some property and is not already defined as a logical table name
// if ( null == getUserProperty(ltName + LTDEF_SUFFIX) ) {
// String[] prs = getAllUserPropertiesWithPrefix(ltName);
// if ( 0 != prs.length ) {
// String msg = "Cannot set logical table " + ltName + " due to conflicting properties: " + Arrays.asList(prs);
// logger.logWarning(msg);
// throw new Exception(msg);
// }
// }
// Construct and apply the new meta data - thereby validating the syntax
// Note that this definition won't be proper because it doesnt include node constants and provenance/explain cols, but that
// is ok because it will never be used as the logical table won't be fully registered until the config file has been
// reloaded (specifically, dsArrays in DataSourcesManager only gets set then).
new GaianResultSetMetaData( ltDef, ltConstantColsDef );
// DataSourcesManager.setLogicalTableRSMD( ltName, new GaianResultSetMetaData( ltDef, ltConstantColsDef ) );
// Clear and update logical table properties
Map<String, String> updates = generateNulledOutUpdatesForPropertiesPrefixed(ltName+"_DS"); //new LinkedHashMap<String, String>();
updates.put( ltName + LTDEF_SUFFIX, ltDef );
updates.put( ltName + CONSTANTS_SUFFIX, ltConstantColsDef);
return updates;
}
public static synchronized void setLogicalTable( String ltName, String ltDef, String ltConstantColsDef ) throws Exception {
apiStart(setlt, Arrays.asList(ltName, ltDef, ltConstantColsDef));
ltName = ltName.toUpperCase();
Map<String, String> updates = prepareLogicalTable(ltName, ltDef, ltConstantColsDef);
try {
persistAndApplyConfigUpdatesForSpecificLT(updates, ltName);
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
logger.logInfo("Completed checkUpdateLogicalTableViewsOnAllDBs()");
} catch ( Exception e ) { // Can't leave a bad def hanging due to the automated view creations for it
String errmsg = "Unable to set logical table: " + ltName + ": " + Util.getStackTraceDigest(e);
logger.logWarning(GDBMessages.CONFIG_SET_LT_ERROR, errmsg);
// try to clean up
try { removeLogicalTable(ltName); } catch (Exception e1) {}
throw e;
}
logger.logInfo("Exit setLogicalTable()");
// Note this new config will be reloaded again after this update. but the meta data object will
// not need reconstructing.
}
public static synchronized void setLogicalTableConstants( String ltName, String ltConstantColsDef ) throws Exception {
apiStart(setltconstants, Arrays.asList(ltName, ltConstantColsDef));
ltName = ltName.toUpperCase();
// Check if the string <ltName>_DEF is defined, meaning there is a logical table definition in place.
if ( null == getUserProperty(ltName + LTDEF_SUFFIX) ) {
String msg = "Cannot set constants for logical table " + ltName + " as it is not defined!";
logger.logWarning(GDBMessages.CONFIG_LT_SET_CONSTANTS_ERROR, msg);
throw new Exception(msg);
}
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put( ltName + CONSTANTS_SUFFIX, ltConstantColsDef );
persistAndApplyConfigUpdatesForSpecificLT(updates, ltName);
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
}
public static synchronized void removeLogicalTable( String ltName ) throws Exception {
apiStart(removelt, Arrays.asList(ltName));
ltName = ltName.toUpperCase();
if ( null == getUserProperty(ltName + LTDEF_SUFFIX) ) {
String msg = "Logical Table does not exist (nothing removed): " + ltName;
logger.logWarning(GDBMessages.CONFIG_LT_REMOVE_ERROR, msg);
throw new Exception(msg);
}
Map<String, String> updates = generateNulledOutUpdatesForPropertiesPrefixed(ltName+"_DS");
updates.put(ltName+LTDEF_SUFFIX, null);
if ( null != getUserProperty(ltName + CONSTANTS_SUFFIX) )
updates.put(ltName+CONSTANTS_SUFFIX, null);
persistAndApplyConfigUpdatesForSpecificLT(updates, ltName);
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
}
public static synchronized void setLogicalTableForNode( String ltName, String nodeID ) throws Exception {
apiStart(setltfornode, Arrays.asList(ltName, nodeID));
ltName = ltName.toUpperCase();
//Get logical table definition, with constant values
ResultSet rs = getLogicalTable( ltName, nodeID, "true" );
if ( ! rs.next() ) {
//Get logical table definition, withOUT constant values
rs = getLogicalTable( ltName, nodeID);
if ( ! rs.next() ) {
String msg = "Unable to find Logical Table " + ltName + " at Node " + nodeID;
logger.logWarning(GDBMessages.CONFIG_LT_SET_FOR_NODE_NULL_LT, msg);
throw new Exception(msg);
}
}
String ltDef = rs.getString(LTDEF);
String[] physicalAndConstantCols = getColumnsDefArray(ltDef.toUpperCase());
StringBuffer physicalColsDefSB = new StringBuffer();
for (int i=0; i<physicalAndConstantCols.length; i++) {
String[] tokens = Util.splitByTrimmedDelimiter( physicalAndConstantCols[i], ' ' );
logger.logInfo("Logical Table Def tokens are: " + Arrays.asList(tokens));
// Is this a constant column definition ? If so it will have 3 tokens.
if ( 2 < tokens.length ) {
logger.logWarning(GDBMessages.CONFIG_RT_COLUMN_CONSTANT_VALUE, "From 'setltfornode(" + ltName + ", " + nodeID + ")': The remote table column '" + tokens[0]
+ "' was a constant column with value '" + tokens[2] + "'. To replicate this locally, use API: 'call setltconstants('<ltName>' , '<ltconstants*>')'");
}
// new physical col
physicalColsDefSB.append( (0<i ? "," : "") + tokens[0] + " " + tokens[1]);
}
Map<String, String> updates = prepareLogicalTable(ltName, physicalColsDefSB.toString(), "");
persistAndApplyConfigUpdatesForSpecificLT(updates, ltName);
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
Statement s = rs.getStatement();
Connection c = s.getConnection();
s.close(); c.close();
}
public static synchronized void setLogicalTableForRDBTable( String ltName, String cid, String fromExpression ) throws Exception {
apiStart(setltforrdbtable, Arrays.asList(ltName, cid, fromExpression));
ltName = ltName.toUpperCase();
String cdef = null;
cid = cid.toUpperCase();
try { cdef = getRDBConnectionDetailsAsString( cid ); }
catch (Exception e) {
String errmsg = "Unable to resolve name as a Connection ID: " + cid + ": " + e;
logger.logWarning(GDBMessages.CONFIG_LT_SET_RDBT_ERROR, errmsg);
throw new Exception(errmsg);
}
Map<String, String> updates = null;
fromExpression = fromExpression.trim();
// If the expression is bracketed, then assume there is no where-clause. Otherwise, take the first token only
String ptName = fromExpression.startsWith("(") ? fromExpression : fromExpression.split("\\s")[0];
try {
RDBProvider provider = RDBProvider.fromGaianConnectionID(cdef);
logger.logInfo("Creating logical table " + ltName + " for RDBMS provider: " + provider);
ResultSetMetaData rsmd = getNewDBConnector( getConnectionTokens(cdef) ).getTableMetaData(ptName);
String tableDef = new GaianResultSetMetaData(rsmd, null, provider).getColumnsDefinition().toUpperCase();
logger.logInfo("Obtained tableDef for RDB table: " + tableDef);
updates = prepareLogicalTable(ltName, tableDef, "");
} catch ( Exception e ) {
String warning = "Failed to create Logical Table '" + ltName + "', cause: ";
logger.logException(GDBMessages.CONFIG_LT_CREATE_RDB_ERROR, warning, e);
throw new Exception(warning + e);
}
String nodeDefName = ltName + "_DS0"; // + ptName.toUpperCase();
Properties backup = null;
try {
// Now setup the data source - note stale properties (with ltName as their prefix) were checked for already
// when setting up the logical table.
// Validate that the jdbc connection is defined
getRDBConnectionDetailsAsString(cid);
updates.put(nodeDefName + CONNECTION_SUFFIX, cid + " " + fromExpression);
updates.put(nodeDefName + OPTIONS_SUFFIX, MAP_COLUMNS_BY_POSITION); // this will give more flexibility to change LT column names
// Apply the updates and load the data source (thus validating syntax aswell) before updating the config file
// Config file reload will still occur (for now) but will do minimal work.
backup = new Properties();
synchronized(upr) {
backup.putAll( upr );
/*
* Update upr with the new values.
* To avoid NPEs we iterate through the values & do null checks, rather than use putAll.
* Note: because upr is a Properties object (and extends Hashtable), it is automatically synchronized
*/
Set<Entry<String,String>> updatesEntries = updates.entrySet();
for (Entry<String, String> updateEntry : updatesEntries) {
if(updateEntry.getKey() != null) {
if(updateEntry.getValue() == null) {
upr.remove(updateEntry.getKey());
}
else {
upr.put( updateEntry.getKey(), updateEntry.getValue() );
}
}
}
}
// DataSourcesManager.loadDataSource( ltName, nodeDefName );
// DataSourcesManager.primeJDBCSourceHandlesPoolSynchronously( nodeDefName );
persistAndApplyConfigUpdatesForSpecificLT(updates, ltName);
} catch ( Exception e ) {
if ( null != backup ) { Properties tmp = upr; synchronized(upr) { upr = backup; } tmp.clear(); }
String warning = "Failed to create Data Source '" + nodeDefName + "', cause: ";
logger.logException(GDBMessages.CONFIG_LT_CREATE_DS_RDB_ERROR, warning, e);
throw new Exception(warning + e);
}
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
}
public static synchronized void setLogicalTableForFile( String ltName, String filePathTagged ) throws Exception {
if ( Util.isWindowsOS && null != filePathTagged ) filePathTagged = filePathTagged.replaceAll("\\\\", "/");
apiStart(setltforfile, Arrays.asList(ltName, filePathTagged));
ltName = ltName.toUpperCase();
Map<String, String> updates = null;
final String filePath = resolvePathTags( filePathTagged );
if ( !new File(filePath).exists() )
logger.logWarning(GDBMessages.CONFIG_LT_SET_FILE_NOT_FOUND, "API " + setltforfile + " warning: File not found: " + filePath);
try {
String tableDef = new GaianResultSetMetaData(new FileImport(filePath).getMetaData(), null).getColumnsDefinition();
logger.logInfo("Obtained tableDef for file: " + tableDef);
updates = prepareLogicalTable(ltName, tableDef, "");
} catch ( SQLException e ) {
System.out.println("Check File: " + filePath);
String warning = "Error while importing " + filePath + ", cause: ";
logger.logException(GDBMessages.CONFIG_LT_FILE_IMPORT_ERROR_SQL, warning, e);
throw new Exception(warning + e);
} catch ( Exception e ) {
String warning = "Failed to create Logical Table '" + ltName + "', cause: ";
logger.logException(GDBMessages.CONFIG_LT_FILE_IMPORT_ERROR, warning, e);
throw new Exception(warning + e);
}
// String canName = new File(fileName).getName();
// int dotIndex = canName.indexOf('.');
// if ( -1 != dotIndex ) canName = canName.substring(0, dotIndex);
String nodeDefName = ltName + "_DS0"; // + canName.toUpperCase();
Properties backup = null;
try {
// Now setup the data source - note stale properties (with ltName as their prefix) were checked for already
// when setting up the logical table.
updates.put(nodeDefName + VTI_SUFFIX, FileImport.class.getName());
updates.put(nodeDefName + ARGS_SUFFIX, filePathTagged); // keep tagged version
updates.put(nodeDefName + OPTIONS_SUFFIX, MAP_COLUMNS_BY_POSITION); // this will give more flexibility to change LT column names
// Load data source now so we can report if any errors occur in doing so (e.g. file not found)
// properties must be set to load data source successfully
backup = new Properties();
synchronized(upr) {
backup.putAll( upr );
/*
* Update upr with the new values.
* To avoid NPEs we iterate through the values & do null checks, rather than use putAll.
* Note: because upr is a Properties object (and extends Hashtable), it is automatically synchronized
*/
Set<Entry<String,String>> updatesEntries = updates.entrySet();
for (Entry<String, String> updateEntry : updatesEntries) {
if(updateEntry.getKey() != null) {
if(updateEntry.getValue() == null) {
upr.remove(updateEntry.getKey());
}
else {
upr.put( updateEntry.getKey(), updateEntry.getValue() );
}
}
}
}
// DataSourcesManager.loadDataSource( ltName, nodeDefName );
persistAndApplyConfigUpdatesForSpecificLT(updates, ltName);
} catch ( Exception e ) {
if ( null != backup ) { Properties tmp = upr; synchronized(upr) { upr = backup; } tmp.clear(); }
String warning = "Failed to create Data Source '" + nodeDefName + "', cause: ";
logger.logException(GDBMessages.CONFIG_LT_CREATE_DS_ERROR, warning, e);
throw new Exception(warning + e);
}
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
}
// Denis : setLogicalTableForExcel
public static synchronized void setLogicalTableForExcel( String ltName, String spreadsheetLocation ) throws Exception {
if ( Util.isWindowsOS && null != spreadsheetLocation ) spreadsheetLocation = spreadsheetLocation.replaceAll("\\\\", "/");
apiStart(setltforexcel, Arrays.asList(ltName, spreadsheetLocation));
ltName = ltName.toUpperCase();
Map<String, String> updates = null;
try {
String tableDef = new GaianResultSetMetaData(new GExcel(spreadsheetLocation).getMetaDataByInferringTypes(), null).getColumnsDefinition().toUpperCase();
logger.logInfo("Obtained tableDef for file: " + tableDef);
updates = prepareLogicalTable(ltName, tableDef, "");
} catch ( Exception e ) {
String warning = "Failed to create Logical Table '" + ltName + "', cause: ";
logger.logException(GDBMessages.CONFIG_LT_CREATE_EXCEL_ERROR, warning, e);
throw new Exception(warning + e);
}
String nodeDefName = ltName + "_DS0";
Properties backup = null;
try {
// Now setup the data source - note stale properties (with ltName as their prefix) were checked for already
// when setting up the logical table.
updates.put(nodeDefName + VTI_SUFFIX, GExcel.class.getName());
updates.put(nodeDefName + ARGS_SUFFIX, spreadsheetLocation);
// Load data source now so we can report if any errors occur in doing so (e.g. file not found)
// properties must be set to load data source successfully
backup = new Properties();
synchronized(upr) {
backup.putAll( upr );
/*
* Update upr with the new values.
* To avoid NPEs we iterate through the values & do null checks, rather than use putAll.
* Note: because upr is a Properties object (and extends Hashtable), it is automatically synchronized
*/
Set<Entry<String,String>> updatesEntries = updates.entrySet();
for (Entry<String, String> updateEntry : updatesEntries) {
if(updateEntry.getKey() != null) {
if(updateEntry.getValue() == null) {
upr.remove(updateEntry.getKey());
}
else {
upr.put( updateEntry.getKey(), updateEntry.getValue() );
}
}
}
}
persistAndApplyConfigUpdatesForSpecificLT(updates, ltName);
} catch ( Exception e ) {
if ( null != backup ) { Properties tmp = upr; synchronized(upr) { upr = backup; } tmp.clear(); }
String warning = "Failed to create Data Source '" + nodeDefName + "', cause: ";
logger.logException(GDBMessages.CONFIG_LT_CREATE_DS_EXCEL_ERROR, warning, e);
throw new Exception(warning + e);
}
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
}
/**
* This method sends a request to a web service, reads the data returned by the
* web service, and determines which columns can represent the
* associated logical table. It also writes the necessary properties into the
* gaian proerty file in order to query the logical table later on.
*
* @author remi - IBM Hursley
*
* @param ltName
* Name of the generated logical table.
* @param url
* Url accessing the web service.
* @param wsdlLocation
* FOR A SOAP WEB SERVICE, the wsdl location of the web service.
* @param postData
* The data to send to the web service in order to get an answer from it.
* @throws Exception
*/
public static synchronized void setLogicalTableForWS( String ltName, String url, String wsdlLocation, String postData ) throws Exception {
apiStart(setltforws, Arrays.asList(ltName, url, wsdlLocation, postData));
ltName = ltName.toUpperCase();
logger.logInfo("Obtained tableDef for ws service: " + url);
// -----------------------------------------------------------
// Starts the web service and get its results
WebService ws;
if (wsdlLocation != null && !wsdlLocation.isEmpty()) {
// This is a SOAP web service
ws = new SoapWS(url, wsdlLocation, postData);
}
else {
// This is a REST web service
if (postData == null || postData.isEmpty()) {
// no post data => REST / GET
ws = new RestWS(url);
}
else {
// Post data => REST / POST
ws = new PostRestWS(url, postData);
}
}
ws.openConnection();
InputStream is = ws.getInputStream();
// -----------------------------------------------------------
// Defines returned format. -> JSON / XML / UNKNOWN
is = new FormatSpecifierInputStream(is);
WsDataFormat format = ((FormatSpecifierInputStream)is).defineFormat();
// //-------------------------------------------------------
// // Checks the juno package is installed
// if (format == WsDataFormat.JSON) {
// try {
// com.ibm.juno.core.json.JsonParser.class.isLocalClass();
//// GaianDBConfigProcedures.class.getClass().getClassLoader().loadClass(
//// com.ibm.juno.core.json.JsonParser.class.getName()
//// );
// } catch ( Throwable e ) {
// logger.logInfo("The Juno package is not installed. GenericWS cannot parse JSON:\n" + e);
// format = WsDataFormat.UNKNOWN_FORMAT;
// }
// }
//
// if (format == WsDataFormat.UNKNOWN_FORMAT) {
// return;
// }
// -----------------------------------------------------------
// Defines characteristics of the logical table which is read
GenericWsLogicalTableGenerator ltGenerator =
new GenericWsLogicalTableGenerator(is, format);
// -----------------------------------------------------------
// Defines logical table and VTI properties
ltGenerator.addProperty(ltName, GenericWS.PROP_URL, url);
ltGenerator.addGenericWsProperties(ltName, ltName);
if (ws instanceof SoapWS) {
ltGenerator.addProperty(ltName, GenericWS.PROP_WS_TYPE, GenericWS.PROP_WS_TYPE_VALUE_SOAP);
}
else { // if (ws instanceof RestWS) {
String urlCopy = new String(url);
if (urlCopy.toLowerCase().startsWith("file:")) {
ltGenerator.addProperty(ltName, GenericWS.PROP_WS_TYPE, GenericWS.PROP_WS_TYPE_VALUE_LOCAL_FILE);
}
else {
ltGenerator.addProperty(ltName, GenericWS.PROP_WS_TYPE, GenericWS.PROP_WS_TYPE_VALUE_REST);
}
}
ltGenerator.addProperty(ltName, GenericWS.PROP_DATA_FORMAT, format.name());
ltGenerator.addProperty(ltName, GenericWS.PROP_WSDL, wsdlLocation);
ltGenerator.addProperty(ltName, GenericWS.PROP_POST_DATA, postData);
Map<String, String> properties = ltGenerator.getProperties();
// -----------------------------------------------------------
// Updates the properties
// Copied from setLogicalTableForExcel(...)
Properties backup = null;
try {
// Load data source now so we can report if any errors occur in doing so (e.g. file not found)
// properties must be set to load data source successfully
backup = new Properties();
synchronized(upr) {
backup.putAll( upr );
/*
* Update upr with the new values.
* To avoid NPEs we iterate through the values & do null checks, rather than use putAll.
* Note: because upr is a Properties object (and extends Hashtable), it is automatically synchronized
*/
Set<Entry<String,String>> updatesEntries = properties.entrySet();
for (Entry<String, String> updateEntry : updatesEntries) {
if(updateEntry.getKey() != null) {
if(updateEntry.getValue() == null) {
upr.remove(updateEntry.getKey());
}
else {
upr.put( updateEntry.getKey(), updateEntry.getValue() );
}
}
}
upr.putAll(properties);
}
persistAndApplyConfigUpdates(properties);
} catch ( Exception e ) {
if ( null != backup ) { Properties tmp = upr; synchronized(upr) { upr = backup; } tmp.clear(); }
String warning = "Failed to create Data Source '" + url + "', cause: ";
logger.logException(GDBMessages.CONFIG_LT_CREATE_DS_EXCEL_ERROR, warning, e);
throw new Exception(warning + e);
}
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
}
/**
* Generic API for setting up a logical table to access a data source exposed by a VTI.
*
* vtiConstructor should be of the form: 'MyVTI( String vtiArgsCSV )'
* vtiStaticProperties should contain a CSV list of property keys and values - or perhaps a SQL SELECT expression to have
* the option to retrieve them from somewhere, like the setconfigproperties() API does.
*
* @param ltName
* @param vtiConstructor
* @param vtiStaticProperties
*/
public static void setLogicalTableForVTI( String ltName, String vtiConstructor, String vtiStaticProperties ) {
// TODO: Need good javadoc for this
//
// i.e.
// dsOptions might contain SCHEMA property
// dsArgs = vtiArgs => csv list - first string is prefix, i.e. can be used for referencing shared vti properties set
//
// avoids having to register the stored procedure for clients implementing their own VTIs
}
public static synchronized void setNodeConstants( String globalConstantColsDef ) throws Exception {
apiStart(setnodeconstants, Arrays.asList(globalConstantColsDef));
// All tables will need reloading - just validate the syntax here
new GaianResultSetMetaData( "", globalConstantColsDef );
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put( NODE_CONSTANTS, globalConstantColsDef);
persistAndApplyConfigUpdates(updates);
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
}
public static synchronized void setRDBConnection( String connectionID, String driver, String url, String usr, String pwd ) throws Exception {
apiStart(setrdbc, Arrays.asList(connectionID, driver, url, usr, "<pwd>"));
connectionID = connectionID.toUpperCase();
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(connectionID + LABEL_DRIVER, driver);
updates.put(connectionID + LABEL_URL, url);
updates.put(connectionID + LABEL_USR, usr);
updates.put(connectionID + LABEL_PWD, pwd);
persistAndApplyConfigUpdates(updates);
}
public static synchronized void removeRDBConnection( String connectionID ) throws Exception {
apiStart(removerdbc, Arrays.asList(connectionID));
connectionID = connectionID.toUpperCase();
String cdef = null;
try { cdef = getRDBConnectionDetailsAsString( connectionID ); }
catch (Exception e) {}
if ( null == getUserProperty(connectionID+LABEL_DRIVER) && null == getUserProperty(connectionID+LABEL_URL) &&
null == getUserProperty(connectionID+LABEL_USR) && null == getUserProperty(connectionID+LABEL_PWD) ) {
String msg = "RDB Connection does not exist (nothing removed): " + connectionID;
logger.logWarning(GDBMessages.CONFIG_RDB_CONN_REMOVE_NOT_FOUND, msg);
throw new Exception(msg);
}
Set<String> loadedConnections = DataSourcesManager.getLoadedRDBSourceHandles();
if ( loadedConnections.contains( cdef ) ) {
String msg = "Cannot remove active connection " + connectionID + ", dependant data sources must be removed first";
logger.logWarning(GDBMessages.CONFIG_RDB_CONN_REMOVE_ACTIVE, msg);
throw new Exception(msg);
}
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(connectionID + LABEL_DRIVER, null);
updates.put(connectionID + LABEL_URL, null);
updates.put(connectionID + LABEL_USR, null);
updates.put(connectionID + LABEL_PWD, null);
persistAndApplyConfigUpdates(updates);
}
public final static Map<String, String> internalDiags = new LinkedHashMap<String, String>();
public static synchronized String internalDiags( String cmd ) {
if ( null != cmd ) {
if ( cmd.startsWith("hang_on_") ) { internalDiags.put(cmd, null); return "ok_to_hang_on_polls"; }
if ( "clear".equals(cmd) ) synchronized(GaianDBConfigProcedures.class) { internalDiags.clear(); return "clear"; }
}
return internalDiags.get(cmd);
}
public static String getNodeRunStatus() {
switch ( GaianNode.getRunStatus() ) {
case GaianNode.RUN_STATUS_PENDING_ON: return "RUN_STATUS_PENDING_ON";
case GaianNode.RUN_STATUS_PENDING_OFF: return "RUN_STATUS_PENDING_OFF";
case GaianNode.RUN_STATUS_ON: return "RUN_STATUS_ON";
case GaianNode.RUN_STATUS_OFF: default: return "RUN_STATUS_OFF";
}
}
// public static synchronized int getTestInvocationCount( String spf ) { return internalValues.get(spf); }
private static synchronized void incrementTestInvocationCount(String spf) {
String count = internalDiags.get(spf);
count = null == count ? "1" : Integer.parseInt(count)+1+"";
internalDiags.put(spf, count);
}
public static void listWarningsToDepth( int depth, ResultSet[] rs ) throws Exception {
apiStart(listwarningsx);
rs[0] = getResultSetFromQueryAgainstDefaultConnection(
"select gdb_node, tstamp, tstampfirst, repeatCount, warning from new com.ibm.db2j.GaianQuery(" +
"'select * from new com.ibm.db2j.GaianConfig(''" + USERWARNING + "'') GC', 'with_provenance, maxDepth=" + depth
+ "') GQ order by gdb_node, tstamp desc"
);
if ( GaianNode.isInTestMode() ) { incrementTestInvocationCount(listwarningsx); }
rs[0].getStatement().getConnection().close();
}
public static void listWarnings( ResultSet[] rs ) throws Exception {
apiStart(listwarnings);
rs[0] = getResultSetFromQueryAgainstDefaultConnection(
"select gdb_node, tstamp, tstampfirst, repeatCount, warning from new com.ibm.db2j.GaianQuery(" +
"'select * from new com.ibm.db2j.GaianConfig(''" + USERWARNING + "'') GC', 'with_provenance') GQ order by gdb_node, tstamp desc"
);
rs[0].getStatement().getConnection().close();
}
public static void listAPI( ResultSet[] rs ) throws Exception {
apiStart(listapi);
rs[0] = getResultSetFromQueryAgainstDefaultConnection(
"select * from new com.ibm.db2j.GaianConfig('" + API + "') GC"
);
rs[0].getStatement().getConnection().close();
}
public static void gaianConnect( String connectionID, String host ) throws Exception {
gaianConnect(connectionID, host, GaianNode.DEFAULT_PORT);
}
public static void gaianConnect( String connectionID, String host, int port ) throws Exception {
gaianConnect(connectionID, host, port, GAIAN_NODE_DEFAULT_USR, GAIAN_NODE_DEFAULT_PWD);
}
public static synchronized void gaianConnect( String connectionID, String host, int port, String usr, String pwd ) throws Exception {
apiStart(gconnect, Arrays.asList(connectionID, host, port+"", usr, pwd));
connectionID = connectionID.toUpperCase();
Map<String, String> updates = new LinkedHashMap<String, String>();
String definedgcs = getUserProperty(DEFINED_GAIAN_CONNECTIONS);
Properties backup = null;
try {
String driver = GaianNode.isLite() ? GDB_UDP_DRIVER : DERBY_CLIENT_DRIVER;
String url = "jdbc" + (GaianNode.isLite() ? ":udp:" : ":") + "derby://" + host + ":" + port + "/" +
(GaianNode.DEFAULT_PORT==port ? GAIANDB_NAME : GAIANDB_NAME + port) + ";create=true";
Set<String> gcs = getGaianConnectionsAsSet();
if ( !gcs.contains( connectionID ) ) { // throw new Exception("Gaian Connection already active");
updates.put(DEFINED_GAIAN_CONNECTIONS,
( null == definedgcs || 0 == definedgcs.trim().length() ? "" : definedgcs + ", " ) + connectionID);
// Commented out check against previous def - just set properties again every time.
if ( isDiscoveredConnection(connectionID) ) {
String msg = "Connot re-assign discovered connection ID '" + connectionID + "' as a gateway connection";
logger.logImportant(msg);
throw new Exception(msg);
}
// String newcdef = driver+"'"+url+"'"+usr+"'"+pwd;
// String oldcdef = getRDBConnectionDetailsAsString(connectionID, false);
//
// // Check conn def doesnt already exist
// if ( null == oldcdef ) {
//
updates.put(connectionID + LABEL_DRIVER, driver);
updates.put(connectionID + LABEL_URL, url);
updates.put(connectionID + LABEL_USR, usr);
updates.put(connectionID + LABEL_PWD, pwd);
//
// } else if ( !oldcdef.equals(newcdef) ) {
// logger.logImportant("Cdefs don't match: " + oldcdef + " != " + newcdef);
// throw new Exception("Connection definition already exists and doesn't match");
// }
// System.out.println("Adding properties: " + updates);
// Load the connection - thereby validating it - config reload will still occur after but do minimal work.
backup = new Properties();
synchronized(upr) {
backup.putAll( upr );
/*
* Update upr with the new values.
* To avoid NPEs we iterate through the values & do null checks, rather than use putAll.
* Note: because upr is a Properties object (and extends Hashtable), it is automatically synchronized
*/
Set<Entry<String,String>> updatesEntries = updates.entrySet();
for (Entry<String, String> updateEntry : updatesEntries) {
if(updateEntry.getKey() != null) {
if(updateEntry.getValue() == null) {
upr.remove(updateEntry.getKey());
}
else {
upr.put( updateEntry.getKey(), updateEntry.getValue() );
}
}
}
}
// DataSourcesManager.primeJDBCSourceHandlesPoolSynchronously( connectionID );
System.out.println(new Date(System.currentTimeMillis()) +
": Gateway Connection defined accessing GaianDB node on " + host + ":" + port );
}
persistAndApplyConfigUpdates(updates);
} catch ( Exception e ) {
// upr.setProperty(DEFINED_GAIAN_CONNECTIONS, definedgcs); // undo main update
if ( null != backup ) { Properties tmp = upr; synchronized(upr) { upr = backup; } tmp.clear(); }
String warning = "Failed to create Gaian Connection '" + connectionID + "', cause: " ;
logger.logException(GDBMessages.CONFIG_CONNECTION_CREATE_ERROR, warning, e);
throw new Exception(warning + e);
}
}
public static synchronized void gaianDisconnect( String connectionID ) throws Exception {
apiStart(gdisconnect, Arrays.asList(connectionID));
connectionID = connectionID.toUpperCase();
Map<String, String> updates = new LinkedHashMap<String, String>();
try {
Set<String> dcs = new HashSet<String>( Arrays.asList( getDefinedConnections() ) );
if ( !dcs.remove( connectionID ) ) throw new Exception("Defined connection not found");
StringBuffer list = new StringBuffer();
if ( !dcs.isEmpty() ) {
Iterator<String> i = dcs.iterator();
list.append( i.next() );
while ( i.hasNext() ) list.append( ", " + i.next() );
}
updates.put(DEFINED_GAIAN_CONNECTIONS, list.toString());
persistAndApplyConfigUpdates(updates);
} catch ( Exception e ) {
String warning = "Cannot remove Gaian Connection '" + connectionID + "', cause: " ;
logger.logException(GDBMessages.CONFIG_CONNECTION_REMOVE_ERROR, warning, e);
throw new Exception(warning + e);
}
// removeRDBConnection(connectionID);
}
public static synchronized void setDataSourceExcel( String ltName, String dsName, String fileName, String options, String columns ) throws Exception {
// if ( Util.isWindowsOS && null != fileName ) fileName = fileName.replaceAll("\\\\", "/"); // Don't do this - leave the input path as is - users may want it to stay as it was.
apiStart(setdsexcel, Arrays.asList(ltName, dsName, fileName, options, columns) );
String[] fileNameSplit = Util.splitByCommas(fileName);
if(1 > fileNameSplit.length || fileNameSplit[0] == null) {
logger.logWarning(GDBMessages.CONFIG_DS_EXCEL_FILE_NOT_SPECIFIED, "API " + setdsexcel + " warning: No fileName was specified.");
}
else if(!new File(fileNameSplit[0]).exists()) {
logger.logWarning(GDBMessages.CONFIG_DS_EXCEL_FILE_NOT_FOUND, "API " + setdsexcel + " warning: File not found: " + fileNameSplit[0]);
}
setDataSourceVTIPrivate(ltName, dsName, GExcel.class.getName(), fileName, options, columns);
}
public static synchronized void setDataSourceFile( String ltName, String dsName, String filePath, String options, String columnsCSV ) throws Exception {
// Note - No code in GaianDB should assume that file paths on Windows will exclusively use the '\' character as path separator.
// if ( Util.isWindowsOS && null != filePath ) filePath = filePath.replaceAll("\\\\", "/"); // Don't do this - leave the input path as is - users may want it to stay as it was.
apiStart(setdsfile, Arrays.asList(ltName, dsName, filePath, options, columnsCSV) );
// Do some early checks - as long as the file is not a variable expression, i.e. containing wildcards or a regex
if ( null == options || -1 == options.indexOf(PLURALIZED) ) {
if ( !new File(filePath).exists() )
logger.logWarning(GDBMessages.CONFIG_DS_FILE_NOT_FOUND, "API " + setdsfile + " warning: File not found: " + filePath);
// Attempt to resolve control file now - this will print an early warning to the logs if it can't be found -
FileImport.getControlFile(filePath);
}
setDataSourceVTIPrivate(ltName, dsName, FileImport.class.getName(), filePath, options, columnsCSV);
}
public static synchronized void setDataSourceVTI( String ltName, String dsName, String vtiClass,
String args, String options, String columnsCSV ) throws Exception {
apiStart(setdsvti, Arrays.asList(ltName, dsName, vtiClass, args, options, columnsCSV) );
setDataSourceVTIPrivate(ltName, dsName, vtiClass, args, options, columnsCSV);
}
// Tough associating connections with batch updates - Implemented setconfigproperties() for simpler batch-updates for now instead...
// private static final Set<Connection> connectionsHavingCommitStatusOff = new HashSet<Connection>(); // A connection auto-commit status is always "on" initially
// public static boolean isCommitAPIsOn() throws SQLException {
// return connectionsHavingCommitStatusOff.isEmpty() ? true : false == connectionsHavingCommitStatusOff.contains( getDefaultDerbyConnection() );
// }
//
// public static synchronized void setCommitAPIsOn() throws Exception { setCommitAPIs(true); }
// public static synchronized void setCommitAPIsOff() throws Exception { setCommitAPIs(false); }
//
// private static void setCommitAPIs( boolean isCommit ) throws Exception {
// Connection c = getDefaultDerbyConnection();
// if ( isCommit ) connectionsHavingCommitStatusOff.add( c ); else connectionsHavingCommitStatusOff.remove( c );
// logger.logAlways("setCommitAPIs for Connection: " + c + " => " + isCommit );
// }
private static void setDataSourceVTIPrivate( String ltName, String dsName, String vtiClass,
String args, String options, String columnsCSV ) throws Exception {
ltName = ltName.toUpperCase();
String nodeDefName = ltName + "_DS" + dsName.toUpperCase();
Properties backup = null;
try {
if ( !DataSourcesManager.isLogicalTableLoaded(ltName) ) throw new Exception("Parent logical table is not loaded: " + ltName);
// Clear any trace of this data source in upr and return corresponding null mappings for persitence.
Map<String, String> updates = generateNulledOutUpdatesForPropertiesPrefixed(nodeDefName);
try {
if ( -1 == vtiClass.indexOf('.') && null != Class.forName("com.ibm.db2j."+vtiClass) )
vtiClass = "com.ibm.db2j."+vtiClass;
} catch (ClassNotFoundException e1) {}
// This restriction below is no longer in Derby from version 10.5
// if ( !vtiClass.startsWith("com.ibm.db2j.") ) throw new Exception("VTI class package name must start with: 'com.ibm.db2j.'");
updates.put(nodeDefName + VTI_SUFFIX, vtiClass);
updates.put(nodeDefName + ARGS_SUFFIX, args);
deriveRequiredDataSourceUpdatesFromOptionsAndColumnsLists( updates, nodeDefName, options, columnsCSV );
// Apply the updates and load the data source (thus validating syntax aswell) before updating the config file
// Config file reload will still occur (for now) but will do minimal work.
// Putting all updates in upr immediately will mean a concurrent reload of data sources will have a chance to
// load this data source before we next get a chance to synchronize on the full DataSourcesManager.class (when reloading the config file)
// Note upr is a Hashtable therefore it cannot be given null values - insead we have to remove those keys
backup = new Properties();
synchronized(upr) {
backup.putAll( upr );
for ( String key : updates.keySet() ) {
String val = updates.get(key);
if ( null == val ) upr.remove(key);
else upr.put(key, val);
}
}
// DataSourcesManager.loadDataSource( ltName, nodeDefName );
// load Data Source Immediately If Transient
if ( new HashSet<String>( Arrays.asList( Util.splitByCommas(options) ) ).contains( "TRANSIENT" ) ) {
DataSourcesManager.refreshDataSource( ltName, nodeDefName );
logger.logInfo("Recording transient property updates: " + updates);
for ( Iterator<Object> iter = inMemoryDataSourceProperties.keySet().iterator(); iter.hasNext(); )
if ( ((String)iter.next()).startsWith(nodeDefName) ) iter.remove();
for ( String key : updates.keySet() ) {
String val = updates.get(key);
if ( null != val ) inMemoryDataSourceProperties.put(key, val);
}
// inMemoryDataSourceProperties.putAll( updates );
// TODO: also put any VTI properties (e.g. "cache.expires" and "schema") into
// transient properties? probably not because they don't require a reload...
} else
persistAndApplyConfigUpdatesForSpecificLT(updates, ltName);
} catch (Exception e) {
// upr.remove(nodeDefName + VTI_SUFFIX); // undo main update
if ( null != backup ) { Properties tmp = upr; synchronized(upr) { upr = backup; } tmp.clear(); }
logger.logException(GDBMessages.CONFIG_DS_SET_VTI_ERROR, "Unable to load Data Source " + nodeDefName + ": ", e);
throw e;
}
}
private static void deriveRequiredDataSourceUpdatesFromOptionsAndColumnsLists(
Map<String, String> updates, String nodeDefName, String options, String columnsCSV ) {
if ( null != options ) updates.put(nodeDefName + OPTIONS_SUFFIX, options);
String[] cols = Util.splitByTrimmedDelimiterNonNestedInCurvedBracketsOrSingleQuotes( columnsCSV, ',' );
Pattern explicitColumnMappingPattern = Pattern.compile("[\\s]*C[1-9][0-9]*[\\s]*=.*");
for ( int i=0; i<cols.length; i++ ) {
// Note this doesn't conflict with option 'MAP_COLUMNS_BY_POSITION' -
// The explicitly defined mappings will override positional mapping for those cols.
if ( explicitColumnMappingPattern.matcher(cols[i]).matches() ) {
int idx = cols[i].indexOf('=');
updates.put(nodeDefName + '_' + cols[i].substring(0, idx).trim(), cols[i].substring(idx+1).trim());
} else
// default matching by position
updates.put(nodeDefName + COLUMN_LABEL + (i+1), cols[i]);
}
}
public static synchronized void setDataSourceRDBTable( String ltName, String dsName, String cid, String rdbTable,
String options, String columnsCSV ) throws Exception {
apiStart(setdsrdbtable, Arrays.asList(ltName, dsName, cid, rdbTable, options, columnsCSV));
cid = cid.toUpperCase();
ltName = ltName.toUpperCase();
String nodeDefName = ltName + "_DS" + dsName.toUpperCase();
Properties backup = null;
try {
if ( !DataSourcesManager.isLogicalTableLoaded(ltName) ) throw new Exception("Parent logical table is not loaded: " + ltName);
// Clear any trace of this data source in upr and return corresponding null mappings for persitence.
Map<String, String> updates = generateNulledOutUpdatesForPropertiesPrefixed(nodeDefName);
// Validate that the jdbc connection is defined
getRDBConnectionDetailsAsString(cid);
updates.put(nodeDefName + CONNECTION_SUFFIX, cid + " " + rdbTable);
deriveRequiredDataSourceUpdatesFromOptionsAndColumnsLists( updates, nodeDefName, options, columnsCSV );
// detect explicit mappings too, e.g. "c1=kks, c6=kk"
// Apply the updates and load the data source (thus validating syntax aswell) before updating the config file
// Config file reload will still occur (for now) but will do minimal work.
// Putting all updates in upr immediately will mean a concurrent reload of data sources will have a chance to
// load this data source before we next get a chance to synchronize on the full DataSourcesManager.class (when reloading the config file)
// Note upr is a Hashtable therefore it cannot be given null values - insead we have to remove those keys
backup = new Properties();
synchronized(upr) {
backup.putAll( upr );
for ( String key : updates.keySet() ) {
String val = updates.get(key);
if ( null == val ) upr.remove(key);
else upr.put(key, val);
}
}
// DataSourcesManager.loadDataSource( ltName, nodeDefName );
// DataSourcesManager.primeJDBCSourceHandlesPoolSynchronously( nodeDefName );
// load Data Source Immediately If Transient
if ( new HashSet<String>( Arrays.asList( Util.splitByCommas(options) ) ).contains( "TRANSIENT" ) ) {
DataSourcesManager.refreshDataSource( ltName, nodeDefName );
logger.logInfo("Recording transient property updates: " + updates);
for ( Iterator<Object> iter = inMemoryDataSourceProperties.keySet().iterator(); iter.hasNext(); )
if ( ((String)iter.next()).startsWith(nodeDefName) ) iter.remove();
for ( String key : updates.keySet() ) {
String val = updates.get(key);
if ( null != val ) inMemoryDataSourceProperties.put(key, val);
}
// inMemoryDataSourceProperties.putAll( updates );
// TODO: also put any VTI properties (e.g. "cache.expires" and "schema") into
// transient properties? probably not because they don't require a reload...
} else
persistAndApplyConfigUpdatesForSpecificLT(updates, ltName);
} catch ( Exception e ) {
// upr.remove(nodeDefName + CONNECTION_SUFFIX); // undo main update
if ( null != backup ) { Properties tmp = upr; synchronized(upr) { upr = backup; } tmp.clear(); }
String warning = "Failed to create Data Source '" + nodeDefName + "', cause: ";
logger.logException(GDBMessages.CONFIG_DS_SET_RDB_ERROR, warning, e);
throw new Exception(warning + e);
}
}
public static synchronized void setDataSourceRDBTable( String ltName, String dsName, String rdbTable, String options, String columnsCSV ) throws Exception {
apiStart(setdslocalderby, Arrays.asList(ltName, dsName, rdbTable, options, columnsCSV));
setDataSourceRDBTable(ltName, dsName, LOCALDERBY_CONNECTION, rdbTable, options, columnsCSV);
}
public static synchronized void removeDataSource( String dsID ) throws Exception {
apiStart(removeds, Arrays.asList(dsID));
dsID = dsID.toUpperCase();
try {
int ltend = dsID.lastIndexOf("_DS");
if ( -1 == ltend ) throw new Exception("This ID does not identify a Data Source (no _DS suffix)");
// boolean wasTransient = false;
synchronized( upr ) {
for ( Iterator<Object> iter = inMemoryDataSourceProperties.keySet().iterator(); iter.hasNext(); ) {
String key = (String) iter.next();
if ( key.startsWith(dsID) ) {
logger.logInfo("Removing transient ds property (from transient and user props): " + key);
iter.remove();
upr.remove(key);
// wasTransient = true;
}
}
}
// Let the data source be removed automatically on forced config reload...
// if ( wasTransient ) {
// while ( -1 != ltend ) {
// String ltName = dsID.substring(0, ltend);
// if ( DataSourcesManager.isLogicalTableLoaded(ltName) ) {
// logger.logInfo("Removing data source: " + key);
// DataSourcesManager.removeDataSource(ltName, dsID);
// break;
// }
// }
// }
Set<String> possibleLTs = new HashSet<String>();
int dsIdx = -1;
// Determine if this might be a data source property of a defined logical table.
while ( -1 < ( dsIdx = dsID.indexOf("_DS", dsIdx+1) ) ) {
String possibleLT = getUserProperty( dsID.substring(0, dsIdx) + LTDEF_SUFFIX );
if ( null != possibleLT ) possibleLTs.add( possibleLT );
}
// We don't really need to persist/apply cfg updates if the data source consisted of transient properties
// only. However - best be sure we remove any potentially overriding properties in the cfg file as well.
Map<String, String> updates = generateNulledOutUpdatesForPropertiesPrefixed(dsID);
persistAndApplyConfigUpdatesForSpecificLTs(updates, possibleLTs);
} catch ( Exception e ) {
String warning = "Failed to remove Data Source '" + dsID + "', cause: ";
logger.logException(GDBMessages.CONFIG_DS_REMOVE_ERROR, warning, e);
throw new Exception(warning + e);
}
}
// private static Map<String, String> generateNulledOutPropertiesForDataSourceEntriesWithPrefix( String dsID ) throws Exception {
//
// int ltend = dsID.lastIndexOf("_DS");
// if ( -1 == ltend ) throw new Exception("This ID does not identify a Data Source (no _DS suffix)");
// // Validate logical table exists
//// String ltName = dsID.substring(0, ltend);
//// if ( null == getUserProperty(ltName + LTDEF_SUFFIX) )
//// throw new Exception("Logical Table does not exist: " + ltName);
//
// // Not correct.. suffixes can come beyond the _DSX marker, e.g. _CONNECTION
//// try { Integer.parseInt( dsID.substring(ltend+3) ); }
//// catch ( Exception e ) {
//// throw new Exception("This ID does not identify a Data Source (cannot resolve index integer after _DS suffix)");
//// }
//
// Map<String, String> m = generateNulledOutUpdatesForPropertiesPrefixed(dsID, null);
// Iterator<String> i = m.keySet().iterator();
// synchronized( upr ) {
// while ( i.hasNext() ) upr.remove(i.next());
// }
// return m;
//// persistAndApplyConfigUpdates(updates);
// }
// Take care using this method...... Only use if the prefix will definitely encompass only the properties you want
private static Map<String, String> generateNulledOutUpdatesForPropertiesPrefixed( String prefix ) throws Exception {
Map<String, String> m = new LinkedHashMap<String, String>();
// Now we can use wildcards...
// This is cleaner because they get applied to the properties in the current config file, not the loaded properties of an old file anymore...!
if ( null != prefix && !prefix.endsWith("*") )
prefix += '*';
m.put(prefix, null);
// String[] props = getAllUserPropertiesWithPrefix(prefix);
// for ( int i=0; i<props.length; i++ )
//// if ( null == allowedSuffixes || stringEndsWithOneOf( props[i], allowedSuffixes ) )
// m.put(props[i], null);
return m;
}
// private static boolean stringEndsWithOneOf( String s, String[] suffixes ) {
// for ( String suffix : suffixes ) if ( s.endsWith(suffix) ) return true;
// return false;
// }
// public static void setGaianCred( String usr, String pwd ) throws Exception {
// apiStart(setgaiancred, Arrays.asList(usr, pwd));
// Map updates = new LinkedHashMap();
// updates.put(GAIAN_NODE_USR, usr);
// updates.put(GAIAN_NODE_PWD, pwd);
// persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
// }
public static synchronized void setNumConnectionsSought( int numConnections ) throws Exception {
apiStart(setminconnections, Arrays.asList(numConnections+""));
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(MIN_DISCOVERED_CONNECTIONS, numConnections + "");
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void setAccessClusters( String clustersCSV ) throws Exception {
apiStart(setaccessclusters, Arrays.asList(clustersCSV));
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(ACCESS_CLUSTERS, clustersCSV);
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void setDiscoveryHosts( String columnsCSV ) throws Exception {
apiStart(setdiscoveryhosts, Arrays.asList(columnsCSV));
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(ACCESS_HOSTS_PERMITTED, columnsCSV);
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void setDiscoveryIP( String ip ) throws Exception {
apiStart(setdiscoveryip, Arrays.asList(ip));
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(DISCOVERY_IP, ip);
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void setMaxPropagation( int depth ) throws Exception {
apiStart(setmaxpropagation, Arrays.asList(depth+""));
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(MAX_PROPAGATION, depth+"");
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void setMaxPoolsizes( int size ) throws Exception {
apiStart(setmaxpoolsizes, Arrays.asList(size+""));
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(MAX_POOLSIZES, size+"");
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void setLogLevel( String level ) throws Exception {
apiStart(setloglevel, Arrays.asList(level));
level = level.toUpperCase();
if ( false == Logger.setLogLevel(level) )
throw new Exception("Not a valid log level: " + level + " Allowed values are: " + Arrays.asList( Logger.POSSIBLE_LEVELS ));
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(LOGLEVEL, level);
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void setSourceList( String listID, String sourcesCSV ) throws Exception {
apiStart(setsourcelist, Arrays.asList(listID, sourcesCSV));
listID = listID.toUpperCase();
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(listID + SOURCELIST_SUFFIX, sourcesCSV);
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void setMsgBrokerDetails( String host, int port, String topic ) throws Exception {
apiStart(setmsgbrokerdetails, Arrays.asList(host, port+""));
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(MSGBROKER_HOST, host);
updates.put(MSGBROKER_PORT, port+"");
updates.put(MSGBROKER_TOPIC, topic);
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void setConfigProperty( String propertyName, String value ) throws Exception {
apiStart(setconfigproperty, Arrays.asList(propertyName, value));
if ( null == propertyName ) return;
// TODO: check if property name relates to a transient data source - e.g. if it exists in transient set.
// If so, change the transient one, update the upr as well and reload the data source (don't reload if it was a VTI Property)
// Blocked on this for now as cant resolve ltname and dsname deterministically...
if ( inMemoryDataSourceProperties.contains(propertyName) )
throw new Exception("Cannot update transient data source property: '" + propertyName +
"'. Please remove the data source first with removeds().");
Map<String, String> updates = new LinkedHashMap<String, String>();
updates.put(propertyName, value); //null == value || 0 == value.length() ? null : value);
if ( isPropertyPotentiallyImpactingALogicalTable( propertyName ) )
persistAndApplyConfigUpdates(updates);
else
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
if ( propertyName.endsWith(LTDEF_SUFFIX) || propertyName.endsWith(CONSTANTS_SUFFIX) || MANAGE_LTVIEWS_WITH_TABLE_FUNCTIONS.equals( propertyName ) )
DataSourcesManager.checkUpdateLogicalTableViewsOnAllDBs();
if ( GaianNode.isInTestMode() ) { incrementTestInvocationCount(setconfigproperty); }
}
//TBD PDS to comment this.
public static boolean setConfigProperties (Map<String, String> properties){
// Copied from setLogicalTableForExcel(...)
Properties backup = null;
try {
// Load data source now so we can report if any errors occur in doing so (e.g. file not found)
// properties must be set to load data source successfully
backup = new Properties();
synchronized(upr) {
backup.putAll( upr );
/*
* Update upr with the new values.
* To avoid NPEs we iterate through the values & do null checks, rather than use putAll.
* Note: because upr is a Properties object (and extends Hashtable), it is automatically synchronized
*/
Set<Entry<String,String>> updatesEntries = properties.entrySet();
for (Entry<String, String> updateEntry : updatesEntries) {
if(updateEntry.getKey() != null) {
if(updateEntry.getValue() == null) {
upr.remove(updateEntry.getKey()); //TBD wild card null execution
}
else {
upr.put( updateEntry.getKey(), updateEntry.getValue() );
}
}
}
// upr.putAll(properties);
}
persistAndApplyConfigUpdates(properties);
} catch ( Exception e ) {
if ( null != backup ) { Properties tmp = upr; synchronized(upr) { upr = backup; } tmp.clear(); }
String warning = "Failed to update Properties, cause: ";
logger.logException(GDBMessages.CONFIG_LT_CREATE_DS_EXCEL_ERROR, warning, e);
return false; //failure
}
return true; //success
}
public static synchronized void setConfigProperties( String sqlQueryReturningPropertyKeysAndValues ) throws Exception {
apiStart(setConfigProperties, Arrays.asList( sqlQueryReturningPropertyKeysAndValues ));
Connection c = getDefaultDerbyConnection();
try { setConfigProperties( sqlQueryReturningPropertyKeysAndValues, c); }
finally { if ( null != c ) c.close(); }
}
public static void addGateway( String ip ) throws Exception {
apiStart(addgateway, Arrays.asList(ip));
Map<String, String> updates = new LinkedHashMap<String, String>();
String dgprop = getDiscoveryGateways();
List<String> dg = Arrays.asList( Util.splitByCommas( dgprop ) );
if ( dg.contains(ip) )
throw new Exception("Gateway is already registered: " + ip);
if ( null != dgprop && 0 < dgprop.trim().length() ) dgprop += ", " + ip; else dgprop = ip;
updates.put(DISCOVERY_GATEWAYS, dgprop);
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
public static synchronized void removeGateway( String ip ) throws Exception {
apiStart(removegateway, Arrays.asList(ip));
Map<String, String> updates = new LinkedHashMap<String, String>();
String dgprop = getDiscoveryGateways();
List<String> dg = Arrays.asList( Util.splitByCommas( dgprop ) );
if ( false == dg.remove(ip) )
throw new Exception("Gateway was not registered: " + ip + ", current list is: " + dgprop);
String list = dg.toString();
updates.put(DISCOVERY_GATEWAYS, list.substring(1, list.length()-1));
persistAndApplyConfigUpdatesAffectingNoLogicalTables(updates);
}
// public static void registerUser( String usr, String pwd ) throws SQLException {
// logEnteredMsg("registerUser", Arrays.asList(usr, pwd) );
// SecurityManager.registerUser(usr, pwd);
// }
public static void setUser( String usr, String affiliation, String clearance, String pwd ) throws Exception {
try {
SecurityManager.registerUser(usr, affiliation, clearance, pwd);
} catch ( Exception e ) {
String warning = "Failed to set/register new user: '" + usr + "', cause: ";
logger.logException(GDBMessages.CONFIG_USER_SET_ERROR, warning, e);
throw new Exception(warning + e);
}
}
public static void removeUser( String usr ) throws Exception {
try {
SecurityManager.removeUser(usr);
} catch ( Exception e ) {
String warning = "Failed to remove user: '" + usr + "', cause: ";
logger.logException(GDBMessages.CONFIG_USER_REMOVE_ERROR, warning, e);
throw new Exception(warning + e);
}
}
}