001: /*
002: * $Id: TestTransactions.java,v 1.15 2004/10/29 01:05:53 ahimanikya Exp $
003: * =======================================================================
004: * Copyright (c) 2002 Axion Development Team. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above
011: * copyright notice, this list of conditions and the following
012: * disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The names "Tigris", "Axion", nor the names of its contributors may
020: * not be used to endorse or promote products derived from this
021: * software without specific prior written permission.
022: *
023: * 4. Products derived from this software may not be called "Axion", nor
024: * may "Tigris" or "Axion" appear in their names without specific prior
025: * written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
030: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
032: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
033: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
034: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
035: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
037: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038: * =======================================================================
039: */
040:
041: package org.axiondb.functional;
042:
043: import java.sql.Connection;
044: import java.sql.DriverManager;
045: import java.sql.PreparedStatement;
046: import java.sql.ResultSet;
047: import java.sql.SQLException;
048: import java.util.Random;
049:
050: import junit.framework.Test;
051: import junit.framework.TestSuite;
052:
053: import org.apache.commons.collections.HashBag;
054:
055: /**
056: * @version $Revision: 1.15 $ $Date: 2004/10/29 01:05:53 $
057: * @author Rodney Waldhoff
058: */
059: public class TestTransactions extends AbstractFunctionalTest {
060:
061: //------------------------------------------------------------ Conventional
062:
063: public TestTransactions(String testName) {
064: super (testName);
065: }
066:
067: public static Test suite() {
068: return new TestSuite(TestTransactions.class);
069: }
070:
071: //--------------------------------------------------------------- Lifecycle
072:
073: public void setUp() throws Exception {
074: super .setUp();
075: }
076:
077: public void tearDown() throws Exception {
078: super .tearDown();
079: }
080:
081: //------------------------------------------------------------------- Tests
082:
083: public void testBug() throws Exception {
084: createTableFoo();
085: populateTableFoo();
086: Connection cone = DriverManager
087: .getConnection(getConnectString());
088: cone.setAutoCommit(false);
089: {
090: PreparedStatement stmt = null;
091: try {
092: stmt = cone
093: .prepareStatement("insert into FOO ( NUM, STR, NUMTWO ) values ( ?, ?, ? )");
094: for (int i = 0; i < 1; i++) {
095: int num = _random.nextInt(100);
096: stmt.setInt(1, num);
097: stmt.setString(2, String.valueOf(num));
098: stmt.setInt(3, num / 2);
099: assertEquals(1, stmt.executeUpdate());
100: }
101: } finally {
102: try {
103: stmt.close();
104: } catch (Exception t) {
105: }
106: }
107: }
108: {
109: PreparedStatement stmt = null;
110: ResultSet rset = null;
111: try {
112: stmt = cone
113: .prepareStatement("select NUM, STR from FOO where NUM > ? and NUM < ?");
114: stmt.setInt(1, _random.nextInt(10));
115: stmt.setInt(2, _random.nextInt(100) + 10);
116: rset = stmt.executeQuery();
117: while (rset.next()) {
118: int num = rset.getInt(1);
119: String str = rset.getString(2);
120: assertEquals(String.valueOf(num), str);
121: }
122: } finally {
123: try {
124: rset.close();
125: } catch (Exception t) {
126: }
127: try {
128: stmt.close();
129: } catch (Exception t) {
130: }
131: }
132: }
133: cone.close();
134: }
135:
136: public void testDeleteUpdatedRowBug() throws Exception {
137: createTableFoo();
138: populateTableFoo();
139:
140: Connection conn = DriverManager
141: .getConnection(getConnectString());
142: conn.setAutoCommit(false);
143: // update a row
144: {
145: PreparedStatement stmt = null;
146: try {
147: stmt = conn
148: .prepareStatement("update FOO set NUM = ?, STR = ?, NUMTWO = ? where NUM = ?");
149: stmt.setInt(1, 1000);
150: stmt.setString(2, String.valueOf(1000));
151: stmt.setInt(3, 1000 / 2);
152: stmt.setInt(4, 2);
153: assertEquals(1, stmt.executeUpdate());
154: } finally {
155: try {
156: stmt.close();
157: } catch (Exception t) {
158: }
159: }
160: }
161: // now delete the updated row
162: {
163: PreparedStatement stmt = null;
164: try {
165: stmt = conn
166: .prepareStatement("delete from FOO where NUM between ? and ?");
167: stmt.setInt(1, 999);
168: stmt.setInt(2, 1001);
169: assertEquals(1, stmt.executeUpdate());
170: } finally {
171: try {
172: stmt.close();
173: } catch (Exception t) {
174: }
175: }
176: }
177: conn.commit();
178: conn.close();
179: }
180:
181: public void testInsertIsolation() throws Exception {
182: createTableFoo();
183: _stmt.execute("create index FOO_NUM_NDX on FOO ( NUM )");
184: populateTableFoo();
185:
186: // create a transactional connection
187: Connection cone = DriverManager
188: .getConnection(getConnectString());
189: cone.setAutoCommit(false);
190:
191: // insert into it
192: {
193: PreparedStatement stmt = cone
194: .prepareStatement("insert into FOO ( NUM, STR, NUMTWO ) values ( ?, ?, ? )");
195: for (int i = NUM_ROWS_IN_FOO + 10; i < NUM_ROWS_IN_FOO + 20; i++) {
196: stmt.setInt(1, i);
197: stmt.setString(2, String.valueOf(i));
198: stmt.setInt(3, i / 2);
199: assertEquals(1, stmt.executeUpdate());
200: }
201: stmt.close();
202: }
203: // we should see those rows in c1
204: {
205: PreparedStatement stmt = cone
206: .prepareStatement("select NUM, STR from FOO where NUM >= ? and NUM < ?");
207: stmt.setInt(1, NUM_ROWS_IN_FOO + 10);
208: stmt.setInt(2, NUM_ROWS_IN_FOO + 20);
209: ResultSet rset = stmt.executeQuery();
210:
211: HashBag expected = new HashBag();
212: HashBag found = new HashBag();
213: for (int i = NUM_ROWS_IN_FOO + 10; i < NUM_ROWS_IN_FOO + 20; i++) {
214: assertTrue(rset.next());
215: String value = rset.getString(2);
216: assertNotNull(value);
217: assertTrue(!found.contains(value));
218: expected.add(String.valueOf(i));
219: found.add(value);
220: }
221: assertTrue(!rset.next());
222: assertEquals(expected, found);
223: rset.close();
224: stmt.close();
225: }
226:
227: // but not in a new connection, because they haven't been committed yet
228: Connection ctwo = DriverManager
229: .getConnection(getConnectString());
230: ctwo.setAutoCommit(false);
231: {
232: PreparedStatement stmt = null;
233: stmt = ctwo
234: .prepareStatement("select NUM, STR from FOO where NUM >= ? and NUM < ?");
235: stmt.setInt(1, NUM_ROWS_IN_FOO + 10);
236: stmt.setInt(2, NUM_ROWS_IN_FOO + 20);
237: ResultSet rset = stmt.executeQuery();
238: assertTrue(!rset.next());
239: rset.close();
240: stmt.close();
241: }
242:
243: // but if we commit c1
244: cone.commit();
245:
246: // we still see the rows in c1
247: {
248: PreparedStatement stmt = cone
249: .prepareStatement("select NUM, STR from FOO where NUM >= ? and NUM < ?");
250: stmt.setInt(1, NUM_ROWS_IN_FOO + 10);
251: stmt.setInt(2, NUM_ROWS_IN_FOO + 20);
252: ResultSet rset = stmt.executeQuery();
253:
254: HashBag expected = new HashBag();
255: HashBag found = new HashBag();
256: for (int i = NUM_ROWS_IN_FOO + 10; i < NUM_ROWS_IN_FOO + 20; i++) {
257: assertTrue(rset.next());
258: String value = rset.getString(2);
259: assertNotNull(value);
260: assertTrue(!found.contains(value));
261: expected.add(String.valueOf(i));
262: found.add(value);
263: }
264: assertTrue(!rset.next());
265: assertEquals(expected, found);
266: rset.close();
267: stmt.close();
268: }
269:
270: // and we see the rows in a newly created connection
271: Connection cthree = DriverManager
272: .getConnection(getConnectString());
273: cthree.setAutoCommit(false);
274: {
275: PreparedStatement stmt = cthree
276: .prepareStatement("select NUM, STR from FOO where NUM >= ? and NUM < ?");
277: stmt.setInt(1, NUM_ROWS_IN_FOO + 10);
278: stmt.setInt(2, NUM_ROWS_IN_FOO + 20);
279: ResultSet rset = stmt.executeQuery();
280:
281: HashBag expected = new HashBag();
282: HashBag found = new HashBag();
283: for (int i = NUM_ROWS_IN_FOO + 10; i < NUM_ROWS_IN_FOO + 20; i++) {
284: assertTrue(rset.next());
285: String value = rset.getString(2);
286: assertNotNull(value);
287: assertTrue(!found.contains(value));
288: expected.add(String.valueOf(i));
289: found.add(value);
290: }
291: assertTrue(!rset.next());
292: assertEquals(expected, found);
293: rset.close();
294: stmt.close();
295: }
296: cthree.close();
297:
298: // but not in c2, since it was opened before c1 was committed
299: {
300: PreparedStatement stmt = ctwo
301: .prepareStatement("select NUM, STR from FOO where NUM >= ? and NUM < ?");
302: stmt.setInt(1, NUM_ROWS_IN_FOO + 10);
303: stmt.setInt(2, NUM_ROWS_IN_FOO + 20);
304: ResultSet rset = stmt.executeQuery();
305: assertTrue(!rset.next());
306: rset.close();
307: stmt.close();
308: }
309:
310: // now commit c2:
311: ctwo.commit();
312: // and we should see the inserted rows
313: {
314: PreparedStatement stmt = ctwo
315: .prepareStatement("select NUM, STR from FOO where NUM >= ? and NUM < ?");
316: stmt.setInt(1, NUM_ROWS_IN_FOO + 10);
317: stmt.setInt(2, NUM_ROWS_IN_FOO + 20);
318: ResultSet rset = stmt.executeQuery();
319:
320: HashBag expected = new HashBag();
321: HashBag found = new HashBag();
322: for (int i = NUM_ROWS_IN_FOO + 10; i < NUM_ROWS_IN_FOO + 20; i++) {
323: assertTrue(rset.next());
324: String value = rset.getString(2);
325: assertNotNull(value);
326: assertTrue(!found.contains(value));
327: expected.add(String.valueOf(i));
328: found.add(value);
329: }
330: assertTrue(!rset.next());
331: assertEquals(expected, found);
332: rset.close();
333: stmt.close();
334: }
335: ctwo.close();
336: cone.close();
337: }
338:
339: public void testUpdateIsolation() throws Exception {
340: createTableFoo();
341: _stmt.execute("create index FOO_NUM_NDX on FOO ( NUM )");
342: populateTableFoo();
343:
344: // create a transactional connection
345: Connection cone = DriverManager
346: .getConnection(getConnectString());
347: cone.setAutoCommit(false);
348:
349: // update using it
350: {
351: PreparedStatement stmt = null;
352: stmt = cone
353: .prepareStatement("update FOO set NUM = ? where NUM = ?");
354: stmt.setInt(1, 1000);
355: stmt.setInt(2, 2);
356: assertEquals(1, stmt.executeUpdate());
357: stmt.close();
358: }
359:
360: // we should see the update in c1
361: {
362: PreparedStatement stmt = null;
363: ResultSet rset = null;
364: stmt = cone
365: .prepareStatement("select NUM, STR from FOO where NUM = ?");
366: stmt.setInt(1, 1000);
367: rset = stmt.executeQuery();
368: assertTrue(rset.next());
369: assertEquals("2", rset.getString(2));
370: assertTrue(!rset.next());
371: rset.close();
372: stmt.close();
373: }
374:
375: // but not in a new connection, because they haven't been committed yet
376: Connection ctwo = DriverManager
377: .getConnection(getConnectString());
378: ctwo.setAutoCommit(false);
379:
380: {
381: PreparedStatement stmt = null;
382: ResultSet rset = null;
383: stmt = ctwo
384: .prepareStatement("select NUM, STR from FOO where NUM = ?");
385: stmt.setInt(1, 1000);
386: rset = stmt.executeQuery();
387: assertTrue(!rset.next());
388: rset.close();
389: stmt.close();
390: }
391:
392: // but if we commit c1
393: cone.commit();
394:
395: // we still see the update in c1
396: {
397: PreparedStatement stmt = null;
398: ResultSet rset = null;
399: stmt = cone
400: .prepareStatement("select NUM, STR from FOO where NUM = ?");
401: stmt.setInt(1, 1000);
402: rset = stmt.executeQuery();
403: assertTrue(rset.next());
404: assertEquals("2", rset.getString(2));
405: assertTrue(!rset.next());
406: rset.close();
407: stmt.close();
408: }
409:
410: // and in a newly created connection
411: Connection cthree = DriverManager
412: .getConnection(getConnectString());
413: cthree.setAutoCommit(false);
414: {
415: PreparedStatement stmt = null;
416: ResultSet rset = null;
417: stmt = cthree
418: .prepareStatement("select NUM, STR from FOO where NUM = ?");
419: stmt.setInt(1, 1000);
420: rset = stmt.executeQuery();
421: assertTrue(rset.next());
422: assertEquals("2", rset.getString(2));
423: assertTrue(!rset.next());
424: rset.close();
425: stmt.close();
426: }
427: cthree.close();
428:
429: // but not in c2, since it was opened before c1 was committed
430: {
431: PreparedStatement stmt = null;
432: ResultSet rset = null;
433: stmt = ctwo
434: .prepareStatement("select NUM, STR from FOO where NUM = ?");
435: stmt.setInt(1, 1000);
436: rset = stmt.executeQuery();
437: assertTrue(!rset.next());
438: rset.close();
439: stmt.close();
440: }
441:
442: // now commit c2:
443: ctwo.commit();
444:
445: // and we should see the updated row
446: {
447: PreparedStatement stmt = null;
448: ResultSet rset = null;
449: stmt = ctwo
450: .prepareStatement("select NUM, STR from FOO where NUM = ?");
451: stmt.setInt(1, 1000);
452: rset = stmt.executeQuery();
453: assertTrue(rset.next());
454: assertEquals("2", rset.getString(2));
455: assertTrue(!rset.next());
456: rset.close();
457: stmt.close();
458: }
459:
460: ctwo.close();
461: cone.close();
462: }
463:
464: public void testDeleteIsolation() throws Exception {
465: createTableFoo();
466: _stmt.execute("create index FOO_NUM_NDX on FOO ( NUM )");
467: populateTableFoo();
468:
469: // create a transactional connection
470: Connection cone = DriverManager
471: .getConnection(getConnectString());
472: cone.setAutoCommit(false);
473:
474: // update using it
475: {
476: PreparedStatement stmt = null;
477: stmt = cone
478: .prepareStatement("delete from FOO where NUM = ?");
479: stmt.setInt(1, 2);
480: assertEquals(1, stmt.executeUpdate());
481: stmt.close();
482: }
483:
484: // we should not see the row in c1
485: {
486: PreparedStatement stmt = null;
487: ResultSet rset = null;
488: stmt = cone
489: .prepareStatement("select NUM, STR from FOO where NUM = ?");
490: stmt.setInt(1, 2);
491: rset = stmt.executeQuery();
492: assertTrue(!rset.next());
493: rset.close();
494: stmt.close();
495: }
496:
497: // but we should still see it in a new connection, because they c1 hasn't been committed yet
498: Connection ctwo = DriverManager
499: .getConnection(getConnectString());
500: ctwo.setAutoCommit(false);
501:
502: {
503: PreparedStatement stmt = null;
504: ResultSet rset = null;
505: stmt = ctwo
506: .prepareStatement("select NUM, STR from FOO where NUM = ?");
507: stmt.setInt(1, 2);
508: rset = stmt.executeQuery();
509: assertTrue(rset.next());
510: assertEquals("2", rset.getString(2));
511: assertTrue(!rset.next());
512: rset.close();
513: stmt.close();
514: }
515:
516: // but if we commit c1
517: cone.commit();
518:
519: // we still see delete in c1
520: {
521: PreparedStatement stmt = null;
522: ResultSet rset = null;
523: stmt = cone
524: .prepareStatement("select NUM, STR from FOO where NUM = ?");
525: stmt.setInt(1, 2);
526: rset = stmt.executeQuery();
527: assertTrue(!rset.next());
528: rset.close();
529: stmt.close();
530: }
531:
532: // and in a newly created connection
533: Connection cthree = DriverManager
534: .getConnection(getConnectString());
535: cthree.setAutoCommit(false);
536: {
537: PreparedStatement stmt = null;
538: ResultSet rset = null;
539: stmt = cthree
540: .prepareStatement("select NUM, STR from FOO where NUM = ?");
541: stmt.setInt(1, 2);
542: rset = stmt.executeQuery();
543: assertTrue(!rset.next());
544: rset.close();
545: stmt.close();
546: }
547: cthree.close();
548:
549: // but not in c2, since it was opened before c1 was committed
550: {
551: PreparedStatement stmt = null;
552: ResultSet rset = null;
553: stmt = ctwo
554: .prepareStatement("select NUM, STR from FOO where NUM = ?");
555: stmt.setInt(1, 2);
556: rset = stmt.executeQuery();
557: assertTrue(rset.next());
558: assertEquals("2", rset.getString(2));
559: assertTrue(!rset.next());
560: rset.close();
561: stmt.close();
562: }
563:
564: // now commit c2:
565: ctwo.commit();
566:
567: // and we should see the deletion
568: {
569: PreparedStatement stmt = null;
570: ResultSet rset = null;
571: stmt = ctwo
572: .prepareStatement("select NUM, STR from FOO where NUM = ?");
573: stmt.setInt(1, 2);
574: rset = stmt.executeQuery();
575: assertTrue(!rset.next());
576: rset.close();
577: stmt.close();
578: }
579:
580: ctwo.close();
581: cone.close();
582: }
583:
584: public void testInterleavedWithAutoCommit() throws Exception {
585: createTableFoo();
586: populateTableFoo();
587:
588: Connection cone = DriverManager
589: .getConnection(getConnectString());
590: Connection ctwo = DriverManager
591: .getConnection(getConnectString());
592: interleaved(cone, ctwo);
593: ctwo.close();
594: cone.close();
595: }
596:
597: public void testInterleavedWithoutAutoCommit() throws Exception {
598: createTableFoo();
599: populateTableFoo();
600:
601: Connection cone = DriverManager
602: .getConnection(getConnectString());
603: cone.setAutoCommit(false);
604: Connection ctwo = DriverManager
605: .getConnection(getConnectString());
606: ctwo.setAutoCommit(false);
607: interleaved(cone, ctwo);
608: ctwo.commit();
609: ctwo.close();
610: try {
611: cone.commit();
612: fail("Expected SQLException");
613: } catch (SQLException e) {
614: // expected
615: }
616: try {
617: cone.close();
618: } catch (Exception e) {
619: }
620: }
621:
622: public void testIndexedInterleavedWithAutoCommit() throws Exception {
623: createTableFoo();
624: _stmt.execute("create index FOO_NUM_NDX on FOO ( NUM )");
625: populateTableFoo();
626:
627: Connection cone = DriverManager
628: .getConnection(getConnectString());
629: Connection ctwo = DriverManager
630: .getConnection(getConnectString());
631: interleaved(cone, ctwo);
632: ctwo.close();
633: cone.close();
634: }
635:
636: public void testIndexedInterleavedWithoutAutoCommit()
637: throws Exception {
638: createTableFoo();
639: _stmt.execute("create index FOO_NUM_NDX on FOO ( NUM )");
640: populateTableFoo();
641:
642: Connection cone = DriverManager
643: .getConnection(getConnectString());
644: cone.setAutoCommit(false);
645: Connection ctwo = DriverManager
646: .getConnection(getConnectString());
647: ctwo.setAutoCommit(false);
648: interleaved(cone, ctwo);
649: ctwo.close();
650: cone.close();
651: }
652:
653: private void interleaved(Connection cone, Connection ctwo)
654: throws Exception {
655: select(cone);
656: select(ctwo);
657: insert(cone);
658: insert(ctwo);
659: select(cone);
660: select(ctwo);
661: update(cone);
662: update(ctwo);
663: select(cone);
664: select(ctwo);
665: delete(cone);
666: delete(ctwo);
667: select(cone);
668: select(ctwo);
669: }
670:
671: private void select(Connection conn) throws Exception {
672: PreparedStatement stmt = null;
673: ResultSet rset = null;
674: try {
675: stmt = conn
676: .prepareStatement("select NUM, STR from FOO where NUM > ? and NUM < ?");
677: stmt.setInt(1, _random.nextInt(10));
678: stmt.setInt(2, _random.nextInt(100) + 10);
679: rset = stmt.executeQuery();
680: while (rset.next()) {
681: int num = rset.getInt(1);
682: String str = rset.getString(2);
683: if (!str.equals(String.valueOf(num))) {
684: throw new RuntimeException(str + " != " + num);
685: }
686: }
687: } finally {
688: try {
689: rset.close();
690: } catch (Exception t) {
691: }
692: try {
693: stmt.close();
694: } catch (Exception t) {
695: }
696: }
697: }
698:
699: private void insert(Connection conn) throws Exception {
700: PreparedStatement stmt = null;
701: try {
702: stmt = conn
703: .prepareStatement("insert into FOO ( NUM, STR, NUMTWO ) values ( ?, ?, ? )");
704: for (int i = 0; i < 5; i++) {
705: int num = _random.nextInt(100);
706: stmt.setInt(1, num);
707: stmt.setString(2, String.valueOf(num));
708: stmt.setInt(3, num / 2);
709: if (1 != stmt.executeUpdate()) {
710: throw new RuntimeException("Expected 1");
711: }
712: }
713: } finally {
714: try {
715: stmt.close();
716: } catch (Exception t) {
717: }
718: }
719: }
720:
721: private void update(Connection conn) throws Exception {
722: PreparedStatement stmt = null;
723: try {
724: stmt = conn
725: .prepareStatement("update FOO set NUM = ?, STR = ?, NUMTWO = ? where NUM = ? or NUMTWO = ?");
726: for (int i = 0; i < 5; i++) {
727: int num = _random.nextInt(100);
728: stmt.setInt(1, num);
729: stmt.setString(2, String.valueOf(num));
730: stmt.setInt(3, num / 2);
731: stmt.setInt(4, _random.nextInt(100));
732: stmt.setInt(5, _random.nextInt(100));
733: stmt.executeUpdate();
734: }
735: } finally {
736: try {
737: stmt.close();
738: } catch (Exception t) {
739: }
740: }
741: }
742:
743: private void delete(Connection conn) throws Exception {
744: PreparedStatement stmt = null;
745: try {
746: stmt = conn
747: .prepareStatement("delete from FOO where NUM between ? and ?");
748: for (int i = 0; i < 5; i++) {
749: int num = _random.nextInt(100);
750: stmt.setInt(1, num);
751: stmt.setInt(2, num + _random.nextInt(10));
752: stmt.executeUpdate();
753: }
754: } finally {
755: try {
756: stmt.close();
757: } catch (Exception t) {
758: }
759: }
760: }
761:
762: private Random _random = new Random();
763: }
|