1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.ejb;
23
24 import java.util.HashMap;
25
26 import org.jboss.monitor.EntityLockMonitor;
27 import org.jboss.monitor.LockMonitor;
28 import org.jboss.logging.Logger;
29 import javax.naming.InitialContext;
30
31 /**
32 * Manages BeanLocks. All BeanLocks have a reference count.
33 * When the reference count goes to 0, the lock is released from the
34 * id -> lock mapping.
35 *
36 * @author <a href="bill@burkecentral.com">Bill Burke</a>
37 * @author <a href="marc.fleury@jboss.org">Marc Fleury</a>
38 * @author Scott.Stark@jboss.org
39 */
40 public class BeanLockManager
41 {
42 private static final int NUMBER_OF_INSTANCES=40;
43 private static Logger log = Logger.getLogger(BeanLockManager.class);
44
45 /** Multiple instances of hashMap to diminish locking contentions.
46 * Rules for accessing this are determined by {@link getHashMap(Object)}*/
47 private HashMap map[] = new HashMap[NUMBER_OF_INSTANCES];
48
49
50 /** The container this manager reports to */
51 private Container container;
52
53 /** Reentrancy of calls */
54 private boolean reentrant = false;
55 private int txTimeout = 5000;
56 /** The logging trace flag, only set in ctor */
57 private boolean trace;
58 public Class lockClass;
59 protected LockMonitor monitor = null;
60
61 private BeanLockManager()
62 {
63 for (int i=0;i<map.length;i++)
64 {
65 map[i] = new HashMap();
66 }
67 }
68 public BeanLockManager(Container container)
69 {
70 this();
71 this.container = container;
72 trace = log.isTraceEnabled();
73 try
74 {
75 InitialContext ctx = new InitialContext();
76 EntityLockMonitor elm = (EntityLockMonitor) ctx.lookup(EntityLockMonitor.JNDI_NAME);
77 String jndiName = container.getBeanMetaData().getContainerObjectNameJndiName();
78 monitor = elm.getEntityLockMonitor(jndiName);
79 }
80 catch (Exception ignored)
81 {
82 // Ignore the lack of an EntityLockMonitor
83 }
84 }
85
86 public LockMonitor getLockMonitor()
87 {
88 return monitor;
89 }
90
91 private HashMap getHashMap(Object id)
92 {
93 final int mapInUse = id.hashCode()%NUMBER_OF_INSTANCES;
94 if (mapInUse>0)
95 {
96 return map[mapInUse];
97 }
98 else
99 {
100 return map[mapInUse*-1];
101 }
102 }
103
104 /**
105 * returns the lock associated with the key passed. If there is
106 * no lock one is created this call also increments the number of
107 * references interested in Lock.
108 *
109 * WARNING: All access to this method MUST have an equivalent
110 * removeLockRef cleanup call, or this will create a leak in the map,
111 */
112 public BeanLock getLock(Object id)
113 {
114 if (id == null)
115 throw new IllegalArgumentException("Attempt to get lock ref with a null object");
116
117 HashMap mapInUse = getHashMap(id);
118
119 synchronized (mapInUse)
120 {
121 BeanLock lock = (BeanLock) mapInUse.get(id);
122 if (lock!=null)
123 {
124 lock.addRef();
125 return lock;
126 }
127 }
128
129 try
130 {
131 BeanLock lock2 = (BeanLock)createLock(id);
132 synchronized(mapInUse)
133 {
134 BeanLock lock = (BeanLock) mapInUse.get(id);
135 // in case of bad luck, this might happen
136 if (lock != null)
137 {
138 lock.addRef();
139 return lock;
140 }
141 mapInUse.put(id, lock2);
142 lock2.addRef();
143 return lock2;
144 }
145 }
146 catch (Exception e)
147 {
148 // schrouf: should we really proceed with lock object
149 // in case of exception ??
150 log.warn("Failed to initialize lock:"+id, e);
151 throw new RuntimeException (e);
152 }
153 }
154
155 private BeanLock createLock(Object id) throws Exception
156 {
157 BeanLock lock = (BeanLock) lockClass.newInstance();
158 lock.setId(id);
159 lock.setTimeout(txTimeout);
160 lock.setContainer(container);
161
162 return lock;
163 }
164
165 public void removeLockRef(Object id)
166 {
167 if (id == null)
168 throw new IllegalArgumentException("Attempt to remove lock ref with a null object");
169
170 HashMap mapInUse = getHashMap(id);
171
172 synchronized(mapInUse)
173 {
174 BeanLock lock = (BeanLock) mapInUse.get(id);
175
176 if (lock != null)
177 {
178 try
179 {
180 lock.removeRef();
181 if( trace )
182 log.trace("Remove ref lock:"+lock);
183 }
184 finally
185 {
186 // schrouf: ALLWAYS ensure proper map lock removal even in case
187 // of exception within lock.removeRef ! There seems to be a bug
188 // in the ref counting of QueuedPessimisticEJBLock under certain
189 // conditions ( lock.ref < 0 should never happen !!! )
190 if (lock.getRefs() <= 0)
191 {
192 Object mapLock = mapInUse.remove(lock.getId());
193 if( trace )
194 log.trace("Lock no longer referenced, lock: "+lock);
195 }
196 }
197 }
198 }
199 }
200
201 public boolean canPassivate(Object id)
202 {
203 if (id == null)
204 throw new IllegalArgumentException("Attempt to passivate with a null object");
205
206 HashMap mapInUse = getHashMap(id);
207 synchronized (mapInUse)
208 {
209 BeanLock lock = (BeanLock) mapInUse.get(id);
210 if (lock == null)
211 throw new IllegalStateException("Attempt to passivate without a lock");
212
213 return (lock.getRefs() <= 1);
214 }
215 }
216
217 public void setLockCLass(Class lockClass)
218 {
219 this.lockClass = lockClass;
220 }
221
222 public void setReentrant(boolean reentrant)
223 {
224 this.reentrant = reentrant;
225 }
226
227 public void setContainer(Container container)
228 {
229 this.container = container;
230 }
231 }