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