package org.syrup.sql;
import org.syrup.Data;
import org.syrup.Function;
import org.syrup.LogEntry;
import org.syrup.LogEntryTemplate;
import org.syrup.PTask;
import org.syrup.PTaskTemplate;
import org.syrup.Result;
import org.syrup.WorkSpace;
import org.syrup.helpers.ExecutionMonitor;
import org.syrup.jndi.SyrupFactory;
import java.io.Serializable;
import java.sql.Connection;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.sql.DataSource;
/**
* Provides a JDBC implementation of a WorkSpace.
*
* @author Robbert van Dalen
*/
public class SQLWorkSpace implements WorkSpace, Serializable, Referenceable
{
static final String COPYRIGHT = "Copyright 2005 Robbert van Dalen."
+ "At your option, you may copy, distribute, or make derivative works under "
+ "the terms of The Artistic License. This License may be found at "
+ "http://www.opensource.org/licenses/artistic-license.php. "
+ "THERE IS NO WARRANTY; USE THIS PRODUCT AT YOUR OWN RISK.";
// [TODO: make the datasource operate in the default Transaction context =
// enable JTA],
// Do not serialize the dataSource
private transient DataSource dataSource = null;
// Do not serialize the sqlImpl
private transient SQLImpl sqlImpl = null;
public static final long serialVersionUID = 1;
private final static Logger logger = Logger.getLogger("org.syrup.sql.SQLWorkSpace");
/**
*/
public Reference getReference() throws NamingException
{
return new Reference(SQLWorkSpace.class.getName(), new StringRefAddr("Syrup SQL WorkSpace", "default"), SyrupFactory.class.getName(), null);
}
/**
* Returns the DataSource to create JDBC connections from.
*
* @return the Datasource to create JDBC connections from.
*/
protected synchronized DataSource dataSource() throws Exception
{
if (dataSource == null)
{
dataSource = (DataSource) (new InitialContext().lookup("syrupDataSource"));
}
return dataSource;
}
/**
* Returns the JDBC Connection that is used internally.
*
* @return The JDBC Connection.
*/
protected Connection connection() throws Exception
{
Connection con = dataSource().getConnection();
if (con != null)
{
con.setAutoCommit(false);
return con;
}
throw new Exception("could not get connection from "
+ dataSource());
}
/**
* Return the SyrupConnection that is used to optimize SQL commands.
*
* @return the SyrupConnection
*/
protected SyrupConnection syrupConnection() throws Exception
{
return new SyrupConnection(connection());
}
/**
* Returns an instance of a SQLImpl. The implementation is fetched using the
* JDNI initial context with key 'syrupSQLImpl'.
*
* @return an instance of a SQLImpl.
*/
public synchronized SQLImpl sqlImpl()
{
if (sqlImpl == null)
{
try
{
sqlImpl = (SQLImpl) (new InitialContext().lookup("syrupSQLImpl"));
}
catch (Exception e)
{
logger.log(Level.INFO, "Did not get syrupSQLImpl key via JDNI. Reverted to default SQLImpl implementation");
// Revert to default implementation.
sqlImpl = new SQLImpl();
}
}
return sqlImpl;
}
/**
* Returns the read-only SyrupConnection that is used internally.
*
* @return The read-only SyrupConnection.
*/
protected SyrupConnection readConnection() throws Exception
{
SyrupConnection c = syrupConnection();
c.setTransactionIsolation(java.sql.Connection.TRANSACTION_REPEATABLE_READ);
return c;
}
/**
* Returns the read-write SyrupConnection that is used internally.
*
* @return The read-write SyrupConnection Connection.
*/
protected SyrupConnection writeConnection() throws Exception
{
SyrupConnection c = syrupConnection();
c.setTransactionIsolation(java.sql.Connection.TRANSACTION_SERIALIZABLE);
return c;
}
/**
*/
public void reset() throws Exception
{
SyrupConnection con = null;
try
{
con = writeConnection();
sqlImpl().genericFunctions().reset(con);
}
finally
{
sqlImpl().genericFunctions().close(con);
}
}
/**
*/
public void set_in_1(Data data) throws Exception
{
SyrupConnection con = null;
try
{
con = writeConnection();
sqlImpl().genericFunctions().set_in_1(data, con);
}
finally
{
sqlImpl().genericFunctions().close(con);
}
}
/**
*/
public void set_in_2(Data data) throws Exception
{
SyrupConnection con = null;
try
{
con = writeConnection();
sqlImpl().genericFunctions().set_in_2(data, con);
}
finally
{
sqlImpl().genericFunctions().close(con);
}
}
/**
*/
public Data get_out_1() throws Exception
{
SyrupConnection con = null;
try
{
con = writeConnection();
return sqlImpl().genericFunctions().get_out_1(con);
}
finally
{
sqlImpl().genericFunctions().close(con);
}
}
/**
*/
public Data get_out_2() throws Exception
{
SyrupConnection con = null;
try
{
con = writeConnection();
return sqlImpl().genericFunctions().get_out_2(con);
}
finally
{
sqlImpl().genericFunctions().close(con);
}
}
/**
*/
public PTask[] match(PTaskTemplate template) throws Exception
{
PTask[] p = null;
SyrupConnection con = null;
try
{
con = readConnection();
p = sqlImpl().genericFunctions().match(template, con);
con.commit();
}
finally
{
sqlImpl().genericFunctions().close(con);
}
return p;
}
/**
*/
public org.syrup.Context[] get(PTaskTemplate t) throws Exception
{
SyrupConnection con = null;
org.syrup.Context c[] = null;
try
{
con = readConnection();
c = sqlImpl().genericFunctions().get(t, con);
con.commit();
}
finally
{
sqlImpl().genericFunctions().close(con);
}
return c;
}
/**
*/
public PTask stop(PTask task) throws Exception
{
SyrupConnection con = null;
try
{
con = writeConnection();
return sqlImpl().genericFunctions().stop(task, con);
}
finally
{
sqlImpl().genericFunctions().close(con);
}
}
/**
*/
public PTask[] remove(PTask[] tasks) throws Exception
{
// Collecting garbage should carefully implemented - but not now.
throw new Exception("Not implemented.");
}
/**
*/
public PTask execute(PTask pt) throws Exception
{
// [TODO: scrutinize the control flow - is it correct in all cases?]
Result r = null;
org.syrup.Context c = null;
SyrupConnection con = null;
String p = null;
try
{
// Register the callee which is the Worker executing the PTask.
// Request sent to the the Worker are from this point on,
// Replied.
p = ExecutionMonitor.checkin(pt.key());
// Here is a small gap: The Worker can respond to requests but this
// isn't known to the WorkSpace and thus the address cannot be known
// by other Workers.
// Conclusion: no problem here, because the Worker isn't known to
// anyone.
try
{
// The execution of the PTask has started - make this known to
// the WorkSpace.
con = writeConnection();
c = sqlImpl().executionFunctions().start(pt, p, con);
}
finally
{
con.rollback();
sqlImpl().genericFunctions().close(con);
}
// From here, there are no JDBC Cnnections open, so the Function can run for
// a long period of time without holding an open Connection.
if (c != null)
{
// Find the Function to execute - could throw an exception if
// not found.
Class fClass = Class.forName(c.task().functionClass());
Function f = (Function) fClass.newInstance();
// Executes it.
r = f.execute(c);
}
if (r != null)
{
// Got a result - commit this to the WorkSpace.
try
{
con = writeConnection();
pt = sqlImpl().executionFunctions().commit_result(r, con);
}
finally
{
con.rollback();
}
}
else
{
// Got no result. There can be different reasons, but probably
// there the PTask was already taken by another Worker.
// Fall through.
}
}
finally
{
// Deregister the Worker (could be after failure/execptions like
// deadlocks). Any Request sent to the Worker get no
// Reply after deregistering.
if (p != null)
{
ExecutionMonitor.checkout(pt.key());
// Make sure that this Worker (or others) are stopped and
// that this is known to the Workspace.
stop(pt);
}
}
return pt;
}
/*
*/
public LogEntry[] match(LogEntryTemplate template) throws Exception
{
LogEntry[] l = null;
SyrupConnection con = null;
try
{
con = readConnection();
l = sqlImpl().genericFunctions().match(template, con);
con.commit();
}
finally
{
sqlImpl().genericFunctions().close(con);
}
return l;
}
}