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 */
017package org.apache.commons.transaction.memory;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import junit.framework.Test;
023import junit.framework.TestSuite;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027
028import org.apache.commons.transaction.util.CommonsLoggingLogger;
029import org.apache.commons.transaction.util.LoggerFacade;
030import org.apache.commons.transaction.util.RendezvousBarrier;
031
032/**
033 * Tests for map wrapper. 
034 *
035 * @version $Id: OptimisticMapWrapperTest.java 493628 2007-01-07 01:42:48Z joerg $
036 */
037public class OptimisticMapWrapperTest extends MapWrapperTest {
038
039    private static final Log log = LogFactory.getLog(OptimisticMapWrapperTest.class.getName());
040    private static final LoggerFacade sLogger = new CommonsLoggingLogger(log);
041
042    public static Test suite() {
043        TestSuite suite = new TestSuite(OptimisticMapWrapperTest.class);
044        return suite;
045    }
046
047    public static void main(java.lang.String[] args) {
048        junit.textui.TestRunner.run(suite());
049    }
050
051    public OptimisticMapWrapperTest(String testName) {
052        super(testName);
053    }
054
055        protected TransactionalMapWrapper getNewWrapper(Map map) {
056                return new OptimisticMapWrapper(map);
057        }
058
059        // XXX no need for this code, just to make clear those tests are run as well 
060    public void testBasic() throws Throwable {
061                super.testBasic();
062    }
063
064        public void testComplex() throws Throwable {
065                super.testComplex();
066        }
067
068        public void testSets() throws Throwable {
069                super.testSets();
070        }
071
072    public void testMulti() throws Throwable {
073        log.info("Checking concurrent transaction features");
074
075        final Map map1 = new HashMap();
076
077        final OptimisticMapWrapper txMap1 = (OptimisticMapWrapper) getNewWrapper(map1);
078
079        final RendezvousBarrier beforeCommitBarrier =
080            new RendezvousBarrier("Before Commit", 2, BARRIER_TIMEOUT, sLogger);
081
082        final RendezvousBarrier afterCommitBarrier = new RendezvousBarrier("After Commit", 2, BARRIER_TIMEOUT, sLogger);
083
084        Thread thread1 = new Thread(new Runnable() {
085            public void run() {
086                txMap1.startTransaction();
087                try {
088                    beforeCommitBarrier.meet();
089                    txMap1.put("key1", "value2");
090                    txMap1.commitTransaction();
091                    afterCommitBarrier.call();
092                } catch (InterruptedException e) {
093                    sLogger.logWarning("Thread interrupted", e);
094                    afterCommitBarrier.reset();
095                    beforeCommitBarrier.reset();
096                }
097            }
098        }, "Thread1");
099
100        txMap1.put("key1", "value1");
101
102        txMap1.startTransaction();
103        thread1.start();
104
105        report("value1", (String) txMap1.get("key1"));
106        beforeCommitBarrier.call();
107        afterCommitBarrier.meet();
108        // we have serializable as isolation level, that's why I will still see the old value
109        report("value1", (String) txMap1.get("key1"));
110
111        // now when I override it it should of course be my value
112        txMap1.put("key1", "value3");
113        report("value3", (String) txMap1.get("key1"));
114
115        // after rollback it must be the value written by the other thread
116        txMap1.rollbackTransaction();
117        report("value2", (String) txMap1.get("key1"));
118    }
119
120        public void testConflict() throws Throwable {
121                log.info("Checking concurrent transaction features");
122
123                final Map map1 = new HashMap();
124
125                final OptimisticMapWrapper txMap1 = (OptimisticMapWrapper) getNewWrapper(map1);
126
127                final RendezvousBarrier beforeCommitBarrier =
128                        new RendezvousBarrier("Before Commit", 2, BARRIER_TIMEOUT, sLogger);
129
130                final RendezvousBarrier afterCommitBarrier = new RendezvousBarrier("After Commit", 2, BARRIER_TIMEOUT, sLogger);
131
132                Thread thread1 = new Thread(new Runnable() {
133                        public void run() {
134                                txMap1.startTransaction();
135                                try {
136                                        beforeCommitBarrier.meet();
137                                        txMap1.put("key1", "value2");
138                                        txMap1.commitTransaction();
139                                        afterCommitBarrier.call();
140                                } catch (InterruptedException e) {
141                                        sLogger.logWarning("Thread interrupted", e);
142                                        afterCommitBarrier.reset();
143                                        beforeCommitBarrier.reset();
144                                }
145                        }
146                }, "Thread1");
147
148                txMap1.put("key1", "value1");
149
150                txMap1.startTransaction();
151                thread1.start();
152
153                report("value1", (String) txMap1.get("key1"));
154                beforeCommitBarrier.call();
155                afterCommitBarrier.meet();
156                // we have serializable as isolation level, that's why I will still see the old value
157                report("value1", (String) txMap1.get("key1"));
158
159                // now when I override it it should of course be my value
160                txMap1.put("key1", "value3");
161                report("value3", (String) txMap1.get("key1"));
162                
163                boolean conflict = false;
164                
165                try {
166                        txMap1.commitTransaction();
167                } catch (ConflictException ce) {
168                        conflict = true;
169                }
170                assertTrue(conflict);
171                // after failed commit it must be the value written by the other thread
172                report("value2", (String) map1.get("key1"));
173
174                // force commit anyhow...
175                txMap1.commitTransaction(true);
176                // after successful commit it must be the value written by this thread
177                report("value3", (String) txMap1.get("key1"));
178                report("value3", (String) map1.get("key1"));
179        }
180
181    public void testTxControl() throws Throwable {
182                super.testTxControl();
183    }
184
185}