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