/*
* Indiana University Extreme! Lab Software License, Version 1.2
*
* Copyright (C) 2002 The Trustees of Indiana University.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1) All redistributions of source code must retain the above
* copyright notice, the list of authors in the original source
* code, this list of conditions and the disclaimer listed in this
* license;
*
* 2) All redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the disclaimer
* listed in this license in the documentation and/or other
* materials provided with the distribution;
*
* 3) Any documentation included with all redistributions must include
* the following acknowledgement:
*
* "This product includes software developed by the Indiana
* University Extreme! Lab. For further information please visit
* http://www.extreme.indiana.edu/"
*
* Alternatively, this acknowledgment may appear in the software
* itself, and wherever such third-party acknowledgments normally
* appear.
*
* 4) The name "Indiana Univeristy" or "Indiana Univeristy
* Extreme! Lab" shall not be used to endorse or promote
* products derived from this software without prior written
* permission from Indiana University. For written permission,
* please contact http://www.extreme.indiana.edu/.
*
* 5) Products derived from this software may not use "Indiana
* Univeristy" name nor may "Indiana Univeristy" appear in their name,
* without prior written permission of the Indiana University.
*
* Indiana University provides no reassurances that the source code
* provided does not infringe the patent or any other intellectual
* property rights of any other entity. Indiana University disclaims any
* liability to any recipient for claims brought by any other entity
* based on infringement of intellectual property rights or otherwise.
*
* LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
* NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA
* UNIVERSITY GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT
* SOFTWARE IS FREE OF INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR
* OTHER PROPRIETARY RIGHTS. INDIANA UNIVERSITY MAKES NO WARRANTIES THAT
* SOFTWARE IS FREE FROM "BUGS", "VIRUSES", "TROJAN HORSES", "TRAP
* DOORS", "WORMS", OR OTHER HARMFUL CODE. LICENSEE ASSUMES THE ENTIRE
* RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR ASSOCIATED MATERIALS,
* AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION GENERATED USING
* SOFTWARE.
*/
/**
* @version $Revision: 1.30 $ $Author: srikrish $ $Date: 2004/09/09 06:55:32 $ (GMT)
* @author Sriram Krishnan [mailto:srikrish@extreme.indiana.edu]
*/
package xcat.mobile.coordinator;
import gov.cca.TypeMap;
import intf.ccacore.XCATConnectionID;
import intf.ccacore.XCATConnectionInfo;
import intf.services.XCATBuilderService;
import intf.mobile.ccacore.MobileComponentID;
import intf.mobile.coordinator.AppCoordinator;
import intf.mobile.coordinator.AppCoordinatorCallback;
import intf.mobile.coordinator.ComponentInfo;
import intf.mobile.storage.MasterStorageService;
import intf.mobile.storage.IndividualStorageService;
import xcat.ports.BasicPortImpl;
import xcat.ccacore.XCATConnectionIDImpl;
import xcat.services.XCATBuilderServiceImpl;
import xcat.mobile.ccacore.MobileComponentIDClientImpl;
import xcat.exceptions.NonstandardException;
import xcat.util.HandleResolver;
import xcat.util.URLToReference;
import xcat.types.TypeMapImpl;
import xcat.schema.ComponentStaticInformation;
import xcat.schema.ExecutionEnv;
import xcat.schema.NameValuePair;
import soaprmi.util.logging.Logger;
import soaprmi.server.UnicastRemoteObject;
import soaprmi.soaprpc.SoapServices;
import org.exolab.castor.xml.Unmarshaller;
import java.util.Hashtable;
import java.util.Vector;
import java.io.StringReader;
import com.mysql.jdbc.Driver;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.PreparedStatement;
/**
* The Application Coordinator which is responsible for checkpointing and
* migration of an application (which is a set of components)
*/
public class AppCoordinatorImpl extends BasicPortImpl
implements AppCoordinator {
private static Logger logger = Logger.getLogger();
// map of component names and componentInfos
private Hashtable componentMap;
// map of component names and handles
private Hashtable instanceMap;
// connection graph for the components
private Hashtable connectionMap;
// a map for consistent checkpoints
private Hashtable checkpointMap;
// a map for intermediate checkpoints
private Hashtable tempCheckpointMap;
// a map for storing component migration status
private Hashtable migrationMap;
// number of components frozen not accounted for - needed for distributed checkpointing
private int outstandingFrozenComps;
// number of components who have stored their states already
private int numComponentsStateStored;
// the status of distributed checkpoint
private int checkpointStatus;
// the name of the application coordinator instance
private String applicationName;
// the unique ID for this application
private String applicationID;
// database parameters
String dburl;
String dbuser;
String dbpasswd;
boolean cInfoStored;
/**
* Default constructor
* @param applicationName_ the name of this application (coordinator)
* @param applicationID_ the unique ID for this application
*/
public AppCoordinatorImpl(String applicationName_,
String applicationID_)
throws Exception {
this(applicationName_, applicationID_, null, null, null);
}
/**
* Constructor
* @param applicationName_ the name of this application (coordinator)
* @param applicationID_ the unique ID for this application
* @param dburl_ the URL for the database to store the state
* @param dbuser_ the username to use while connecting to the database
* @param dbpasswd_ the password for the above user
*/
public AppCoordinatorImpl(String applicationName_,
String applicationID_,
String dburl_,
String dbuser_,
String dbpasswd_)
throws Exception {
applicationName = applicationName_;
applicationID = applicationID_;
dburl = dburl_;
dbuser = dbuser_;
dbpasswd = dbpasswd_;
cInfoStored = false;
// initialize the maps
componentMap = new Hashtable();
instanceMap = new Hashtable();
connectionMap = new Hashtable();
checkpointMap = new Hashtable();
tempCheckpointMap = new Hashtable();
migrationMap = new Hashtable();
// export the AppCoordinator as a Grid service
logger.finest("exporting AppCoordinator as a Grid service");
UnicastRemoteObject.exportObject(this,
new Class[]{AppCoordinatorCallback.class});
// set the outstanding number of components frozen to zero
outstandingFrozenComps = 0;
// set the number of components who have stored their states to zero
numComponentsStateStored = 0;
// set the checkpoint status
checkpointStatus = AppCoordinatorCallback.READY;
// load the MySQL database driver
new Driver();
}
//----------------------------------------------------------------//
// List of methods added to create the component connection graph //
//----------------------------------------------------------------//
/**
* Loads the state of the Application Coordinator from a database
*/
public void loadFromDatabase()
throws gov.cca.CCAException {
// make sure the database URL is not null
if (dburl == null)
throw new NonstandardException("Database URL is Null");
// connection information is already in database
cInfoStored = true;
try {
Connection conn = null;
// connect to the database
conn = DriverManager.getConnection(dburl,
dbuser,
dbpasswd);
// get the list of components for this application
String sqlStmt0 =
"select * from coordinator_table where " +
"application_id = '" + applicationID + "';";
PreparedStatement stmt0 = conn.prepareStatement(sqlStmt0);
ResultSet rs0 = stmt0.executeQuery();
// convert the result set into ComponentInfo[]'s
rs0.last();
ComponentInfo[] cInfo = new ComponentInfo[rs0.getRow()];
rs0.beforeFirst();
int i = 0;
while (rs0.next()) {
cInfo[i++] = new ComponentInfoImpl(rs0.getString("instance_name"),
rs0.getString("instance_handle"),
rs0.getString("instance_location"),
rs0.getString("creation_proto"),
rs0.getString("component_xml"));
}
// create a connection graph from retrieved information
createConnectionGraph(cInfo);
// load information about old checkpoints
String sqlStmt1 =
"select * from distributed_checkpoint_table where " +
"application_id = '" + applicationID + "';";
PreparedStatement stmt1 = conn.prepareStatement(sqlStmt1);
ResultSet rs1 = stmt1.executeQuery();
// convert the result set into ComponentInfo[]'s
rs1.beforeFirst();
while (rs1.next()) {
CheckpointInfo cpInfo =
new CheckpointInfo(rs1.getString("individual_storage_service_url"),
rs1.getString("storage_id"));
String instanceName =
(String) instanceMap.get(rs1.getString("instance_handle"));
checkpointMap.put(instanceName, cpInfo);
}
// clean up
conn.close();
} catch (Exception e) {
logger.severe("Error while loading AppCoordinator state from database", e);
throw new NonstandardException("Error while loading AppCoordinator state from database",
e);
}
}
/**
* Set up the connection graph using an array of ComponentInfo
* @param componentInfo an array of ComponentInfo objects
*/
public void createConnectionGraph(ComponentInfo[] componentInfo)
throws gov.cca.CCAException {
// set up the nodes
for (int i = 0; i < componentInfo.length; i++) {
addNode(componentInfo[i].getInstanceName(),
componentInfo[i]);
}
// set up the edges
for (int i = 0; i < componentInfo.length; i++) {
String usesComponentName = componentInfo[i].getInstanceName();
MobileComponentID cid =
new MobileComponentIDClientImpl(usesComponentName,
componentInfo[i].getInstanceHandle());
String[] usesPortNames = cid.getUsedPortNames();
// Add an edge for every uses port
for (int j = 0; j < usesPortNames.length; j++) {
logger.finest("Adding a connection for" +
" uses component: " +
usesComponentName +
", uses port: " +
usesPortNames[j]);
// Get the ConnectionID for this uses port
XCATConnectionInfo connInfo = cid.getConnectionInfo(usesPortNames[j]);
// If this port is not connected, skip to next iteration
if (connInfo == null)
continue;
// Add this edge physically
addEdge(connInfo.getUserName(),
connInfo.getUserIDHandle(),
connInfo.getUserPortName(),
connInfo.getProviderName(),
connInfo.getProviderIDHandle(),
connInfo.getProviderPortName());
}
}
// make sure a database url is provided, and this is already not done
if ((dburl == null) || cInfoStored)
return;
// Commit component information into database
Connection conn = null;
try {
// connect to the database
conn = DriverManager.getConnection(dburl,
dbuser,
dbpasswd);
// set autocommit to false so that all of the following is atomic
conn.setAutoCommit(false);
} catch (Exception e) {
logger.severe("Error while connecting to database", e);
throw new NonstandardException("Error while connecting to database", e);
}
try {
// for every ComponentInfo, add one insert statement
for (int i = 0; i < componentInfo.length; i++) {
String sqlStmt =
"insert into coordinator_table(instance_name, " +
"instance_location, creation_proto, component_xml, " +
"instance_handle, application_id) values (" +
"'" + componentInfo[i].getInstanceName() + "', " +
"'" + componentInfo[i].getInstanceLocation() + "', " +
"'" + componentInfo[i].getCreationProto() + "', " +
"'" + componentInfo[i].getComponentXML() + "', " +
"'" + componentInfo[i].getInstanceHandle() + "', " +
"'" + applicationID + "');";
PreparedStatement stmt = conn.prepareStatement(sqlStmt);
stmt.executeUpdate();
}
// add a single entry into the application map table
String sqlStmt =
"insert into app_map_table(application_id, application_name) values " +
"('" + applicationID + "', '" + applicationName + "');";
PreparedStatement stmt = conn.prepareStatement(sqlStmt);
stmt.executeUpdate();
// commit all inserts
conn.commit();
// clean up
conn.close();
} catch (Exception e) {
logger.severe("Error while trying to store state into database: " +
" Trying to rollback", e);
// try to rollback
try {
conn.rollback();
} catch (Exception re) {
logger.severe("Error while trying to store state into database: " +
" Rollback failed", re);
throw new NonstandardException("Error while trying to store state into database: " +
" Rollback failed", re);
}
throw new NonstandardException("Error while trying to store state into database: " +
" Rollback successful", e);
}
}
/**
* Add a component instance to the component graph
* @param componentName the instanceName for the component
* @param componentInfo the information for this component
*/
private void addNode(String componentName,
ComponentInfo componentInfo)
throws gov.cca.CCAException {
logger.finest("called with component name: " + componentName);
if (componentMap.containsKey(componentName))
throw new NonstandardException("Component " + componentName +
" already exists");
// add the ComponentInfo for this component
componentMap.put(componentName, componentInfo);
// add a mapping from instanceHandle to componentName
instanceMap.put(componentInfo.getInstanceHandle(), componentName);
}
/**
* Add an edge to the component graph
* @param usesComponentName the instanceName for the uses side of the connection
* @param usesComponentHandle the GSH of the uses component
* @param usesPortName the name of the uses port
* @param providesComponentName the instanceName for the provides side
* @param providesComponentHandle the GSH of the provides component
* @param providesPortName the name of the provides port
*/
private void addEdge(String usesComponentName,
String usesComponentHandle,
String usesPortName,
String providesComponentName,
String providesComponentHandle,
String providesPortName)
throws gov.cca.CCAException {
logger.finest("called with uses component: " + usesComponentName +
" and provides component: " + providesComponentName);
if (!componentMap.containsKey(usesComponentName))
throw new NonstandardException("Component " + usesComponentName +
" not present in the graph");
if (!componentMap.containsKey(providesComponentName))
throw new NonstandardException("Component " + providesComponentName +
" not present in the graph");
// retrieve componentIDs for the uses and provides side
MobileComponentID userID =
new MobileComponentIDClientImpl(usesComponentName,
usesComponentHandle);
MobileComponentID providerID =
new MobileComponentIDClientImpl(providesComponentName,
providesComponentHandle);
// create a ConnectionID for this connection
XCATConnectionID connID =
new XCATConnectionIDImpl(providerID,
userID,
providesPortName,
usesPortName,
providesComponentHandle);
// add this connectionID for the provides side
if (connectionMap.containsKey(providesComponentName)) {
Vector connIDs = (Vector) connectionMap.get(providesComponentName);
connIDs.add(connID);
} else {
Vector connIDs = new Vector();
connIDs.add(connID);
connectionMap.put(providesComponentName, connIDs);
}
}
//------------------------------------------------------//
// List of methods added to support migration //
//------------------------------------------------------//
/**
* A request to migrate a component, which might be triggered by several reasons
* @param componentName the instance name of the component requesting migration
* @param targetLocation the location that the component should migrate to
* @param masterStorageServiceURL the URL of the Master Storage Service
*/
public void migrateComponent(String componentName,
String targetLocation,
String masterStorageServiceURL)
throws gov.cca.CCAException {
// Retrieve information for this component
if (!componentMap.containsKey(componentName))
throw new NonstandardException("Unknown component: " + componentName);
ComponentInfo cInfo = (ComponentInfo) componentMap.get(componentName);
// create empty connection list if entry for this component doesn't exist
if (!connectionMap.containsKey(componentName)) {
Vector connIDs = new Vector();
connectionMap.put(componentName, connIDs);
}
// Get a list of connections for this component
Vector connIDs = (Vector) connectionMap.get(componentName);
// Send migration requests to all connected components
int numConns = connIDs.size();
logger.finest("found " + numConns + " connection " +
"for component " + componentName);
// create an entry into the migrationMap
MigrationInfo mInfo = new MigrationInfo(numConns);
migrationMap.put(componentName, mInfo);
for (int i = 0; i < numConns; i++) {
XCATConnectionID connID = (XCATConnectionID) connIDs.get(i);
MobileComponentID userID = (MobileComponentID) connID.getUser();
userID.requestMigration(componentName,
connID.getUserPortName(),
getGSH());
}
// Wait till all users are OK with migration
synchronized(mInfo) {
if (mInfo.getApprovedUsers() < numConns) {
try {
mInfo.wait();
} catch (InterruptedException ie) {
logger.severe("Exception when waiting for uses sides to approve migration",
ie);
throw new NonstandardException("Exception when waiting for migration approval",
ie);
}
}
}
// check if the above call was successful
if (mInfo.getMigrationStatus() == AppCoordinatorCallback.EXCEPTION)
throw new NonstandardException("Remote side did not approve migration");
// Confirm to all users that provider is migrating
for (int i = 0; i < numConns; i++) {
XCATConnectionID connID = (XCATConnectionID) connIDs.get(i);
MobileComponentID userID = (MobileComponentID) connID.getUser();
userID.confirmMigration(connID.getUserPortName());
}
// Freeze the execution of the component
MobileComponentID cid =
new MobileComponentIDClientImpl(cInfo.getInstanceName(),
cInfo.getInstanceHandle());
cid.freezeComponent(getGSH());
// Wait till the component sends back a notification that it is frozen
synchronized(mInfo) {
if (!mInfo.getIsFrozen()) {
try {
mInfo.wait();
} catch (InterruptedException ie) {
logger.severe("Exception when waiting for migration to complete",
ie);
throw new NonstandardException("Exception when waiting for migration to complete",
ie);
}
}
}
// decrement number of outstanding frozen components
synchronized(this) {
outstandingFrozenComps--;
}
// check if the above call was successful
if (mInfo.getMigrationStatus() == AppCoordinatorCallback.EXCEPTION)
throw new NonstandardException("Remote component threw exception while being frozen");
// Store the individual component state into persistent storage
MasterStorageService mss = (MasterStorageService)
URLToReference.createReference(masterStorageServiceURL,
MasterStorageService.class.getName());
String individualStorageServiceURL = mss.getIndividualStorageServiceLocation();
String storageID = cid.storeIndividualComponentState(individualStorageServiceURL);
// destroy the remote component
try {
cid.destroy();
} catch (Exception e) {
logger.severe("Caught exception while trying to destroy component",
e);
throw new NonstandardException("Caught exception while trying to destroy component",
e);
}
// create a new instance of the component at the specified location
cInfo.setInstanceLocation(targetLocation);
createComponentInstance(cInfo);
// retrieve the state for the migrated component
cid.loadComponentState(individualStorageServiceURL, storageID);
// delete state from storage service
IndividualStorageService iss = (IndividualStorageService)
URLToReference.createReference(individualStorageServiceURL,
IndividualStorageService.class.getName());
iss.deleteState(storageID);
// resume execution of the migrated component
cid.resumeExecution();
// set the isFrozen false to false
mInfo.setIsFrozen(false);
// send a notification that all connected components that migration is complete
for (int i = 0; i < numConns; i++) {
XCATConnectionID connID = (XCATConnectionID) connIDs.get(i);
MobileComponentID userID = (MobileComponentID) connID.getUser();
userID.migrationComplete(connID.getUserPortName());
}
// get rid of the entry inside migrationMap
migrationMap.remove(componentName);
}
/**
* Creates an instance of the component from the ComponentInfo object
* @param cInfo the ComponentInfo object for the component needing instantiation
*/
private void createComponentInstance(ComponentInfo cInfo)
throws gov.cca.CCAException {
// create a new instance of the component
ExecutionEnv[] executionEnvList = null;
try {
// parse the xml description of the component
StringReader readerCompStaticInfo = new StringReader(cInfo.getComponentXML());
ComponentStaticInformation compStaticInfo =
(ComponentStaticInformation)Unmarshaller.unmarshal
(ComponentStaticInformation.class, readerCompStaticInfo);
executionEnvList = compStaticInfo.getExecutionEnv();
} catch (Exception e) {
logger.severe("Exception thrown while parsing Component XML", e);
throw new NonstandardException("Exception while parsing Component XML", e);
}
// create a new TypeMap and fill in the values
TypeMap componentEnv = new TypeMapImpl();
componentEnv.putString("execHost", cInfo.getInstanceLocation());
componentEnv.putString("creationProto", cInfo.getCreationProto());
// check if a valid installation exists for this host/proto
boolean found = false;
for (int k = 0; k < executionEnvList.length; k++) {
ExecutionEnv executionEnv = executionEnvList[k];
String[] hostName = executionEnv.getHostName();
String[] creationMech = executionEnv.getCreationProto();
// check if there is a match for the hostName
int h;
for (h = 0; h < hostName.length; h++) {
if (hostName[h].equals(cInfo.getInstanceLocation())) {
break;
}
}
// if h equals hostName.length, then skip
if (h == hostName.length)
continue;
// check if there is a match for creationProto
int m;
for (m = 0; m < creationMech.length; m++) {
if (creationMech[m].equals(cInfo.getCreationProto())) {
break;
}
}
// if h equals creationMech.length, then skip
if (m == creationMech.length)
continue;
// if we get here, this is a valid installation for this
// creationProto/execHost pair
NameValuePair[] nameValuePairList =
executionEnv.getNameValuePair();
for (int l = 0; l < nameValuePairList.length; l++) {
NameValuePair nameValuePair = nameValuePairList[l];
String nameOfVariable = nameValuePair.getName();
String value = nameValuePair.getValue();
componentEnv.putString(nameOfVariable, value);
}
// found an installation, break here
found = true;
break;
}
// if no valid installation found, throw an Exception
if (!found) {
String message = new String ("No valid installation for : " +
cInfo.getInstanceLocation() + " , " +
cInfo.getCreationProto());
logger.severe(message);
throw new NonstandardException(message);
}
// get the name of the class for the component
String className = componentEnv.getString("className",
"None");
if (className.equals("None"))
throw new NonstandardException("Property className for component not defined");
// notify that this is a migration
componentEnv.putString("componentHandle", cInfo.getInstanceHandle());
componentEnv.putBool("isMigrated", true);
// invoke the Builder Service to create a new instance
XCATBuilderService builderService = null;
try {
builderService = new XCATBuilderServiceImpl(false);
} catch (Exception e) {
logger.severe("Can't instantiate Builder service", e);
throw new NonstandardException("Can't instantiate Builder service", e);
}
String builderGSH = HandleResolver.createGSH("coordinatorbuilderService");
builderService.setGSH(builderGSH);
HandleResolver.addReference(builderGSH, builderService);
builderService.createInstance(cInfo.getInstanceName(),
className,
componentEnv);
}
//--------------------------------------------------------//
// List of methods added for distributed checkpointing //
//--------------------------------------------------------//
/**
* A request to the AppCoordinator to checkpoint all components
* @param masterStorageServiceURL the URL of the Master Storage Service
*/
public void checkpointComponents(String masterStorageServiceURL)
throws gov.cca.CCAException {
logger.finest("called");
// send a request to freeze execution of all components
long time0 = System.currentTimeMillis();
Object[] componentList = componentMap.values().toArray();
for (int i = 0; i < componentList.length; i++) {
ComponentInfo cInfo = (ComponentInfo) componentList[i];
MobileComponentID cid =
new MobileComponentIDClientImpl(cInfo.getInstanceName(),
cInfo.getInstanceHandle());
cid.freezeComponent(getGSH());
}
long time1 = System.currentTimeMillis();
logger.info("Freeze request: " + (time1 - time0));
// wait till all components are frozen
if (outstandingFrozenComps < componentList.length) {
synchronized(this) {
try {
wait();
} catch (InterruptedException ie) {
logger.severe("Exception when waiting for component to be frozen",
ie);
throw new NonstandardException("Exception when waiting for component to be frozen",
ie);
}
// set the number of outstanding frozen components to 0
outstandingFrozenComps = 0;
// check if the above call was successful
if (checkpointStatus == AppCoordinatorCallback.EXCEPTION)
throw new NonstandardException("Exception while freezing components");
}
}
long time2 = System.currentTimeMillis();
logger.info("Time for components to freeze: " + (time2 - time1));
// send request to store the state of the components
MasterStorageService mss = (MasterStorageService)
URLToReference.createReference(masterStorageServiceURL,
MasterStorageService.class.getName());
for (int i = 0; i < componentList.length; i++) {
String individualStorageServiceURL = mss.getIndividualStorageServiceLocation();
ComponentInfo cInfo = (ComponentInfo) componentList[i];
MobileComponentID cid =
new MobileComponentIDClientImpl(cInfo.getInstanceName(),
cInfo.getInstanceHandle());
cid.appendStateToCheckpoint(individualStorageServiceURL, getGSH());
}
// wait till all components have stored their states
if (numComponentsStateStored < componentList.length) {
synchronized(this) {
try {
wait();
} catch (InterruptedException ie) {
logger.severe("Exception when waiting for components to store states",
ie);
throw new NonstandardException("Exception when waiting for components to store states",
ie);
}
// set the number of outstanding frozen components to 0
numComponentsStateStored = 0;
// check if the above call was successful
if (checkpointStatus == AppCoordinatorCallback.EXCEPTION)
throw new NonstandardException("Exception while storing component state");
}
}
long time3 = System.currentTimeMillis();
logger.info("Storing checkpoints: " + (time3 - time2));
// Atomically update locations of checkpoints in the database
Hashtable oldCheckpointMap = checkpointMap;
checkpointMap = tempCheckpointMap;
tempCheckpointMap = new Hashtable();
if (dburl != null) {
// Commit component information into database
Connection conn = null;
try {
// connect to the database
conn = DriverManager.getConnection(dburl,
dbuser,
dbpasswd);
// set autocommit to false so that all of the following is atomic
conn.setAutoCommit(false);
} catch (Exception e) {
logger.severe("Error while connecting to database", e);
throw new NonstandardException("Error while connecting to database", e);
}
try {
// for every component, update the distributed_checkpoint_table
for (int i = 0; i < componentList.length; i++) {
ComponentInfo cInfo = (ComponentInfo) componentList[i];
CheckpointInfo cpInfo =
(CheckpointInfo) checkpointMap.get(cInfo.getInstanceName());
// update the entry into the table
// have to delete + insert since MySQL 4.0.x doesn't support
// ON DUPLICATE KEY UPDATE
String sqlStmt0 =
"delete from distributed_checkpoint_table where " +
"instance_handle = '" + cInfo.getInstanceHandle() + "';";
PreparedStatement stmt0 = conn.prepareStatement(sqlStmt0);
stmt0.executeUpdate();
String sqlStmt1 =
"insert into distributed_checkpoint_table" +
"(instance_handle, application_id, " +
"individual_storage_service_url, " +
"storage_id) values (" +
"'" + cInfo.getInstanceHandle() + "', " +
"'" + applicationID + "', " +
"'" + cpInfo.getStorageServiceURL() + "', " +
"'" + cpInfo.getStorageID() + "');";
PreparedStatement stmt1 = conn.prepareStatement(sqlStmt1);
stmt1.executeUpdate();
}
// commit all inserts
conn.commit();
// clean up
conn.close();
} catch (Exception e) {
logger.severe("Error while trying to store checkpoint locations into database: " +
" Trying to rollback", e);
// try to rollback
try {
conn.rollback();
} catch (Exception re) {
logger.severe("Error while trying to store checkpoint locations into database: " +
" Rollback failed", re);
throw new NonstandardException("Error while trying to store checkpoint locations " +
"into database: Rollback failed", re);
}
throw new NonstandardException("Error while trying to store checkpoint locations " +
"into database: Rollback successful", e);
}
}
// delete old checkpoints
Object[] cpList = oldCheckpointMap.values().toArray();
for (int i = 0; i < cpList.length; i++) {
CheckpointInfo cpInfo = (CheckpointInfo) cpList[i];
IndividualStorageService iss = (IndividualStorageService)
URLToReference.createReference(cpInfo.getStorageServiceURL(),
IndividualStorageService.class.getName());
iss.deleteState(cpInfo.getStorageID());
}
long time4 = System.currentTimeMillis();
logger.info("Committing checkpoints: " + (time4 - time3));
// send a notification to components that checkpointing is complete
for (int i = 0; i < componentList.length; i++) {
ComponentInfo cInfo = (ComponentInfo) componentList[i];
MobileComponentID cid =
new MobileComponentIDClientImpl(cInfo.getInstanceName(),
cInfo.getInstanceHandle());
cid.unfreezeComponent();
}
long time5 = System.currentTimeMillis();
logger.info("Unfreezing components: " + (time5 - time4));
}
/**
* A request to the AppCoordinator to restart from latest checkpoint
*/
public void restartFromCheckpoint()
throws gov.cca.CCAException {
// destroy all the components, if they are still alive
Object[] componentList = componentMap.values().toArray();
for (int i = 0; i < componentList.length; i++) {
ComponentInfo cInfo = (ComponentInfo) componentList[i];
MobileComponentID cid =
new MobileComponentIDClientImpl(cInfo.getInstanceName(),
cInfo.getInstanceHandle());
try {
cid.destroy();
} catch (Exception e) {
logger.severe("Caught exception while trying to destroy component",
e);
// continue since the component may be dead already
}
}
// instantiate all components again
for (int i = 0; i < componentList.length; i++) {
ComponentInfo cInfo = (ComponentInfo) componentList[i];
createComponentInstance(cInfo);
}
// load the checkpointed state
for (int i = 0; i < componentList.length; i++) {
ComponentInfo cInfo = (ComponentInfo) componentList[i];
CheckpointInfo cpInfo = (CheckpointInfo) checkpointMap.get(cInfo.getInstanceName());
MobileComponentID cid =
new MobileComponentIDClientImpl(cInfo.getInstanceName(),
cInfo.getInstanceHandle());
cid.loadComponentState(cpInfo.getStorageServiceURL(),
cpInfo.getStorageID());
}
// tell all components to proceed
for (int i = 0; i < componentList.length; i++) {
ComponentInfo cInfo = (ComponentInfo) componentList[i];
MobileComponentID cid =
new MobileComponentIDClientImpl(cInfo.getInstanceName(),
cInfo.getInstanceHandle());
cid.resumeExecution();
}
}
//----------------------------------------------------//
// List of callback methods for the AppCoordinator //
//----------------------------------------------------//
/**
* A notification that this component has been frozen
* @param componentName the instance name of the component undergoing migration
* @param status Possible values: AppCoordinatorCallback.FROZEN,
* AppCoordinatorCallback.EXCEPTION
*/
public void componentFrozen(String componentName,
int status)
throws gov.cca.CCAException {
logger.finest("called for component: " + componentName);
if (!componentMap.containsKey(componentName))
throw new NonstandardException("Unknown component: " + componentName);
synchronized(this) {
// increment the number of outstanding components frozen
outstandingFrozenComps++;
// if this is an exception, set the appropriate flag
if (status == AppCoordinatorCallback.EXCEPTION)
checkpointStatus = status;
// wake up sleeping thread if all components have been frozen
if (outstandingFrozenComps == componentMap.size())
notify();
}
// the following is executed only during migration
MigrationInfo mInfo =
(MigrationInfo) migrationMap.get(componentName);
if (mInfo != null) {
synchronized(mInfo) {
// set a flag to signify that component has been frozen
mInfo.setIsFrozen(true);
// if this is an exception, set the appropriate flag
if (status == AppCoordinatorCallback.EXCEPTION)
mInfo.setMigrationStatus(status);
// wake up thread which is sleeping for migration to complete
mInfo.notify();
}
}
}
/**
* A notification that the component has stored its state
* @param componentName the instance name of the component undergoing migration
* @param storageServiceURL the URL of the Individual Storage Service used
* @param storageID the storageID returned by the Individual Storage Service
* @param status Possible values: AppCoordinatorCallback.FROZEN,
* AppCoordinatorCallback.EXCEPTION
*/
public void componentCheckpointed(String componentName,
String storageServiceURL,
String storageID,
int status)
throws gov.cca.CCAException {
logger.finest("called for component: " + componentName);
if (!componentMap.containsKey(componentName))
throw new NonstandardException("Unknown component: " + componentName);
// add checkpoint information inside the checkpointMap
CheckpointInfo cpInfo = new CheckpointInfo(storageServiceURL,
storageID);
tempCheckpointMap.put(componentName, cpInfo);
synchronized(this) {
// increment the number of components who have stored their state
numComponentsStateStored++;
// if this is an exception, set the appropriate flag
if (status == AppCoordinatorCallback.EXCEPTION)
checkpointStatus = status;
// wake up sleeping thread if all components have been frozen
if (numComponentsStateStored == componentMap.size())
notify();
}
}
/**
* A notification that the component using this uses port is
* ready for migration of the provides side
* @param providesComponentName the name of the provides side requesting migration
* @param componentName the instance name for component with the uses port
* @param usingPortName the name of the uses port that is connected to
* the component undergoing migration
* @param status Possible values: AppCoordinor.READY, AppCoordinator.EXCEPTION
*/
public void migrationApproval(String providesComponentName,
String componentName,
String usingPortName,
int status)
throws gov.cca.CCAException {
logger.finest("called by component : " + componentName +
" for uses port: " + usingPortName +
" with status: " + status +
" for component: " + providesComponentName);
if (!componentMap.containsKey(providesComponentName) ||
!migrationMap.containsKey(providesComponentName))
throw new NonstandardException("Unknown component: " + providesComponentName);
MigrationInfo mInfo =
(MigrationInfo) migrationMap.get(providesComponentName);
synchronized(mInfo) {
// increment the number of users who approve this migration
mInfo.incrApprovedUsers();
// if this is an exception message, set it inside mStatus
if (status == AppCoordinatorCallback.EXCEPTION)
mInfo.setMigrationStatus(status);
// notify sleeping thread if all uses sides have approved migration
if (mInfo.getApprovedUsers() == mInfo.getNumUsers())
mInfo.notify();
}
}
}