001: package org.syrup.sql;
002:
003: import org.syrup.Data;
004: import org.syrup.Function;
005: import org.syrup.LogEntry;
006: import org.syrup.LogEntryTemplate;
007: import org.syrup.PTask;
008: import org.syrup.PTaskTemplate;
009: import org.syrup.Result;
010: import org.syrup.WorkSpace;
011: import org.syrup.helpers.ExecutionMonitor;
012: import org.syrup.jndi.SyrupFactory;
013:
014: import java.io.Serializable;
015: import java.sql.Connection;
016: import java.util.logging.Level;
017: import java.util.logging.Logger;
018:
019: import javax.naming.InitialContext;
020: import javax.naming.NamingException;
021: import javax.naming.Reference;
022: import javax.naming.Referenceable;
023: import javax.naming.StringRefAddr;
024: import javax.sql.DataSource;
025:
026: /**
027: * Provides a JDBC implementation of a WorkSpace.
028: *
029: * @author Robbert van Dalen
030: */
031: public class SQLWorkSpace implements WorkSpace, Serializable,
032: Referenceable {
033: static final String COPYRIGHT = "Copyright 2005 Robbert van Dalen."
034: + "At your option, you may copy, distribute, or make derivative works under "
035: + "the terms of The Artistic License. This License may be found at "
036: + "http://www.opensource.org/licenses/artistic-license.php. "
037: + "THERE IS NO WARRANTY; USE THIS PRODUCT AT YOUR OWN RISK.";
038:
039: // [TODO: make the datasource operate in the default Transaction context =
040: // enable JTA],
041:
042: // Do not serialize the dataSource
043: private transient DataSource dataSource = null;
044:
045: // Do not serialize the sqlImpl
046: private transient SQLImpl sqlImpl = null;
047:
048: public static final long serialVersionUID = 1;
049:
050: private final static Logger logger = Logger
051: .getLogger("org.syrup.sql.SQLWorkSpace");
052:
053: /**
054: */
055: public Reference getReference() throws NamingException {
056: return new Reference(SQLWorkSpace.class.getName(),
057: new StringRefAddr("Syrup SQL WorkSpace", "default"),
058: SyrupFactory.class.getName(), null);
059: }
060:
061: /**
062: * Returns the DataSource to create JDBC connections from.
063: *
064: * @return the Datasource to create JDBC connections from.
065: */
066: protected synchronized DataSource dataSource() throws Exception {
067: if (dataSource == null) {
068: dataSource = (DataSource) (new InitialContext()
069: .lookup("syrupDataSource"));
070: }
071: return dataSource;
072: }
073:
074: /**
075: * Returns the JDBC Connection that is used internally.
076: *
077: * @return The JDBC Connection.
078: */
079: protected Connection connection() throws Exception {
080: Connection con = dataSource().getConnection();
081: if (con != null) {
082: con.setAutoCommit(false);
083: return con;
084: }
085: throw new Exception("could not get connection from "
086: + dataSource());
087: }
088:
089: /**
090: * Return the SyrupConnection that is used to optimize SQL commands.
091: *
092: * @return the SyrupConnection
093: */
094:
095: protected SyrupConnection syrupConnection() throws Exception {
096: return new SyrupConnection(connection());
097: }
098:
099: /**
100: * Returns an instance of a SQLImpl. The implementation is fetched using the
101: * JDNI initial context with key 'syrupSQLImpl'.
102: *
103: * @return an instance of a SQLImpl.
104: */
105: public synchronized SQLImpl sqlImpl() {
106: if (sqlImpl == null) {
107: try {
108: sqlImpl = (SQLImpl) (new InitialContext()
109: .lookup("syrupSQLImpl"));
110: } catch (Exception e) {
111: logger
112: .log(
113: Level.INFO,
114: "Did not get syrupSQLImpl key via JDNI. Reverted to default SQLImpl implementation");
115: // Revert to default implementation.
116: sqlImpl = new SQLImpl();
117: }
118: }
119: return sqlImpl;
120: }
121:
122: /**
123: * Returns the read-only SyrupConnection that is used internally.
124: *
125: * @return The read-only SyrupConnection.
126: */
127: protected SyrupConnection readConnection() throws Exception {
128: SyrupConnection c = syrupConnection();
129: c
130: .setTransactionIsolation(java.sql.Connection.TRANSACTION_REPEATABLE_READ);
131: return c;
132: }
133:
134: /**
135: * Returns the read-write SyrupConnection that is used internally.
136: *
137: * @return The read-write SyrupConnection Connection.
138: */
139: protected SyrupConnection writeConnection() throws Exception {
140: SyrupConnection c = syrupConnection();
141: c
142: .setTransactionIsolation(java.sql.Connection.TRANSACTION_SERIALIZABLE);
143: return c;
144: }
145:
146: /**
147: */
148: public void reset() throws Exception {
149: SyrupConnection con = null;
150:
151: try {
152: con = writeConnection();
153: sqlImpl().genericFunctions().reset(con);
154: } finally {
155: sqlImpl().genericFunctions().close(con);
156: }
157: }
158:
159: /**
160: */
161: public void set_in_1(Data data) throws Exception {
162: SyrupConnection con = null;
163:
164: try {
165: con = writeConnection();
166: sqlImpl().genericFunctions().set_in_1(data, con);
167: } finally {
168: sqlImpl().genericFunctions().close(con);
169: }
170: }
171:
172: /**
173: */
174: public void set_in_2(Data data) throws Exception {
175: SyrupConnection con = null;
176:
177: try {
178: con = writeConnection();
179: sqlImpl().genericFunctions().set_in_2(data, con);
180: } finally {
181: sqlImpl().genericFunctions().close(con);
182: }
183: }
184:
185: /**
186: */
187: public Data get_out_1() throws Exception {
188: SyrupConnection con = null;
189:
190: try {
191: con = writeConnection();
192: return sqlImpl().genericFunctions().get_out_1(con);
193: } finally {
194: sqlImpl().genericFunctions().close(con);
195: }
196: }
197:
198: /**
199: */
200: public Data get_out_2() throws Exception {
201: SyrupConnection con = null;
202:
203: try {
204: con = writeConnection();
205: return sqlImpl().genericFunctions().get_out_2(con);
206: } finally {
207: sqlImpl().genericFunctions().close(con);
208: }
209: }
210:
211: /**
212: */
213: public PTask[] match(PTaskTemplate template) throws Exception {
214: PTask[] p = null;
215: SyrupConnection con = null;
216:
217: try {
218: con = readConnection();
219: p = sqlImpl().genericFunctions().match(template, con);
220: con.commit();
221: } finally {
222: sqlImpl().genericFunctions().close(con);
223: }
224: return p;
225: }
226:
227: /**
228: */
229: public org.syrup.Context[] get(PTaskTemplate t) throws Exception {
230: SyrupConnection con = null;
231: org.syrup.Context c[] = null;
232:
233: try {
234: con = readConnection();
235: c = sqlImpl().genericFunctions().get(t, con);
236: con.commit();
237: } finally {
238: sqlImpl().genericFunctions().close(con);
239: }
240: return c;
241: }
242:
243: /**
244: */
245: public PTask stop(PTask task) throws Exception {
246: SyrupConnection con = null;
247:
248: try {
249: con = writeConnection();
250: return sqlImpl().genericFunctions().stop(task, con);
251: } finally {
252: sqlImpl().genericFunctions().close(con);
253: }
254: }
255:
256: /**
257: */
258: public PTask[] remove(PTask[] tasks) throws Exception {
259: // Collecting garbage should carefully implemented - but not now.
260: throw new Exception("Not implemented.");
261: }
262:
263: /**
264: */
265: public PTask execute(PTask pt) throws Exception {
266: // [TODO: scrutinize the control flow - is it correct in all cases?]
267: Result r = null;
268: org.syrup.Context c = null;
269: SyrupConnection con = null;
270:
271: String p = null;
272:
273: try {
274: // Register the callee which is the Worker executing the PTask.
275: // Request sent to the the Worker are from this point on,
276: // Replied.
277: p = ExecutionMonitor.checkin(pt.key());
278: // Here is a small gap: The Worker can respond to requests but this
279: // isn't known to the WorkSpace and thus the address cannot be known
280: // by other Workers.
281: // Conclusion: no problem here, because the Worker isn't known to
282: // anyone.
283: try {
284: // The execution of the PTask has started - make this known to
285: // the WorkSpace.
286: con = writeConnection();
287: c = sqlImpl().executionFunctions().start(pt, p, con);
288: } finally {
289: con.rollback();
290: sqlImpl().genericFunctions().close(con);
291: }
292:
293: // From here, there are no JDBC Cnnections open, so the Function can run for
294: // a long period of time without holding an open Connection.
295: if (c != null) {
296: // Find the Function to execute - could throw an exception if
297: // not found.
298: Class fClass = Class.forName(c.task().functionClass());
299: Function f = (Function) fClass.newInstance();
300: // Executes it.
301: r = f.execute(c);
302: }
303:
304: if (r != null) {
305: // Got a result - commit this to the WorkSpace.
306: try {
307: con = writeConnection();
308: pt = sqlImpl().executionFunctions().commit_result(
309: r, con);
310: } finally {
311: con.rollback();
312: }
313: } else {
314: // Got no result. There can be different reasons, but probably
315: // there the PTask was already taken by another Worker.
316: // Fall through.
317: }
318: } finally {
319: // Deregister the Worker (could be after failure/execptions like
320: // deadlocks). Any Request sent to the Worker get no
321: // Reply after deregistering.
322:
323: if (p != null) {
324: ExecutionMonitor.checkout(pt.key());
325: // Make sure that this Worker (or others) are stopped and
326: // that this is known to the Workspace.
327: stop(pt);
328: }
329:
330: }
331: return pt;
332: }
333:
334: /*
335: */
336: public LogEntry[] match(LogEntryTemplate template) throws Exception {
337: LogEntry[] l = null;
338: SyrupConnection con = null;
339:
340: try {
341: con = readConnection();
342: l = sqlImpl().genericFunctions().match(template, con);
343: con.commit();
344: } finally {
345: sqlImpl().genericFunctions().close(con);
346: }
347: return l;
348: }
349: }
|