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 473821 2006-11-11 22:37:25Z 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.remove();
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 * Remove an object from the cache
229 *
230 * @param key the cache key for the object
231 * @return the object one last time
232 * @throws TorqueException Any exceptions caught during processing will be
233 * rethrown wrapped into a TorqueException.
234 */
235 protected Persistent removeInstanceImpl(Serializable key)
236 throws TorqueException
237 {
238 Persistent oldOm = null;
239 if (cache != null)
240 {
241 try
242 {
243 synchronized (this)
244 {
245 oldOm = (Persistent) cache.get(key);
246 cache.remove(key);
247 }
248 }
249 catch (CacheException ce)
250 {
251 throw new TorqueException
252 ("Could not remove from cache due to internal JCS error",
253 ce);
254 }
255 }
256 return oldOm;
257 }
258
259 /***
260 * Put an object into the cache
261 *
262 * @param om the object
263 * @return if an object with the same key already is in the cache
264 * this object will be returned, else null
265 * @throws TorqueException Any exceptions caught during processing will be
266 * rethrown wrapped into a TorqueException.
267 */
268 protected Persistent putInstanceImpl(Persistent om)
269 throws TorqueException
270 {
271 ObjectKey key = om.getPrimaryKey();
272 return putInstanceImpl(key, om);
273 }
274
275 /***
276 * Put an object into the cache
277 *
278 * @param key the cache key for the object
279 * @param om the object
280 * @return if an object with this key already is in the cache
281 * this object will be returned, else null
282 * @throws TorqueException Any exceptions caught during processing will be
283 * rethrown wrapped into a TorqueException.
284 */
285 protected Persistent putInstanceImpl(Serializable key, Persistent om)
286 throws TorqueException
287 {
288 if (getOMClass() != null && !getOMClass().isInstance(om))
289 {
290 throw new TorqueException(om + "; class=" + om.getClass().getName()
291 + "; id=" + om.getPrimaryKey() + " cannot be cached with "
292 + getOMClass().getName() + " objects");
293 }
294
295 Persistent oldOm = null;
296 if (cache != null)
297 {
298 try
299 {
300 synchronized (this)
301 {
302 oldOm = (Persistent) cache.get(key);
303 cache.put(key, om);
304 }
305 }
306 catch (CacheException ce)
307 {
308 throw new TorqueException
309 ("Could not cache due to internal JCS error", ce);
310 }
311 }
312 return oldOm;
313 }
314
315 /***
316 * Retrieve an object from persistent storage
317 *
318 * @param id the primary key of the object
319 * @return the object
320 * @throws TorqueException Any exceptions caught during processing will be
321 * rethrown wrapped into a TorqueException.
322 */
323 protected abstract Persistent retrieveStoredOM(ObjectKey id)
324 throws TorqueException;
325
326 /***
327 * Gets a list of om's based on id's.
328 *
329 * @param ids a <code>ObjectKey[]</code> value
330 * @return a <code>List</code> value
331 * @throws TorqueException Any exceptions caught during processing will be
332 * rethrown wrapped into a TorqueException.
333 */
334 protected List getOMs(ObjectKey[] ids)
335 throws TorqueException
336 {
337 return getOMs(Arrays.asList(ids));
338 }
339
340 /***
341 * Gets a list of om's based on id's.
342 *
343 * @param ids a <code>List</code> of <code>ObjectKey</code>'s
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(List ids)
349 throws TorqueException
350 {
351 return getOMs(ids, true);
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, boolean fromCache)
363 throws TorqueException
364 {
365 List oms = null;
366 if (ids != null && ids.size() > 0)
367 {
368
369 oms = new ArrayList(ids);
370 List newIds = new ArrayList(ids.size());
371 for (int i = 0; i < ids.size(); i++)
372 {
373 ObjectKey key = (ObjectKey) ids.get(i);
374 Persistent om = null;
375 if (fromCache)
376 {
377 om = cacheGet(key);
378 }
379 if (om == null)
380 {
381 newIds.add(key);
382 }
383 else
384 {
385 oms.set(i, om);
386 }
387 }
388
389 if (newIds.size() > 0)
390 {
391 List newOms = retrieveStoredOMs(newIds);
392 for (int i = 0; i < oms.size(); i++)
393 {
394 if (oms.get(i) instanceof ObjectKey)
395 {
396 for (int j = newOms.size() - 1; j >= 0; j--)
397 {
398 Persistent om = (Persistent) newOms.get(j);
399 if (om.getPrimaryKey().equals(oms.get(i)))
400 {
401
402
403 oms.set(i, om);
404 newOms.remove(j);
405 if (fromCache)
406 {
407 putInstanceImpl(om);
408 }
409 break;
410 }
411 }
412 }
413 }
414 }
415 }
416 return oms;
417 }
418
419 /***
420 * Gets a list of om's based on id's.
421 * This method must be implemented in the drived class
422 *
423 * @param ids a <code>List</code> of <code>ObjectKey</code>'s
424 * @return a <code>List</code> value
425 * @throws TorqueException Any exceptions caught during processing will be
426 * rethrown wrapped into a TorqueException.
427 */
428 protected abstract List retrieveStoredOMs(List ids)
429 throws TorqueException;
430
431 /***
432 * Get the value of region.
433 *
434 * @return value of region.
435 */
436 public String getRegion()
437 {
438 return region;
439 }
440
441 /***
442 * Set the value of region.
443 *
444 * @param v Value to assign to region.
445 * @throws TorqueException Any exceptions caught during processing will be
446 * rethrown wrapped into a TorqueException.
447 */
448 public void setRegion(String v)
449 throws TorqueException
450 {
451 this.region = v;
452 try
453 {
454 if (Torque.getConfiguration().getBoolean(Torque.CACHE_KEY, false))
455 {
456 cache = JCS.getInstance(getRegion());
457 mrCache = new MethodResultCache(cache);
458 }
459 else
460 {
461 mrCache = new NoOpMethodResultCache(cache);
462 }
463 }
464 catch (CacheException e)
465 {
466 throw new TorqueException("Cache could not be initialized", e);
467 }
468
469 if (cache == null)
470 {
471 log.info("Cache could not be initialized for region: " + v);
472 }
473 }
474
475 /***
476 * @return The cache instance.
477 */
478 public MethodResultCache getMethodResultCache()
479 {
480 if (isNew)
481 {
482 synchronized (this)
483 {
484 if (isNew)
485 {
486 registerAsListener();
487 isNew = false;
488 }
489 }
490 }
491 return mrCache;
492 }
493
494 /***
495 * NoOp version. Managers should override this method to notify other
496 * managers that they are interested in CacheEvents.
497 */
498 protected void registerAsListener()
499 {
500 }
501
502 /***
503 *
504 * @param listener A new listener for cache events.
505 */
506 public void addCacheListenerImpl(CacheListener listener)
507 {
508 List keys = listener.getInterestedFields();
509 Iterator i = keys.iterator();
510 while (i.hasNext())
511 {
512 String key = (String) i.next();
513
514 if (validFields != null && validFields.containsKey(key))
515 {
516 List listeners = (List) listenersMap.get(key);
517 if (listeners == null)
518 {
519 listeners = createSubsetList(key);
520 }
521
522 boolean isNew = true;
523 Iterator j = listeners.iterator();
524 while (j.hasNext())
525 {
526 Object listener2 =
527 ((WeakReference) j.next()).get();
528 if (listener2 == null)
529 {
530
531
532
533
534 }
535 else if (listener2 == listener)
536 {
537 isNew = false;
538 break;
539 }
540 }
541 if (isNew)
542 {
543 listeners.add(new WeakReference(listener));
544 }
545 }
546 }
547 }
548
549 /***
550 *
551 * @param key
552 * @return A subset of the list identified by <code>key</code>.
553 */
554 private synchronized List createSubsetList(String key)
555 {
556 FastArrayList list = null;
557 if (listenersMap.containsKey(key))
558 {
559 list = (FastArrayList) listenersMap.get(key);
560 }
561 else
562 {
563 list = new FastArrayList();
564 list.setFast(true);
565 listenersMap.put(key, list);
566 }
567 return list;
568 }
569
570 /***
571 *
572 * @param listeners
573 * @param oldOm
574 * @param om
575 */
576 protected void notifyListeners(List listeners,
577 Persistent oldOm, Persistent om)
578 {
579 if (listeners != null)
580 {
581 synchronized (listeners)
582 {
583 Iterator i = listeners.iterator();
584 while (i.hasNext())
585 {
586 CacheListener listener = (CacheListener)
587 ((WeakReference) i.next()).get();
588 if (listener == null)
589 {
590
591 i.remove();
592 }
593 else
594 {
595 if (oldOm == null)
596 {
597
598 listener.addedObject(om);
599 }
600 else
601 {
602
603 listener.refreshedObject(om);
604 }
605 }
606 }
607 }
608 }
609 }
610
611
612 /***
613 * helper methods for the Serializable interface
614 *
615 * @param out
616 * @throws IOException
617 */
618 private void writeObject(java.io.ObjectOutputStream out)
619 throws IOException
620 {
621 out.defaultWriteObject();
622 }
623
624 /***
625 * Helper methods for the <code>Serializable</code> interface.
626 *
627 * @param in The stream to read a <code>Serializable</code> from.
628 * @throws IOException
629 * @throws ClassNotFoundException
630 */
631 private void readObject(ObjectInputStream in)
632 throws IOException, ClassNotFoundException
633 {
634 in.defaultReadObject();
635
636 try
637 {
638 if (region != null)
639 {
640 setRegion(region);
641 }
642 }
643 catch (Exception e)
644 {
645 log.error("Cache could not be initialized for region '"
646 + region + "' after deserialization");
647 }
648 }
649 }