/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  }