/Users/lyon/j4p/src/classUtils/pack/util/pool/ObjectPool.java
|
1 package classUtils.pack.util.pool;
2
3 import java.io.PrintWriter;
4 import java.io.StringWriter;
5 import java.lang.reflect.Constructor;
6 import java.lang.reflect.InvocationTargetException;
7 import java.util.HashSet;
8 import java.util.Iterator;
9 import java.util.NoSuchElementException;
10 import java.util.Set;
11
12 import classUtils.pack.util.Setup;
13
14 /**
15 * An object pool, which holds <i>n</i> identical copies of an
16 * object and allows access and release of each of them.
17 * <p>
18 * An object can be created by providing its class - in which case
19 * the default constructor will be used to create the instances;
20 * or by providing the construction parameters as an object array.
21 * <p>
22 * Optionally, after creation, a {@link classUtils.pack.util.Setup Setup object} can
23 * be used to differentiate or further set up the created object.
24 * <p>
25 * This version supports Enterprise Java Beans creation via a JNDI name and home interface
26 *
27 * @author C. Sadun
28 * @version 2.2
29 */
30 public class ObjectPool {
31
32 protected Set free;
33 protected Set used;
34 protected PooledObjectWrapper [] pool;
35 private Factory factory;
36 private boolean verbose=( System.getProperty("org.sadun.verbose") != null);
37
38
39 /**
40 * An exception thrown in case of pooling operation errors.
41 */
42 public static final class ObjectPoolException extends RuntimeException {
43
44 private Throwable e;
45
46 public ObjectPoolException(String msg) {
47 super(msg);
48 }
49
50 public ObjectPoolException(Throwable e) {
51 this("", e);
52 }
53
54 public ObjectPoolException(String msg, Throwable e) {
55 super(msg+"(caused by "+e.getClass().getName()+": "+e.getMessage()+")");
56 this.e=e;
57 }
58
59 public Throwable getRootException() { return e; }
60
61 }
62
63 // This is just to have another object monitor for index-based acquire/released
64 protected static final class PooledObjectWrapper {
65 private Object object;
66 PooledObjectWrapper(Object object) { this.object=object; }
67 public Object getObject() { return object; }
68 }
69
70
71 /**
72 * If an object to be pooled can't be constructed directly by
73 * invoking a constructor, a Factory can be provided
74 * to do the construction job.
75 */
76 public static interface Factory {
77
78 /**
79 * This method is invoked by the pool when a pooled instance
80 * has to be created.
81 * @return the newly created object
82 * @exception ObjectPoolException if there is a problem creating the object
83 */
84 public Object create() throws ObjectPoolException;
85
86 /**
87 * This method is invoked when validating the created class.
88 *
89 * @return the class object for the type of object this factory will create
90 */
91 public Class getProducedClass();
92 }
93
94 /**
95 * A base implementation of {@link ExtendedObjectPool.Factory ExtendedObjectPool.Factory}
96 * relying on reflection, holding a Class object, an optional parameter array
97 * and an optional {@link classUtils.pack.util.Setup Setup} object.
98 * <p>
99 * These objects are made available to subclasses by the members {@link #cls cls},
100 * {@link #params params} and {@link #ps ps}.
101 *
102 * @author Cristiano Sadun
103 * @version 1.0
104 */
105 public static abstract class BaseFactory implements Factory {
106
107 /**
108 * The class of the objects produced by the factory.
109 */
110 protected Class cls;
111
112 /**
113 * The parameters to use for constructing the object. It is never <b>null</b>, but may have size zero.
114 */
115 protected Object [] params;
116
117 /**
118 * The setup object to use for post-construction initialization. Can be <b>null</b>.
119 */
120 protected Setup ps;
121
122 /**
123 * The types of the parameters to use for constructing the object. It is never <b>null</b>, but may have size zero.
124 */
125 protected Class [] paramCls;
126
127 /**
128 * Create a BaseFactory whose member {@link #cls cls} will hold the class of the objects to produce
129 * @param cls the class of the objects to produce
130 * @param params the construction parameters, or <b>null</b>
131 * @param ps the {@link classUtils.pack.util.Setup Setup} object to be used for post-construction setup, or <b>null</b>
132 */
133 public BaseFactory(Class cls, Object [] params, Setup ps) {
134 if (params==null) params=new Object[0];
135 this.cls=cls;
136 this.params=params;
137 this.ps=ps;
138 this.paramCls = new Class[params.length];
139 for(int i=0;i<params.length;i++)
140 paramCls[i]=params[i].getClass();
141 }
142
143 /**
144 * Create a BaseFactory whose member {@link #cls cls} will hold the class of the objects to produce
145 * @param clsName the name of the class of the objects to produce
146 * @param params the construction parameters, or <b>null</b>
147 * @param ps the {@link classUtils.pack.util.Setup Setup} object to be used for post-construction setup, or <b>null</b>
148 */
149 public BaseFactory(String clsName, Object [] params, Setup ps) {
150 this(findClass(clsName), params, ps);
151 }
152
153 public abstract Object create();
154
155 /**
156 * Return the class object for the type of object this factory creates
157 *
158 * @return the class object for the type of object this factory creates
159 */
160 public Class getProducedClass() { return cls; }
161
162
163 }
164
165 private static Class findClass(String clsName) throws ObjectPoolException {
166 try {
167 return Class.forName(clsName);
168 } catch(ClassNotFoundException e) {
169 throw new ObjectPoolException(e);
170 }
171 }
172
173
174
175 /**
176 * An {@link ExtendedObjectPool.Factory ExtendedObjectPool.Factory} implementation which uses reflection
177 * to create instances of a certain class.
178 * <p>
179 * After creation, objects can be optionally setup by an user-provided class implementing
180 * the {@link classUtils.pack.util.Setup Setup} interface.
181 *
182 * @author Cristiano Sadun
183 * @version 1.0
184 */
185 public static class ObjectFactory extends BaseFactory {
186
187 /**
188 * Create a factory which will construct object of the given class, with the given parameters,
189 * using the given {@link classUtils.pack.util.Setup Setup object}.
190 * <p>
191 * The construction parameter types are deduced from the types of the passed parameter objects,
192 * and a compatible constructor is searched for.
193 *
194 * @param cls the class of the object to build
195 * @param params the construction parameters to use, or <b>null</b>
196 * @param ps the {@link classUtils.pack.util.Setup Setup} object to be used for post-construction setup, or <b>null</b>
197 */
198 public ObjectFactory(Class cls, Object [] params, Setup ps) {
199 super(cls, params, ps);
200 }
201
202 /**
203 * Create a factory which will construct object of the given class, with the given parameters,
204 * using the given {@link classUtils.pack.util.Setup Setup object}.
205 * <p>
206 * The construction parameter types are deduced from the types of the passed parameter objects,
207 * and a compatible constructor is searched for.
208 *
209 * @param clsName the name of class of the object to build
210 * @param params the construction parameters to use, or <b>null</b>
211 * @param ps the {@link classUtils.pack.util.Setup Setup} object to be used for post-construction setup, or <b>null</b>
212 */
213 public ObjectFactory(String clsName, Object [] params, Setup ps) {
214 super(clsName, params, ps);
215 }
216
217 /**
218 * Create an instance of the class defined at construction, using the (optional) parameters
219 * and the (optional) {@link classUtils.pack.util.Setup Setup} object defined at construction for construction
220 * and post-construction initialization.
221 * @return the newly created object
222 * @exception ObjectPoolException if there is a problem creating the object
223 */
224 public Object create() {
225 try {
226 Constructor ctor = this.cls.getConstructor(paramCls);
227 Object obj =ctor.newInstance(params);
228 if (ps!=null) ps.setup(obj);
229 return obj;
230 } catch(NoSuchMethodException e) {
231 throw new ObjectPoolException(
232 "The class "+this.cls.getName()+
233 " does not have a constructor matching the passed parameter objects"
234 );
235 } catch(InstantiationException e) {
236 throw new ObjectPoolException("Could not instantiate "+this.cls.getName());
237 } catch(IllegalAccessException e) {
238 throw new ObjectPoolException(
239 "Could not access proper constructor in "+this.cls.getName()
240 );
241 } catch(InvocationTargetException e) {
242 throw new ObjectPoolException(
243 "Object construction failed with exception "+e.getTargetException()
244 );
245 }
246 }
247 }
248
249 /**
250 * Create a pool of <i>n</i> objects using the given factory
251 */
252 public ObjectPool(int n, Factory factory) {
253 if (n==0) throw new IllegalArgumentException("Can't build a pool of 0 objects");
254 this.used=new HashSet();
255 this.free=new HashSet();
256 this.pool=new PooledObjectWrapper[n];
257 this.factory=factory;
258 Class cls=factory.getProducedClass();
259 if (verbose)
260 System.out.println("Creating "+n+" objects of type "+cls.getName());
261 for(int i=0;i<n;i++) {
262 pool[i]=new PooledObjectWrapper(factory.create());
263 if (! cls.isAssignableFrom(pool[i].object.getClass()))
264 throw new ObjectPoolException(
265 "The provided factory "+factory+" must create only objects of type "+
266 cls.getName()+". The produced object has type "+
267 pool[i].object.getClass().getName()+" instead");
268 free.add(pool[i].object);
269 }
270 if (verbose) System.out.println("Object pool created");
271 }
272
273 /**
274 * Create a pool of <i>n</i> object of the given class (by name)
275 * using the given construction parameters.
276 * <p>
277 * If some post-construction setup is needed, it can
278 * be provided as an Setup object.
279 *
280 * @param n the size of the pool
281 * @param clsName the name of the class of the objects to pool
282 * @param params the construction parameters, or <b>null</b>
283 * @param the post-construction setup object, or <b>null</b>
284 * @exception ClassNotFoundException if the given class name cannot be resolved
285 */
286 public ObjectPool(int n, String clsName, Object [] params, Setup ps) throws ClassNotFoundException {
287 this(n, new ObjectFactory(Class.forName(clsName), params, ps));
288 }
289
290 /**
291 * Create a pool of <i>n</i> object of the given class (by name)
292 * using the given construction parameters.
293 * <p>
294 * @param n the size of the pool
295 * @param clsName the name of the class of the objects to pool
296 * @param params the construction parameters, or <b>null</b>
297 * @exception ClassNotFoundException if the given class name cannot be resolved
298 */
299 public ObjectPool(int n, String clsName, Object [] params) throws ClassNotFoundException {
300 this(n,Class.forName(clsName),params,null);
301 }
302
303 /**
304 * Create a pool of <i>n</i> object of the given class (by name)
305 * using the default constructor.
306 * @param n the size of the pool
307 * @param clsName the name of the class of the objects to pool
308 * @exception ClassNotFoundException if the given class name cannot be resolved
309 */
310 public ObjectPool(int n, String clsName) throws ClassNotFoundException {
311 this(n, Class.forName(clsName), null);
312 }
313
314 /**
315 * Create a pool of <i>n</i> object of the given class
316 * using the given construction parameters.
317 * <p>
318 * If some post-construction setup is needed, it can
319 * be provided as an Setup object.
320 *
321 * @param n the size of the pool
322 * @param cls the class of the objects to pool
323 * @param params the construction parameters, or <b>null</b>
324 * @param the post-construction setup object, or <b>null</b>
325 */
326 public ObjectPool(int n, Class cls, Object [] params, Setup ps) {
327 this(n, new ObjectFactory(cls, params, ps));
328 }
329
330 /**
331 * Create a pool of <i>n</i> object of the given class
332 * using the given construction parameters.
333 * <p>
334 * @param n the size of the pool
335 * @param cls the class of the objects to pool
336 * @param params the construction parameters, or <b>null</b>
337 */
338 public ObjectPool(int n, Class cls, Object [] params) {
339 this(n,cls,params,null);
340 }
341
342 /**
343 * Create a pool of <i>n</i> object of the given class
344 * using the default constructor.
345 * @param n the size of the pool
346 * @param cls the class of the objects to pool
347 */
348 public ObjectPool(int n, Class cls) {
349 this(n, cls, null);
350 }
351
352 /**
353 * Return the number of available objects in the pool
354 * @return the number of available objects in the pool
355 */
356 public synchronized int getFreeCount() { return free.size(); }
357
358 /**
359 * Return the number of used objects in the pool
360 * @return the number of used objects in the pool
361 */
362 public synchronized int getUsedCount() { return used.size(); }
363
364 /**
365 * Return the size of the pool
366 * @return the size of the pool
367 */
368 public synchronized int getSize() { return free.size()+used.size(); }
369
370 /**
371 * Attempt to acquire an object.
372 * @param waitIfUnavailable if <b>true</b>, in case all the pooled objects
373 * are used, the call will block until an object is released.
374 * If <b>false</b>, in the same condition the method returns <b>null</b>.
375 * @return a pooled object
376 */
377 public synchronized Object acquire(boolean waitIfUnavailable) {
378 if (free.isEmpty())
379 if (waitIfUnavailable) {
380 try {
381 wait(0);
382 } catch(InterruptedException e) {
383 return null;
384 }
385 } else return null;
386 // The object may have been released because has been renewed,
387 // and taken out of the pool; in this case, we just have to
388 // re-perform the emptyness check by recursively calling acquire()
389 if (free.isEmpty()) return acquire(waitIfUnavailable);
390 Object obj=free.iterator().next();
391 acquire0(obj);
392 return obj;
393 }
394
395 private void acquire0(Object obj) {
396 free.remove(obj);
397 used.add(obj);
398 }
399
400 /**
401 * Attempt to acquire an object.
402 * <p>
403 * If there aren't any objects available, this method blocks until
404 * one becomes available.
405 * @return a pooled object
406 */
407 public Object acquire() {
408 return acquire(true);
409 }
410
411 /**
412 * Attempt to acquire the i-th object.
413 * <p>
414 * If the object is not available, this method blocks until
415 * the object becomes available.
416 * @return a pooled object
417 */
418 public synchronized Object acquire(int i) {
419 PooledObjectWrapper pObj = pool[i];
420 if (used.contains(pObj.object)) {
421 try {
422 pObj.wait();
423 } catch(InterruptedException e) {
424 return null;
425 }
426 }
427 acquire0(pObj.object);
428 return pObj.object;
429 }
430
431 /**
432 * Releases the i-th object, notifying the waiting thread (if any) that
433 * one pooled object has become available.
434 * @param the pooled object to release
435 */
436 public synchronized void release(int i) {
437 PooledObjectWrapper pObj = pool[i];
438 release0(pObj.object);
439 pObj.notify();
440 }
441
442 /**
443 * Release an object.
444 * <p>
445 * @param releaseWaitingCalls if <b>true</b>, the method notifies waiting objects that
446 * one pooled object has become available.
447 * @param the pooled object to release
448 */
449 public synchronized void release(Object obj, boolean releaseWaitingCalls) {
450 if (!used.contains(obj))
451 throw new IllegalArgumentException("The object "+obj+" is not a pooled object or has been renewed");
452 release0(obj);
453 if (releaseWaitingCalls) notify();
454 }
455
456 private void release0(Object obj) {
457 used.remove(obj);
458 free.add(obj);
459 }
460
461 /**
462 * Release an object, notifying waiting thread (if any) that
463 * one pooled object has become available.
464 * @param the pooled object to release
465 */
466 public void release(Object obj) {
467 release(obj, true);
468 }
469
470
471
472 /**
473 * Renew one object in the pool.
474 * <p>
475 * A new instance is created substituting the passed object in
476 * the pool, and the new instance is returned. The object is released()
477 * but any thread waiting on that object shifts waiting for another object.
478 */
479 public synchronized Object renew(Object obj) {
480 // Create a new instance
481 Object obj2 = factory.create();
482
483 // Find the object in the pool
484 int c=-1;
485 for(int i=0;i<pool.length;i++)
486 if (pool[i].object==obj) { c=i; break; }
487 if (c==-1) {
488 if (verbose) System.out.println(this);
489 throw new IllegalArgumentException("The object <"+obj+"> is not a pooled object");
490 }
491
492 // Replace the object
493 if (used.contains(obj)) {
494 synchronized(obj) {
495 release(obj);
496 free.remove(obj);
497 used.add(obj2);
498 if (verbose) System.out.println("Used object "+obj+" renewed by "+obj2);
499 }
500 } else if (free.contains(obj)) {
501 synchronized(obj) {
502 free.remove(obj);
503 free.add(obj2);
504 if (verbose) System.out.println("Free object "+obj+" renewed by "+obj2);
505 }
506 }
507 pool[c]=new PooledObjectWrapper(obj2);
508 return obj2;
509 }
510
511
512 /**
513 * Return a string description of the pool
514 * @return a string description of the pool
515 */
516 public synchronized String toString() {
517 StringWriter sw = new StringWriter();
518 PrintWriter pw = new PrintWriter(sw);
519 pw.println("Pool of "+pool.length+" objects of type "+factory.getProducedClass().getName());
520 for(Iterator i=used.iterator();i.hasNext();) {
521 pw.println("[Used] <"+i.next()+">");
522 }
523 for(Iterator i=free.iterator();i.hasNext();) {
524 pw.println("[Free] <"+i.next()+">");
525 }
526 return sw.toString();
527 }
528
529 /**
530 * A convenience method to create a new pool of objects of a given class, by name,
531 * using their default constructor.
532 * @param poolSize the size of the pool
533 * @param clsName the name of the class of the pooled objects
534 * @exception ObjectPoolException if the pool cannot be initialized
535 */
536 public static ObjectPool newPool(int poolSize, String clsName) throws ObjectPoolException {
537 return new ObjectPool(poolSize, new ObjectFactory(clsName, null, null));
538 }
539
540 /**
541 * A convenience method to create a new pool of objects of a given class, by name,
542 * using the given parameters.
543 * @param poolSize the size of the pool
544 * @param clsName the name of the class of the pooled objects
545 * @param params the parameters used to initialize the objects of the pool
546 * @exception ObjectPoolException if the pool cannot be initialized
547 */
548 public static ObjectPool newPool(int poolSize, String clsName, Object [] params) throws ObjectPoolException {
549 return new ObjectPool(poolSize, new ObjectFactory(clsName, params, null));
550 }
551
552
553 /**
554 * Return the object type pooled by this pool
555 */
556 public Class getObjectType() { return factory.getProducedClass(); }
557
558 /**
559 * Returns the factory.
560 * @return Factory
561 */
562 protected Factory getFactory() {
563 return factory;
564 }
565
566 /**
567 * Sets the factory.
568 * @param factory The factory to set
569 */
570 protected void setFactory(Factory factory) {
571 this.factory = factory;
572 }
573
574
575 /*
576 public static void main(String args[]) throws Exception {
577 final ExtendedObjectPool pool = new ExtendedObjectPool(2, String.class, new Object [] { "Hello world" });
578 final java.util.Random random = new java.util.Random();
579 final Set acquired = new HashSet();
580
581 Thread t1 = new Thread("acquirer") {
582 public void run() {
583 while(true) {
584 if (random.nextInt(3) < 2) {
585 System.out.println("Aquiring - "+pool.getFreeCount()+" free objects in pool");
586 acquired.add(pool.acquire());
587 } else {
588 System.out.println("Consumer sleeping");
589 try {
590 sleep(1000);
591 } catch(InterruptedException e) {
592 }
593 System.out.println("Consumer awakening");
594 }
595 }
596 }
597 };
598
599 Thread t2 = new Thread("remover") {
600 public void run() {
601 while(true) {
602 if (random.nextInt(3) < 1)
603 if (! acquired.isEmpty()) {
604 Object obj = acquired.iterator().next();
605 acquired.remove(obj);
606 pool.release(obj);
607 System.out.println("Removed - "+pool.getFreeCount()+" free objects in pool");
608 }
609 }
610 }
611 };
612
613 t2.start();
614 t1.start();
615 }
616
617 */
618
619 }