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 018package org.apache.commons.transaction.memory; 019 020import java.util.Collection; 021import java.util.Map; 022import java.util.Set; 023 024import org.apache.commons.transaction.locking.ReadWriteLockManager; 025import 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 */ 045public 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}