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.plugins;
23
24 import java.rmi.RemoteException;
25 import java.util.LinkedList;
26 import java.lang.reflect.UndeclaredThrowableException;
27 import javax.ejb.EJBException;
28 import javax.ejb.CreateException;
29
30 import org.jboss.ejb.Container;
31 import org.jboss.ejb.InstancePool;
32 import org.jboss.ejb.EnterpriseContext;
33
34 import org.jboss.deployment.DeploymentException;
35 import org.jboss.metadata.MetaData;
36 import org.jboss.metadata.XmlLoadable;
37 import org.jboss.system.ServiceMBeanSupport;
38
39 import org.w3c.dom.Element;
40 import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore;
41
42 /**
43 * Abstract Instance Pool class containing the basic logic to create
44 * an EJB Instance Pool.
45 *
46 * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
47 * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
48 * @author <a href="mailto:andreas.schaefer@madplanet.com">Andreas Schaefer</a>
49 * @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>
50 * @author <a href="mailto:scott.stark@jboss.org">Scott Stark/a>
51 * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
52 * @version $Revision: 38303 $
53 *
54 * @jmx:mbean extends="org.jboss.system.ServiceMBean"
55 */
56 public abstract class AbstractInstancePool
57 extends ServiceMBeanSupport
58 implements AbstractInstancePoolMBean, InstancePool, XmlLoadable
59 {
60 // Constants -----------------------------------------------------
61
62 // Attributes ----------------------------------------------------
63 /** A FIFO semaphore that is set when the strict max size behavior is in effect.
64 When set, only maxSize instances may be active and any attempt to get an
65 instance will block until an instance is freed.
66 */
67 private FIFOSemaphore strictMaxSize;
68 /** The time in milliseconds to wait for the strictMaxSize semaphore.
69 */
70 private long strictTimeout = Long.MAX_VALUE;
71 /** The Container the instance pool is associated with */
72 protected Container container;
73 /** The pool data structure */
74 protected LinkedList pool = new LinkedList();
75 /** The maximum number of instances allowed in the pool */
76 protected int maxSize = 30;
77 /** determine if we reuse EnterpriseContext objects i.e. if we actually do pooling */
78 protected boolean reclaim = false;
79
80
81 // Static --------------------------------------------------------
82
83 // Constructors --------------------------------------------------
84
85 // Public --------------------------------------------------------
86
87 /**
88 * Set the callback to the container. This is for initialization.
89 * The IM may extract the configuration from the container.
90 *
91 * @param c
92 */
93 public void setContainer(Container c)
94 {
95 this.container = c;
96 }
97
98 /**
99 * @return Callback to the container which can be null if not set proviously
100 */
101 public Container getContainer()
102 {
103 return container;
104 }
105
106 /**
107 * @jmx:managed-attribute
108 * @return the current pool size
109 */
110 public int getCurrentSize()
111 {
112 synchronized (pool)
113 {
114 return this.pool.size();
115 }
116 }
117
118 /**
119 * @jmx:managed-attribute
120 * @return the current pool size
121 */
122 public int getMaxSize()
123 {
124 return this.maxSize;
125 }
126
127 /** Get the current avaiable count from the strict max view. If there is
128 * no strict max then this will be Long.MAX_VALUE to indicate there is no
129 * restriction.
130 * @jmx:managed-attribute
131 * @return the current avaiable count from the strict max view
132 */
133 public long getAvailableCount()
134 {
135 long size = Long.MAX_VALUE;
136 if( strictMaxSize != null )
137 size = strictMaxSize.permits();
138 return size;
139 }
140
141 /**
142 * Get an instance without identity.
143 * Can be used by finders,create-methods, and activation
144 *
145 * @return Context /w instance
146 * @exception RemoteException
147 */
148 public EnterpriseContext get()
149 throws Exception
150 {
151 boolean trace = log.isTraceEnabled();
152 if( trace )
153 log.trace("Get instance "+this+"#"+pool.size()+"#"+getContainer().getBeanClass());
154
155 if( strictMaxSize != null )
156 {
157 // Block until an instance is available
158 boolean acquired = strictMaxSize.attempt(strictTimeout);
159 if( trace )
160 log.trace("Acquired("+acquired+") strictMaxSize semaphore, remaining="+strictMaxSize.permits());
161 if( acquired == false )
162 throw new EJBException("Failed to acquire the pool semaphore, strictTimeout="+strictTimeout);
163 }
164
165 synchronized (pool)
166 {
167 if ( pool.isEmpty() == false )
168 {
169 return (EnterpriseContext) pool.removeFirst();
170 }
171 }
172
173 // Pool is empty, create an instance
174 try
175 {
176 Object instance = container.createBeanClassInstance();
177 return create(instance);
178 }
179 catch (Throwable e)
180 {
181 // Release the strict max size mutex if it exists
182 if( strictMaxSize != null )
183 {
184 strictMaxSize.release();
185 }
186 // Don't wrap CreateExceptions
187 if( e instanceof CreateException )
188 throw (CreateException) e;
189
190 // Wrap e in an Exception if needed
191 Exception ex = null;
192 if(e instanceof Exception)
193 {
194 ex = (Exception)e;
195 } else
196 {
197 ex = new UndeclaredThrowableException(e);
198 }
199 throw new EJBException("Could not instantiate bean", ex);
200 }
201 }
202
203 /**
204 * Return an instance after invocation.
205 *
206 * Called in 2 cases:
207 * a) Done with finder method
208 * b) Just removed
209 *
210 * @param ctx
211 */
212 public void free(EnterpriseContext ctx)
213 {
214 if( log.isTraceEnabled() )
215 {
216 String msg = pool.size() + "/" + maxSize+" Free instance:"+this
217 +"#"+ctx.getId()
218 +"#"+ctx.getTransaction()
219 +"#"+reclaim
220 +"#"+getContainer().getBeanClass();
221 log.trace(msg);
222 }
223
224 ctx.clear();
225
226 try
227 {
228 // If the pool is not full, add the unused context back into the pool,
229 // otherwise, just discard the extraneous context and leave it for GC
230 boolean addedToPool = false;
231
232 synchronized (pool)
233 {
234 if (pool.size() < maxSize)
235 {
236 pool.addFirst(ctx);
237 addedToPool = true;
238 }
239 }
240
241 if (addedToPool)
242 {
243 // If we block when maxSize instances are in use, invoke release on strictMaxSize
244 if(strictMaxSize != null)
245 {
246 strictMaxSize.release();
247 }
248 }
249 else
250 {
251 // Get rid of the extraneous instance; strictMaxSize should be null
252 // (otherwise we wouldn't have gotten the extra instance)
253 discard(ctx);
254 }
255 }
256 catch (Exception ignored)
257 {
258 }
259 }
260
261 public void discard(EnterpriseContext ctx)
262 {
263 if( log.isTraceEnabled() )
264 {
265 String msg = "Discard instance:"+this+"#"+ctx
266 +"#"+ctx.getTransaction()
267 +"#"+reclaim
268 +"#"+getContainer().getBeanClass();
269 log.trace(msg);
270 }
271
272 // If we block when maxSize instances are in use, invoke release on strictMaxSize
273 if( strictMaxSize != null )
274 strictMaxSize.release();
275
276 // Throw away, unsetContext()
277 try
278 {
279 ctx.discard();
280 }
281 catch (RemoteException e)
282 {
283 if( log.isTraceEnabled() )
284 log.trace("Ctx.discard error", e);
285 }
286 }
287
288 public void clear()
289 {
290 synchronized (pool)
291 {
292 freeAll();
293 }
294 }
295
296 /**
297 * XmlLoadable implementation
298 */
299 public void importXml(Element element) throws DeploymentException
300 {
301 String maximumSize = MetaData.getElementContent(MetaData.getUniqueChild(element, "MaximumSize"));
302 try
303 {
304 this.maxSize = Integer.parseInt(maximumSize);
305 }
306 catch (NumberFormatException e)
307 {
308 throw new DeploymentException("Invalid MaximumSize value for instance pool configuration");
309 }
310
311 // Get whether the pool will block when MaximumSize instances are active
312 String strictValue = MetaData.getElementContent(MetaData.getOptionalChild(element, "strictMaximumSize"));
313 Boolean strictFlag = Boolean.valueOf(strictValue);
314 if( strictFlag == Boolean.TRUE )
315 this.strictMaxSize = new FIFOSemaphore(this.maxSize);
316 String delay = MetaData.getElementContent(MetaData.getOptionalChild(element, "strictTimeout"));
317 try
318 {
319 if( delay != null )
320 this.strictTimeout = Long.parseLong(delay);
321 }
322 catch (NumberFormatException e)
323 {
324 throw new DeploymentException("Invalid strictTimeout value for instance pool configuration");
325 }
326 }
327
328 // Package protected ---------------------------------------------
329
330 // Protected -----------------------------------------------------
331 protected abstract EnterpriseContext create(Object instance)
332 throws Exception;
333
334 protected void destroyService() throws Exception
335 {
336 freeAll();
337 this.container = null;
338 }
339
340 // Private -------------------------------------------------------
341
342 /**
343 * At undeployment we want to free completely the pool.
344 */
345 private void freeAll()
346 {
347 LinkedList clone = (LinkedList)pool.clone();
348 for (int i = 0; i < clone.size(); i++)
349 {
350 EnterpriseContext ec = (EnterpriseContext)clone.get(i);
351 // Clear TX so that still TX entity pools get killed as well
352 ec.clear();
353 discard(ec);
354 }
355 pool.clear();
356 }
357
358 // Inner classes -------------------------------------------------
359
360 }