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