1 package org.apache.turbine.services.pool;
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2001 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" and
29 * "Apache Turbine" must not be used to endorse or promote products
30 * derived from this software without prior written permission. For
31 * written permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * "Apache Turbine", nor may "Apache" appear in their name, without
35 * prior written permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 */
56
57 import java.lang.reflect.Method;
58 import java.util.ArrayList;
59 import java.util.HashMap;
60 import java.util.Iterator;
61 import org.apache.commons.configuration.Configuration;
62 import org.apache.turbine.services.InitializationException;
63 import org.apache.turbine.services.TurbineServices;
64 import org.apache.turbine.services.factory.FactoryService;
65 import org.apache.turbine.services.factory.TurbineFactoryService;
66 import org.apache.turbine.util.TurbineException;
67 import org.apache.turbine.util.pool.ArrayCtorRecyclable;
68 import org.apache.turbine.util.pool.BoundedBuffer;
69 import org.apache.turbine.util.pool.Recyclable;
70
71 /***
72 * The Pool Service extends the Factory Service by adding support
73 * for pooling instantiated objects. When a new instance is
74 * requested, the service first checks its pool if one is available.
75 * If the the pool is empty, a new instance will be requested
76 * from the FactoryService.
77 *
78 * <p>For objects implementing the Recyclable interface, a recycle
79 * method will be called, when they taken from the pool, and
80 * a dispose method, when they are returned to the pool.
81 *
82 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
83 * @version $Id: TurbinePoolService.java,v 1.4 2002/07/11 16:53:25 mpoeschl Exp $
84 */
85 public class TurbinePoolService
86 extends TurbineFactoryService
87 implements PoolService
88 {
89 /***
90 * The property specifying the pool capacity.
91 */
92 public static final String POOL_CAPACITY = "pool.capacity";
93
94 /***
95 * An inner class for class specific pools.
96 */
97 private class PoolBuffer
98 {
99 /***
100 * An inner class for cached recycle methods.
101 */
102 private class Recycler
103 {
104 /***
105 * The method.
106 */
107 private final Method recycle;
108
109 /***
110 * The signature.
111 */
112 private final String[] signature;
113
114 /***
115 * Constructs a new recycler.
116 *
117 * @param rec the recycle method.
118 * @param sign the signature.
119 */
120 public Recycler(Method rec, String[] sign)
121 {
122 recycle = rec;
123 signature = (sign != null) && (sign.length > 0) ? sign : null;
124 }
125
126 /***
127 * Matches the given signature against
128 * that of the recycle method of this recycler.
129 *
130 * @param sign the signature.
131 * @return the matching recycle method or null.
132 */
133 public Method match(String[] sign)
134 {
135 if ((sign != null) && (sign.length > 0))
136 {
137 if ((signature != null)
138 && (sign.length == signature.length))
139 {
140 for (int i = 0; i < signature.length; i++)
141 {
142 if (!signature[i].equals(sign[i]))
143 {
144 return null;
145 }
146 }
147 return recycle;
148 }
149 else
150 {
151 return null;
152 }
153 }
154 else if (signature == null)
155 {
156 return recycle;
157 }
158 else
159 {
160 return null;
161 }
162 }
163 }
164
165 /***
166 * A buffer for class instances.
167 */
168 private BoundedBuffer pool;
169
170 /***
171 * A flag to determine if a more efficient recycler is implemented.
172 */
173 private boolean arrayCtorRecyclable;
174
175 /***
176 * A cache for recycling methods.
177 */
178 private ArrayList recyclers;
179
180 /***
181 * Contructs a new pool buffer with a specific capacity.
182 *
183 * @param capacity a capacity.
184 */
185 public PoolBuffer(int capacity)
186 {
187 pool = new BoundedBuffer(capacity);
188 }
189
190 /***
191 * Tells pool that it contains objects which can be
192 * initialized using an Object array.
193 *
194 * @param isArrayCtor a <code>boolean</code> value
195 */
196 public void setArrayCtorRecyclable(boolean isArrayCtor)
197 {
198 arrayCtorRecyclable = isArrayCtor;
199 }
200
201 /***
202 * Polls for an instance from the pool.
203 *
204 * @return an instance or null.
205 */
206 public Object poll(Object[] params, String[] signature)
207 throws TurbineException
208 {
209 Object instance = pool.poll();
210 if (instance != null)
211 {
212 if (arrayCtorRecyclable)
213 {
214 ((ArrayCtorRecyclable) instance).recycle(params);
215 }
216 else if (instance instanceof Recyclable)
217 {
218 try
219 {
220 if ((signature != null) && (signature.length > 0))
221 {
222 /* Get the recycle method from the cache. */
223 Method recycle = getRecycle(signature);
224 if (recycle == null)
225 {
226 synchronized(this)
227 {
228 /* Make a synchronized recheck. */
229 recycle = getRecycle(signature);
230 if (recycle == null)
231 {
232 Class clazz = instance.getClass();
233 recycle = clazz.getMethod("recycle",
234 TurbinePoolService.this.getSignature(
235 clazz,params,signature));
236 ArrayList cache = recyclers != null ?
237 (ArrayList) recyclers.clone() :
238 new ArrayList();
239 cache.add(
240 new Recycler(recycle,signature));
241 recyclers = cache;
242 }
243 }
244 }
245 recycle.invoke(instance,params);
246 }
247 else
248 {
249 ((Recyclable) instance).recycle();
250 }
251 }
252 catch (Exception x)
253 {
254 throw new TurbineException(
255 "Recycling failed for " + instance.getClass().getName(),x);
256 }
257 }
258 }
259 return instance;
260 }
261
262 /***
263 * Offers an instance to the pool.
264 *
265 * @param instance an instance.
266 */
267 public boolean offer(Object instance)
268 {
269 if (instance instanceof Recyclable)
270 {
271 try
272 {
273 ((Recyclable) instance).dispose();
274 }
275 catch (Exception x)
276 {
277 return false;
278 }
279 }
280 return pool.offer(instance);
281 }
282
283 /***
284 * Returns the capacity of the pool.
285 *
286 * @return the capacity.
287 */
288 public int capacity()
289 {
290 return pool.capacity();
291 }
292
293 /***
294 * Returns the size of the pool.
295 *
296 * @return the size.
297 */
298 public int size()
299 {
300 return pool.size();
301 }
302
303 /***
304 * Returns a cached recycle method
305 * corresponding to the given signature.
306 *
307 * @param signature the signature.
308 * @return the recycle method or null.
309 */
310 private Method getRecycle(String[] signature)
311 {
312 ArrayList cache = recyclers;
313 if (cache != null)
314 {
315 Method recycle;
316 for (Iterator i = cache.iterator(); i.hasNext();)
317 {
318 recycle = ((Recycler) i.next()).match(signature);
319 if (recycle != null)
320 {
321 return recycle;
322 }
323 }
324 }
325 return null;
326 }
327 }
328
329 /***
330 * The default capacity of pools.
331 */
332 private int poolCapacity = DEFAULT_POOL_CAPACITY;
333
334 /***
335 * The pool repository, one pool for each class.
336 */
337 private HashMap poolRepository = new HashMap();
338
339 /***
340 * Constructs a Pool Service.
341 */
342 public TurbinePoolService()
343 {
344 }
345
346 /***
347 * Initializes the service by setting the pool capacity.
348 *
349 * @param config initialization configuration.
350 * @throws InitializationException if initialization fails.
351 */
352 public void init()
353 throws InitializationException
354 {
355 Configuration conf = getConfiguration();
356 if (conf != null)
357 {
358 try
359 {
360 int capacity = conf.getInt(POOL_CAPACITY,DEFAULT_POOL_CAPACITY);
361 if (capacity <= 0)
362 {
363 throw new IllegalArgumentException("Capacity must be >0");
364 }
365 poolCapacity = capacity;
366 }
367 catch (Exception x)
368 {
369 throw new InitializationException(
370 "Failed to initialize TurbinePoolService",x);
371 }
372 }
373 setInit(true);
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 *
380 * @param className the name of the class.
381 * @return the instance.
382 * @throws TurbineException if recycling fails.
383 */
384 public Object getInstance(String className)
385 throws TurbineException
386 {
387 Object instance = pollInstance(className,null,null);
388 return instance == null ?
389 getFactory().getInstance(className) : instance;
390 }
391
392 /***
393 * Gets an instance of a named class either from the pool
394 * or by calling the Factory Service if the pool is empty.
395 * The specified class loader will be passed to the Factory Service.
396 *
397 * @param className the name of the class.
398 * @param loader the class loader.
399 * @return the instance.
400 * @throws TurbineException if recycling fails.
401 */
402 public Object getInstance(String className,
403 ClassLoader loader)
404 throws TurbineException
405 {
406 Object instance = pollInstance(className,null,null);
407 return instance == null ?
408 getFactory().getInstance(className,loader) : instance;
409 }
410
411 /***
412 * Gets an instance of a named class either from the pool
413 * or by calling the Factory Service if the pool is empty.
414 * Parameters for its constructor are given as an array of objects,
415 * primitive types must be wrapped with a corresponding class.
416 *
417 * @param className the name of the class.
418 * @param loader the class loader.
419 * @param params an array containing the parameters of the constructor.
420 * @param signature an array containing the signature of the constructor.
421 * @return the instance.
422 * @throws TurbineException if recycling fails.
423 */
424 public Object getInstance(String className,
425 Object[] params,
426 String[] signature)
427 throws TurbineException
428 {
429 Object instance = pollInstance(className,params,signature);
430 return instance == null ?
431 getFactory().getInstance(className,params,signature) : instance;
432 }
433
434 /***
435 * Gets an instance of a named class either from the pool
436 * or by calling the Factory Service if the pool is empty.
437 * Parameters for its constructor are given as an array of objects,
438 * primitive types must be wrapped with a corresponding class.
439 * The specified class loader will be passed to the Factory Service.
440 *
441 * @param className the name of the class.
442 * @param loader the class loader.
443 * @param params an array containing the parameters of the constructor.
444 * @param signature an array containing the signature of the constructor.
445 * @return the instance.
446 * @throws TurbineException if recycling fails.
447 */
448 public Object getInstance(String className,
449 ClassLoader loader,
450 Object[] params,
451 String[] signature)
452 throws TurbineException
453 {
454 Object instance = pollInstance(className,params,signature);
455 return instance == null ?
456 getFactory().getInstance(className,loader,params,signature) : instance;
457 }
458
459 /***
460 * Tests if specified class loaders are supported for a named class.
461 *
462 * @param className the name of the class.
463 * @return true if class loaders are supported, false otherwise.
464 * @throws TurbineException if test fails.
465 */
466 public boolean isLoaderSupported(String className)
467 throws TurbineException
468 {
469 return getFactory().isLoaderSupported(className);
470 }
471
472 /***
473 * Gets an instance of a specified class either from the pool
474 * or by instatiating from the class if the pool is empty.
475 *
476 * @param clazz the class.
477 * @return the instance.
478 * @throws TurbineException if recycling fails.
479 */
480 public Object getInstance(Class clazz)
481 throws TurbineException
482 {
483 Object instance = pollInstance(clazz.getName(),null,null);
484 return instance == null ?
485 super.getInstance(clazz) : instance;
486 }
487
488 /***
489 * Gets an instance of a specified class either from the pool
490 * or by instatiating from the class if the pool is empty.
491 *
492 * @param clazz the class.
493 * @param params an array containing the parameters of the constructor.
494 * @param signature an array containing the signature of the constructor.
495 * @return the instance.
496 * @throws TurbineException if recycling fails.
497 */
498 public Object getInstance(Class clazz,
499 Object params[],
500 String signature[])
501 throws TurbineException
502 {
503 Object instance = pollInstance(clazz.getName(),params,signature);
504 return instance == null ?
505 super.getInstance(clazz,params,signature) : instance;
506 }
507
508 /***
509 * Puts a used object back to the pool. Objects implementing
510 * the Recyclable interface can provide a recycle method to
511 * be called when they are reused and a dispose method to be
512 * called when they are returned to the pool.
513 *
514 * @param instance the object instance to recycle.
515 * @return true if the instance was accepted.
516 */
517 public boolean putInstance(Object instance)
518 {
519 if (instance != null)
520 {
521 HashMap repository = poolRepository;
522 String className = instance.getClass().getName();
523 PoolBuffer pool = (PoolBuffer) repository.get(className);
524 if (pool == null)
525 {
526 pool = new PoolBuffer(getCapacity(className));
527 repository = (HashMap) repository.clone();
528 repository.put(className,pool);
529 poolRepository = repository;
530
531 if (instance instanceof ArrayCtorRecyclable)
532 {
533 pool.setArrayCtorRecyclable(true);
534 }
535 }
536 return pool.offer(instance);
537 }
538 else
539 {
540 return false;
541 }
542 }
543
544 /***
545 * Gets the capacity of the pool for a named class.
546 *
547 * @param className the name of the class.
548 */
549 public int getCapacity(String className)
550 {
551 PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
552 if (pool == null)
553 {
554 /* Check class specific capacity. */
555 int capacity = poolCapacity;
556 Configuration conf = getConfiguration();
557 if (conf != null)
558 {
559 try
560 {
561 capacity = conf.getInt(
562 POOL_CAPACITY + '.' + className,poolCapacity);
563 if (capacity <= 0)
564 {
565 capacity = poolCapacity;
566 }
567 }
568 catch (Exception x)
569 {
570 }
571 }
572 return capacity;
573 }
574 else
575 {
576 return pool.capacity();
577 }
578 }
579
580 /***
581 * Sets the capacity of the pool for a named class.
582 * Note that the pool will be cleared after the change.
583 *
584 * @param className the name of the class.
585 * @param capacity the new capacity.
586 */
587 public void setCapacity(String className,
588 int capacity)
589 {
590 HashMap repository = poolRepository;
591 repository = repository != null ?
592 (HashMap) repository.clone() : new HashMap();
593 repository.put(className,new PoolBuffer(capacity));
594 poolRepository = repository;
595 }
596
597 /***
598 * Gets the current size of the pool for a named class.
599 *
600 * @param className the name of the class.
601 */
602 public int getSize(String className)
603 {
604 PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
605 return pool != null ? pool.size() : 0;
606 }
607
608 /***
609 * Clears instances of a named class from the pool.
610 *
611 * @param className the name of the class.
612 */
613 public void clearPool(String className)
614 {
615 HashMap repository = poolRepository;
616 if (repository.get(className) != null)
617 {
618 repository = (HashMap) repository.clone();
619 repository.remove(className);
620 poolRepository = repository;
621 }
622 }
623
624 /***
625 * Clears all instances from the pool.
626 */
627 public void clearPool()
628 {
629 poolRepository = new HashMap();
630 }
631
632 /***
633 * Polls and recycles an object of the named class from the pool.
634 *
635 * @param className the name of the class.
636 * @param params an array containing the parameters of the constructor.
637 * @param signature an array containing the signature of the constructor.
638 * @return the object or null.
639 * @throws TurbineException if recycling fails.
640 */
641 private Object pollInstance(String className,
642 Object[] params,
643 String[] signature)
644 throws TurbineException
645 {
646 PoolBuffer pool = (PoolBuffer) poolRepository.get(className);
647 return pool != null ? pool.poll(params,signature) : null;
648 }
649
650 /***
651 * Gets the factory service.
652 *
653 * @return the factory service.
654 */
655 private FactoryService getFactory()
656 {
657 return (FactoryService) TurbineServices.
658 getInstance().getService(FactoryService.SERVICE_NAME);
659 }
660 }
This page was automatically generated by Maven