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 }