001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.transaction.memory;
018:
019: import java.util.HashMap;
020: import java.util.Map;
021:
022: import junit.framework.Test;
023: import junit.framework.TestSuite;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027:
028: import org.apache.commons.transaction.locking.LockException;
029: import org.apache.commons.transaction.util.CommonsLoggingLogger;
030: import org.apache.commons.transaction.util.LoggerFacade;
031: import org.apache.commons.transaction.util.RendezvousBarrier;
032:
033: /**
034: * Tests for map wrapper.
035: *
036: * @version $Id: PessimisticMapWrapperTest.java 493628 2007-01-07 01:42:48Z joerg $
037: */
038: public class PessimisticMapWrapperTest extends MapWrapperTest {
039:
040: private static final Log log = LogFactory
041: .getLog(PessimisticMapWrapperTest.class.getName());
042: private static final LoggerFacade sLogger = new CommonsLoggingLogger(
043: log);
044:
045: protected static final long TIMEOUT = Long.MAX_VALUE;
046:
047: private static int deadlockCnt = 0;
048:
049: public static Test suite() {
050: TestSuite suite = new TestSuite(PessimisticMapWrapperTest.class);
051: return suite;
052: }
053:
054: public static void main(java.lang.String[] args) {
055: junit.textui.TestRunner.run(suite());
056: }
057:
058: public PessimisticMapWrapperTest(String testName) {
059: super (testName);
060: }
061:
062: protected TransactionalMapWrapper getNewWrapper(Map map) {
063: return new PessimisticMapWrapper(map, sLogger);
064: }
065:
066: // XXX no need for this code, just to make clear those tests are run as well
067: public void testBasic() throws Throwable {
068: super .testBasic();
069: }
070:
071: public void testComplex() throws Throwable {
072: super .testComplex();
073: }
074:
075: public void testSets() throws Throwable {
076: super .testSets();
077: }
078:
079: public void testMulti() throws Throwable {
080: sLogger.logInfo("Checking concurrent transaction features");
081:
082: final Map map1 = new HashMap();
083:
084: final PessimisticMapWrapper txMap1 = (PessimisticMapWrapper) getNewWrapper(map1);
085:
086: Thread thread1 = new Thread(new Runnable() {
087: public void run() {
088: txMap1.startTransaction();
089: txMap1.put("key1", "value2");
090: synchronized (txMap1) {
091: txMap1.commitTransaction();
092: report("value2", (String) txMap1.get("key1"));
093: }
094: }
095: }, "Thread1");
096:
097: txMap1.put("key1", "value1");
098:
099: txMap1.startTransaction();
100:
101: report("value1", (String) txMap1.get("key1"));
102:
103: thread1.start();
104:
105: // we have serializable as isolation level, that's why I will still see the old value
106: report("value1", (String) txMap1.get("key1"));
107:
108: txMap1.put("key1", "value3");
109:
110: // after commit it must be our value
111: synchronized (txMap1) {
112: txMap1.commitTransaction();
113: report("value3", (String) txMap1.get("key1"));
114: }
115: }
116:
117: public void testConflict() throws Throwable {
118: sLogger.logInfo("Checking concurrent transaction features");
119:
120: final Map map1 = new HashMap();
121:
122: final PessimisticMapWrapper txMap1 = (PessimisticMapWrapper) getNewWrapper(map1);
123:
124: final RendezvousBarrier restart = new RendezvousBarrier(
125: "restart", TIMEOUT, sLogger);
126:
127: for (int i = 0; i < 25; i++) {
128:
129: final RendezvousBarrier deadlockBarrier1 = new RendezvousBarrier(
130: "deadlock" + i, TIMEOUT, sLogger);
131:
132: Thread thread1 = new Thread(new Runnable() {
133: public void run() {
134: txMap1.startTransaction();
135: try {
136: // first both threads get a lock, this one on res2
137: txMap1.put("key2", "value2");
138: synchronized (deadlockBarrier1) {
139: deadlockBarrier1.meet();
140: deadlockBarrier1.reset();
141: }
142: // if I am first, the other thread will be dead, i.e.
143: // exactly one
144: txMap1.put("key1", "value2");
145: txMap1.commitTransaction();
146: } catch (LockException le) {
147: assertEquals(le.getCode(),
148: LockException.CODE_DEADLOCK_VICTIM);
149: deadlockCnt++;
150: txMap1.rollbackTransaction();
151: } catch (InterruptedException ie) {
152: } finally {
153: try {
154: synchronized (restart) {
155: restart.meet();
156: restart.reset();
157: }
158: } catch (InterruptedException ie) {
159: }
160:
161: }
162: }
163: }, "Thread1");
164:
165: thread1.start();
166:
167: txMap1.startTransaction();
168: try {
169: // first both threads get a lock, this one on res2
170: txMap1.get("key1");
171: synchronized (deadlockBarrier1) {
172: deadlockBarrier1.meet();
173: deadlockBarrier1.reset();
174: }
175: // if I am first, the other thread will be dead, i.e. exactly
176: // one
177: txMap1.get("key2");
178: txMap1.commitTransaction();
179: } catch (LockException le) {
180: assertEquals(le.getCode(),
181: LockException.CODE_DEADLOCK_VICTIM);
182: deadlockCnt++;
183: txMap1.rollbackTransaction();
184: } finally {
185: try {
186: synchronized (restart) {
187: restart.meet();
188: restart.reset();
189: }
190: } catch (InterruptedException ie) {
191: }
192:
193: }
194:
195: // XXX in special scenarios the current implementation might cause both
196: // owners to be deadlock victims
197: if (deadlockCnt != 1) {
198: sLogger
199: .logWarning("More than one thread was deadlock victim!");
200: }
201: assertTrue(deadlockCnt >= 1);
202: deadlockCnt = 0;
203: }
204: }
205:
206: public void testTxControl() throws Throwable {
207: super.testTxControl();
208: }
209:
210: }
|