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