View Javadoc

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      protected static final Object GLOBAL_LOCK = "GLOBAL";
51  
52      protected ReadWriteLockManager lockManager;
53  //    protected MultiLevelLock globalLock;
54      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          this(wrapped, new HashMapFactory(), new HashSetFactory(), logger);
66      }
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          super(wrapped, mapFactory, setFactory);
80          lockManager = new ReadWriteLockManager(logger, readTimeOut);
81  //        globalLock = new GenericLock(GLOBAL_LOCK_NAME, WRITE, logger);
82      }
83  
84      public void startTransaction() {
85          if (getActiveTx() != null) {
86              throw new IllegalStateException(
87                  "Active thread " + Thread.currentThread() + " already associated with a transaction!");
88          }
89          LockingTxContext context = new LockingTxContext();
90          setActiveTx(context);
91      }
92  
93      public Collection values() {
94          assureGlobalReadLock();
95          return super.values();
96      }
97  
98      public Set entrySet() {
99          assureGlobalReadLock();
100         return super.entrySet();
101     }
102 
103     public Set keySet() {
104         assureGlobalReadLock();
105         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         assureWriteLock(key);
112         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         assureWriteLock(key);
119         return super.put(key, value);
120     }
121 
122     protected void assureWriteLock(Object key) {
123         LockingTxContext txContext = (LockingTxContext) getActiveTx();
124         if (txContext != null) {
125             lockManager.writeLock(txContext, key);
126             // XXX fake intention lock (prohibits global WRITE)
127             lockManager.readLock(txContext, GLOBAL_LOCK); 
128         }
129     }
130     
131     protected void assureGlobalReadLock() {
132         LockingTxContext txContext = (LockingTxContext) getActiveTx();
133         if (txContext != null) {
134             // XXX fake intention lock (prohibits global WRITE)
135             lockManager.readLock(txContext, GLOBAL_LOCK); 
136         }
137     }
138     
139     public class LockingTxContext extends TxContext {
140 
141         protected Set keys() {
142             lockManager.readLock(this, GLOBAL_LOCK); 
143             return super.keys();
144         }
145 
146         protected Object get(Object key) {
147             lockManager.readLock(this, key);
148             // XXX fake intention lock (prohibits global WRITE)
149             lockManager.readLock(this, GLOBAL_LOCK);
150             return super.get(key);
151         }
152 
153         protected void put(Object key, Object value) {
154             lockManager.writeLock(this, key);
155             // XXX fake intention lock (prohibits global WRITE)
156             lockManager.readLock(this, GLOBAL_LOCK);
157             super.put(key, value);
158         }
159 
160         protected void remove(Object key) {
161             lockManager.writeLock(this, key);
162             // XXX fake intention lock (prohibits global WRITE)
163             lockManager.readLock(this, GLOBAL_LOCK);
164             super.remove(key);
165         }
166 
167         protected int size() {
168             // XXX this is bad luck, we need a global read lock just for the size :( :( :(
169             lockManager.readLock(this, GLOBAL_LOCK);
170             return super.size();
171         }
172 
173         protected void clear() {
174             lockManager.writeLock(this, GLOBAL_LOCK);
175             super.clear();
176         }
177 
178         protected void dispose() {
179             super.dispose();
180             lockManager.releaseAll(this);
181         }
182 
183         protected void finalize() throws Throwable {
184             dispose();
185             super.finalize();
186         }
187     }
188 
189 }