View Javadoc

1   package org.apache.torque.manager;
2   
3   /* ====================================================================
4    * The Apache Software License, Version 1.1
5    *
6    * Copyright (c) 2001-2003 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.ref.WeakReference;
58  import java.util.Arrays;
59  import java.util.List;
60  import java.util.ArrayList;
61  import java.util.Map;
62  import java.util.HashMap;
63  import java.util.Iterator;
64  import java.io.Serializable;
65  import java.io.IOException;
66  import java.io.ObjectInputStream;
67  
68  import org.apache.commons.collections.FastArrayList;
69  import org.apache.jcs.JCS;
70  import org.apache.jcs.access.GroupCacheAccess;
71  import org.apache.jcs.access.exception.CacheException;
72  
73  import org.apache.torque.Torque;
74  import org.apache.torque.TorqueException;
75  import org.apache.torque.om.ObjectKey;
76  import org.apache.torque.om.Persistent;
77  
78  import org.apache.commons.logging.Log;
79  import org.apache.commons.logging.LogFactory;
80  
81  /***
82   * This class contains common functionality of a Manager for
83   * instantiating OM's.
84   *
85   * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
86   * @version $Id: AbstractBaseManager.java,v 1.16 2003/06/20 08:13:47 dlr Exp $
87   */
88  public abstract class AbstractBaseManager
89      implements Serializable
90  {
91      /*** the log */
92      protected static Log log = LogFactory.getLog(AbstractBaseManager.class);
93  
94      /*** used to cache the om objects. cache is set by the region property */
95      protected transient GroupCacheAccess cache;
96  
97      /*** method results cache */
98      protected MethodResultCache mrCache;
99  
100     /*** the class that the service will instantiate */
101     private Class omClass;
102 
103     private String className;
104 
105     private String region;
106 
107     private boolean isNew = true;
108 
109     protected Map validFields;
110     protected Map listenersMap = new HashMap();
111 
112     /***
113      * Get the Class instance
114      *
115      * @return the om class
116      */
117     protected Class getOMClass()
118     {
119         return omClass;
120     }
121 
122     /***
123      * Set the Class that will be instantiated by this manager
124      *
125      * @param omClass the om class
126      */
127     protected void setOMClass(Class omClass)
128     {
129         this.omClass = omClass;
130     }
131 
132     /***
133      * Get a fresh instance of an om
134      *
135      * @return an instance of the om class
136      * @throws InstantiationException
137      * @throws IllegalAccessException
138      */
139     protected Persistent getOMInstance()
140         throws InstantiationException, IllegalAccessException
141     {
142         return (Persistent) omClass.newInstance();
143     }
144 
145     /***
146      * Get the classname to instantiate for getInstance()
147      * @return value of className.
148      */
149     public String getClassName()
150     {
151         return className;
152     }
153 
154     /***
155      * Set the classname to instantiate for getInstance()
156      * @param v  Value to assign to className.
157      * @throws TorqueException Any exceptions caught during processing will be
158      *         rethrown wrapped into a TorqueException.
159      */
160     public void setClassName(String  v)
161         throws TorqueException
162     {
163         this.className = v;
164 
165         try
166         {
167             setOMClass(Class.forName(getClassName()));
168         }
169         catch (ClassNotFoundException cnfe)
170         {
171             throw new TorqueException("Could not load " + getClassName());
172         }
173     }
174 
175 
176     /***
177      * Return an instance of an om based on the id
178      *
179      * @param id
180      * @throws TorqueException Any exceptions caught during processing will be
181      *         rethrown wrapped into a TorqueException.
182      */
183     protected Persistent getOMInstance(ObjectKey id)
184         throws TorqueException
185     {
186         return getOMInstance(id, true);
187     }
188 
189     /***
190      * Return an instance of an om based on the id
191      *
192      * @throws TorqueException Any exceptions caught during processing will be
193      *         rethrown wrapped into a TorqueException.
194      */
195     protected Persistent getOMInstance(ObjectKey key, boolean fromCache)
196         throws TorqueException
197     {
198         Persistent om = null;
199         if (fromCache)
200         {
201             om = cacheGet(key);
202         }
203 
204         if (om == null)
205         {
206             om = retrieveStoredOM(key);
207             if (fromCache)
208             {
209                 putInstanceImpl(om);
210             }
211         }
212 
213         return om;
214     }
215 
216     protected Persistent cacheGet(Serializable key)
217     {
218         Persistent om = null;
219         if (cache != null)
220         {
221             synchronized (this)
222             {
223                 om = (Persistent) cache.get(key);
224             }
225         }
226         return om;
227     }
228 
229     /***
230      * 
231      * @throws TorqueException Any exceptions caught during processing will be
232      *         rethrown wrapped into a TorqueException.
233      */
234     protected void clearImpl()
235         throws TorqueException
236     {
237         if (cache != null)
238         {
239             try
240             {
241                 cache.remove();
242             }
243             catch (CacheException ce)
244             {
245                 throw new TorqueException(
246                         "Could not clear cache due to internal JCS error.", ce);
247             }
248         }
249     }
250 
251     /***
252      * 
253      * @param key
254      * @return
255      * @throws TorqueException Any exceptions caught during processing will be
256      *         rethrown wrapped into a TorqueException.
257      */
258     protected Persistent removeInstanceImpl(Serializable key)
259         throws TorqueException
260     {
261         Persistent oldOm = null;
262         if (cache != null)
263         {
264             try
265             {
266                 synchronized (this)
267                 {
268                     oldOm = (Persistent) cache.get(key);
269                     cache.remove(key);
270                 }
271             }
272             catch (CacheException ce)
273             {
274                 throw new TorqueException
275                     ("Could not remove from cache due to internal JCS error",
276                      ce);
277             }
278         }
279         return oldOm;
280     }
281 
282     /***
283      * 
284      * @param om
285      * @return
286      * @throws TorqueException Any exceptions caught during processing will be
287      *         rethrown wrapped into a TorqueException.
288      */
289     protected Persistent putInstanceImpl(Persistent om)
290         throws TorqueException
291     {
292         ObjectKey key = om.getPrimaryKey();
293         return putInstanceImpl(key, om);
294     }
295 
296     /***
297      * 
298      * @param key
299      * @param om
300      * @return
301      * @throws TorqueException Any exceptions caught during processing will be
302      *         rethrown wrapped into a TorqueException.
303      */
304     protected Persistent putInstanceImpl(Serializable key, Persistent om)
305         throws TorqueException
306     {
307         if (getOMClass() != null && !getOMClass().isInstance(om))
308         {
309             throw new TorqueException(om + "; class=" + om.getClass().getName()
310                 + "; id=" + om.getPrimaryKey() + " cannot be cached with "
311                 + getOMClass().getName() + " objects");
312         }
313 
314         Persistent oldOm = null;
315         if (cache != null)
316         {
317             try
318             {
319                 synchronized (this)
320                 {
321                     oldOm = (Persistent) cache.get(key);
322                     cache.put(key, om);
323                 }
324             }
325             catch (CacheException ce)
326             {
327                 throw new TorqueException
328                     ("Could not cache due to internal JCS error", ce);
329             }
330         }
331         return oldOm;
332     }
333 
334     /***
335      * 
336      * @param id
337      * @return
338      * @throws TorqueException Any exceptions caught during processing will be
339      *         rethrown wrapped into a TorqueException.
340      */
341     protected abstract Persistent retrieveStoredOM(ObjectKey id)
342         throws TorqueException;
343 
344     /***
345      * Gets a list of om's based on id's.
346      *
347      * @param ids a <code>ObjectKey[]</code> value
348      * @return a <code>List</code> value
349      * @throws TorqueException Any exceptions caught during processing will be
350      *         rethrown wrapped into a TorqueException.
351      */
352     protected List getOMs(ObjectKey[] ids)
353         throws TorqueException
354     {
355         return getOMs(Arrays.asList(ids));
356     }
357 
358     /***
359      * Gets a list of om's based on id's.
360      *
361      * @param ids a <code>List</code> of <code>ObjectKey</code>'s
362      * @return a <code>List</code> value
363      * @throws TorqueException Any exceptions caught during processing will be
364      *         rethrown wrapped into a TorqueException.
365      */
366     protected List getOMs(List ids)
367         throws TorqueException
368     {
369         return getOMs(ids, true);
370     }
371 
372     /***
373      * Gets a list of om's based on id's.
374      *
375      * @param ids a <code>List</code> of <code>ObjectKey</code>'s
376      * @return a <code>List</code> value
377      * @throws TorqueException Any exceptions caught during processing will be
378      *         rethrown wrapped into a TorqueException.
379      */
380     protected List getOMs(List ids, boolean fromCache)
381         throws TorqueException
382     {
383         List oms = null;
384         if (ids != null && ids.size() > 0)
385         {
386             // start a new list where we will replace the id's with om's
387             oms = new ArrayList(ids);
388             List newIds = new ArrayList(ids.size());
389             for (int i = 0; i < ids.size(); i++)
390             {
391                 ObjectKey key = (ObjectKey) ids.get(i);
392                 Persistent om = null;
393                 if (fromCache)
394                 {
395                     om = cacheGet(key);
396                 }
397                 if (om == null)
398                 {
399                     newIds.add(key);
400                 }
401                 else
402                 {
403                     oms.set(i, om);
404                 }
405             }
406 
407             if (newIds.size() > 0)
408             {
409                 List newOms = retrieveStoredOMs(newIds);
410                 for (int i = 0; i < oms.size(); i++)
411                 {
412                     if (oms.get(i) instanceof ObjectKey)
413                     {
414                         for (int j = newOms.size() - 1; j >= 0; j--)
415                         {
416                             Persistent om = (Persistent) newOms.get(j);
417                             if (om.getPrimaryKey().equals(oms.get(i)))
418                             {
419                                 // replace the id with the om and add the om
420                                 // to the cache
421                                 oms.set(i, om);
422                                 newOms.remove(j);
423                                 if (fromCache)
424                                 {
425                                     putInstanceImpl(om);
426                                 }
427                                 break;
428                             }
429                         }
430                     }
431                 }
432             }
433         }
434         return oms;
435     }
436 
437     /***
438      * 
439      * @param ids
440      * @return
441      * @throws TorqueException Any exceptions caught during processing will be
442      *         rethrown wrapped into a TorqueException.
443      */
444     protected abstract List retrieveStoredOMs(List ids)
445         throws TorqueException;
446 
447     /***
448      * Get the value of region.
449      * 
450      * @return value of region.
451      */
452     public String getRegion()
453     {
454         return region;
455     }
456 
457     /***
458      * Set the value of region.
459      *
460      * @param v  Value to assign to region.
461      * @throws TorqueException Any exceptions caught during processing will be
462      *         rethrown wrapped into a TorqueException.
463      */
464     public void setRegion(String v)
465         throws TorqueException
466     {
467         this.region = v;
468         try
469         {
470             if (Torque.getConfiguration().getBoolean(Torque.CACHE_KEY))
471             {
472                 cache = JCS.getInstance(getRegion());
473                 mrCache = new MethodResultCache(cache);
474             }
475             else
476             {
477                 mrCache = new NoOpMethodResultCache(cache);
478             }
479         }
480         catch (Exception e)
481         {
482             throw new TorqueException("Cache could not be initialized", e);
483         }
484         if (cache == null)
485         {
486             log.info("Cache could not be initialized for region: " + v);
487         }
488     }
489 
490     /***
491      * @return The cache instance.
492      */
493     public MethodResultCache getMethodResultCache()
494     {
495         if (isNew)
496         {
497             synchronized (this)
498             {
499                 if (isNew)
500                 {
501                     registerAsListener();
502                     isNew = false;
503                 }
504             }
505         }
506         return mrCache;
507     }
508 
509     /***
510      * NoOp version.  Managers should override this method to notify other
511      * managers that they are interested in CacheEvents.
512      */
513     protected void registerAsListener()
514     {
515     }
516 
517     /***
518      * 
519      * @param listener A new listener for cache events.
520      */
521     public void addCacheListenerImpl(CacheListener listener)
522     {
523         List keys = listener.getInterestedFields();
524         Iterator i = keys.iterator();
525         while (i.hasNext())
526         {
527             String key = (String) i.next();
528             // Peer.column names are the fields
529             if (validFields != null && validFields.containsKey(key))
530             {
531                 List listeners = (List) listenersMap.get(key);
532                 if (listeners == null)
533                 {
534                     listeners = createSubsetList(key);
535                 }
536 
537                 boolean isNew = true;
538                 Iterator j = listeners.iterator();
539                 while (j.hasNext())
540                 {
541                     Object listener2 =
542                         ((WeakReference) j.next()).get();
543                     if (listener2 == null)
544                     {
545                         // do a little cleanup while checking for dupes
546                         // not thread-safe, not likely to be many nulls
547                         // but should revisit
548                         //j.remove();
549                     }
550                     else if (listener2 == listener)
551                     {
552                         isNew = false;
553                         break;
554                     }
555                 }
556                 if (isNew)
557                 {
558                     listeners.add(new WeakReference(listener));
559                 }
560             }
561         }
562     }
563 
564     /***
565      * 
566      * @param key
567      * @return A subset of the list identified by <code>key</code>.
568      */
569     private synchronized List createSubsetList(String key)
570     {
571         FastArrayList list = null;
572         if (listenersMap.containsKey(key))
573         {
574             list = (FastArrayList) listenersMap.get(key);
575         }
576         else
577         {
578             list = new FastArrayList();
579             list.setFast(true);
580             listenersMap.put(key, list);
581         }
582         return list;
583     }
584 
585     /***
586      * 
587      * @param listeners
588      * @param oldOm
589      * @param om
590      */
591     protected void notifyListeners(List listeners,
592                                    Persistent oldOm, Persistent om)
593     {
594         if (listeners != null)
595         {
596             synchronized (listeners)
597             {
598                 Iterator i = listeners.iterator();
599                 while (i.hasNext())
600                 {
601                     CacheListener listener = (CacheListener)
602                         ((WeakReference) i.next()).get();
603                     if (listener == null)
604                     {
605                         // remove reference as its object was cleared
606                         i.remove();
607                     }
608                     else
609                     {
610                         if (oldOm == null)
611                         {
612                             // object was added
613                             listener.addedObject(om);
614                         }
615                         else
616                         {
617                             // object was refreshed
618                             listener.refreshedObject(om);
619                         }
620                     }
621                 }
622             }
623         }
624     }
625 
626 
627     /***
628      * helper methods for the Serializable interface
629      * 
630      * @param out
631      * @throws IOException
632      */
633     private void writeObject(java.io.ObjectOutputStream out)
634         throws IOException
635     {
636         out.defaultWriteObject();
637     }
638 
639     /***
640      * Helper methods for the <code>Serializable</code> interface.
641      * 
642      * @param in The stream to read a <code>Serializable</code> from.
643      * @throws IOException
644      * @throws ClassNotFoundException
645      */
646     private void readObject(ObjectInputStream in)
647         throws IOException, ClassNotFoundException
648     {
649         in.defaultReadObject();
650         // initialize the cache
651         try
652         {
653             if (region != null)
654             {
655                 setRegion(region);
656             }
657         }
658         catch (Exception e)
659         {
660             log.error("Cache could not be initialized for region '"
661                       + region + "' after deserialization");
662         }
663     }
664 }