001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb;
023:
024: import java.util.HashMap;
025:
026: import org.jboss.ejb.Container;
027: import org.jboss.monitor.EntityLockMonitor;
028: import org.jboss.monitor.LockMonitor;
029: import org.jboss.logging.Logger;
030: import javax.naming.InitialContext;
031:
032: /**
033: * Manages BeanLocks. All BeanLocks have a reference count.
034: * When the reference count goes to 0, the lock is released from the
035: * id -> lock mapping.
036: *
037: * @author <a href="bill@burkecentral.com">Bill Burke</a>
038: * @author <a href="marc.fleury@jboss.org">Marc Fleury</a>
039: * @author Scott.Stark@jboss.org
040: */
041: public class BeanLockManager {
042: private static final int NUMBER_OF_INSTANCES = 40;
043: private static Logger log = Logger.getLogger(BeanLockManager.class);
044:
045: /** Multiple instances of hashMap to diminish locking contentions.
046: * Rules for accessing this are determined by {@link getHashMap(Object)}*/
047: private HashMap map[] = new HashMap[NUMBER_OF_INSTANCES];
048:
049: /** The container this manager reports to */
050: private Container container;
051:
052: /** Reentrancy of calls */
053: private boolean reentrant = false;
054: private int txTimeout = 5000;
055: /** The logging trace flag, only set in ctor */
056: private boolean trace;
057: public Class lockClass;
058: protected LockMonitor monitor = null;
059:
060: private BeanLockManager() {
061: for (int i = 0; i < map.length; i++) {
062: map[i] = new HashMap();
063: }
064: }
065:
066: public BeanLockManager(Container container) {
067: this ();
068: this .container = container;
069: trace = log.isTraceEnabled();
070: try {
071: InitialContext ctx = new InitialContext();
072: EntityLockMonitor elm = (EntityLockMonitor) ctx
073: .lookup(EntityLockMonitor.JNDI_NAME);
074: String jndiName = container.getBeanMetaData()
075: .getContainerObjectNameJndiName();
076: monitor = elm.getEntityLockMonitor(jndiName);
077: } catch (Exception ignored) {
078: // Ignore the lack of an EntityLockMonitor
079: }
080: }
081:
082: public LockMonitor getLockMonitor() {
083: return monitor;
084: }
085:
086: private HashMap getHashMap(Object id) {
087: final int mapInUse = id.hashCode() % NUMBER_OF_INSTANCES;
088: if (mapInUse > 0) {
089: return map[mapInUse];
090: } else {
091: return map[mapInUse * -1];
092: }
093: }
094:
095: /**
096: * returns the lock associated with the key passed. If there is
097: * no lock one is created this call also increments the number of
098: * references interested in Lock.
099: *
100: * WARNING: All access to this method MUST have an equivalent
101: * removeLockRef cleanup call, or this will create a leak in the map,
102: */
103: public BeanLock getLock(Object id) {
104: if (id == null)
105: throw new IllegalArgumentException(
106: "Attempt to get lock ref with a null object");
107:
108: HashMap mapInUse = getHashMap(id);
109:
110: synchronized (mapInUse) {
111: BeanLock lock = (BeanLock) mapInUse.get(id);
112: if (lock != null) {
113: lock.addRef();
114: return lock;
115: }
116: }
117:
118: try {
119: BeanLock lock2 = (BeanLock) createLock(id);
120: synchronized (mapInUse) {
121: BeanLock lock = (BeanLock) mapInUse.get(id);
122: // in case of bad luck, this might happen
123: if (lock != null) {
124: lock.addRef();
125: return lock;
126: }
127: mapInUse.put(id, lock2);
128: lock2.addRef();
129: return lock2;
130: }
131: } catch (Exception e) {
132: // schrouf: should we really proceed with lock object
133: // in case of exception ??
134: log.warn("Failed to initialize lock:" + id, e);
135: throw new RuntimeException(e);
136: }
137: }
138:
139: private BeanLock createLock(Object id) throws Exception {
140: BeanLock lock = (BeanLock) lockClass.newInstance();
141: lock.setId(id);
142: lock.setTimeout(txTimeout);
143: lock.setContainer(container);
144:
145: return lock;
146: }
147:
148: public void removeLockRef(Object id) {
149: if (id == null)
150: throw new IllegalArgumentException(
151: "Attempt to remove lock ref with a null object");
152:
153: HashMap mapInUse = getHashMap(id);
154:
155: synchronized (mapInUse) {
156: BeanLock lock = (BeanLock) mapInUse.get(id);
157:
158: if (lock != null) {
159: try {
160: lock.removeRef();
161: if (trace)
162: log.trace("Remove ref lock:" + lock);
163: } finally {
164: // schrouf: ALLWAYS ensure proper map lock removal even in case
165: // of exception within lock.removeRef ! There seems to be a bug
166: // in the ref counting of QueuedPessimisticEJBLock under certain
167: // conditions ( lock.ref < 0 should never happen !!! )
168: if (lock.getRefs() <= 0) {
169: Object mapLock = mapInUse.remove(lock.getId());
170: if (trace)
171: log
172: .trace("Lock no longer referenced, lock: "
173: + lock);
174: }
175: }
176: }
177: }
178: }
179:
180: public boolean canPassivate(Object id) {
181: if (id == null)
182: throw new IllegalArgumentException(
183: "Attempt to passivate with a null object");
184:
185: HashMap mapInUse = getHashMap(id);
186: synchronized (mapInUse) {
187: BeanLock lock = (BeanLock) mapInUse.get(id);
188: if (lock == null)
189: throw new IllegalStateException(
190: "Attempt to passivate without a lock");
191:
192: return (lock.getRefs() <= 1);
193: }
194: }
195:
196: public void setLockCLass(Class lockClass) {
197: this .lockClass = lockClass;
198: }
199:
200: public void setReentrant(boolean reentrant) {
201: this .reentrant = reentrant;
202: }
203:
204: public void setContainer(Container container) {
205: this.container = container;
206: }
207: }
|