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 }
This page was automatically generated by Maven