001: package org.apache.ojb.broker;
002:
003: import org.apache.commons.lang.SerializationUtils;
004: import org.apache.ojb.broker.util.logging.LoggerFactory;
005: import org.apache.ojb.junit.JUnitExtensions;
006:
007: /**
008: * Test optimistic-locking implementation with multiple threads.
009: * Different threads try to update the same instance / or a copy
010: * of the same object.
011: *
012: * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
013: * @version $Id: OptimisticLockingMultithreadedTest.java,v 1.1.2.2 2004/12/12 01:35:12 arminw Exp $
014: */
015: public class OptimisticLockingMultithreadedTest extends
016: JUnitExtensions.MultiThreadedTestCase {
017: private static int threadCount;
018: static final String msg = "Thread write order: ";
019:
020: public static void main(String[] args) {
021: String[] arr = { OptimisticLockingMultithreadedTest.class
022: .getName() };
023: junit.textui.TestRunner.main(arr);
024: }
025:
026: public OptimisticLockingMultithreadedTest(String s) {
027: super (s);
028: }
029:
030: protected void setUp() throws Exception {
031: super .setUp();
032: }
033:
034: protected void tearDown() throws Exception {
035: super .tearDown();
036: }
037:
038: public void testLockingOfObject() throws Exception {
039: LockedByVersion targetObject = createLockedByVersion();
040: storeObject(targetObject);
041:
042: // number of concurrent threads to run
043: int threads = 6;
044: // number of updates each thread performs against the object
045: int objectUpdates = 20;
046:
047: TestCaseRunnable tct[] = new TestCaseRunnable[threads];
048: for (int i = 0; i < threads; i++) {
049: /*
050: several threads try to lock the same object (shared object),
051: the other threads lock deep copies of the shared object
052: */
053: if (i % 2 == 0)
054: tct[i] = new LockHandle(targetObject, objectUpdates);
055: else
056: tct[i] = new LockHandle(
057: (LockedByVersion) SerializationUtils
058: .clone(targetObject), objectUpdates);
059: }
060: System.out.println("*** START - Multithreaded lock test ***");
061: System.out.println("Number of concurrent threads: " + threads);
062: System.out.println("Number of object updates per thread: "
063: + objectUpdates);
064: System.out
065: .println("Each thread try to update the same object. If an OptimisticLockException"
066: + " was thrown, the thread wait and try later again (200 attempts, then fail)");
067: // run test classes
068: runTestCaseRunnables(tct);
069: System.out.println(targetObject.getValue());
070: System.out
071: .println("An '-' indicate write success at first attempt");
072: System.out
073: .println("An '+' indicate write success after several OptimisticLockException");
074: System.out.println("*** END - Multithreaded lock test ***");
075: }
076:
077: private LockedByVersion createLockedByVersion() {
078: int number = newThreadKey();
079: LockedByVersion lo = new LockedByVersion();
080: lo.setValue(msg + number);
081: return lo;
082: }
083:
084: private void storeObject(Object obj) throws Exception {
085: PersistenceBroker broker = PersistenceBrokerFactory
086: .defaultPersistenceBroker();
087: try {
088: broker.beginTransaction();
089: broker.store(obj);
090: broker.commitTransaction();
091: } finally {
092: if (broker != null)
093: broker.close();
094: }
095: }
096:
097: public static synchronized int newThreadKey() {
098: return threadCount++;
099: }
100:
101: //=======================================================================
102: // inner classes
103: //=======================================================================
104:
105: class LockHandle extends TestCaseRunnable {
106: LockedByVersion obj;
107: int threadNumber;
108: int objectUpdates = 30;
109:
110: public LockHandle(LockedByVersion obj, int objectUpdates) {
111: super ();
112: this .objectUpdates = objectUpdates;
113: this .obj = obj;
114: }
115:
116: public void runTestCase() throws Throwable {
117: threadNumber = newThreadKey();
118: for (int i = 0; i < objectUpdates; i++) {
119: updateObject(obj, false);
120: }
121: }
122:
123: private int counter = 0;
124: private static final int maxAttempts = 200;
125: private static final int nearMax = (int) (maxAttempts * 0.9);
126:
127: private void updateObject(LockedByVersion obj,
128: boolean LNGEthrown) throws Exception {
129: PersistenceBroker broker = PersistenceBrokerFactory
130: .defaultPersistenceBroker();
131: try {
132: broker.beginTransaction();
133: updateName(obj, LNGEthrown);
134: broker.store(obj);
135: broker.commitTransaction();
136: } catch (OptimisticLockException e) {
137: if (broker != null) {
138: broker.abortTransaction();
139: broker.close();
140: }
141: // we try X times again to update the object
142: if (counter < maxAttempts) {
143: counter++;
144: if (counter > nearMax)
145: LoggerFactory
146: .getDefaultLogger()
147: .warn(
148: "OptimisticLockingMultithreadedTest: thread "
149: + threadNumber
150: + " waits "
151: + counter
152: + " times to update object. Maximal attempts before fail are "
153: + maxAttempts
154: + ". This can be a result of low hardware.");
155: Thread.sleep(10);
156: PersistenceBroker pb = PersistenceBrokerFactory
157: .defaultPersistenceBroker();
158: LockedByVersion temp;
159: try {
160: // lookup object instance again to get vaild lock value
161: Identity oid = pb.serviceIdentity()
162: .buildIdentity(obj);
163: temp = (LockedByVersion) pb
164: .getObjectByIdentity(oid);
165: } finally {
166: if (pb != null)
167: pb.close();
168: }
169: updateObject(temp, true);
170: } else {
171: LoggerFactory.getDefaultLogger().error(
172: "* Can't lock given object, will throw exception"
173: + " for thread number "
174: + threadNumber + " *");
175: throw e;
176: }
177: } finally {
178: counter = 0;
179: if (broker != null) {
180: broker.close();
181: }
182: }
183: }
184:
185: private void updateName(LockedByVersion obj, boolean LNGEthrown) {
186: String token;
187: if (LNGEthrown) {
188: token = "+";
189: } else {
190: token = "-";
191: }
192: if (obj.getValue().length() < 120) {
193: obj.setValue(obj.getValue() + token + threadNumber);
194: } else {
195: System.out.println(obj.getValue());
196: obj.setValue(msg + threadNumber);
197: }
198: }
199: }
200: }
|