001: package org.apache.turbine.services.pool;
002:
003: /*
004: * Copyright 2001-2005 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License")
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.lang.reflect.Method;
020:
021: import java.util.ArrayList;
022: import java.util.HashMap;
023: import java.util.Iterator;
024:
025: import org.apache.commons.configuration.Configuration;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029:
030: import org.apache.turbine.services.InitializationException;
031: import org.apache.turbine.services.TurbineBaseService;
032: import org.apache.turbine.services.factory.FactoryService;
033: import org.apache.turbine.services.factory.TurbineFactory;
034: import org.apache.turbine.util.TurbineException;
035: import org.apache.turbine.util.pool.ArrayCtorRecyclable;
036: import org.apache.turbine.util.pool.BoundedBuffer;
037: import org.apache.turbine.util.pool.Recyclable;
038:
039: /**
040: * The Pool Service extends the Factory Service by adding support
041: * for pooling instantiated objects. When a new instance is
042: * requested, the service first checks its pool if one is available.
043: * If the the pool is empty, a new instance will be requested
044: * from the FactoryService.
045: *
046: * <p>For objects implementing the Recyclable interface, a recycle
047: * method will be called, when they taken from the pool, and
048: * a dispose method, when they are returned to the pool.
049: *
050: * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
051: * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
052: * @version $Id: TurbinePoolService.java 264148 2005-08-29 14:21:04Z henning $
053: */
054: public class TurbinePoolService extends TurbineBaseService implements
055: PoolService {
056: /** Are we currently debugging the pool recycling? */
057: private boolean debugPool = false;
058:
059: /** Internal Reference to the Factory */
060: private FactoryService factoryService;
061:
062: /** Logging */
063: private static Log log = LogFactory
064: .getLog(TurbinePoolService.class);
065:
066: /**
067: * An inner class for class specific pools.
068: */
069: private class PoolBuffer {
070: /**
071: * An inner class for cached recycle methods.
072: */
073: private class Recycler {
074: /** The recycle method. */
075: private final Method recycle;
076:
077: /** The signature. */
078: private final String[] signature;
079:
080: /**
081: * Constructs a new recycler.
082: *
083: * @param rec the recycle method.
084: * @param sign the signature.
085: */
086: public Recycler(Method rec, String[] sign) {
087: recycle = rec;
088: signature = ((sign != null) && (sign.length > 0)) ? sign
089: : null;
090: }
091:
092: /**
093: * Matches the given signature against
094: * that of the recycle method of this recycler.
095: *
096: * @param sign the signature.
097: * @return the matching recycle method or null.
098: */
099: public Method match(String[] sign) {
100: if ((sign != null) && (sign.length > 0)) {
101: if ((signature != null)
102: && (sign.length == signature.length)) {
103: for (int i = 0; i < signature.length; i++) {
104: if (!signature[i].equals(sign[i])) {
105: return null;
106: }
107: }
108: return recycle;
109: } else {
110: return null;
111: }
112: } else if (signature == null) {
113: return recycle;
114: } else {
115: return null;
116: }
117: }
118: }
119:
120: /** A buffer for class instances. */
121: private BoundedBuffer pool;
122:
123: /** A flag to determine if a more efficient recycler is implemented. */
124: private boolean arrayCtorRecyclable;
125:
126: /** A cache for recycling methods. */
127: private ArrayList recyclers;
128:
129: /**
130: * Constructs a new pool buffer with a specific capacity.
131: *
132: * @param capacity a capacity.
133: */
134: public PoolBuffer(int capacity) {
135: pool = new BoundedBuffer(capacity);
136: }
137:
138: /**
139: * Tells pool that it contains objects which can be
140: * initialized using an Object array.
141: *
142: * @param isArrayCtor a <code>boolean</code> value
143: */
144: public void setArrayCtorRecyclable(boolean isArrayCtor) {
145: arrayCtorRecyclable = isArrayCtor;
146: }
147:
148: /**
149: * Polls for an instance from the pool.
150: *
151: * @return an instance or null.
152: */
153: public Object poll(Object[] params, String[] signature)
154: throws TurbineException {
155: // If we're debugging the recycling code, we want different
156: // objects to be used when pulling from the pool. Ensure that
157: // each pool fills up at least to half its capacity.
158: if (debugPool && (pool.size() < (pool.capacity() / 2))) {
159: log.debug("Size: " + pool.size() + ", capacity: "
160: + pool.capacity());
161: return null;
162: }
163:
164: Object instance = pool.poll();
165: if (instance != null) {
166: if (arrayCtorRecyclable) {
167: ((ArrayCtorRecyclable) instance).recycle(params);
168: } else if (instance instanceof Recyclable) {
169: try {
170: if ((signature != null)
171: && (signature.length > 0)) {
172: /* Get the recycle method from the cache. */
173: Method recycle = getRecycle(signature);
174: if (recycle == null) {
175: synchronized (this ) {
176: /* Make a synchronized recheck. */
177: recycle = getRecycle(signature);
178: if (recycle == null) {
179: Class clazz = instance
180: .getClass();
181: recycle = clazz
182: .getMethod(
183: "recycle",
184: factoryService
185: .getSignature(
186: clazz,
187: params,
188: signature));
189: ArrayList cache = recyclers != null ? (ArrayList) recyclers
190: .clone()
191: : new ArrayList();
192: cache.add(new Recycler(recycle,
193: signature));
194: recyclers = cache;
195: }
196: }
197: }
198: recycle.invoke(instance, params);
199: } else {
200: ((Recyclable) instance).recycle();
201: }
202: } catch (Exception x) {
203: throw new TurbineException(
204: "Recycling failed for "
205: + instance.getClass().getName(),
206: x);
207: }
208: }
209: }
210: return instance;
211: }
212:
213: /**
214: * Offers an instance to the pool.
215: *
216: * @param instance an instance.
217: */
218: public boolean offer(Object instance) {
219: if (instance instanceof Recyclable) {
220: try {
221: ((Recyclable) instance).dispose();
222: } catch (Exception x) {
223: return false;
224: }
225: }
226: return pool.offer(instance);
227: }
228:
229: /**
230: * Returns the capacity of the pool.
231: *
232: * @return the capacity.
233: */
234: public int capacity() {
235: return pool.capacity();
236: }
237:
238: /**
239: * Returns the size of the pool.
240: *
241: * @return the size.
242: */
243: public int size() {
244: return pool.size();
245: }
246:
247: /**
248: * Returns a cached recycle method
249: * corresponding to the given signature.
250: *
251: * @param signature the signature.
252: * @return the recycle method or null.
253: */
254: private Method getRecycle(String[] signature) {
255: ArrayList cache = recyclers;
256: if (cache != null) {
257: Method recycle;
258: for (Iterator i = cache.iterator(); i.hasNext();) {
259: recycle = ((Recycler) i.next()).match(signature);
260: if (recycle != null) {
261: return recycle;
262: }
263: }
264: }
265: return null;
266: }
267: }
268:
269: /**
270: * The default capacity of pools.
271: */
272: private int poolCapacity = DEFAULT_POOL_CAPACITY;
273:
274: /**
275: * The pool repository, one pool for each class.
276: */
277: private HashMap poolRepository = new HashMap();
278:
279: /**
280: * Constructs a Pool Service.
281: */
282: public TurbinePoolService() {
283: }
284:
285: /**
286: * Initializes the service by setting the pool capacity.
287: *
288: * @param config initialization configuration.
289: * @throws InitializationException if initialization fails.
290: */
291: public void init() throws InitializationException {
292: Configuration conf = getConfiguration();
293:
294: int capacity = conf.getInt(POOL_CAPACITY_KEY,
295: DEFAULT_POOL_CAPACITY);
296:
297: if (capacity <= 0) {
298: throw new IllegalArgumentException("Capacity must be >0");
299: }
300: poolCapacity = capacity;
301:
302: debugPool = conf.getBoolean(POOL_DEBUG_KEY, POOL_DEBUG_DEFAULT);
303:
304: if (debugPool) {
305: log.info("Activated Pool Debugging!");
306: }
307:
308: factoryService = TurbineFactory.getService();
309:
310: if (factoryService == null) {
311: throw new InitializationException(
312: "Factory Service is not configured"
313: + " but required for the Pool Service!");
314: }
315:
316: setInit(true);
317: }
318:
319: /**
320: * Gets an instance of a named class either from the pool
321: * or by calling the Factory Service if the pool is empty.
322: *
323: * @param className the name of the class.
324: * @return the instance.
325: * @throws TurbineException if recycling fails.
326: */
327: public Object getInstance(String className) throws TurbineException {
328: Object instance = pollInstance(className, null, null);
329: return (instance == null) ? factoryService
330: .getInstance(className) : instance;
331: }
332:
333: /**
334: * Gets an instance of a named class either from the pool
335: * or by calling the Factory Service if the pool is empty.
336: * The specified class loader will be passed to the Factory Service.
337: *
338: * @param className the name of the class.
339: * @param loader the class loader.
340: * @return the instance.
341: * @throws TurbineException if recycling fails.
342: */
343: public Object getInstance(String className, ClassLoader loader)
344: throws TurbineException {
345: Object instance = pollInstance(className, null, null);
346: return (instance == null) ? factoryService.getInstance(
347: className, loader) : instance;
348: }
349:
350: /**
351: * Gets an instance of a named class either from the pool
352: * or by calling the Factory Service if the pool is empty.
353: * Parameters for its constructor are given as an array of objects,
354: * primitive types must be wrapped with a corresponding class.
355: *
356: * @param className the name of the class.
357: * @param loader the class loader.
358: * @param params an array containing the parameters of the constructor.
359: * @param signature an array containing the signature of the constructor.
360: * @return the instance.
361: * @throws TurbineException if recycling fails.
362: */
363: public Object getInstance(String className, Object[] params,
364: String[] signature) throws TurbineException {
365: Object instance = pollInstance(className, params, signature);
366: return (instance == null) ? factoryService.getInstance(
367: className, params, signature) : instance;
368: }
369:
370: /**
371: * Gets an instance of a named class either from the pool
372: * or by calling the Factory Service if the pool is empty.
373: * Parameters for its constructor are given as an array of objects,
374: * primitive types must be wrapped with a corresponding class.
375: * The specified class loader will be passed to the Factory Service.
376: *
377: * @param className the name of the class.
378: * @param loader the class loader.
379: * @param params an array containing the parameters of the constructor.
380: * @param signature an array containing the signature of the constructor.
381: * @return the instance.
382: * @throws TurbineException if recycling fails.
383: */
384: public Object getInstance(String className, ClassLoader loader,
385: Object[] params, String[] signature)
386: throws TurbineException {
387: Object instance = pollInstance(className, params, signature);
388: return (instance == null) ? factoryService.getInstance(
389: className, loader, params, signature) : instance;
390: }
391:
392: /**
393: * Tests if specified class loaders are supported for a named class.
394: *
395: * @param className the name of the class.
396: * @return true if class loaders are supported, false otherwise.
397: * @throws TurbineException if test fails.
398: * @deprecated Use TurbineFactory.isLoaderSupported(className);
399: */
400: public boolean isLoaderSupported(String className)
401: throws TurbineException {
402: return factoryService.isLoaderSupported(className);
403: }
404:
405: /**
406: * Gets an instance of a specified class either from the pool
407: * or by instatiating from the class if the pool is empty.
408: *
409: * @param clazz the class.
410: * @return the instance.
411: * @throws TurbineException if recycling fails.
412: */
413: public Object getInstance(Class clazz) throws TurbineException {
414: Object instance = pollInstance(clazz.getName(), null, null);
415: return (instance == null) ? factoryService.getInstance(clazz
416: .getName()) : instance;
417: }
418:
419: /**
420: * Gets an instance of a specified class either from the pool
421: * or by instatiating from the class if the pool is empty.
422: *
423: * @param clazz the class.
424: * @param params an array containing the parameters of the constructor.
425: * @param signature an array containing the signature of the constructor.
426: * @return the instance.
427: * @throws TurbineException if recycling fails.
428: */
429: public Object getInstance(Class clazz, Object params[],
430: String signature[]) throws TurbineException {
431: Object instance = pollInstance(clazz.getName(), params,
432: signature);
433: return (instance == null) ? factoryService.getInstance(clazz
434: .getName(), params, signature) : instance;
435: }
436:
437: /**
438: * Puts a used object back to the pool. Objects implementing
439: * the Recyclable interface can provide a recycle method to
440: * be called when they are reused and a dispose method to be
441: * called when they are returned to the pool.
442: *
443: * @param instance the object instance to recycle.
444: * @return true if the instance was accepted.
445: */
446: public boolean putInstance(Object instance) {
447: if (instance != null) {
448: HashMap repository = poolRepository;
449: String className = instance.getClass().getName();
450: PoolBuffer pool = (PoolBuffer) repository.get(className);
451: if (pool == null) {
452: pool = new PoolBuffer(getCapacity(className));
453: repository = (HashMap) repository.clone();
454: repository.put(className, pool);
455: poolRepository = repository;
456:
457: if (instance instanceof ArrayCtorRecyclable) {
458: pool.setArrayCtorRecyclable(true);
459: }
460: }
461: return pool.offer(instance);
462: } else {
463: return false;
464: }
465: }
466:
467: /**
468: * Gets the capacity of the pool for a named class.
469: *
470: * @param className the name of the class.
471: */
472: public int getCapacity(String className) {
473: PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
474: if (pool == null) {
475: /* Check class specific capacity. */
476: int capacity;
477:
478: Configuration conf = getConfiguration();
479: capacity = conf.getInt(POOL_CAPACITY_KEY + '.' + className,
480: poolCapacity);
481: capacity = (capacity <= 0) ? poolCapacity : capacity;
482: return capacity;
483: } else {
484: return pool.capacity();
485: }
486: }
487:
488: /**
489: * Sets the capacity of the pool for a named class.
490: * Note that the pool will be cleared after the change.
491: *
492: * @param className the name of the class.
493: * @param capacity the new capacity.
494: */
495: public void setCapacity(String className, int capacity) {
496: HashMap repository = poolRepository;
497: repository = (repository != null) ? (HashMap) repository
498: .clone() : new HashMap();
499:
500: capacity = (capacity <= 0) ? poolCapacity : capacity;
501:
502: repository.put(className, new PoolBuffer(capacity));
503: poolRepository = repository;
504: }
505:
506: /**
507: * Gets the current size of the pool for a named class.
508: *
509: * @param className the name of the class.
510: */
511: public int getSize(String className) {
512: PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
513: return (pool != null) ? pool.size() : 0;
514: }
515:
516: /**
517: * Clears instances of a named class from the pool.
518: *
519: * @param className the name of the class.
520: */
521: public void clearPool(String className) {
522: HashMap repository = poolRepository;
523: if (repository.get(className) != null) {
524: repository = (HashMap) repository.clone();
525: repository.remove(className);
526: poolRepository = repository;
527: }
528: }
529:
530: /**
531: * Clears all instances from the pool.
532: */
533: public void clearPool() {
534: poolRepository = new HashMap();
535: }
536:
537: /**
538: * Polls and recycles an object of the named class from the pool.
539: *
540: * @param className the name of the class.
541: * @param params an array containing the parameters of the constructor.
542: * @param signature an array containing the signature of the constructor.
543: * @return the object or null.
544: * @throws TurbineException if recycling fails.
545: */
546: private Object pollInstance(String className, Object[] params,
547: String[] signature) throws TurbineException {
548: PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
549: return (pool != null) ? pool.poll(params, signature) : null;
550: }
551: }
|