commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ozeigerm...@apache.org
Subject svn commit: r566128 - in /commons/proper/transaction/branches/TRANSACTION_2/src: java/org/apache/commons/transaction/ java/org/apache/commons/transaction/file/ java/org/apache/commons/transaction/locking/ java/org/apache/commons/transaction/memory/ tes...
Date Wed, 15 Aug 2007 12:46:26 GMT
Author: ozeigermann
Date: Wed Aug 15 05:46:25 2007
New Revision: 566128

URL: http://svn.apache.org/viewvc?view=rev&rev=566128
Log:
New tests including fixes for revealed bugs. RWLockmanagers had to be deleted as default Lock implementation do not fit such an implementation. Deadlock-Detection had to be deleted for the same reason. Future versions might reintroduce those features.

Added:
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java
      - copied, changed from r565953, commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/HierarchicalRWLockManager.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java
    commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/
    commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/BasicTxMapTest.java
    commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/OptimisticTxMapTest.java
    commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java
Removed:
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/HierarchicalRWLockManager.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/RWLockManager.java
Modified:
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/DefaultTransaction.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/TxMap.java
    commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/package.html
    commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/DefaultTransactionTest.java
    commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/AbstractTransactionalResourceManager.java Wed Aug 15 05:46:25 2007
@@ -50,7 +50,7 @@
         this.name = name;
     }
 
-    // can be used to share a lock manager with other transactinal resource
+    // can be used to share a lock manager with other transactional resource
     // managers
     public AbstractTransactionalResourceManager(String name, LockManager<Object, Object> lm) {
         this.name = name;
@@ -188,9 +188,6 @@
     }
 
     public void setLm(LockManager<Object, Object> lm) {
-        if (this.lm != null) {
-            throw new IllegalStateException("You can set the lock manager only once!");
-        }
         this.lm = lm;
     }
 

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/DefaultTransaction.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/DefaultTransaction.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/DefaultTransaction.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/DefaultTransaction.java Wed Aug 15 05:46:25 2007
@@ -20,6 +20,7 @@
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.commons.transaction.locking.DefaultLockManager;
 import org.apache.commons.transaction.locking.LockManager;
 
 /**
@@ -62,11 +63,20 @@
     /**
      * Creates a new transaction implementation.
      * 
-     * @param lm the lock manager shared by all resource managers
+     * @param lm
+     *            the lock manager shared by all resource managers
      */
     public DefaultTransaction(LockManager<Object, Object> lm) {
         this.lm = lm;
         this.rms = new LinkedList<ManageableResourceManager>();
+    }
+
+    /**
+     * Creates a new transaction implementation using the default lock manager.
+     * 
+     */
+    public DefaultTransaction() {
+        this(new DefaultLockManager<Object, Object>());
     }
 
     public synchronized void commit() throws TransactionException {

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/file/TxFileResourceManager.java Wed Aug 15 05:46:25 2007
@@ -32,8 +32,9 @@
 import org.apache.commons.transaction.ManageableResourceManager;
 import org.apache.commons.transaction.AbstractTransactionalResourceManager.AbstractTxContext;
 import org.apache.commons.transaction.file.FileResourceManager.FileResource;
+import org.apache.commons.transaction.locking.DefaultLockManager;
 import org.apache.commons.transaction.locking.HierarchicalLockManager;
-import org.apache.commons.transaction.locking.HierarchicalRWLockManager;
+import org.apache.commons.transaction.locking.DefaultHierarchicalLockManager;
 import org.apache.commons.transaction.locking.LockManager;
 import org.apache.commons.transaction.resource.ResourceException;
 import org.apache.commons.transaction.resource.ResourceManager;
@@ -55,6 +56,7 @@
     public TxFileResourceManager(String name, String rootPath) {
         super(name);
         wrapped = new FileResourceManager(rootPath);
+        setLm(new DefaultLockManager<Object, Object>());
     }
 
     @Override
@@ -69,7 +71,7 @@
     @Override
     public void setLm(LockManager<Object, Object> lm) {
         super.setLm(lm);
-        hlm = new HierarchicalRWLockManager(getRootPath(), lm);
+        hlm = new DefaultHierarchicalLockManager(getRootPath(), lm);
     }
 
     public class FileTxContext extends AbstractTxContext implements

Copied: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java (from r565953, commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/HierarchicalRWLockManager.java)
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java?view=diff&rev=566128&p1=commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/HierarchicalRWLockManager.java&r1=565953&p2=commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/HierarchicalRWLockManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultHierarchicalLockManager.java Wed Aug 15 05:46:25 2007
@@ -18,13 +18,13 @@
 
 import java.util.concurrent.TimeUnit;
 
-public class HierarchicalRWLockManager<M> implements HierarchicalLockManager<Object, M> {
+public class DefaultHierarchicalLockManager<M> implements HierarchicalLockManager<Object, M> {
 
     private final String rootPath;
 
     private final LockManager<Object, M> lm;
 
-    public HierarchicalRWLockManager(String rootPath, LockManager<Object, M> lm) {
+    public DefaultHierarchicalLockManager(String rootPath, LockManager<Object, M> lm) {
         this.rootPath = rootPath;
         this.lm = lm;
     }

Added: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java?view=auto&rev=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java (added)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/DefaultLockManager.java Wed Aug 15 05:46:25 2007
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.transaction.locking;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.locking.LockException.Code;
+
+/**
+ * 
+ * @author olli
+ * 
+ * @param <K>
+ * @param <M>
+ */
+public class DefaultLockManager<K, M> implements LockManager<K, M> {
+    private Log logger = LogFactory.getLog(getClass());
+
+    protected ConcurrentHashMap<KeyEntry<K, M>, ReentrantLock> locks = new ConcurrentHashMap<KeyEntry<K, M>, ReentrantLock>();
+
+    protected Map<Thread, CopyOnWriteArraySet<ReentrantLock>> locksForThreads = new ConcurrentHashMap<Thread, CopyOnWriteArraySet<ReentrantLock>>();
+
+    protected Map<Thread, Long> effectiveGlobalTimeouts = new ConcurrentHashMap<Thread, Long>();
+
+    @Override
+    public void endWork() {
+        release();
+    }
+
+    @Override
+    public void startWork(long timeout, TimeUnit unit) {
+        if (isWorking()) {
+            throw new IllegalStateException("work has already been started");
+        }
+        locksForThreads.put(Thread.currentThread(), new CopyOnWriteArraySet<ReentrantLock>());
+
+        long timeoutMSecs = unit.toMillis(timeout);
+        long now = System.currentTimeMillis();
+        long effectiveTimeout = now + timeoutMSecs;
+        effectiveGlobalTimeouts.put(Thread.currentThread(), effectiveTimeout);
+    }
+
+    // TODO
+    protected boolean checkForDeadlock() {
+        return false;
+
+    }
+
+    protected long computeRemainingTime(Thread thread) {
+        long timeout = effectiveGlobalTimeouts.get(thread);
+        long now = System.currentTimeMillis();
+        long remaining = timeout - now;
+        return remaining;
+    }
+
+    protected ReentrantLock create() {
+        return new ReentrantLock();
+    }
+
+    protected static class KeyEntry<K, M> {
+
+        private K k;
+
+        private M m;
+
+        public KeyEntry(K k, M m) {
+            this.k = k;
+            this.m = m;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj instanceof KeyEntry) {
+                KeyEntry otherEntry = (KeyEntry) obj;
+                return (otherEntry.k.equals(k) && otherEntry.m.equals(m));
+            }
+            return false;
+        }
+
+        public int hashCode() {
+            return k.hashCode() + m.hashCode();
+        }
+    }
+
+    public boolean isWorking() {
+        return locksForThreads.get(Thread.currentThread()) != null;
+    }
+
+    @Override
+    public void lock(M managedResource, K key, boolean exclusive) throws LockException {
+        long remainingTime = computeRemainingTime(Thread.currentThread());
+
+        boolean locked = tryLockInternal(managedResource, key, exclusive, remainingTime,
+                TimeUnit.MILLISECONDS);
+        if (!locked) {
+            throw new LockException(Code.TIMED_OUT, key);
+        }
+    }
+
+    @Override
+    public boolean tryLock(M managedResource, K key, boolean exclusive) {
+        return tryLockInternal(managedResource, key, exclusive, 0, TimeUnit.MILLISECONDS);
+    }
+
+    protected boolean tryLockInternal(M managedResource, K key, boolean exclusive, long time,
+            TimeUnit unit) throws LockException {
+        reportTimeout(Thread.currentThread());
+
+        KeyEntry<K, M> entry = new KeyEntry<K, M>(key, managedResource);
+
+        ReentrantLock lock = create();
+        ReentrantLock existingLock = locks.putIfAbsent(entry, lock);
+        if (existingLock != null)
+            lock = existingLock;
+        Set<ReentrantLock> locks = locksForThreads.get(Thread.currentThread());
+        if (locks == null) {
+            throw new IllegalStateException("lock() can only be called after startWork()");
+        }
+
+        boolean locked;
+        if (time == 0) {
+            locked = lock.tryLock();
+        } else {
+            try {
+                locked = lock.tryLock(time, unit);
+            } catch (InterruptedException e) {
+                throw new LockException(Code.INTERRUPTED);
+            }
+
+        }
+        if (locked) {
+            locks.add(lock);
+        }
+        return locked;
+    }
+
+    protected void reportTimeout(Thread thread) throws LockException {
+        if (hasTimedOut(thread)) {
+            throw new LockException(LockException.Code.TIMED_OUT);
+        }
+    }
+
+    protected boolean hasTimedOut(Thread thread) {
+        long remainingTime = computeRemainingTime(thread);
+        return (remainingTime < 0);
+
+    }
+
+    protected void release() {
+        Set<ReentrantLock> locks = locksForThreads.get(Thread.currentThread());
+        // graceful reaction...
+        if (locks == null) {
+            return;
+        }
+        for (ReentrantLock lock : locks) {
+            int holdCount = lock.getHoldCount();
+            logger.debug("Locks held by this thread: " + holdCount);
+            while (true) {
+                try {
+                    lock.unlock();
+                } catch (IllegalMonitorStateException imse) {
+                    // We are lacking information on whether we have a read
+                    // lock and if so how many.
+                    // XXX Just free as many as possible.
+                    break;
+                }
+            }
+        }
+
+        locksForThreads.remove(Thread.currentThread());
+    }
+
+}

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/locking/LockManager.java Wed Aug 15 05:46:25 2007
@@ -20,7 +20,11 @@
 
 /**
  * 
- * @author olli
+ * <p>
+ * Implementations are free to decide whether they want to make use of the
+ * <code>exclusive</code> flag passed in
+ * {@link #lock(Object, Object, boolean)} and
+ * {@link #tryLock(Object, Object, boolean)}.
  * 
  * 
  * 
@@ -51,6 +55,7 @@
      *            resource for on which this block of work shall be done
      */
     public void lock(M managedResource, K key, boolean exclusive) throws LockException;
+
     public boolean tryLock(M managedResource, K key, boolean exclusive);
 
 }

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/BasicTxMap.java Wed Aug 15 05:46:25 2007
@@ -27,34 +27,45 @@
 
 import org.apache.commons.transaction.AbstractTransactionalResourceManager;
 import org.apache.commons.transaction.AbstractTransactionalResourceManager.AbstractTxContext;
+import org.apache.commons.transaction.locking.DefaultLockManager;
+import org.apache.commons.transaction.locking.LockManager;
 
 /**
- * Wrapper that adds transactional control to all kinds of maps that implement
- * the {@link Map} interface. This wrapper has rather weak isolation, but is
- * simply, neven blocks and commits will never fail for logical reasons. <br>
- * Start a transaction by calling {@link #startTransaction()}. Then perform the
- * normal actions on the map and finally either call
- * {@link #commitTransaction()} to make your changes permanent or
- * {@link #rollbackTransaction()} to undo them. <br>
- * <em>Caution:</em> Do not modify values retrieved by {@link #get(Object)} as
- * this will circumvent the transactional mechanism. Rather clone the value or
- * copy it in a way you see fit and store it back using
- * {@link #put(Object, Object)}. <br>
+ * Map featuring transactional control.
+ * 
+ * <p>
+ * This implementation has rather weak isolation, but is simple, never blocks
+ * and commits will never fail for logical reasons.
+ * 
+ * <p>
  * <em>Note:</em> This wrapper guarantees isolation level
  * <code>READ COMMITTED</code> only. I.e. as soon a value is committed in one
  * transaction it will be immediately visible in all other concurrent
  * transactions.
  * 
+ * <p>
+ * This implementation wraps a map of type {@link ConcurrentHashMap}. All
+ * internal synchronization is delegated to this class.
+ * 
  * @see OptimisticTxMap
  * @see PessimisticTxMap
+ * @see ConcurrentHashMap
  */
-public class BasicTxMap<K, V> extends AbstractTransactionalResourceManager<BasicTxMap.MapTxContext> implements
-        TxMap<K, V> {
+public class BasicTxMap<K, V> extends AbstractTransactionalResourceManager<BasicTxMap.MapTxContext>
+        implements TxMap<K, V> {
 
-    protected Map<K, V> wrapped = new ConcurrentHashMap<K, V>();
+    protected final Map<K, V> wrapped = new ConcurrentHashMap<K, V>();
+
+    public BasicTxMap(String name, LockManager<Object, Object> lm) {
+        super(name, lm);
+    }
 
     public BasicTxMap(String name) {
-        super(name);
+        this(name, new DefaultLockManager<Object, Object>());
+    }
+
+    public Map<K, V> getWrappedMap() {
+        return wrapped;
     }
 
     //
@@ -120,7 +131,7 @@
     /**
      * @see Map#values()
      */
-    public Collection values() {
+    public Collection<V> values() {
 
         MapTxContext txContext = getActiveTx();
 
@@ -128,11 +139,11 @@
             return wrapped.values();
         } else {
             // XXX expensive :(
-            Collection values = new ArrayList();
-            for (Iterator it = keySet().iterator(); it.hasNext();) {
-                Object key = it.next();
-                Object value = get(key);
-                // XXX we have no isolation, so get entry might have been
+            Collection<V> values = new ArrayList<V>();
+            Set<K> keys = keySet();
+            for (K key : keys) {
+                V value = get(key);
+                // XXX we have no isolation, so entry might have been
                 // deleted in the meantime
                 if (value != null) {
                     values.add(value);
@@ -145,7 +156,7 @@
     /**
      * @see Map#putAll(java.util.Map)
      */
-    public void putAll(Map map) {
+    public void putAll(Map<? extends K, ? extends V> map) {
         MapTxContext txContext = getActiveTx();
 
         if (txContext == null) {
@@ -161,7 +172,7 @@
     /**
      * @see Map#entrySet()
      */
-    public Set entrySet() {
+    public Set<Map.Entry<K, V>> entrySet() {
         MapTxContext txContext = getActiveTx();
         if (txContext == null) {
             return wrapped.entrySet();
@@ -184,7 +195,7 @@
     /**
      * @see Map#keySet()
      */
-    public Set keySet() {
+    public Set<K> keySet() {
         MapTxContext txContext = getActiveTx();
 
         if (txContext == null) {
@@ -299,7 +310,7 @@
     }
 
     public class MapTxContext extends AbstractTxContext {
-        protected Set deletes;
+        protected Set<K> deletes;
 
         protected Map<K, V> changes;
 
@@ -308,14 +319,14 @@
         protected boolean cleared;
 
         protected MapTxContext() {
-            deletes = new HashSet();
+            deletes = new HashSet<K>();
             changes = new HashMap<K, V>();
             adds = new HashMap<K, V>();
             cleared = false;
         }
 
-        protected Set keys() {
-            Set keySet = new HashSet();
+        protected Set<K> keys() {
+            Set<K> keySet = new HashSet<K>();
             if (!cleared) {
                 keySet.addAll(wrapped.keySet());
                 keySet.removeAll(deletes);
@@ -372,7 +383,7 @@
                 changes.remove(key);
                 adds.remove(key);
                 if (wrapped.containsKey(key) && !cleared) {
-                    deletes.add(key);
+                    deletes.add((K) key);
                 }
             } catch (RuntimeException e) {
                 markForRollback();

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/OptimisticTxMap.java Wed Aug 15 05:46:25 2007
@@ -16,77 +16,89 @@
  */
 package org.apache.commons.transaction.memory;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import org.apache.commons.transaction.locking.DefaultLockManager;
 import org.apache.commons.transaction.locking.LockException;
+import org.apache.commons.transaction.locking.LockManager;
 
 /**
- * Wrapper that adds transactional control to all kinds of maps that implement
- * the {@link Map} interface. By using a naive optimistic transaction control
- * this wrapper has better isolation than {@link BasicTxMap}, but
- * may also fail to commit.
+ * Map featuring transactional control.
  * 
- * <br>
- * Start a transaction by calling {@link #startTransaction()}. Then perform the
- * normal actions on the map and finally either call
- * {@link #commitTransaction()} to make your changes permanent or
- * {@link #rollbackTransaction()} to undo them. <br>
- * <em>Caution:</em> Do not modify values retrieved by {@link #get(Object)} as
- * this will circumvent the transactional mechanism. Rather clone the value or
- * copy it in a way you see fit and store it back using
- * {@link #put(Object, Object)}. <br>
- * <em>Note:</em> This wrapper guarantees isolation level
- * <code>SERIALIZABLE</code>. <br>
- * <em>Caution:</em> This implementation might be slow when large amounts of
+ * <p>By using a naive optimistic transaction control
+ * this implementation has better isolation than {@link BasicTxMap}, but may also fail
+ * to commit.
+ * 
+ * <p><em>Caution:</em> This implementation might be slow when large amounts of
  * data is changed in a transaction as much references will need to be copied
  * around.
  * 
- * @version $Id: OptimisticMapWrapper.java 493628 2007-01-07 01:42:48Z joerg $
+ * <p>This implementation wraps a map of type {@link ConcurrentHashMap}. 
+ * 
  * @see BasicTxMap
  * @see PessimisticTxMap
+ * @see ConcurrentHashMap
  */
 public class OptimisticTxMap<K, V> extends BasicTxMap<K, V> implements TxMap<K, V> {
 
-    private Set<CopyingTxContext> activeTransactions = new HashSet<CopyingTxContext>();
+    private Set<CopyingTxContext> activeTransactions = Collections
+            .synchronizedSet(new HashSet<CopyingTxContext>());
+
     private ReadWriteLock commitLock = new ReentrantReadWriteLock();
 
     private long commitTimeout = 1000 * 60; // 1 minute
 
     private long accessTimeout = 1000 * 30; // 30 seconds
+
+    public OptimisticTxMap(String name) {
+        this(name, new DefaultLockManager<Object, Object>());
+    }
+
+    public OptimisticTxMap(String name, LockManager<Object, Object> lm) {
+        super(name, lm);
+    }
+    
+    @Override
+    public void startTransaction(long timeout, TimeUnit unit) {
+        super.startTransaction(timeout, unit);
+        MapTxContext txContext = getActiveTx();
+        activeTransactions.add((CopyingTxContext)txContext);
+    }
+    
+    @Override
     public void rollbackTransaction() {
         MapTxContext txContext = getActiveTx();
         super.rollbackTransaction();
         activeTransactions.remove(txContext);
     }
 
-    public OptimisticTxMap(String name) {
-        super(name);
-    }
-
-
+    @Override
     public boolean commitTransaction() throws LockException {
         return commitTransaction(false);
     }
 
     public boolean commitTransaction(boolean force) throws LockException {
         MapTxContext txContext = getActiveTx();
-        
+
         if (txContext == null) {
-            throw new IllegalStateException(
-                "Active thread " + Thread.currentThread() + " not associated with a transaction!");
+            throw new IllegalStateException("Active thread " + Thread.currentThread()
+                    + " not associated with a transaction!");
         }
 
         if (txContext.isMarkedForRollback()) {
-            throw new IllegalStateException("Active thread " + Thread.currentThread() + " is marked for rollback!");
+            throw new IllegalStateException("Active thread " + Thread.currentThread()
+                    + " is marked for rollback!");
         }
-        
+
         try {
             // in this final commit phase we need to be the only one access the
             // map
@@ -99,7 +111,7 @@
                     throw new LockException(LockException.Code.CONFLICT, conflictKey);
                 }
             }
-    
+
             activeTransactions.remove(txContext);
             copyChangesToConcurrentTransactions();
             return super.commitTransaction();
@@ -166,7 +178,7 @@
             }
         }
     }
-    
+
     @Override
     protected CopyingTxContext createContext() {
         return new CopyingTxContext();
@@ -339,7 +351,7 @@
     public void setCommitTimeout(long commitTimeout) {
         this.commitTimeout = commitTimeout;
     }
-    
+
     @Override
     public boolean commitCanFail() {
         return true;

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/PessimisticTxMap.java Wed Aug 15 05:46:25 2007
@@ -18,50 +18,55 @@
 package org.apache.commons.transaction.memory;
 
 import java.util.Collection;
-import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.apache.commons.transaction.locking.DefaultLockManager;
+import org.apache.commons.transaction.locking.LockManager;
 
 /**
- * Wrapper that adds transactional control to all kinds of maps that implement
- * the {@link Map} interface. By using pessimistic transaction control (blocking
- * locks) this wrapper has better isolation than {@link BasicTxMap},
- * but also has less possible concurrency and may even deadlock. A commit,
- * however, will never fail. <br>
- * Start a transaction by calling {@link #startTransaction()}. Then perform the
- * normal actions on the map and finally either call
- * {@link #commitTransaction()} to make your changes permanent or
- * {@link #rollbackTransaction()} to undo them. <br>
- * <em>Caution:</em> Do not modify values retrieved by {@link #get(Object)} as
- * this will circumvent the transactional mechanism. Rather clone the value or
- * copy it in a way you see fit and store it back using
- * {@link #put(Object, Object)}. <br>
- * <em>Note:</em> This wrapper guarantees isolation level
- * <code>SERIALIZABLE</code>.
+ * Map featuring transactional control.
+ * 
+ * <p>
+ * By using pessimistic transaction control (blocking locks) this wrapper has
+ * better isolation than {@link BasicTxMap}, but also has less possible
+ * concurrency and may even deadlock. A commit, however, will never fail.
+ * 
+ * <p>
+ * <em>Caution:</em>Some operations that would require global locks (e.g.
+ * <code>size()</code> or <code>clear()</code> or not properly isolated as
+ * this implementation does not support global locks.
+ * 
+ * <p>
+ * This implementation wraps a map of type {@link ConcurrentHashMap}.
  * 
- * @version $Id: PessimisticMapWrapper.java 493628 2007-01-07 01:42:48Z joerg $
  * @see BasicTxMap
  * @see OptimisticTxMap
+ * @see ConcurrentHashMap
  */
 public class PessimisticTxMap<K, V> extends BasicTxMap<K, V> implements TxMap<K, V> {
 
-    protected static final Object GLOBAL_LOCK = "GLOBAL";
+    private ReadWriteLock globalLock = new ReentrantReadWriteLock();
 
     public PessimisticTxMap(String name) {
-        super(name);
+        this(name, new DefaultLockManager<Object, Object>());
+    }
+
+    public PessimisticTxMap(String name, LockManager<Object, Object> lm) {
+        super(name, lm);
     }
 
     public Collection values() {
-        assureGlobalReadLock();
         return super.values();
     }
 
     public Set entrySet() {
-        assureGlobalReadLock();
         return super.entrySet();
     }
 
     public Set keySet() {
-        assureGlobalReadLock();
         return super.keySet();
     }
 
@@ -86,15 +91,6 @@
         if (txContext != null) {
             txContext.writeLock(key);
             // XXX fake intention lock (prohibits global WRITE)
-            txContext.readLock(GLOBAL_LOCK);
-        }
-    }
-
-    protected void assureGlobalReadLock() {
-        LockingTxContext txContext = (LockingTxContext) getActiveTx();
-        if (txContext != null) {
-            // XXX fake intention lock (prohibits global WRITE)
-            txContext.readLock(GLOBAL_LOCK);
         }
     }
 
@@ -106,45 +102,34 @@
     public class LockingTxContext extends MapTxContext {
 
         protected Set keys() {
-            readLock(GLOBAL_LOCK);
             return super.keys();
         }
 
         protected V get(Object key) {
             readLock(key);
-            // XXX fake intention lock (prohibits global WRITE)
-            readLock(GLOBAL_LOCK);
             return super.get(key);
         }
 
         protected void put(K key, V value) {
             writeLock(key);
-            // XXX fake intention lock (prohibits global WRITE)
-            readLock(GLOBAL_LOCK);
             super.put(key, value);
         }
 
         protected void remove(Object key) {
             writeLock(key);
-            // XXX fake intention lock (prohibits global WRITE)
-            readLock(GLOBAL_LOCK);
             super.remove(key);
         }
 
         protected int size() {
-            // XXX this is bad luck, we need a global read lock just for the
-            // size :( :( :(
-            readLock(GLOBAL_LOCK);
             return super.size();
         }
 
         protected void clear() {
-            writeLock(GLOBAL_LOCK);
             super.clear();
         }
 
     }
-    
+
     @Override
     public boolean commitCanFail() {
         return false;

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/TxMap.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/TxMap.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/TxMap.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/TxMap.java Wed Aug 15 05:46:25 2007
@@ -19,6 +19,31 @@
 import java.util.Map;
 
 import org.apache.commons.transaction.ManageableResourceManager;
+import org.apache.commons.transaction.TransactionalResourceManager;
 
+/**
+ * Interface for a map that features transactional support.
+ * 
+ * <p>Start a transaction by calling {@link TransactionalResourceManager#startTransaction(long, java.util.concurrent.TimeUnit)}. Then perform the
+ * normal actions on the map and finally either call
+ * {@link TransactionalResourceManager#commitTransaction()} to make your changes permanent or
+ * {@link TransactionalResourceManager#rollbackTransaction()} to undo them.
+ * 
+ * <p><em>Caution:</em> Do not modify values retrieved by {@link Map#get(Object)} as
+ * this will circumvent the transactional mechanism. Rather clone the value or
+ * copy it in a way you see fit and store it back using
+ * {@link Map#put(Object, Object)}. <br>
+ * 
+ * @see BasicTxMap
+ * @see OptimisticTxMap
+ * @see PessimisticTxMap
+ * 
+ */
 public interface TxMap<K, V> extends Map<K, V>, ManageableResourceManager {
+    /**
+     * Gets the underlying map that is wrapped by this transactional implementation.
+     * 
+     * @return the wrapped map
+     */
+    Map<K, V> getWrappedMap();
 }

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/package.html
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/package.html?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/package.html (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/java/org/apache/commons/transaction/memory/package.html Wed Aug 15 05:46:25 2007
@@ -4,339 +4,17 @@
 <p>Working on maps as if they were databases.
 </p>
 
- <p>One major part of the Transaction Component are a set of wrappers that allow to add transactional control
-to any kind of map implemening the <code>java.util.Map</code> interface. Transactional control refers 
+ <p>The implementation of this package add transactional control to the <code>java.util.Map</code> interface. Transactional control refers 
 to the procedure that you start a transaction do a number of statements and finally decide to either
 commit the changes, i.e. make them permanent, or rather roll back the transaction by discarding all your changes.
 </p>
 
-<p>As an example imagine a client application where you do complex stuff (maybe 
-talking to one or more servers) and the user is allowed to cancel the 
-whole operation. Or it might be needed to be canceled because of errors that occur in the middle of the whole request. 
-Now image all the results have been stored in a transactional map and you 
-simply do a rollback on errors and a commit upon success.
-</p>
-
-<p>The whole thing gets a bit more complicated when more than one transaction is executed on a map at the same time. 
-	In this case these transactions are running <em>concurrently</em>. 
+<p>You are free to have any number of transactions running on such a map at the same time. 
+	In this case these transactions are said to be running <em>concurrently</em>. 
 	Problems that might occur with concurrent transaction are diverse and implementations differ in how much 
-	spurious phenomena can be observed. Which phenomenon can occur with which transactional map implementation can be found in the chart a little bit down this page. Finally, there is a
+	spurious phenomena can be observed. Finally, there is a
 	<a
 	href="http://www.cs.umb.edu/cs734/CritiqueANSI_Iso.pdf">great
 	more general article about different isolations</a>.  </p>
-<p>If none of
-the known phenomena is observable - which of course is the best you can get - a transaction is called <em>serializable</em>.
- The origin of this term comes from a differet, but equivalent definition. This
-definition switches the perspective from local phenomena observed inside
-transactions to global one, more specific global corretctness. If a set of
-transactions are executed
-concurrently and there is a strictly sequential execution schedule of those transactions that leaves the
-map in the same state as the concurrent one those transactions are called <em>
-serializable</em>.
-</p>
-
-<h2>Comparision of transactional map implementations</h2>
-
-<table summary="" border="1" frame="HSIDES" rules="GROUPS" width="100%" cellpadding="3" cellspacing="0" dir="ltr" title="">
-
-<thead>
-<tr>
-<th align="left" valign="bottom" scope="col"></th>
-<th align="left" valign="bottom" scope="col"> <font face="Arial,
-Helvetica, sans-serif"><strong><a href="../apidocs/org/apache/commons/transaction/memory/TransactionalMapWrapper.html">TransactionalMapWrapper</a></strong></font></th>
-<th align="left" valign="bottom" scope="col"> <font face="Arial, Helvetica, sans-serif"><strong><a href="../apidocs/org/apache/commons/transaction/memory/OptimisticMapWrapper.html">OptimisticMapWrapper</a></strong></font></th>
-<th align="left" valign="bottom" scope="col"> <font face="Arial, Helvetica, sans-serif"><strong><a href="../apidocs/org/apache/commons/transaction/memory/PessimisticMapWrapper.html">PessimisticMapWrapper</a></strong></font></th>
-</tr>
-</thead>
-
-<tbody>
-<tr align="left" valign="top">
-<td> 
-
-<p>Lost update</p>
-
-</td>
-<td> 
-
-<p>Possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Dirty write</p>
-
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Dirty read</p>
-</td>
-<td> 
-
-
-<p>Not possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Lost update</p>
-
-</td>
-<td> 
-
-<p>Possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Nonrepeatable read</p>
-</td>
-<td> 
-
-<p>Possible</p>
-
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Phantoms</p>
-</td>
-<td> 
-
-<p>Possible</p>
-</td>
-<td> 
-
-
-<p>Not possible</p>
-</td>
-<td> 
-
-
-<p>Not possible</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Read Skew</p>
-
-</td>
-<td> 
-
-<p>Possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Write Skew</p>
-
-</td>
-<td> 
-
-<p>Possible</p>
-</td>
-<td> 
-
-<p>Possible</p>
-</td>
-<td> 
-
-<p>Not possible</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-
-<p>Readers block writers</p>
-</td>
-<td> 
-
-<p>No</p>
-</td>
-<td> 
-
-<p>No</p>
-</td>
-<td> 
-
-<p>Yes</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Writers block readers</p>
-
-</td>
-<td> 
-
-<p>No</p>
-</td>
-<td> 
-
-<p>No</p>
-</td>
-<td> 
-
-<p>Yes</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Writers block writers</p>
-</td>
-<td> 
-
-<p>No</p>
-
-</td>
-<td> 
-
-<p>No</p>
-
-</td>
-<td> 
-
-<p>Yes</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Might deadlock</p>
-</td>
-<td> 
-
-<p>No</p>
-
-</td>
-<td> 
-
-<p>No</p>
-
-</td>
-<td> 
-
-<p>Yes</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Commit might fail</p>
-</td>
-<td> 
-
-<p>No</p>
-
-</td>
-<td> 
-
-<p>Yes</p>
-
-</td>
-<td> 
-
-<p>No</p>
-</td>
-</tr>
-
-<tr align="left" valign="top">
-<td> 
-
-<p>Isolation Level</p>
-
-</td>
-<td> 
-
-<p>Read Committed (with lost updates possible)</p>
-</td>
-<td> 
-
-<p>Snapshot (Oracle would call it Serializable)</p>
-</td>
-<td> 
-
-<p>Serializable</p>
-</td>
-</tr>
-
-</tbody>
-</table>
-
-
 </body>
 </html>

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/DefaultTransactionTest.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/DefaultTransactionTest.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/DefaultTransactionTest.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/DefaultTransactionTest.java Wed Aug 15 05:46:25 2007
@@ -20,8 +20,6 @@
 
 import junit.framework.JUnit4TestAdapter;
 
-import org.apache.commons.transaction.locking.LockManager;
-import org.apache.commons.transaction.locking.RWLockManager;
 import org.apache.commons.transaction.memory.PessimisticTxMap;
 import org.apache.commons.transaction.memory.TxMap;
 import org.junit.Test;
@@ -33,8 +31,7 @@
 
     @Test
     public void basic() {
-        LockManager<Object, Object> lm = new RWLockManager<Object, Object>();
-        Transaction t = new DefaultTransaction(lm);
+        Transaction t = new DefaultTransaction();
         TxMap<String, Object> txMap1 = new PessimisticTxMap<String, Object>("TxMap1");
         t.enlistResourceManager(txMap1);
         TxMap<String, Object> txMap2 = new PessimisticTxMap<String, Object>("TxMap2");

Modified: commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java?view=diff&rev=566128&r1=566127&r2=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java (original)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/file/TxFileResourceManagerTest.java Wed Aug 15 05:46:25 2007
@@ -25,12 +25,12 @@
 import java.util.concurrent.TimeUnit;
 
 import junit.framework.JUnit4TestAdapter;
-import static junit.framework.Assert.fail;
+import static junit.framework.Assert.*;
+import org.junit.Test;
 
 import org.apache.commons.transaction.file.FileResourceManager.FileResource;
+import org.apache.commons.transaction.locking.DefaultLockManager;
 import org.apache.commons.transaction.locking.LockManager;
-import org.apache.commons.transaction.locking.RWLockManager;
-import org.junit.Test;
 
 public class TxFileResourceManagerTest {
     
@@ -168,16 +168,12 @@
         }
     }
 
-
-    
     @Test
     public void basic() {
         TxFileResourceManager manager = new TxFileResourceManager("TxFileManager", "d:/tmp/content");
-        LockManager lm = new RWLockManager<String, String>();
         FileResourceUndoManager um;
         try {
             um = new MemoryUndoManager("d:/tmp/txlogs");
-            manager.setLm(lm);
             manager.setUndoManager(um);
             manager.startTransaction(60, TimeUnit.SECONDS);
             FileResource file = manager.getResource("d:/tmp/content/aha");
@@ -195,7 +191,7 @@
 
     }
 
-    public static void main(String[] args) {
-        new TxFileResourceManagerTest().basic();
+    public static void main(java.lang.String[] args) {
+        junit.textui.TestRunner.run(suite());
     }
 }

Added: commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/BasicTxMapTest.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/BasicTxMapTest.java?view=auto&rev=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/BasicTxMapTest.java (added)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/BasicTxMapTest.java Wed Aug 15 05:46:25 2007
@@ -0,0 +1,345 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.transaction.memory;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.Test;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.transaction.locking.LockManager;
+import org.apache.commons.transaction.util.RendezvousBarrier;
+
+/**
+ * Tests for basic tx map.
+ * 
+ */
+public class BasicTxMapTest {
+
+    private static final Log log = LogFactory.getLog(BasicTxMapTest.class.getName());
+
+    protected static final long BARRIER_TIMEOUT = 20000;
+
+    // XXX need this, as JUnit seems to print only part of these strings
+    protected static void report(String should, String is) {
+        if (!should.equals(is)) {
+            fail("\nWrong output:\n'" + is + "'\nShould be:\n'" + should + "'\n");
+        }
+    }
+
+    protected static void checkCollection(Collection col, Object[] values) {
+        int cnt = 0;
+        int trueCnt = 0;
+
+        for (Iterator it = col.iterator(); it.hasNext();) {
+            cnt++;
+            Object value1 = it.next();
+            for (int i = 0; i < values.length; i++) {
+                Object value2 = values[i];
+                if (value2.equals(value1))
+                    trueCnt++;
+            }
+        }
+        assertEquals(cnt, values.length);
+        assertEquals(trueCnt, values.length);
+    }
+
+    public static junit.framework.Test suite() {
+        return new JUnit4TestAdapter(BasicTxMapTest.class);
+    }
+
+    public static void main(java.lang.String[] args) {
+        junit.textui.TestRunner.run(suite());
+    }
+
+    @Test
+    public void testBasic() {
+
+        log.info("Checking basic transaction features");
+
+        final BasicTxMap<String, String> txMap1 = new BasicTxMap("txMap1");
+        final Map map1 = txMap1.getWrappedMap();
+
+        assertTrue(txMap1.isEmpty());
+
+        // make sure changes are propagated to wrapped map outside tx
+        txMap1.put("key1", "value1");
+        report("value1", (String) map1.get("key1"));
+        assertFalse(txMap1.isEmpty());
+
+        // make sure changes are propagated to wrapped map only after commit
+        txMap1.startTransaction(1, TimeUnit.HOURS);
+        assertFalse(txMap1.isEmpty());
+        txMap1.put("key1", "value2");
+        report("value1", (String) map1.get("key1"));
+        report("value2", (String) txMap1.get("key1"));
+        txMap1.commitTransaction();
+        report("value2", (String) map1.get("key1"));
+        report("value2", (String) txMap1.get("key1"));
+
+        // make sure changes are reverted after roll back
+        txMap1.startTransaction(1, TimeUnit.HOURS);
+        txMap1.put("key1", "value3");
+        txMap1.rollbackTransaction();
+        report("value2", (String) map1.get("key1"));
+        report("value2", (String) txMap1.get("key1"));
+    }
+
+    @Test
+    public void testComplex() {
+
+        log.info("Checking advanced and complex transaction features");
+
+        final BasicTxMap<String, String> txMap1 = new BasicTxMap("txMap1");
+        final Map map1 = txMap1.getWrappedMap();
+
+        // first fill in some global values:
+        txMap1.put("key1", "value1");
+        txMap1.put("key2", "value2");
+
+        // let's see if we have all values:
+        log.info("Checking if global values are present");
+
+        assertTrue(txMap1.containsValue("value1"));
+        assertTrue(txMap1.containsValue("value2"));
+        assertFalse(txMap1.containsValue("novalue"));
+
+        // ... and all keys
+        log.info("Checking if global keys are present");
+        assertTrue(txMap1.containsKey("key1"));
+        assertTrue(txMap1.containsKey("key2"));
+        assertFalse(txMap1.containsKey("nokey"));
+
+        // and now some inside a transaction
+        txMap1.startTransaction(1, TimeUnit.HOURS);
+        txMap1.put("key3", "value3");
+        txMap1.put("key4", "value4");
+
+        // let's see if we have all values:
+        log.info("Checking if values inside transactions are present");
+        assertTrue(txMap1.containsValue("value1"));
+        assertTrue(txMap1.containsValue("value2"));
+        assertTrue(txMap1.containsValue("value3"));
+        assertTrue(txMap1.containsValue("value4"));
+        assertFalse(txMap1.containsValue("novalue"));
+
+        // ... and all keys
+        log.info("Checking if keys inside transactions are present");
+        assertTrue(txMap1.containsKey("key1"));
+        assertTrue(txMap1.containsKey("key2"));
+        assertTrue(txMap1.containsKey("key3"));
+        assertTrue(txMap1.containsKey("key4"));
+        assertFalse(txMap1.containsKey("nokey"));
+
+        // now let's delete some old stuff
+        log.info("Checking remove inside transactions");
+        txMap1.remove("key1");
+        assertFalse(txMap1.containsKey("key1"));
+        assertFalse(txMap1.containsValue("value1"));
+        assertNull(txMap1.get("key1"));
+        assertEquals(3, txMap1.size());
+
+        // and some newly created
+        txMap1.remove("key3");
+        assertFalse(txMap1.containsKey("key3"));
+        assertFalse(txMap1.containsValue("value3"));
+        assertNull(txMap1.get("key3"));
+        assertEquals(2, txMap1.size());
+
+        log.info("Checking remove and propagation after commit");
+        txMap1.commitTransaction();
+
+        txMap1.remove("key1");
+        assertFalse(txMap1.containsKey("key1"));
+        assertFalse(txMap1.containsValue("value1"));
+        assertNull(txMap1.get("key1"));
+        assertFalse(txMap1.containsKey("key3"));
+        assertFalse(txMap1.containsValue("value3"));
+        assertNull(txMap1.get("key3"));
+        assertEquals(2, txMap1.size());
+    }
+
+    @Test
+    public void testSets() {
+
+        log.info("Checking set opertaions");
+
+        final BasicTxMap<String, String> txMap1 = new BasicTxMap("txMap1");
+        final Map map1 = txMap1.getWrappedMap();
+
+        // first fill in some global values:
+        txMap1.put("key1", "value1");
+        txMap1.put("key2", "value200");
+
+        // and now some inside a transaction
+        txMap1.startTransaction(1, TimeUnit.HOURS);
+        txMap1.put("key2", "value2"); // modify
+        txMap1.put("key3", "value3");
+        txMap1.put("key4", "value4");
+
+        // check entry set
+        boolean key1P, key2P, key3P, key4P;
+        key1P = key2P = key3P = key4P = false;
+        int cnt = 0;
+        for (Iterator it = txMap1.entrySet().iterator(); it.hasNext();) {
+            cnt++;
+            Map.Entry entry = (Map.Entry) it.next();
+            if (entry.getKey().equals("key1") && entry.getValue().equals("value1"))
+                key1P = true;
+            else if (entry.getKey().equals("key2") && entry.getValue().equals("value2"))
+                key2P = true;
+            else if (entry.getKey().equals("key3") && entry.getValue().equals("value3"))
+                key3P = true;
+            else if (entry.getKey().equals("key4") && entry.getValue().equals("value4"))
+                key4P = true;
+        }
+        assertEquals(cnt, 4);
+        assertTrue(key1P && key2P && key3P && key4P);
+
+        checkCollection(txMap1.values(), new String[] { "value1", "value2", "value3", "value4" });
+        checkCollection(txMap1.keySet(), new String[] { "key1", "key2", "key3", "key4" });
+
+        txMap1.commitTransaction();
+
+        // check again after commit (should be the same)
+        key1P = key2P = key3P = key4P = false;
+        cnt = 0;
+        for (Iterator it = txMap1.entrySet().iterator(); it.hasNext();) {
+            cnt++;
+            Map.Entry entry = (Map.Entry) it.next();
+            if (entry.getKey().equals("key1") && entry.getValue().equals("value1"))
+                key1P = true;
+            else if (entry.getKey().equals("key2") && entry.getValue().equals("value2"))
+                key2P = true;
+            else if (entry.getKey().equals("key3") && entry.getValue().equals("value3"))
+                key3P = true;
+            else if (entry.getKey().equals("key4") && entry.getValue().equals("value4"))
+                key4P = true;
+        }
+        assertEquals(cnt, 4);
+        assertTrue(key1P && key2P && key3P && key4P);
+
+        checkCollection(txMap1.values(), new String[] { "value1", "value2", "value3", "value4" });
+        checkCollection(txMap1.keySet(), new String[] { "key1", "key2", "key3", "key4" });
+
+        // now try clean
+
+        txMap1.startTransaction(1, TimeUnit.HOURS);
+
+        // add
+        txMap1.put("key5", "value5");
+        // modify
+        txMap1.put("key4", "value400");
+        // delete
+        txMap1.remove("key1");
+
+        assertEquals(txMap1.size(), 4);
+
+        txMap1.clear();
+        assertEquals(txMap1.size(), 0);
+        assertEquals(map1.size(), 4);
+
+        // add
+        txMap1.put("key5", "value5");
+        // delete
+        txMap1.remove("key1");
+
+        // adding one, not removing anything gives size 1
+        assertEquals(txMap1.size(), 1);
+        assertEquals(map1.size(), 4);
+        assertNull(txMap1.get("key4"));
+        assertNotNull(txMap1.get("key5"));
+
+        txMap1.commitTransaction();
+
+        // after commit clear must have been propagated to wrapped map:
+        assertEquals(txMap1.size(), 1);
+        assertEquals(map1.size(), 1);
+        assertNull(txMap1.get("key4"));
+        assertNotNull(txMap1.get("key5"));
+        assertNull(map1.get("key4"));
+        assertNotNull(map1.get("key5"));
+    }
+
+    @Test
+    public void testMulti() {
+        log.info("Checking concurrent transaction features");
+
+        final BasicTxMap<String, String> txMap1 = new BasicTxMap("txMap1");
+        final Map map1 = txMap1.getWrappedMap();
+
+        final RendezvousBarrier beforeCommitBarrier = new RendezvousBarrier("Before Commit", 2,
+                BARRIER_TIMEOUT);
+
+        final RendezvousBarrier afterCommitBarrier = new RendezvousBarrier("After Commit", 2,
+                BARRIER_TIMEOUT);
+
+        Thread thread1 = new Thread(new Runnable() {
+            public void run() {
+                txMap1.startTransaction(1, TimeUnit.HOURS);
+                try {
+                    beforeCommitBarrier.meet();
+                    txMap1.put("key1", "value2");
+                    txMap1.commitTransaction();
+                    afterCommitBarrier.call();
+                } catch (InterruptedException e) {
+                    log.warn("Thread interrupted", e);
+                    afterCommitBarrier.reset();
+                    beforeCommitBarrier.reset();
+                }
+            }
+        }, "Thread1");
+
+        txMap1.put("key1", "value1");
+
+        txMap1.startTransaction(1, TimeUnit.HOURS);
+        thread1.start();
+
+        report("value1", (String) txMap1.get("key1"));
+        beforeCommitBarrier.call();
+        try {
+            afterCommitBarrier.meet();
+        } catch (InterruptedException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        // we have read committed as isolation level, that's why I will see the
+        // new value of the other thread now
+        report("value2", (String) txMap1.get("key1"));
+
+        // now when I override it it should of course be my value again
+        txMap1.put("key1", "value3");
+        report("value3", (String) txMap1.get("key1"));
+
+        // after rollback it must be the value written by the other thread again
+        txMap1.rollbackTransaction();
+        report("value2", (String) txMap1.get("key1"));
+    }
+
+}

Added: commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/OptimisticTxMapTest.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/OptimisticTxMapTest.java?view=auto&rev=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/OptimisticTxMapTest.java (added)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/OptimisticTxMapTest.java Wed Aug 15 05:46:25 2007
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.transaction.memory;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.JUnit4TestAdapter;
+import static junit.framework.Assert.*;
+import org.junit.Test;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.commons.transaction.locking.LockException;
+import org.apache.commons.transaction.util.RendezvousBarrier;
+
+/**
+ * Tests for optimistic tx map.
+ * 
+ */
+public class OptimisticTxMapTest extends BasicTxMapTest {
+
+    private static final Log log = LogFactory.getLog(OptimisticTxMapTest.class.getName());
+
+    public static junit.framework.Test suite() {
+        return new JUnit4TestAdapter(OptimisticTxMapTest.class);
+    }
+
+    public static void main(java.lang.String[] args) {
+        junit.textui.TestRunner.run(suite());
+    }
+
+    @Test
+    public void testMulti() {
+        log.info("Checking concurrent transaction features");
+
+        final OptimisticTxMap<String, String> txMap1 = new OptimisticTxMap<String, String>("txMap1");
+        final Map map1 = txMap1.getWrappedMap();
+
+        final RendezvousBarrier beforeCommitBarrier = new RendezvousBarrier("Before Commit", 2,
+                BARRIER_TIMEOUT);
+
+        final RendezvousBarrier afterCommitBarrier = new RendezvousBarrier("After Commit", 2,
+                BARRIER_TIMEOUT);
+
+        Thread thread1 = new Thread(new Runnable() {
+            public void run() {
+                txMap1.startTransaction(1, TimeUnit.HOURS);
+                try {
+                    beforeCommitBarrier.meet();
+                    txMap1.put("key1", "value2");
+                    txMap1.commitTransaction();
+                    afterCommitBarrier.call();
+                } catch (InterruptedException e) {
+                    log.warn("Thread interrupted", e);
+                    afterCommitBarrier.reset();
+                    beforeCommitBarrier.reset();
+                }
+            }
+        }, "Thread1");
+
+        txMap1.put("key1", "value1");
+
+        txMap1.startTransaction(1, TimeUnit.HOURS);
+        thread1.start();
+
+        report("value1", (String) txMap1.get("key1"));
+        beforeCommitBarrier.call();
+        try {
+            afterCommitBarrier.meet();
+        } catch (InterruptedException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        // we have serializable as isolation level, that's why I will still see
+        // the old value
+        report("value1", (String) txMap1.get("key1"));
+
+        // now when I override it it should of course be my value
+        txMap1.put("key1", "value3");
+        report("value3", (String) txMap1.get("key1"));
+
+        // after rollback it must be the value written by the other thread
+        txMap1.rollbackTransaction();
+        report("value2", (String) txMap1.get("key1"));
+    }
+
+    @Test
+    public void testConflict() {
+        log.info("Checking concurrent transaction features");
+
+        final OptimisticTxMap<String, String> txMap1 = new OptimisticTxMap<String, String>("txMap1");
+        final Map map1 = txMap1.getWrappedMap();
+
+        final RendezvousBarrier beforeCommitBarrier = new RendezvousBarrier("Before Commit", 2,
+                BARRIER_TIMEOUT);
+
+        final RendezvousBarrier afterCommitBarrier = new RendezvousBarrier("After Commit", 2,
+                BARRIER_TIMEOUT);
+
+        Thread thread1 = new Thread(new Runnable() {
+            public void run() {
+                txMap1.startTransaction(1, TimeUnit.HOURS);
+                try {
+                    beforeCommitBarrier.meet();
+                    txMap1.put("key1", "value2");
+                    txMap1.commitTransaction();
+                    afterCommitBarrier.call();
+                } catch (InterruptedException e) {
+                    log.warn("Thread interrupted", e);
+                    afterCommitBarrier.reset();
+                    beforeCommitBarrier.reset();
+                }
+            }
+        }, "Thread1");
+
+        txMap1.put("key1", "value1");
+
+        txMap1.startTransaction(1, TimeUnit.HOURS);
+        thread1.start();
+
+        report("value1", (String) txMap1.get("key1"));
+        beforeCommitBarrier.call();
+        try {
+            afterCommitBarrier.meet();
+        } catch (InterruptedException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        // we have serializable as isolation level, that's why I will still see
+        // the old value
+        report("value1", (String) txMap1.get("key1"));
+
+        // now when I override it it should of course be my value
+        txMap1.put("key1", "value3");
+        report("value3", (String) txMap1.get("key1"));
+
+        boolean conflict = false;
+
+        try {
+            txMap1.commitTransaction();
+        } catch (LockException ce) {
+            conflict = true;
+        }
+
+        assertTrue(conflict);
+        // after failed commit it must be the value written by the other thread
+        report("value2", (String) map1.get("key1"));
+
+        // force commit anyhow...
+        txMap1.commitTransaction(true);
+        // after successful commit it must be the value written by this thread
+        report("value3", (String) txMap1.get("key1"));
+        report("value3", (String) map1.get("key1"));
+    }
+
+}
\ No newline at end of file

Added: commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java
URL: http://svn.apache.org/viewvc/commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java?view=auto&rev=566128
==============================================================================
--- commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java (added)
+++ commons/proper/transaction/branches/TRANSACTION_2/src/test/org/apache/commons/transaction/memory/PessimisticTxMapTest.java Wed Aug 15 05:46:25 2007
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.transaction.memory;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.JUnit4TestAdapter;
+import static junit.framework.Assert.*;
+import org.junit.Test;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.commons.transaction.locking.LockException;
+import org.apache.commons.transaction.util.RendezvousBarrier;
+
+/**
+ * Tests for map wrapper.
+ */
+public class PessimisticTxMapTest extends BasicTxMapTest {
+
+    private static final Log log = LogFactory.getLog(PessimisticTxMapTest.class.getName());
+
+    protected static final long TIMEOUT = Long.MAX_VALUE;
+
+    private static int deadlockCnt = 0;
+
+    public static junit.framework.Test suite() {
+        return new JUnit4TestAdapter(PessimisticTxMapTest.class);
+    }
+
+    public static void main(java.lang.String[] args) {
+        junit.textui.TestRunner.run(suite());
+    }
+
+    @Test
+    public void testMulti() {
+        log.info("Checking concurrent transaction features");
+
+        final PessimisticTxMap<String, String> txMap1 = new PessimisticTxMap<String, String>(
+                "txMap1");
+        final Map map1 = txMap1.getWrappedMap();
+
+        Thread thread1 = new Thread(new Runnable() {
+            public void run() {
+                txMap1.startTransaction(5, TimeUnit.MINUTES);
+                txMap1.put("key1", "value2");
+                synchronized (txMap1) {
+                    txMap1.commitTransaction();
+                    report("value2", (String) txMap1.get("key1"));
+                }
+            }
+        }, "Thread1");
+
+        txMap1.put("key1", "value1");
+
+        txMap1.startTransaction(5, TimeUnit.MINUTES);
+
+        report("value1", (String) txMap1.get("key1"));
+
+        thread1.start();
+
+        // we have serializable as isolation level, that's why I will still see
+        // the old value
+        report("value1", (String) txMap1.get("key1"));
+
+        txMap1.put("key1", "value3");
+
+        // after commit it must be our value
+        synchronized (txMap1) {
+            txMap1.commitTransaction();
+            report("value3", (String) txMap1.get("key1"));
+        }
+        try {
+            thread1.join();
+        } catch (InterruptedException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+}



Mime
View raw message