/*
* (C) Copyright IBM Corp. 2013
*
* LICENSE: Eclipse Public License v1.0
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.ibm.gaiandb;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.SQLChar;
import org.apache.derby.vti.IFastPath;
import com.ibm.db2j.GaianQuery;
/**
* This class is a wrapper class for a GaianNode.
* This can be used to embed GaianDB in another project and run in a shared process.
* The main points of interest are: startup/shutdown/restart, and the capability to invoke the primary GaianDB VTI objects 'GaianTable' and 'GaianQuery' directly.
*
* @author DavidVyvyan
*/
public class GaianTask {
// Use PROPRIETARY notice if class contains a main() method, otherwise use COPYRIGHT notice.
public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2013";
private static final Logger logger = new Logger("GaianTask", 20);
public static final String CONFIG_CATEGORY = GaianTask.class.getPackage().getName();
public static final String CONFIG_CLUSTER_ID = "clusterid";
private final static GaianNode gdbNode = new GaianNode();
private static String gdbThreadEndMessage = null;
private final AtomicBoolean isGaianParentThreadRunning = new AtomicBoolean(false);
private final List<String> taskArgs;
public GaianTask() { taskArgs = new ArrayList<String>(); }
public GaianTask( List<String> args ) { taskArgs = new ArrayList<String>( args ); }
/**
* Called by a component wishing to embed a GaianNode
*
*/
public synchronized void startTask() throws Exception {
String gdbHome = System.getProperty("derby.system.home");
if ( null == gdbHome ) {
gdbHome = Util.getInstallPath();
if ( null == gdbHome ) gdbHome = ".";
System.setProperty( "derby.system.home", gdbHome );
}
logger.logInfo("Resolved GaianDB home for config and log file locations to: " + gdbHome);
if ( false == taskArgs.contains("-c") ) { taskArgs.add("-c"); taskArgs.add( gdbHome + "/gaiandb_config.properties" ); }
// e.g new String[] { "-p", "6414", "-n", "FabricNode1" }
final String[] gaianTaskStartupOptions = (String[]) taskArgs.toArray( new String[0] );
final Thread gdbParentThread = new Thread( "GaianDB parent thread" ) {
public void run() {
try {
gdbNode.start( gaianTaskStartupOptions );
gdbThreadEndMessage = "GaianNode parent thread exiting cleanly (no Exception)";
logger.logInfo( gdbThreadEndMessage );
}
catch (Throwable e) {
gdbThreadEndMessage = "GaianNode parent thread Exception: " + Util.getStackTraceDigest(e);
logger.logInfo( gdbThreadEndMessage );
}
finally { isGaianParentThreadRunning.set(false); }
}
};
logger.logInfo( "GaianTask runStatus before start(): " + GaianNode.getRunStatus() + ", isStarted(): " + gdbNode.isStarted() +
", isGaianParentThreadRunning ? " + isGaianParentThreadRunning );
// Ensure the node was not already started
if ( true == isGaianParentThreadRunning.compareAndSet(false, true) ) {
try { gdbParentThread.start(); }
catch ( Throwable t ) {
isGaianParentThreadRunning.set(false);
logger.logInfo("Unable to start() GaianNode Parent Thread");
}
while ( false == gdbNode.isStarted() ) {
if ( false == isGaianParentThreadRunning.get() ) { // Do not use gdbParentThread.isAlive() - this sometimes returns false when the node is still running.
logger.logInfo( "GaianNode parent thread is not running" );
throw new Exception("Unable to start Task - " + gdbThreadEndMessage);
}
Thread.sleep( 100 );
}
} else
logger.logInfo("GaianNode parent thread is already running");
}
public synchronized void shutDown() {
gdbNode.stop(); // tell the node to shut itself down
// Wait for the gdb parent thread to exit
while ( true == isGaianParentThreadRunning.get() ) { // Do not use gdbParentThread.isAlive() - this sometimes returns false when the node is still running.
try { Thread.sleep( 50 ); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
// This method has to be here (rather than in GaianNode or GaianDBUtilityProcedures for instance)
// because we invoke GaianQuery directly rather than in SQL.
public static InetAddress[] getGaianClusterIPs() {
GaianQuery gaianQuery = null;
try {
gaianQuery = new GaianQuery("select substr(curl,14,locate(':',curl,14)-14) gdbip"
+ " from new com.ibm.db2j.GaianConfig('RDBCONNECTIONS') GC where cnodeid is not null");
gaianQuery.executeAsFastPath();
Set<String> ips = new HashSet<String>();
ips.add( InetAddress.getLocalHost().getHostAddress() );
DataValueDescriptor[] row = new DataValueDescriptor[] { new SQLChar() };
while ( IFastPath.SCAN_COMPLETED != gaianQuery.nextRow( row ) )
ips.add( row[0].getString() );
InetAddress[] ipAddresses = new InetAddress[ ips.size() ];
int i = 0; for ( String ip : ips ) ipAddresses[i++] = InetAddress.getByName( ip );
logger.logInfo("Resolved ipAddresses: " + Arrays.asList( ipAddresses ));
return ipAddresses;
} catch ( Exception e ) {
} finally {
if ( null != gaianQuery ) try { gaianQuery.close(); } catch (Exception e1) {}; // cleanup as much as possible
}
return null;
}
// public static InetAddress[] getGaianClusterIPs() {
//
// ResultSet rs = null;
//
// try {
// rs = GaianDBConfig.getEmbeddedDerbyConnection().createStatement().executeQuery(
// "select distinct gdbip from new com.ibm.db2j.GaianQuery('"
// + "select substr(curl,14,locate('':'',curl,14)-14) gdbip"
// + " from new com.ibm.db2j.GaianConfig(''RDBCONNECTIONS'') GC where cnodeid is not null"
// + "') GQ"
// );
//
//// // Other alternative
//// rs = GaianDBConfig.getEmbeddedDerbyConnection().createStatement().executeQuery(
//// + "select ipv4 from new com.ibm.db2j.GaianQuery('call listnet(''" + ipPrefix + "'')', 'with_provenance') GQ"
//
// List<InetAddress> ips = new ArrayList<InetAddress>();
//
// while ( rs.next() ) ips.add( InetAddress.getByName( rs.getString(1) ) );
//
//// logger.logInfo("Resolved ips: " + Arrays.asList( (InetAddress[]) ips.toArray( new InetAddress[0] ) ));
//
// return ips.toArray( new InetAddress[0] );
//
// } catch ( Exception e ) {
// logger.logInfo( "Unable to resolve Gaian cluster IPs (returning null), cause: " + e );
// } finally {
// if ( null != rs ) try { rs.getStatement().getConnection().close(); } catch (Exception e1) {}; // cleanup as much as possible
// }
//
// return null;
// }
}