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.plugins;
023:
024: import java.lang.reflect.UndeclaredThrowableException;
025: import java.rmi.RemoteException;
026: import java.util.LinkedList;
027:
028: import javax.ejb.CreateException;
029: import javax.ejb.EJBException;
030:
031: import org.jboss.deployment.DeploymentException;
032: import org.jboss.ejb.Container;
033: import org.jboss.ejb.EnterpriseContext;
034: import org.jboss.ejb.InstancePool;
035: import org.jboss.metadata.MetaData;
036: import org.jboss.metadata.XmlLoadable;
037: import org.jboss.system.ServiceMBeanSupport;
038: import org.w3c.dom.Element;
039:
040: import EDU.oswego.cs.dl.util.concurrent.FIFOSemaphore;
041:
042: /**
043: * Abstract Instance Pool class containing the basic logic to create
044: * an EJB Instance Pool.
045: *
046: * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
047: * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
048: * @author <a href="mailto:andreas.schaefer@madplanet.com">Andreas Schaefer</a>
049: * @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>
050: * @author <a href="mailto:scott.stark@jboss.org">Scott Stark/a>
051: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
052: * @version $Revision: 57209 $
053: *
054: * @jmx:mbean extends="org.jboss.system.ServiceMBean"
055: */
056: public abstract class AbstractInstancePool extends ServiceMBeanSupport
057: implements AbstractInstancePoolMBean, InstancePool, XmlLoadable {
058: // Constants -----------------------------------------------------
059:
060: // Attributes ----------------------------------------------------
061: /** A FIFO semaphore that is set when the strict max size behavior is in effect.
062: When set, only maxSize instances may be active and any attempt to get an
063: instance will block until an instance is freed.
064: */
065: private FIFOSemaphore strictMaxSize;
066: /** The time in milliseconds to wait for the strictMaxSize semaphore.
067: */
068: private long strictTimeout = Long.MAX_VALUE;
069: /** The Container the instance pool is associated with */
070: protected Container container;
071: /** The pool data structure */
072: protected LinkedList pool = new LinkedList();
073: /** The maximum number of instances allowed in the pool */
074: protected int maxSize = 30;
075: /** determine if we reuse EnterpriseContext objects i.e. if we actually do pooling */
076: protected boolean reclaim = false;
077:
078: // Static --------------------------------------------------------
079:
080: // Constructors --------------------------------------------------
081:
082: // Public --------------------------------------------------------
083:
084: /**
085: * Set the callback to the container. This is for initialization.
086: * The IM may extract the configuration from the container.
087: *
088: * @param c
089: */
090: public void setContainer(Container c) {
091: this .container = c;
092: }
093:
094: /**
095: * @return Callback to the container which can be null if not set proviously
096: */
097: public Container getContainer() {
098: return container;
099: }
100:
101: /**
102: * @jmx:managed-attribute
103: * @return the current pool size
104: */
105: public int getCurrentSize() {
106: synchronized (pool) {
107: return this .pool.size();
108: }
109: }
110:
111: /**
112: * @jmx:managed-attribute
113: * @return the current pool size
114: */
115: public int getMaxSize() {
116: return this .maxSize;
117: }
118:
119: /** Get the current avaiable count from the strict max view. If there is
120: * no strict max then this will be Long.MAX_VALUE to indicate there is no
121: * restriction.
122: * @jmx:managed-attribute
123: * @return the current avaiable count from the strict max view
124: */
125: public long getAvailableCount() {
126: long size = Long.MAX_VALUE;
127: if (strictMaxSize != null)
128: size = strictMaxSize.permits();
129: return size;
130: }
131:
132: public void clear() {
133: synchronized (pool) {
134: freeAll();
135: }
136: }
137:
138: /**
139: * Get an instance without identity.
140: * Can be used by finders,create-methods, and activation
141: *
142: * @return Context /w instance
143: * @exception RemoteException
144: */
145: public EnterpriseContext get() throws Exception {
146: boolean trace = log.isTraceEnabled();
147: if (trace)
148: log.trace("Get instance " + this + "#" + pool.size() + "#"
149: + getContainer().getBeanClass());
150:
151: if (strictMaxSize != null) {
152: // Block until an instance is available
153: boolean acquired = strictMaxSize.attempt(strictTimeout);
154: if (trace)
155: log.trace("Acquired(" + acquired
156: + ") strictMaxSize semaphore, remaining="
157: + strictMaxSize.permits());
158: if (acquired == false)
159: throw new EJBException(
160: "Failed to acquire the pool semaphore, strictTimeout="
161: + strictTimeout);
162: }
163:
164: synchronized (pool) {
165: if (pool.isEmpty() == false) {
166: return (EnterpriseContext) pool.removeFirst();
167: }
168: }
169:
170: // Pool is empty, create an instance
171: try {
172: Object instance = container.createBeanClassInstance();
173: return create(instance);
174: } catch (Throwable e) {
175: // Release the strict max size mutex if it exists
176: if (strictMaxSize != null) {
177: strictMaxSize.release();
178: }
179: // Don't wrap CreateExceptions
180: if (e instanceof CreateException)
181: throw (CreateException) e;
182:
183: // Wrap e in an Exception if needed
184: Exception ex = null;
185: if (e instanceof Exception) {
186: ex = (Exception) e;
187: } else {
188: ex = new UndeclaredThrowableException(e);
189: }
190: throw new EJBException("Could not instantiate bean", ex);
191: }
192: }
193:
194: /**
195: * Return an instance after invocation.
196: *
197: * Called in 2 cases:
198: * a) Done with finder method
199: * b) Just removed
200: *
201: * @param ctx
202: */
203: public void free(EnterpriseContext ctx) {
204: if (log.isTraceEnabled()) {
205: String msg = pool.size() + "/" + maxSize
206: + " Free instance:" + this + "#" + ctx.getId()
207: + "#" + ctx.getTransaction() + "#" + reclaim + "#"
208: + getContainer().getBeanClass();
209: log.trace(msg);
210: }
211:
212: ctx.clear();
213:
214: try {
215: // If the pool is not full, add the unused context back into the pool,
216: // otherwise, just discard the extraneous context and leave it for GC
217: boolean addedToPool = false;
218:
219: synchronized (pool) {
220: if (pool.size() < maxSize) {
221: pool.addFirst(ctx);
222: addedToPool = true;
223: }
224: }
225:
226: if (addedToPool) {
227: // If we block when maxSize instances are in use, invoke release on strictMaxSize
228: if (strictMaxSize != null) {
229: strictMaxSize.release();
230: }
231: } else {
232: // Get rid of the extraneous instance; strictMaxSize should be null
233: // (otherwise we wouldn't have gotten the extra instance)
234: discard(ctx);
235: }
236: } catch (Exception ignored) {
237: }
238: }
239:
240: public void discard(EnterpriseContext ctx) {
241: if (log.isTraceEnabled()) {
242: String msg = "Discard instance:" + this + "#" + ctx + "#"
243: + ctx.getTransaction() + "#" + reclaim + "#"
244: + getContainer().getBeanClass();
245: log.trace(msg);
246: }
247:
248: // If we block when maxSize instances are in use, invoke release on strictMaxSize
249: if (strictMaxSize != null)
250: strictMaxSize.release();
251:
252: // Throw away, unsetContext()
253: try {
254: ctx.discard();
255: } catch (RemoteException e) {
256: if (log.isTraceEnabled())
257: log.trace("Ctx.discard error", e);
258: }
259: }
260:
261: /**
262: * XmlLoadable implementation
263: */
264: public void importXml(Element element) throws DeploymentException {
265: String maximumSize = MetaData.getElementContent(MetaData
266: .getUniqueChild(element, "MaximumSize"));
267: try {
268: this .maxSize = Integer.parseInt(maximumSize);
269: } catch (NumberFormatException e) {
270: throw new DeploymentException(
271: "Invalid MaximumSize value for instance pool configuration");
272: }
273:
274: // Get whether the pool will block when MaximumSize instances are active
275: String strictValue = MetaData.getElementContent(MetaData
276: .getOptionalChild(element, "strictMaximumSize"));
277: Boolean strictFlag = Boolean.valueOf(strictValue);
278: if (strictFlag == Boolean.TRUE)
279: this .strictMaxSize = new FIFOSemaphore(this .maxSize);
280: String delay = MetaData.getElementContent(MetaData
281: .getOptionalChild(element, "strictTimeout"));
282: try {
283: if (delay != null)
284: this .strictTimeout = Long.parseLong(delay);
285: } catch (NumberFormatException e) {
286: throw new DeploymentException(
287: "Invalid strictTimeout value for instance pool configuration");
288: }
289: }
290:
291: // Package protected ---------------------------------------------
292:
293: // Protected -----------------------------------------------------
294: protected abstract EnterpriseContext create(Object instance)
295: throws Exception;
296:
297: protected void destroyService() throws Exception {
298: freeAll();
299: this .container = null;
300: }
301:
302: // Private -------------------------------------------------------
303:
304: /**
305: * At undeployment we want to free completely the pool.
306: */
307: private void freeAll() {
308: LinkedList clone = (LinkedList) pool.clone();
309: for (int i = 0; i < clone.size(); i++) {
310: EnterpriseContext ec = (EnterpriseContext) clone.get(i);
311: // Clear TX so that still TX entity pools get killed as well
312: ec.clear();
313: discard(ec);
314: }
315: pool.clear();
316: }
317:
318: // Inner classes -------------------------------------------------
319:
320: }
|