Coverage Report - org.apache.commons.transaction.memory.PessimisticMapWrapper
 
Classes in this File Line Coverage Branch Coverage Complexity
PessimisticMapWrapper
96%
30/31
83%
5/6
1.222
PessimisticMapWrapper$LockingTxContext
100%
25/25
N/A
1.222
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.transaction.memory;
 19  
 
 20  
 import java.util.Collection;
 21  
 import java.util.Map;
 22  
 import java.util.Set;
 23  
 
 24  
 import org.apache.commons.transaction.locking.ReadWriteLockManager;
 25  
 import org.apache.commons.transaction.util.LoggerFacade;
 26  
 
 27  
 /**
 28  
  * Wrapper that adds transactional control to all kinds of maps that implement the {@link Map} interface. By using
 29  
  * pessimistic transaction control (blocking locks) this wrapper has better isolation than {@link TransactionalMapWrapper}, but
 30  
  * also has less possible concurrency and may even deadlock. A commit, however, will never fail.
 31  
  * <br>
 32  
  * Start a transaction by calling {@link #startTransaction()}. Then perform the normal actions on the map and
 33  
  * finally either call {@link #commitTransaction()} to make your changes permanent or {@link #rollbackTransaction()} to
 34  
  * undo them.
 35  
  * <br>
 36  
  * <em>Caution:</em> Do not modify values retrieved by {@link #get(Object)} as this will circumvent the transactional mechanism.
 37  
  * Rather clone the value or copy it in a way you see fit and store it back using {@link #put(Object, Object)}.
 38  
  * <br>
 39  
  * <em>Note:</em> This wrapper guarantees isolation level <code>SERIALIZABLE</code>.
 40  
  * 
 41  
  * @version $Id: PessimisticMapWrapper.java 493628 2007-01-07 01:42:48Z joerg $
 42  
  * @see TransactionalMapWrapper
 43  
  * @see OptimisticMapWrapper
 44  
  */
 45  
 public class PessimisticMapWrapper extends TransactionalMapWrapper {
 46  
 
 47  
     protected static final int READ = 1;
 48  
     protected static final int WRITE = 2;
 49  
 
 50  1
     protected static final Object GLOBAL_LOCK = "GLOBAL";
 51  
 
 52  
     protected ReadWriteLockManager lockManager;
 53  
 //    protected MultiLevelLock globalLock;
 54  7
     protected long readTimeOut = 60000; /* FIXME: pass in ctor */
 55  
 
 56  
     /**
 57  
      * Creates a new pessimistic transactional map wrapper. Temporary maps and sets to store transactional
 58  
      * data will be instances of {@link java.util.HashMap} and {@link java.util.HashSet}. 
 59  
      * 
 60  
      * @param wrapped map to be wrapped
 61  
      * @param logger
 62  
      *            generic logger used for all kinds of logging
 63  
      */
 64  
     public PessimisticMapWrapper(Map wrapped, LoggerFacade logger) {
 65  7
         this(wrapped, new HashMapFactory(), new HashSetFactory(), logger);
 66  7
     }
 67  
 
 68  
     /**
 69  
      * Creates a new pessimistic transactional map wrapper. Temporary maps and sets to store transactional
 70  
      * data will be created and disposed using {@link MapFactory} and {@link SetFactory}.
 71  
      * 
 72  
      * @param wrapped map to be wrapped
 73  
      * @param mapFactory factory for temporary maps
 74  
      * @param setFactory factory for temporary sets
 75  
      * @param logger
 76  
      *            generic logger used for all kinds of logging
 77  
      */
 78  
     public PessimisticMapWrapper(Map wrapped, MapFactory mapFactory, SetFactory setFactory, LoggerFacade logger) {
 79  7
         super(wrapped, mapFactory, setFactory);
 80  7
         lockManager = new ReadWriteLockManager(logger, readTimeOut);
 81  
 //        globalLock = new GenericLock(GLOBAL_LOCK_NAME, WRITE, logger);
 82  7
     }
 83  
 
 84  
     public void startTransaction() {
 85  61
         if (getActiveTx() != null) {
 86  0
             throw new IllegalStateException(
 87  
                 "Active thread " + Thread.currentThread() + " already associated with a transaction!");
 88  
         }
 89  61
         LockingTxContext context = new LockingTxContext();
 90  61
         setActiveTx(context);
 91  61
     }
 92  
 
 93  
     public Collection values() {
 94  9
         assureGlobalReadLock();
 95  9
         return super.values();
 96  
     }
 97  
 
 98  
     public Set entrySet() {
 99  2
         assureGlobalReadLock();
 100  2
         return super.entrySet();
 101  
     }
 102  
 
 103  
     public Set keySet() {
 104  29
         assureGlobalReadLock();
 105  29
         return super.keySet();
 106  
     }
 107  
 
 108  
     public Object remove(Object key) {
 109  
         // assure we get a write lock before super can get a read lock to avoid lots
 110  
         // of deadlocks
 111  7
         assureWriteLock(key);
 112  7
         return super.remove(key);
 113  
     }
 114  
 
 115  
     public Object put(Object key, Object value) {
 116  
         // assure we get a write lock before super can get a read lock to avoid lots
 117  
         // of deadlocks
 118  73
         assureWriteLock(key);
 119  52
         return super.put(key, value);
 120  
     }
 121  
 
 122  
     protected void assureWriteLock(Object key) {
 123  80
         LockingTxContext txContext = (LockingTxContext) getActiveTx();
 124  80
         if (txContext != null) {
 125  72
             lockManager.writeLock(txContext, key);
 126  
             // XXX fake intention lock (prohibits global WRITE)
 127  51
             lockManager.readLock(txContext, GLOBAL_LOCK); 
 128  
         }
 129  59
     }
 130  
     
 131  
     protected void assureGlobalReadLock() {
 132  40
         LockingTxContext txContext = (LockingTxContext) getActiveTx();
 133  40
         if (txContext != null) {
 134  
             // XXX fake intention lock (prohibits global WRITE)
 135  29
             lockManager.readLock(txContext, GLOBAL_LOCK); 
 136  
         }
 137  40
     }
 138  
     
 139  61
     public class LockingTxContext extends TxContext {
 140  
 
 141  
         protected Set keys() {
 142  20
             lockManager.readLock(this, GLOBAL_LOCK); 
 143  20
             return super.keys();
 144  
         }
 145  
 
 146  
         protected Object get(Object key) {
 147  141
             lockManager.readLock(this, key);
 148  
             // XXX fake intention lock (prohibits global WRITE)
 149  118
             lockManager.readLock(this, GLOBAL_LOCK);
 150  118
             return super.get(key);
 151  
         }
 152  
 
 153  
         protected void put(Object key, Object value) {
 154  45
             lockManager.writeLock(this, key);
 155  
             // XXX fake intention lock (prohibits global WRITE)
 156  45
             lockManager.readLock(this, GLOBAL_LOCK);
 157  45
             super.put(key, value);
 158  45
         }
 159  
 
 160  
         protected void remove(Object key) {
 161  6
             lockManager.writeLock(this, key);
 162  
             // XXX fake intention lock (prohibits global WRITE)
 163  6
             lockManager.readLock(this, GLOBAL_LOCK);
 164  6
             super.remove(key);
 165  6
         }
 166  
 
 167  
         protected int size() {
 168  
             // XXX this is bad luck, we need a global read lock just for the size :( :( :(
 169  8
             lockManager.readLock(this, GLOBAL_LOCK);
 170  8
             return super.size();
 171  
         }
 172  
 
 173  
         protected void clear() {
 174  1
             lockManager.writeLock(this, GLOBAL_LOCK);
 175  1
             super.clear();
 176  1
         }
 177  
 
 178  
         protected void dispose() {
 179  122
             super.dispose();
 180  122
             lockManager.releaseAll(this);
 181  122
         }
 182  
 
 183  
         protected void finalize() throws Throwable {
 184  61
             dispose();
 185  61
             super.finalize();
 186  61
         }
 187  
     }
 188  
 
 189  
 }