001: package simpleorm.examples;
002:
003: import simpleorm.core.*; // .* OK, all classes prefixed with "S".
004: import java.io.*;
005:
006: /** Demonstrates Cache/locking issues, in particular a long, user interaction
007: transaction (optimistic locking), thread test.<p>
008:
009: An Employee record is read, and then detatched. It is then
010: serialized to a file and read back to simulate its journey to
011: another tier. It is then modified, and reattached to a SimpleORM
012: connection.<p> */
013: public class LongTransactionTest implements SConstants {
014:
015: public static void main(String[] argv) throws Exception {
016: TestUte.initializeTest(LongTransactionTest.class); // Look at this code
017: try {
018: TestUte.createDeptEmp();
019: basicOptimisticTest();
020: longTest();
021: detatchTest();
022: flushAndPurgeTest();
023: threadTest();
024: whereNullTest();
025: } catch (Exception ex) {
026: ex.printStackTrace();
027: } finally {
028: SConnection.detachAndClose();
029: }
030: }
031:
032: /** Basic single record optimistic tests. */
033: public static void basicOptimisticTest() throws Exception {
034: SLog.slog
035: .message("################ BasiciOptimisiticTest #################");
036: SConnection.begin();
037:
038: Department d = null;
039:
040: Department dept400 = (Department) Department.meta.select(
041: "DEPT_ID = 400", null).execute().getOnlyRecord();
042:
043: dept400.detach();
044: dept400.detach(); // Not an error.
045: SConnection.commit();
046:
047: SConnection.detachAndClose();
048:
049: /// Update dept400 in detached state.
050: dept400.setDouble(dept400.BUDGET, 50000); // Same as Dept500
051:
052: TestUte.initializeTest(LongTransactionTest.class);
053: SConnection.begin();
054:
055: dept400 = (Department) dept400.attach();
056: TestUte.assertTrue(dept400.isDirty());
057:
058: /// Check double flushing OK
059: SConnection.flush();
060: SConnection.commit();
061:
062: /// Check that the update really happened.
063: SConnection.begin();
064: TestUte
065: .assertTrue(((Number) SConnection
066: .rawQueryJDBC("SELECT BUDGET FROM XX_DEPARTMENT WHERE DEPT_ID = '400'"))
067: .intValue() == 50000);
068: SConnection.commit();
069:
070: /// Now check breaking a lock with update.
071: SConnection.begin();
072: Department d400 = (Department) Department.meta.findOrCreate(
073: "400", SQY_OPTIMISTIC);
074: d400.setDouble(Department.BUDGET, 3333);
075:
076: // Break the lock. This could be happening in another transaction.
077: SConnection
078: .rawUpdateDB("UPDATE XX_DEPARTMENT SET BUDGET = 4444 WHERE DEPT_ID = 400");
079:
080: try {
081: SConnection.commit();
082: throw new SException.Test(
083: "Broken Optimistic Lock Update not detected.");
084: } catch (SRecordInstance.BrokenOptimisticLockException beu) {
085: }
086:
087: SConnection.rollback();
088:
089: /// Now check breaking a lock with delete.
090: SConnection.begin();
091: Department d400d = (Department) Department.meta.findOrCreate(
092: "400", SQY_OPTIMISTIC);
093: d400d.deleteRecord();
094:
095: // Break the lock. This could be happening in another transaction.
096: SConnection
097: .rawUpdateDB("UPDATE XX_DEPARTMENT SET BUDGET = 444 WHERE DEPT_ID = 400");
098:
099: try {
100: SConnection.commit();
101: throw new SException.Test(
102: "Broken Optimistic Lock Delete not detected.");
103: } catch (SRecordInstance.BrokenOptimisticLockException bed) {
104: }
105:
106: SConnection.rollback();
107: }
108:
109: /** Detatach and seriealize before loading and reattaching, do multi
110: record detach. More vigourous test. */
111: public static void longTest() throws Exception {
112: /// Query Employee 200 and detatch it.
113: SLog.slog
114: .message("################ LongTransTest #################");
115: SConnection.begin();
116:
117: // Ceck getReferenceNoQuery && IsDirty
118: Employee emp200 = (Employee) Employee.meta.findOrCreate("200");
119: Object d200NoQ1 = emp200.getReferenceNoQuery(emp200.DEPARTMENT);
120: Object d200NoQ2 = emp200.getReferenceNoQuery(emp200.DEPARTMENT);
121: TestUte.assertTrue(d200NoQ1 == Boolean.FALSE
122: && d200NoQ2 == Boolean.FALSE);
123: Department dept200 = (Department) emp200
124: .getReference(emp200.DEPARTMENT);
125: Object d200NoQ3 = emp200.getReferenceNoQuery(emp200.DEPARTMENT);
126: TestUte.assertTrue(d200NoQ3 == dept200);
127: TestUte.assertTrue(!emp200.isDirty(emp200.DEPARTMENT));
128: emp200.setReference(emp200.DEPARTMENT, dept200);
129: TestUte.assertTrue(!emp200.isDirty(emp200.DEPARTMENT));
130:
131: Employee emp100 = (Employee) emp200
132: .getReference(emp200.MANAGER);
133: dept200.detach();
134: emp200.detach();
135: emp200.detach(); // Not an error.
136: // Note that emp100 is not detatched.
137: SConnection.commit();
138: SConnection.detachAndClose();
139:
140: /// Serialize the Employee. This also serializes the
141: // referenced and detatched department but not the non-retrieved
142: // manager. This may change, see white paper.
143:
144: File file = new File("../temp/serialized.tmp");
145:
146: System.out.println("Serializing...");
147: FileOutputStream out = new FileOutputStream(file);
148: ObjectOutputStream outs = new ObjectOutputStream(out);
149: outs.writeObject(emp200); // Will also write dept200 but not emp100.
150: outs.flush();
151: out.close();
152:
153: // Send the the Employee far away...and bring it back.
154:
155: /// DeSerialize
156: FileInputStream in = new FileInputStream(file);
157: ObjectInputStream ins = new ObjectInputStream(in);
158: Employee emp2 = (Employee) ins.readObject();
159: in.close();
160: TestUte.assertTrue(emp200 != emp2);
161:
162: /// Update the Employee, say from a user input.
163: emp2.getDouble(emp2.SALARY);
164: emp2.setDouble(emp2.SALARY, 2222);
165:
166: /// Create a new Employee while still detached
167: Employee e800 = (Employee) Employee.meta.createDetached("800");
168: e800.setString(Employee.NAME, "New800");
169:
170: /// The Departement Record is available
171: Department dept2 = (Department) emp2
172: .getReference(emp2.DEPARTMENT); // OK
173: TestUte.assertTrue("D200".equals(dept2.getString(dept2.NAME)));
174: TestUte.assertTrue(!dept2.isAttached());
175:
176: /// But the Manger record is not available
177: try {
178: emp2.getReference(emp2.MANAGER); // Should Fail
179: throw new SException.Test(
180: "Could get unattached Manager sithout a begun transaction.");
181: } catch (SException.Error se) {
182: }
183:
184: /// Although the key used for the reference is still available.
185: TestUte.assertTrue(emp2.getString(
186: emp2.meta.getField("MANAGER_EMPEE_ID")).equals("100"));
187:
188: /// Attach the Employee and flush.
189: TestUte.initializeTest(LongTransactionTest.class);
190: SConnection.begin();
191:
192: emp2 = (Employee) emp2.attach(); // recursively attaches dept200
193: TestUte.assertTrue(emp2.isDirty());
194: TestUte.assertTrue(emp2.getDouble(emp2.SALARY) == 2222);
195: SLog.slog.debug(emp2.allFields());
196:
197: TestUte.assertTrue(dept2.isAttached()); // by emp2.attach.
198: Employee mgr = (Employee) emp2.getReference(emp2.MANAGER); // OK Now
199: TestUte.assertTrue("One00".equals(mgr.getString(mgr.NAME)));
200:
201: Object[] e200q = (Object[]) SConnection.rawQueryDB(
202: "SELECT * FROM XX_EMPLOYEE WHERE EMPEE_ID = '200'",
203: null, 7);
204: SLog.slog.debug("JDBC Emp " + SUte.arrayToString(e200q));
205:
206: /// Attache e800
207: e800 = (Employee) e800.attach();
208:
209: /// Check double flushing OK
210: SConnection.flush();
211: emp2.setDouble(emp2.SALARY, 3333);
212:
213: SConnection.commit();
214:
215: /// Check that the update really happened.
216: SConnection.begin();
217: TestUte
218: .assertTrue(((Number) SConnection
219: .rawQueryJDBC("SELECT SALARY FROM XX_EMPLOYEE WHERE EMPEE_ID = '200'"))
220: .intValue() == 3333);
221: TestUte
222: .assertEqual(
223: (String) SConnection
224: .rawQueryJDBC("SELECT NAME FROM XX_EMPLOYEE WHERE EMPEE_ID = '800'"),
225: "New800");
226: SConnection.commit();
227:
228: }
229:
230: /** This mainly tests J2EE detach without closing functionality. */
231: public static void detatchTest() throws Exception {
232: System.out.println("====== detatchTest =======");
233:
234: SConnection.begin();
235: java.sql.Connection con = SConnection.getBegunDBConnection();
236:
237: Department dept100 = (Department) Department.meta
238: .findOrCreate("100");
239: dept100.setString(Department.BUDGET, "12345");
240:
241: SConnection.flush(); // But no Commit!
242: SConnection.detachWithoutClosing(); // Not detatchAndClose
243:
244: // New SimpleORM connection, same JDBC connection.
245: SConnection.attach(con, "ReAttached");
246: SConnection.begin();
247:
248: Department dept100a = (Department) Department.meta
249: .findOrCreate("100");
250:
251: TestUte
252: .assertTrue(dept100a.getDouble(dept100a.BUDGET) == 12345);
253: TestUte.assertTrue(dept100 != dept100a); // Cache destroyed by detatch.
254:
255: SConnection.commit();
256: }
257:
258: static void flushAndPurgeTest() throws Exception {
259: SLog.slog
260: .message("################ FlushAndPurgeTest #################");
261: SConnection.begin();
262:
263: Department dept100a = (Department) Department.meta
264: .findOrCreate("100");
265: dept100a.setDouble(Department.BUDGET, 123);
266:
267: SConnection.flushAndPurge();
268:
269: SConnection
270: .rawUpdateDB("UPDATE xx_DEPARTMENT SET BUDGET = BUDGET * 2 WHERE DEPT_ID = '100'");
271:
272: // Check that the changed value is reflected in the in memory
273: // Department.
274: Department dept100b = (Department) Department.meta
275: .findOrCreate("100");
276: TestUte
277: .assertTrue(dept100b.getDouble(Department.BUDGET) == 246);
278: TestUte.assertTrue(dept100a != dept100b);
279:
280: // Show dubious detach/attach routines.
281: SConnection scon = SConnection.unsafeDetachFromThread();
282: scon.unsafeAttachToThread();
283: Department dept100c = (Department) Department.meta
284: .findOrCreate("100");
285: TestUte.assertTrue(dept100b == dept100c);
286:
287: SConnection.commit();
288: }
289:
290: /** Check locking works OK using two threads. */
291: static void threadTest() throws Exception {
292: SLog.slog
293: .message("################ threadTest #################");
294: SConnection.begin();
295:
296: Employee emp200 = (Employee) Employee.meta.findOrCreate("200",
297: SQY_READ_ONLY);
298:
299: Department d100 = (Department) Department.meta
300: .findOrCreate("100");
301: d100.setDouble(d100.BUDGET, 2143);
302: d100.flush();
303:
304: final Object waiter = new Object();
305:
306: // d100 should be locked, so this thread will wait.
307: Thread t2 = new Thread() {
308: public void run() {
309: try {
310: try {
311: TestUte
312: .initializeTest(LongTransactionTest.class); // Sometimes fails for HSQL?
313: } catch (Exception ex) {
314: throw new SException.Error(ex);
315: }
316: SConnection.begin();
317: threadCheck1 = true;
318: Employee emp200 = (Employee) Employee.meta
319: .findOrCreate("200", SQY_READ_ONLY);
320: threadCheck1a = true;
321:
322: Department d100 = (Department) Department.meta
323: .findOrCreate("100");
324: threadCheck2 = true;
325: d100.setDouble(d100.BUDGET, 666);
326: SConnection.commit();
327: } finally {
328: SConnection.detachAndClose();
329: }
330: synchronized (waiter) {
331: waiter.notify();
332: }
333: }
334: };
335: t2.start();
336:
337: synchronized (waiter) {
338: waiter.wait(); // Force a thread reschedule.
339: }
340: TestUte.assertTrue(threadCheck1); // Really was a reschedule.
341: SLog.slog.message("Read Only is non blocking " + threadCheck1a);
342: /** For some wierd reason, PostgreSQL will occasionally and
343: unreproducatbly hang about here, often the very first time the
344: test is run after a reboot! Needs investigation. */
345: if (SConnection.getDriver() instanceof SDriverPostgres)
346: TestUte.assertTrue(threadCheck1a); // ReadOnly non blocking
347:
348: boolean locking = SConnection.getConnection().getDriver()
349: .supportsLocking();
350: SLog.slog.message("Locking " + locking);
351: if (locking) {
352: TestUte.assertTrue(!threadCheck2); // There really was a lock, waiting.
353:
354: TestUte
355: .assertTrue(((Number) SConnection
356: .rawQueryJDBC("SELECT BUDGET FROM XX_DEPARTMENT WHERE DEPT_ID = '100'"))
357: .intValue() == 2143);
358: SLog.slog.message("Asserted 2143");
359: SConnection.commit();
360:
361: t2.join(); // Now the other thread updates the value
362:
363: SConnection.begin();
364: TestUte
365: .assertTrue(((Number) SConnection
366: .rawQueryJDBC("SELECT BUDGET FROM XX_DEPARTMENT WHERE DEPT_ID = '100'"))
367: .intValue() == 666);
368: SLog.slog.message("Asserted 666");
369: SConnection.commit();
370: } else { // no locking
371: t2.join(); // If this hangs there really is locking after all.
372:
373: d100.setDouble(d100.BUDGET, 13); // NOTE THAT WITHOUT THIS THERE IS NO DETECTION.
374: try {
375: SConnection.commit();
376: throw new SException.Test(
377: "Broken Optimistic Lock not detected.");
378: } catch (SRecordInstance.BrokenOptimisticLockException be) {
379: }
380:
381: SConnection.rollback();
382: }
383: }
384:
385: static boolean threadCheck1 = false, threadCheck1a = false,
386: threadCheck2 = false;
387:
388: static void whereNullTest() throws Exception {
389: SLog.slog
390: .message("################ whereNullTest #################");
391:
392: // Make a null and non-null field value for Department record
393: SConnection.begin();
394: SConnection
395: .rawUpdateDB("UPDATE xx_DEPARTMENT SET NAME = null, BUDGET=666 WHERE DEPT_ID = '100'");
396: SConnection.commit();
397:
398: SConnection.begin();
399: Department d100a = (Department) Department.meta.findOrCreate(
400: "100", SQY_OPTIMISTIC);
401: d100a.setString(d100a.NAME, "One00");
402: d100a.setDouble(d100a.BUDGET, 246); // also test update clause formulation of non-null field
403:
404: SConnection.commit();
405:
406: // Check that the changed value is correct
407: SConnection.begin();
408: Department dept100b = (Department) Department.meta
409: .findOrCreate("100");
410: TestUte.assertTrue("One00".equals(dept100b
411: .getString(dept100b.NAME)));
412: TestUte.assertTrue(dept100b.getDouble(dept100b.BUDGET) == 246);
413: SConnection.commit();
414:
415: }
416:
417: }
|