%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.turbine.services.pool.TurbinePoolService |
|
|
1 | package org.apache.turbine.services.pool; |
|
2 | ||
3 | /* |
|
4 | * Copyright 2001-2005 The Apache Software Foundation. |
|
5 | * |
|
6 | * Licensed under the Apache License, Version 2.0 (the "License") |
|
7 | * you may not use this file except in compliance with the License. |
|
8 | * You may obtain a copy of the License at |
|
9 | * |
|
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 | * |
|
12 | * Unless required by applicable law or agreed to in writing, software |
|
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 | * See the License for the specific language governing permissions and |
|
16 | * limitations under the License. |
|
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 | 21 | public class TurbinePoolService |
55 | extends TurbineBaseService |
|
56 | implements PoolService |
|
57 | { |
|
58 | /** Are we currently debugging the pool recycling? */ |
|
59 | 42 | private boolean debugPool = false; |
60 | ||
61 | /** Internal Reference to the Factory */ |
|
62 | private FactoryService factoryService; |
|
63 | ||
64 | /** Logging */ |
|
65 | 63 | 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 | // If we're debugging the recycling code, we want different |
|
174 | // objects to be used when pulling from the pool. Ensure that |
|
175 | // each pool fills up at least to half its capacity. |
|
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 | /* Get the recycle method from the cache. */ |
|
197 | Method recycle = getRecycle(signature); |
|
198 | if (recycle == null) |
|
199 | { |
|
200 | synchronized (this) |
|
201 | { |
|
202 | /* Make a synchronized recheck. */ |
|
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 | 42 | private int poolCapacity = DEFAULT_POOL_CAPACITY; |
307 | ||
308 | /** |
|
309 | * The pool repository, one pool for each class. |
|
310 | */ |
|
311 | 42 | private HashMap poolRepository = new HashMap(); |
312 | ||
313 | /** |
|
314 | * Constructs a Pool Service. |
|
315 | */ |
|
316 | public TurbinePoolService() |
|
317 | 42 | { |
318 | 42 | } |
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 | 44 | Configuration conf = getConfiguration(); |
330 | ||
331 | 44 | int capacity = conf.getInt(POOL_CAPACITY_KEY, |
332 | DEFAULT_POOL_CAPACITY); |
|
333 | ||
334 | 44 | if (capacity <= 0) |
335 | { |
|
336 | 0 | throw new IllegalArgumentException("Capacity must be >0"); |
337 | } |
|
338 | 44 | poolCapacity = capacity; |
339 | ||
340 | 44 | debugPool = conf.getBoolean(POOL_DEBUG_KEY, |
341 | POOL_DEBUG_DEFAULT); |
|
342 | ||
343 | 44 | if (debugPool) |
344 | { |
|
345 | 0 | log.info("Activated Pool Debugging!"); |
346 | } |
|
347 | ||
348 | 44 | factoryService = TurbineFactory.getService(); |
349 | ||
350 | 44 | if (factoryService == null) |
351 | { |
|
352 | 0 | throw new InitializationException("Factory Service is not configured" |
353 | + " but required for the Pool Service!"); |
|
354 | } |
|
355 | ||
356 | 44 | setInit(true); |
357 | 44 | } |
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 | 0 | Object instance = pollInstance(className, null, class="keyword">null); |
371 | 0 | 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 | 0 | Object instance = pollInstance(className, null, class="keyword">null); |
391 | 0 | 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 | 0 | Object instance = pollInstance(className, params, signature); |
415 | 0 | 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 | 0 | Object instance = pollInstance(className, params, signature); |
441 | 0 | 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 | 0 | 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 | 0 | Object instance = pollInstance(clazz.getName(), null, class="keyword">null); |
472 | 0 | 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 | 0 | Object instance = pollInstance(clazz.getName(), params, signature); |
493 | 0 | 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 | 0 | if (instance != null) |
510 | { |
|
511 | 0 | HashMap repository = poolRepository; |
512 | 0 | String className = instance.getClass().getName(); |
513 | 0 | PoolBuffer pool = (PoolBuffer) repository.get(className); |
514 | 0 | if (pool == null) |
515 | { |
|
516 | 0 | pool = new PoolBuffer(getCapacity(className)); |
517 | 0 | repository = (HashMap) repository.clone(); |
518 | 0 | repository.put(className, pool); |
519 | 0 | poolRepository = repository; |
520 | ||
521 | 0 | if (instance instanceof ArrayCtorRecyclable) |
522 | { |
|
523 | 0 | pool.setArrayCtorRecyclable(true); |
524 | } |
|
525 | } |
|
526 | 0 | return pool.offer(instance); |
527 | } |
|
528 | else |
|
529 | { |
|
530 | 0 | 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 | 0 | PoolBuffer pool = (PoolBuffer) poolRepository.get(className); |
542 | 0 | if (pool == null) |
543 | { |
|
544 | /* Check class specific capacity. */ |
|
545 | int capacity; |
|
546 | ||
547 | 0 | Configuration conf = getConfiguration(); |
548 | 0 | capacity = conf.getInt( |
549 | POOL_CAPACITY_KEY + '.' + className, |
|
550 | poolCapacity); |
|
551 | 0 | capacity = (capacity <= 0) ? poolCapacity : capacity; |
552 | 0 | return capacity; |
553 | } |
|
554 | else |
|
555 | { |
|
556 | 0 | 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 | 0 | HashMap repository = poolRepository; |
571 | 0 | repository = (repository != null) |
572 | ? (HashMap) repository.clone() : new HashMap(); |
|
573 | ||
574 | 0 | capacity = (capacity <= 0) ? poolCapacity : capacity; |
575 | ||
576 | 0 | repository.put(className, new PoolBuffer(capacity)); |
577 | 0 | poolRepository = repository; |
578 | 0 | } |
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 | 0 | PoolBuffer pool = (PoolBuffer) poolRepository.get(className); |
588 | 0 | 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 | 0 | HashMap repository = poolRepository; |
599 | 0 | if (repository.get(className) != null) |
600 | { |
|
601 | 0 | repository = (HashMap) repository.clone(); |
602 | 0 | repository.remove(className); |
603 | 0 | poolRepository = repository; |
604 | } |
|
605 | 0 | } |
606 | ||
607 | /** |
|
608 | * Clears all instances from the pool. |
|
609 | */ |
|
610 | public void clearPool() |
|
611 | { |
|
612 | 0 | poolRepository = new HashMap(); |
613 | 0 | } |
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 | 0 | PoolBuffer pool = (PoolBuffer) poolRepository.get(className); |
630 | 0 | return (pool != null) ? pool.poll(params, signature) : class="keyword">null; |
631 | } |
|
632 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |