1 package org.apache.turbine.services.pool;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.lang.reflect.Method;
20
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.Iterator;
24
25 import org.apache.commons.configuration.Configuration;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import org.apache.turbine.services.InitializationException;
31 import org.apache.turbine.services.TurbineBaseService;
32 import org.apache.turbine.services.factory.FactoryService;
33 import org.apache.turbine.services.factory.TurbineFactory;
34 import org.apache.turbine.util.TurbineException;
35 import org.apache.turbine.util.pool.ArrayCtorRecyclable;
36 import org.apache.turbine.util.pool.BoundedBuffer;
37 import org.apache.turbine.util.pool.Recyclable;
38
39 /***
40 * The Pool Service extends the Factory Service by adding support
41 * for pooling instantiated objects. When a new instance is
42 * requested, the service first checks its pool if one is available.
43 * If the the pool is empty, a new instance will be requested
44 * from the FactoryService.
45 *
46 * <p>For objects implementing the Recyclable interface, a recycle
47 * method will be called, when they taken from the pool, and
48 * a dispose method, when they are returned to the pool.
49 *
50 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
51 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
52 * @version $Id: TurbinePoolService.java 264148 2005-08-29 14:21:04Z henning $
53 */
54 public class TurbinePoolService
55 extends TurbineBaseService
56 implements PoolService
57 {
58 /*** Are we currently debugging the pool recycling? */
59 private boolean debugPool = false;
60
61 /*** Internal Reference to the Factory */
62 private FactoryService factoryService;
63
64 /*** Logging */
65 private static Log log = LogFactory.getLog(TurbinePoolService.class);
66
67 /***
68 * An inner class for class specific pools.
69 */
70 private class PoolBuffer
71 {
72 /***
73 * An inner class for cached recycle methods.
74 */
75 private class Recycler
76 {
77 /*** The recycle method. */
78 private final Method recycle;
79
80 /*** The signature. */
81 private final String[] signature;
82
83 /***
84 * Constructs a new recycler.
85 *
86 * @param rec the recycle method.
87 * @param sign the signature.
88 */
89 public Recycler(Method rec, String[] sign)
90 {
91 recycle = rec;
92 signature = ((sign != null) && (sign.length > 0))
93 ? sign : null;
94 }
95
96 /***
97 * Matches the given signature against
98 * that of the recycle method of this recycler.
99 *
100 * @param sign the signature.
101 * @return the matching recycle method or null.
102 */
103 public Method match(String[] sign)
104 {
105 if ((sign != null) && (sign.length > 0))
106 {
107 if ((signature != null)
108 && (sign.length == signature.length))
109 {
110 for (int i = 0; i < signature.length; i++)
111 {
112 if (!signature[i].equals(sign[i]))
113 {
114 return null;
115 }
116 }
117 return recycle;
118 }
119 else
120 {
121 return null;
122 }
123 }
124 else if (signature == null)
125 {
126 return recycle;
127 }
128 else
129 {
130 return null;
131 }
132 }
133 }
134
135 /*** A buffer for class instances. */
136 private BoundedBuffer pool;
137
138 /*** A flag to determine if a more efficient recycler is implemented. */
139 private boolean arrayCtorRecyclable;
140
141 /*** A cache for recycling methods. */
142 private ArrayList recyclers;
143
144 /***
145 * Constructs a new pool buffer with a specific capacity.
146 *
147 * @param capacity a capacity.
148 */
149 public PoolBuffer(int capacity)
150 {
151 pool = new BoundedBuffer(capacity);
152 }
153
154 /***
155 * Tells pool that it contains objects which can be
156 * initialized using an Object array.
157 *
158 * @param isArrayCtor a <code>boolean</code> value
159 */
160 public void setArrayCtorRecyclable(boolean isArrayCtor)
161 {
162 arrayCtorRecyclable = isArrayCtor;
163 }
164
165 /***
166 * Polls for an instance from the pool.
167 *
168 * @return an instance or null.
169 */
170 public Object poll(Object[] params, String[] signature)
171 throws TurbineException
172 {
173
174
175
176 if (debugPool && (pool.size() < (pool.capacity() / 2)))
177 {
178 log.debug("Size: " + pool.size()
179 + ", capacity: " + pool.capacity());
180 return null;
181 }
182
183 Object instance = pool.poll();
184 if (instance != null)
185 {
186 if (arrayCtorRecyclable)
187 {
188 ((ArrayCtorRecyclable) instance).recycle(params);
189 }
190 else if (instance instanceof Recyclable)
191 {
192 try
193 {
194 if ((signature != null) && (signature.length > 0))
195 {
196
197 Method recycle = getRecycle(signature);
198 if (recycle == null)
199 {
200 synchronized (this)
201 {
202
203 recycle = getRecycle(signature);
204 if (recycle == null)
205 {
206 Class clazz = instance.getClass();
207 recycle = clazz.getMethod("recycle",
208 factoryService.getSignature(
209 clazz, params, signature));
210 ArrayList cache = recyclers != null
211 ? (ArrayList) recyclers.clone()
212 : new ArrayList();
213 cache.add(
214 new Recycler(recycle, signature));
215 recyclers = cache;
216 }
217 }
218 }
219 recycle.invoke(instance, params);
220 }
221 else
222 {
223 ((Recyclable) instance).recycle();
224 }
225 }
226 catch (Exception x)
227 {
228 throw new TurbineException(
229 "Recycling failed for " + instance.getClass().getName(), x);
230 }
231 }
232 }
233 return instance;
234 }
235
236 /***
237 * Offers an instance to the pool.
238 *
239 * @param instance an instance.
240 */
241 public boolean offer(Object instance)
242 {
243 if (instance instanceof Recyclable)
244 {
245 try
246 {
247 ((Recyclable) instance).dispose();
248 }
249 catch (Exception x)
250 {
251 return false;
252 }
253 }
254 return pool.offer(instance);
255 }
256
257 /***
258 * Returns the capacity of the pool.
259 *
260 * @return the capacity.
261 */
262 public int capacity()
263 {
264 return pool.capacity();
265 }
266
267 /***
268 * Returns the size of the pool.
269 *
270 * @return the size.
271 */
272 public int size()
273 {
274 return pool.size();
275 }
276
277 /***
278 * Returns a cached recycle method
279 * corresponding to the given signature.
280 *
281 * @param signature the signature.
282 * @return the recycle method or null.
283 */
284 private Method getRecycle(String[] signature)
285 {
286 ArrayList cache = recyclers;
287 if (cache != null)
288 {
289 Method recycle;
290 for (Iterator i = cache.iterator(); i.hasNext();)
291 {
292 recycle = ((Recycler) i.next()).match(signature);
293 if (recycle != null)
294 {
295 return recycle;
296 }
297 }
298 }
299 return null;
300 }
301 }
302
303 /***
304 * The default capacity of pools.
305 */
306 private int poolCapacity = DEFAULT_POOL_CAPACITY;
307
308 /***
309 * The pool repository, one pool for each class.
310 */
311 private HashMap poolRepository = new HashMap();
312
313 /***
314 * Constructs a Pool Service.
315 */
316 public TurbinePoolService()
317 {
318 }
319
320 /***
321 * Initializes the service by setting the pool capacity.
322 *
323 * @param config initialization configuration.
324 * @throws InitializationException if initialization fails.
325 */
326 public void init()
327 throws InitializationException
328 {
329 Configuration conf = getConfiguration();
330
331 int capacity = conf.getInt(POOL_CAPACITY_KEY,
332 DEFAULT_POOL_CAPACITY);
333
334 if (capacity <= 0)
335 {
336 throw new IllegalArgumentException("Capacity must be >0");
337 }
338 poolCapacity = capacity;
339
340 debugPool = conf.getBoolean(POOL_DEBUG_KEY,
341 POOL_DEBUG_DEFAULT);
342
343 if (debugPool)
344 {
345 log.info("Activated Pool Debugging!");
346 }
347
348 factoryService = TurbineFactory.getService();
349
350 if (factoryService == null)
351 {
352 throw new InitializationException("Factory Service is not configured"
353 + " but required for the Pool Service!");
354 }
355
356 setInit(true);
357 }
358
359 /***
360 * Gets an instance of a named class either from the pool
361 * or by calling the Factory Service if the pool is empty.
362 *
363 * @param className the name of the class.
364 * @return the instance.
365 * @throws TurbineException if recycling fails.
366 */
367 public Object getInstance(String className)
368 throws TurbineException
369 {
370 Object instance = pollInstance(className, null, null);
371 return (instance == null)
372 ? factoryService.getInstance(className)
373 : instance;
374 }
375
376 /***
377 * Gets an instance of a named class either from the pool
378 * or by calling the Factory Service if the pool is empty.
379 * The specified class loader will be passed to the Factory Service.
380 *
381 * @param className the name of the class.
382 * @param loader the class loader.
383 * @return the instance.
384 * @throws TurbineException if recycling fails.
385 */
386 public Object getInstance(String className,
387 ClassLoader loader)
388 throws TurbineException
389 {
390 Object instance = pollInstance(className, null, null);
391 return (instance == null)
392 ? factoryService.getInstance(className, loader)
393 : instance;
394 }
395
396 /***
397 * Gets an instance of a named class either from the pool
398 * or by calling the Factory Service if the pool is empty.
399 * Parameters for its constructor are given as an array of objects,
400 * primitive types must be wrapped with a corresponding class.
401 *
402 * @param className the name of the class.
403 * @param loader the class loader.
404 * @param params an array containing the parameters of the constructor.
405 * @param signature an array containing the signature of the constructor.
406 * @return the instance.
407 * @throws TurbineException if recycling fails.
408 */
409 public Object getInstance(String className,
410 Object[] params,
411 String[] signature)
412 throws TurbineException
413 {
414 Object instance = pollInstance(className, params, signature);
415 return (instance == null)
416 ? factoryService.getInstance(className, params, signature)
417 : instance;
418 }
419
420 /***
421 * Gets an instance of a named class either from the pool
422 * or by calling the Factory Service if the pool is empty.
423 * Parameters for its constructor are given as an array of objects,
424 * primitive types must be wrapped with a corresponding class.
425 * The specified class loader will be passed to the Factory Service.
426 *
427 * @param className the name of the class.
428 * @param loader the class loader.
429 * @param params an array containing the parameters of the constructor.
430 * @param signature an array containing the signature of the constructor.
431 * @return the instance.
432 * @throws TurbineException if recycling fails.
433 */
434 public Object getInstance(String className,
435 ClassLoader loader,
436 Object[] params,
437 String[] signature)
438 throws TurbineException
439 {
440 Object instance = pollInstance(className, params, signature);
441 return (instance == null)
442 ? factoryService.getInstance(className, loader, params, signature)
443 : instance;
444 }
445
446 /***
447 * Tests if specified class loaders are supported for a named class.
448 *
449 * @param className the name of the class.
450 * @return true if class loaders are supported, false otherwise.
451 * @throws TurbineException if test fails.
452 * @deprecated Use TurbineFactory.isLoaderSupported(className);
453 */
454 public boolean isLoaderSupported(String className)
455 throws TurbineException
456 {
457 return factoryService.isLoaderSupported(className);
458 }
459
460 /***
461 * Gets an instance of a specified class either from the pool
462 * or by instatiating from the class if the pool is empty.
463 *
464 * @param clazz the class.
465 * @return the instance.
466 * @throws TurbineException if recycling fails.
467 */
468 public Object getInstance(Class clazz)
469 throws TurbineException
470 {
471 Object instance = pollInstance(clazz.getName(), null, null);
472 return (instance == null)
473 ? factoryService.getInstance(clazz.getName())
474 : instance;
475 }
476
477 /***
478 * Gets an instance of a specified class either from the pool
479 * or by instatiating from the class if the pool is empty.
480 *
481 * @param clazz the class.
482 * @param params an array containing the parameters of the constructor.
483 * @param signature an array containing the signature of the constructor.
484 * @return the instance.
485 * @throws TurbineException if recycling fails.
486 */
487 public Object getInstance(Class clazz,
488 Object params[],
489 String signature[])
490 throws TurbineException
491 {
492 Object instance = pollInstance(clazz.getName(), params, signature);
493 return (instance == null)
494 ? factoryService.getInstance(clazz.getName(), params, signature)
495 : instance;
496 }
497
498 /***
499 * Puts a used object back to the pool. Objects implementing
500 * the Recyclable interface can provide a recycle method to
501 * be called when they are reused and a dispose method to be
502 * called when they are returned to the pool.
503 *
504 * @param instance the object instance to recycle.
505 * @return true if the instance was accepted.
506 */
507 public boolean putInstance(Object instance)
508 {
509 if (instance != null)
510 {
511 HashMap repository = poolRepository;
512 String className = instance.getClass().getName();
513 PoolBuffer pool = (PoolBuffer) repository.get(className);
514 if (pool == null)
515 {
516 pool = new PoolBuffer(getCapacity(className));
517 repository = (HashMap) repository.clone();
518 repository.put(className, pool);
519 poolRepository = repository;
520
521 if (instance instanceof ArrayCtorRecyclable)
522 {
523 pool.setArrayCtorRecyclable(true);
524 }
525 }
526 return pool.offer(instance);
527 }
528 else
529 {
530 return false;
531 }
532 }
533
534 /***
535 * Gets the capacity of the pool for a named class.
536 *
537 * @param className the name of the class.
538 */
539 public int getCapacity(String className)
540 {
541 PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
542 if (pool == null)
543 {
544
545 int capacity;
546
547 Configuration conf = getConfiguration();
548 capacity = conf.getInt(
549 POOL_CAPACITY_KEY + '.' + className,
550 poolCapacity);
551 capacity = (capacity <= 0) ? poolCapacity : capacity;
552 return capacity;
553 }
554 else
555 {
556 return pool.capacity();
557 }
558 }
559
560 /***
561 * Sets the capacity of the pool for a named class.
562 * Note that the pool will be cleared after the change.
563 *
564 * @param className the name of the class.
565 * @param capacity the new capacity.
566 */
567 public void setCapacity(String className,
568 int capacity)
569 {
570 HashMap repository = poolRepository;
571 repository = (repository != null)
572 ? (HashMap) repository.clone() : new HashMap();
573
574 capacity = (capacity <= 0) ? poolCapacity : capacity;
575
576 repository.put(className, new PoolBuffer(capacity));
577 poolRepository = repository;
578 }
579
580 /***
581 * Gets the current size of the pool for a named class.
582 *
583 * @param className the name of the class.
584 */
585 public int getSize(String className)
586 {
587 PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
588 return (pool != null) ? pool.size() : 0;
589 }
590
591 /***
592 * Clears instances of a named class from the pool.
593 *
594 * @param className the name of the class.
595 */
596 public void clearPool(String className)
597 {
598 HashMap repository = poolRepository;
599 if (repository.get(className) != null)
600 {
601 repository = (HashMap) repository.clone();
602 repository.remove(className);
603 poolRepository = repository;
604 }
605 }
606
607 /***
608 * Clears all instances from the pool.
609 */
610 public void clearPool()
611 {
612 poolRepository = new HashMap();
613 }
614
615 /***
616 * Polls and recycles an object of the named class from the pool.
617 *
618 * @param className the name of the class.
619 * @param params an array containing the parameters of the constructor.
620 * @param signature an array containing the signature of the constructor.
621 * @return the object or null.
622 * @throws TurbineException if recycling fails.
623 */
624 private Object pollInstance(String className,
625 Object[] params,
626 String[] signature)
627 throws TurbineException
628 {
629 PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
630 return (pool != null) ? pool.poll(params, signature) : null;
631 }
632 }