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(),
066: logger);
067: }
068:
069: /**
070: * Creates a new pessimistic transactional map wrapper. Temporary maps and sets to store transactional
071: * data will be created and disposed using {@link MapFactory} and {@link SetFactory}.
072: *
073: * @param wrapped map to be wrapped
074: * @param mapFactory factory for temporary maps
075: * @param setFactory factory for temporary sets
076: * @param logger
077: * generic logger used for all kinds of logging
078: */
079: public PessimisticMapWrapper(Map wrapped, MapFactory mapFactory,
080: SetFactory setFactory, LoggerFacade logger) {
081: super (wrapped, mapFactory, setFactory);
082: lockManager = new ReadWriteLockManager(logger, readTimeOut);
083: // globalLock = new GenericLock(GLOBAL_LOCK_NAME, WRITE, logger);
084: }
085:
086: public void startTransaction() {
087: if (getActiveTx() != null) {
088: throw new IllegalStateException("Active thread "
089: + Thread.currentThread()
090: + " already associated with a transaction!");
091: }
092: LockingTxContext context = new LockingTxContext();
093: setActiveTx(context);
094: }
095:
096: public Collection values() {
097: assureGlobalReadLock();
098: return super .values();
099: }
100:
101: public Set entrySet() {
102: assureGlobalReadLock();
103: return super .entrySet();
104: }
105:
106: public Set keySet() {
107: assureGlobalReadLock();
108: return super .keySet();
109: }
110:
111: public Object remove(Object key) {
112: // assure we get a write lock before super can get a read lock to avoid lots
113: // of deadlocks
114: assureWriteLock(key);
115: return super .remove(key);
116: }
117:
118: public Object put(Object key, Object value) {
119: // assure we get a write lock before super can get a read lock to avoid lots
120: // of deadlocks
121: assureWriteLock(key);
122: return super .put(key, value);
123: }
124:
125: protected void assureWriteLock(Object key) {
126: LockingTxContext txContext = (LockingTxContext) getActiveTx();
127: if (txContext != null) {
128: lockManager.writeLock(txContext, key);
129: // XXX fake intention lock (prohibits global WRITE)
130: lockManager.readLock(txContext, GLOBAL_LOCK);
131: }
132: }
133:
134: protected void assureGlobalReadLock() {
135: LockingTxContext txContext = (LockingTxContext) getActiveTx();
136: if (txContext != null) {
137: // XXX fake intention lock (prohibits global WRITE)
138: lockManager.readLock(txContext, GLOBAL_LOCK);
139: }
140: }
141:
142: public class LockingTxContext extends TxContext {
143:
144: protected Set keys() {
145: lockManager.readLock(this , GLOBAL_LOCK);
146: return super .keys();
147: }
148:
149: protected Object get(Object key) {
150: lockManager.readLock(this , key);
151: // XXX fake intention lock (prohibits global WRITE)
152: lockManager.readLock(this , GLOBAL_LOCK);
153: return super .get(key);
154: }
155:
156: protected void put(Object key, Object value) {
157: lockManager.writeLock(this , key);
158: // XXX fake intention lock (prohibits global WRITE)
159: lockManager.readLock(this , GLOBAL_LOCK);
160: super .put(key, value);
161: }
162:
163: protected void remove(Object key) {
164: lockManager.writeLock(this , key);
165: // XXX fake intention lock (prohibits global WRITE)
166: lockManager.readLock(this , GLOBAL_LOCK);
167: super .remove(key);
168: }
169:
170: protected int size() {
171: // XXX this is bad luck, we need a global read lock just for the size :( :( :(
172: lockManager.readLock(this , GLOBAL_LOCK);
173: return super .size();
174: }
175:
176: protected void clear() {
177: lockManager.writeLock(this , GLOBAL_LOCK);
178: super .clear();
179: }
180:
181: protected void dispose() {
182: super .dispose();
183: lockManager.releaseAll(this );
184: }
185:
186: protected void finalize() throws Throwable {
187: dispose();
188: super.finalize();
189: }
190: }
191:
192: }
|