001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.transaction.memory;
019
020 import java.util.Collection;
021 import java.util.Map;
022 import java.util.Set;
023
024 import org.apache.commons.transaction.locking.ReadWriteLockManager;
025 import org.apache.commons.transaction.util.LoggerFacade;
026
027 /**
028 * Wrapper that adds transactional control to all kinds of maps that implement the {@link Map} interface. By using
029 * pessimistic transaction control (blocking locks) this wrapper has better isolation than {@link TransactionalMapWrapper}, but
030 * also has less possible concurrency and may even deadlock. A commit, however, will never fail.
031 * <br>
032 * Start a transaction by calling {@link #startTransaction()}. Then perform the normal actions on the map and
033 * finally either call {@link #commitTransaction()} to make your changes permanent or {@link #rollbackTransaction()} to
034 * undo them.
035 * <br>
036 * <em>Caution:</em> Do not modify values retrieved by {@link #get(Object)} as this will circumvent the transactional mechanism.
037 * Rather clone the value or copy it in a way you see fit and store it back using {@link #put(Object, Object)}.
038 * <br>
039 * <em>Note:</em> This wrapper guarantees isolation level <code>SERIALIZABLE</code>.
040 *
041 * @version $Id: PessimisticMapWrapper.java 493628 2007-01-07 01:42:48Z joerg $
042 * @see TransactionalMapWrapper
043 * @see OptimisticMapWrapper
044 */
045 public class PessimisticMapWrapper extends TransactionalMapWrapper {
046
047 protected static final int READ = 1;
048 protected static final int WRITE = 2;
049
050 protected static final Object GLOBAL_LOCK = "GLOBAL";
051
052 protected ReadWriteLockManager lockManager;
053 // protected MultiLevelLock globalLock;
054 protected long readTimeOut = 60000; /* FIXME: pass in ctor */
055
056 /**
057 * Creates a new pessimistic transactional map wrapper. Temporary maps and sets to store transactional
058 * data will be instances of {@link java.util.HashMap} and {@link java.util.HashSet}.
059 *
060 * @param wrapped map to be wrapped
061 * @param logger
062 * generic logger used for all kinds of logging
063 */
064 public PessimisticMapWrapper(Map wrapped, LoggerFacade logger) {
065 this(wrapped, new HashMapFactory(), new HashSetFactory(), logger);
066 }
067
068 /**
069 * Creates a new pessimistic transactional map wrapper. Temporary maps and sets to store transactional
070 * data will be created and disposed using {@link MapFactory} and {@link SetFactory}.
071 *
072 * @param wrapped map to be wrapped
073 * @param mapFactory factory for temporary maps
074 * @param setFactory factory for temporary sets
075 * @param logger
076 * generic logger used for all kinds of logging
077 */
078 public PessimisticMapWrapper(Map wrapped, MapFactory mapFactory, SetFactory setFactory, LoggerFacade logger) {
079 super(wrapped, mapFactory, setFactory);
080 lockManager = new ReadWriteLockManager(logger, readTimeOut);
081 // globalLock = new GenericLock(GLOBAL_LOCK_NAME, WRITE, logger);
082 }
083
084 public void startTransaction() {
085 if (getActiveTx() != null) {
086 throw new IllegalStateException(
087 "Active thread " + Thread.currentThread() + " already associated with a transaction!");
088 }
089 LockingTxContext context = new LockingTxContext();
090 setActiveTx(context);
091 }
092
093 public Collection values() {
094 assureGlobalReadLock();
095 return super.values();
096 }
097
098 public Set entrySet() {
099 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 }