001: package org.apache.ojb.odmg;
002:
003: import org.apache.commons.lang.SystemUtils;
004: import org.apache.ojb.broker.TestHelper;
005: import org.apache.ojb.junit.JUnitExtensions;
006: import org.odmg.Database;
007: import org.odmg.Implementation;
008: import org.odmg.LockNotGrantedException;
009: import org.odmg.Transaction;
010:
011: /**
012: * Test odmg-locking implementation with multiple threads.
013: * Different threads try to update the same instance / or a copy
014: * of the same object.
015: *
016: * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
017: * @version $Id: LockingMultithreadedTest.java,v 1.3.2.2 2005/03/22 15:59:34 arminw Exp $
018: */
019: public class LockingMultithreadedTest extends
020: JUnitExtensions.MultiThreadedTestCase {
021: private static int threadCount;
022: private static Implementation odmg;
023: private static Database db;
024: private final StringBuffer result = new StringBuffer(2000);
025: private static final String eol = SystemUtils.LINE_SEPARATOR;
026: // number of concurrent threads to run
027: private final int concurrentThreads = 10;
028: // number of updates each thread performs against the object
029: private final int objectUpdates = 30;
030: // max number of attemps to get a lock
031: private static final int maxAttempts = 100;
032: private static final int nearMax = (int) (maxAttempts * 0.75);
033:
034: public static void main(String[] args) {
035: String[] arr = { LockingMultithreadedTest.class.getName() };
036: junit.textui.TestRunner.main(arr);
037: }
038:
039: public LockingMultithreadedTest(String s) {
040: super (s);
041: }
042:
043: protected void setUp() throws Exception {
044: super .setUp();
045: }
046:
047: protected void tearDown() throws Exception {
048: super .tearDown();
049: }
050:
051: public void testLockingOfObject() throws Exception {
052: /*
053: odmg api is threadsafe, so we can share Implementation instance
054: among the different threads
055: */
056: odmg = OJB.getInstance();
057: db = odmg.newDatabase();
058: db.open(TestHelper.DEF_DATABASE_NAME, Database.OPEN_READ_WRITE);
059:
060: LockObject targetObject = createLockObjectWithRef();
061: storeObject(targetObject);
062:
063: TestCaseRunnable tct[] = new TestCaseRunnable[concurrentThreads];
064: for (int i = 0; i < concurrentThreads; i++) {
065: /*
066: several threads try to lock the same object (shared object),
067: the other threads lock deep copies of the shared object
068: */
069: if (i % 2 == 0)
070: tct[i] = new LockHandle(targetObject);
071: else
072: tct[i] = new LockHandle(targetObject.makeCopy());
073: }
074: // run test classes
075: runTestCaseRunnables(tct);
076: System.out.println("*** Result of multithreaded lock test ***");
077: System.out.println(result.toString());
078: //System.out.println(targetObject.getReference().getName());
079: }
080:
081: private LockObject createLockObjectWithRef() {
082: int number = newThreadKey();
083: LockObject lo = new LockObject();
084: lo.setName("modified by thread: " + number);
085:
086: LockObjectRef lor = new LockObjectRef();
087: lor.setName("modified by thread: " + number);
088:
089: lo.setReference(lor);
090:
091: return lo;
092: }
093:
094: private void storeObject(Object obj) throws Exception {
095: Transaction tx = odmg.newTransaction();
096:
097: tx.begin();
098: tx.lock(obj, Transaction.WRITE);
099: tx.commit();
100: }
101:
102: public static synchronized int newThreadKey() {
103: return threadCount++;
104: }
105:
106: //=======================================================================
107: // inner classes
108: //=======================================================================
109:
110: class LockHandle extends
111: JUnitExtensions.MultiThreadedTestCase.TestCaseRunnable {
112: final LockObject obj;
113: int threadNumber;
114: private int counter = 0;
115:
116: public LockHandle(LockObject obj) {
117: super ();
118: this .obj = obj;
119: }
120:
121: public void runTestCase() throws Throwable {
122: threadNumber = newThreadKey();
123: Transaction tx = odmg.newTransaction();
124: for (int i = 0; i < objectUpdates; i++) {
125: tx.begin();
126: updateObject(tx, obj);
127: tx.commit();
128: counter = 0;
129: }
130: }
131:
132: private void updateObject(final Transaction tx,
133: final LockObject obj) throws Exception {
134: try {
135: tx.lock(obj, Transaction.WRITE);
136: tx.lock(obj.getReference(), Transaction.WRITE);
137: updateName(obj);
138: updateName(obj.getReference());
139: } catch (LockNotGrantedException e) {
140: if (counter < maxAttempts) {
141: counter++;
142: if (counter > nearMax)
143: System.out
144: .println("LockingMultithreadedTest: thread "
145: + threadNumber
146: + " waits "
147: + counter
148: + " times to update object. Maximal attempts before fail are "
149: + maxAttempts
150: + ". This can be a result of low hardware.");
151: try {
152: Thread.sleep(30);
153: } catch (InterruptedException e1) {
154: }
155: updateObject(tx, obj);
156: } else {
157: System.out
158: .println("* Can't lock given object, will throw exception"
159: + " for thread number "
160: + threadNumber + " *");
161: throw e;
162: }
163: }
164: }
165:
166: private void updateName(LockObject obj) {
167: if (obj.getName().length() < 100) {
168: obj.setName(obj.getName() + "-" + threadNumber);
169: } else {
170: result.append(eol).append(obj.getName());
171: obj.setName("modified by thread: " + threadNumber);
172: }
173: }
174:
175: private void updateName(LockObjectRef obj) {
176: if (obj.getName().length() < 100) {
177: obj.setName(obj.getName() + "-" + threadNumber);
178: } else {
179: obj.setName("modified by thread: " + threadNumber);
180: }
181: }
182: }
183:
184: public static class LockObject {
185: private Integer id;
186: private String name;
187: private Integer version;
188: private LockObjectRef reference;
189:
190: public LockObject() {
191: }
192:
193: private LockObject(Integer id, String name, Integer version,
194: LockObjectRef reference) {
195: this .id = id;
196: this .name = name;
197: this .version = version;
198: this .reference = reference;
199: }
200:
201: public LockObject makeCopy() {
202: return new LockObject(id, name, version,
203: reference != null ? reference.makeCopy() : null);
204: }
205:
206: public Integer getId() {
207: return id;
208: }
209:
210: public void setId(Integer id) {
211: this .id = id;
212: }
213:
214: public String getName() {
215: return name;
216: }
217:
218: public void setName(String name) {
219: this .name = name;
220: }
221:
222: public Integer getVersion() {
223: return version;
224: }
225:
226: public void setVersion(Integer version) {
227: this .version = version;
228: }
229:
230: public LockObjectRef getReference() {
231: return reference;
232: }
233:
234: public void setReference(LockObjectRef reference) {
235: this .reference = reference;
236: }
237: }
238:
239: public static class LockObjectRef {
240: private Integer id;
241: private String name;
242: private Integer version;
243:
244: public LockObjectRef() {
245: }
246:
247: private LockObjectRef(Integer id, String name, Integer version) {
248: this .id = id;
249: this .name = name;
250: this .version = version;
251: }
252:
253: public LockObjectRef makeCopy() {
254: return new LockObjectRef(id, name, version);
255: }
256:
257: public Integer getId() {
258: return id;
259: }
260:
261: public void setId(Integer id) {
262: this .id = id;
263: }
264:
265: public String getName() {
266: return name;
267: }
268:
269: public void setName(String name) {
270: this .name = name;
271: }
272:
273: public Integer getVersion() {
274: return version;
275: }
276:
277: public void setVersion(Integer version) {
278: this.version = version;
279: }
280: }
281: }
|