%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.torque.manager.AbstractBaseManager |
|
|
1 | package org.apache.torque.manager; |
|
2 | ||
3 | /* |
|
4 | * Licensed to the Apache Software Foundation (ASF) under one |
|
5 | * or more contributor license agreements. See the NOTICE file |
|
6 | * distributed with this work for additional information |
|
7 | * regarding copyright ownership. The ASF licenses this file |
|
8 | * to you under the Apache License, Version 2.0 (the |
|
9 | * "License"); you may not use this file except in compliance |
|
10 | * with the License. You may obtain a copy of the License at |
|
11 | * |
|
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
13 | * |
|
14 | * Unless required by applicable law or agreed to in writing, |
|
15 | * software distributed under the License is distributed on an |
|
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
|
17 | * KIND, either express or implied. See the License for the |
|
18 | * specific language governing permissions and limitations |
|
19 | * under the License. |
|
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 | 0 | public abstract class AbstractBaseManager |
54 | implements Serializable |
|
55 | { |
|
56 | /** the log */ |
|
57 | 0 | 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 | 0 | private boolean isNew = true; |
73 | ||
74 | protected Map validFields; |
|
75 | 0 | 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 | 0 | 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 | 0 | this.omClass = omClass; |
95 | 0 | } |
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 | 0 | 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 | 0 | 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 | 0 | this.className = v; |
129 | ||
130 | try |
|
131 | { |
|
132 | 0 | setOMClass(Class.forName(getClassName())); |
133 | } |
|
134 | 0 | catch (ClassNotFoundException cnfe) |
135 | { |
|
136 | 0 | throw new TorqueException("Could not load " + getClassName()); |
137 | 0 | } |
138 | 0 | } |
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 | 0 | 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 | 0 | Persistent om = null; |
168 | 0 | if (fromCache) |
169 | { |
|
170 | 0 | om = cacheGet(key); |
171 | } |
|
172 | ||
173 | 0 | if (om == null) |
174 | { |
|
175 | 0 | om = retrieveStoredOM(key); |
176 | 0 | if (fromCache) |
177 | { |
|
178 | 0 | putInstanceImpl(om); |
179 | } |
|
180 | } |
|
181 | ||
182 | 0 | 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 | 0 | Persistent om = null; |
194 | 0 | if (cache != null) |
195 | { |
|
196 | 0 | synchronized (this) |
197 | { |
|
198 | 0 | om = (Persistent) cache.get(key); |
199 | 0 | } |
200 | } |
|
201 | 0 | 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 | 0 | if (cache != null) |
214 | { |
|
215 | try |
|
216 | { |
|
217 | 0 | cache.clear(); |
218 | } |
|
219 | 0 | catch (CacheException ce) |
220 | { |
|
221 | 0 | throw new TorqueException( |
222 | "Could not clear cache due to internal JCS error.", ce); |
|
223 | 0 | } |
224 | } |
|
225 | 0 | } |
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 | 0 | if (cache != null) |
235 | { |
|
236 | 0 | cache.dispose(); |
237 | 0 | cache = null; |
238 | } |
|
239 | 0 | } |
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 | 0 | Persistent oldOm = null; |
253 | 0 | if (cache != null) |
254 | { |
|
255 | try |
|
256 | { |
|
257 | 0 | synchronized (this) |
258 | { |
|
259 | 0 | oldOm = (Persistent) cache.get(key); |
260 | 0 | cache.remove(key); |
261 | 0 | } |
262 | } |
|
263 | 0 | catch (CacheException ce) |
264 | { |
|
265 | 0 | throw new TorqueException |
266 | ("Could not remove from cache due to internal JCS error", |
|
267 | ce); |
|
268 | 0 | } |
269 | } |
|
270 | 0 | 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 | 0 | ObjectKey key = om.getPrimaryKey(); |
286 | 0 | 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 | 0 | if (getOMClass() != null && !getOMClass().isInstance(om)) |
303 | { |
|
304 | 0 | throw new TorqueException(om + "; class=" + om.getClass().getName() |
305 | + "; id=" + om.getPrimaryKey() + " cannot be cached with " |
|
306 | + getOMClass().getName() + " objects"); |
|
307 | } |
|
308 | ||
309 | 0 | Persistent oldOm = null; |
310 | 0 | if (cache != null) |
311 | { |
|
312 | try |
|
313 | { |
|
314 | 0 | synchronized (this) |
315 | { |
|
316 | 0 | oldOm = (Persistent) cache.get(key); |
317 | 0 | cache.put(key, om); |
318 | 0 | } |
319 | } |
|
320 | 0 | catch (CacheException ce) |
321 | { |
|
322 | 0 | throw new TorqueException |
323 | ("Could not cache due to internal JCS error", ce); |
|
324 | 0 | } |
325 | } |
|
326 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | List oms = null; |
380 | 0 | if (ids != null && ids.size() > 0) |
381 | { |
|
382 | // start a new list where we will replace the id's with om's |
|
383 | 0 | oms = new ArrayList(ids); |
384 | 0 | List newIds = new ArrayList(ids.size()); |
385 | 0 | for (int i = 0; i < ids.size(); i++) |
386 | { |
|
387 | 0 | ObjectKey key = (ObjectKey) ids.get(i); |
388 | 0 | Persistent om = null; |
389 | 0 | if (fromCache) |
390 | { |
|
391 | 0 | om = cacheGet(key); |
392 | } |
|
393 | 0 | if (om == null) |
394 | { |
|
395 | 0 | newIds.add(key); |
396 | } |
|
397 | else |
|
398 | { |
|
399 | 0 | oms.set(i, om); |
400 | } |
|
401 | } |
|
402 | ||
403 | 0 | if (newIds.size() > 0) |
404 | { |
|
405 | 0 | List newOms = retrieveStoredOMs(newIds); |
406 | 0 | for (int i = 0; i < oms.size(); i++) |
407 | { |
|
408 | 0 | if (oms.get(i) instanceof ObjectKey) |
409 | { |
|
410 | 0 | for (int j = newOms.size() - 1; j >= 0; j--) |
411 | { |
|
412 | 0 | Persistent om = (Persistent) newOms.get(j); |
413 | 0 | if (om.getPrimaryKey().equals(oms.get(i))) |
414 | { |
|
415 | // replace the id with the om and add the om |
|
416 | // to the cache |
|
417 | 0 | oms.set(i, om); |
418 | 0 | newOms.remove(j); |
419 | 0 | if (fromCache) |
420 | { |
|
421 | 0 | putInstanceImpl(om); |
422 | } |
|
423 | break; |
|
424 | } |
|
425 | } |
|
426 | } |
|
427 | } |
|
428 | } |
|
429 | } |
|
430 | 0 | 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 | 0 | 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 | 0 | this.region = v; |
466 | try |
|
467 | { |
|
468 | 0 | if (Torque.getConfiguration().getBoolean(Torque.CACHE_KEY, false)) |
469 | { |
|
470 | 0 | cache = JCS.getInstance(getRegion()); |
471 | 0 | mrCache = new MethodResultCache(cache); |
472 | } |
|
473 | else |
|
474 | { |
|
475 | 0 | mrCache = new NoOpMethodResultCache(cache); |
476 | } |
|
477 | } |
|
478 | 0 | catch (CacheException e) |
479 | { |
|
480 | 0 | throw new TorqueException("Cache could not be initialized", e); |
481 | 0 | } |
482 | ||
483 | 0 | if (cache == null) |
484 | { |
|
485 | 0 | log.info("Cache could not be initialized for region: " + v); |
486 | } |
|
487 | 0 | } |
488 | ||
489 | /** |
|
490 | * @return The cache instance. |
|
491 | */ |
|
492 | public MethodResultCache getMethodResultCache() |
|
493 | { |
|
494 | 0 | if (isNew) |
495 | { |
|
496 | 0 | synchronized (this) |
497 | { |
|
498 | 0 | if (isNew) |
499 | { |
|
500 | 0 | registerAsListener(); |
501 | 0 | isNew = false; |
502 | } |
|
503 | 0 | } |
504 | } |
|
505 | 0 | 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 | 0 | } |
515 | ||
516 | /** |
|
517 | * |
|
518 | * @param listener A new listener for cache events. |
|
519 | */ |
|
520 | public void addCacheListenerImpl(CacheListener listener) |
|
521 | { |
|
522 | 0 | List keys = listener.getInterestedFields(); |
523 | 0 | Iterator i = keys.iterator(); |
524 | 0 | while (i.hasNext()) |
525 | { |
|
526 | 0 | String key = (String) i.next(); |
527 | // Peer.column names are the fields |
|
528 | 0 | if (validFields != null && validFields.containsKey(key)) |
529 | { |
|
530 | 0 | List listeners = (List) listenersMap.get(key); |
531 | 0 | if (listeners == null) |
532 | { |
|
533 | 0 | listeners = createSubsetList(key); |
534 | } |
|
535 | ||
536 | 0 | boolean isNew = true; |
537 | 0 | Iterator j = listeners.iterator(); |
538 | 0 | while (j.hasNext()) |
539 | { |
|
540 | 0 | Object listener2 = |
541 | ((WeakReference) j.next()).get(); |
|
542 | 0 | if (listener2 == null) |
543 | 0 | { |
544 | // do a little cleanup while checking for dupes |
|
545 | // not thread-safe, not likely to be many nulls |
|
546 | // but should revisit |
|
547 | //j.remove(); |
|
548 | } |
|
549 | 0 | else if (listener2 == listener) |
550 | { |
|
551 | 0 | isNew = false; |
552 | 0 | break; |
553 | } |
|
554 | } |
|
555 | 0 | if (isNew) |
556 | { |
|
557 | 0 | listeners.add(new WeakReference(listener)); |
558 | } |
|
559 | } |
|
560 | } |
|
561 | 0 | } |
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 | 0 | FastArrayList list = null; |
571 | 0 | if (listenersMap.containsKey(key)) |
572 | { |
|
573 | 0 | list = (FastArrayList) listenersMap.get(key); |
574 | } |
|
575 | else |
|
576 | { |
|
577 | 0 | list = new FastArrayList(); |
578 | 0 | list.setFast(true); |
579 | 0 | listenersMap.put(key, list); |
580 | } |
|
581 | 0 | 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 | 0 | if (listeners != null) |
594 | { |
|
595 | 0 | synchronized (listeners) |
596 | { |
|
597 | 0 | Iterator i = listeners.iterator(); |
598 | 0 | while (i.hasNext()) |
599 | { |
|
600 | 0 | CacheListener listener = (CacheListener) |
601 | ((WeakReference) i.next()).get(); |
|
602 | 0 | if (listener == null) |
603 | { |
|
604 | // remove reference as its object was cleared |
|
605 | 0 | i.remove(); |
606 | } |
|
607 | else |
|
608 | { |
|
609 | 0 | if (oldOm == null) |
610 | { |
|
611 | // object was added |
|
612 | 0 | listener.addedObject(om); |
613 | } |
|
614 | else |
|
615 | { |
|
616 | // object was refreshed |
|
617 | 0 | listener.refreshedObject(om); |
618 | } |
|
619 | } |
|
620 | } |
|
621 | 0 | } |
622 | } |
|
623 | 0 | } |
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 | 0 | out.defaultWriteObject(); |
636 | 0 | } |
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 | 0 | in.defaultReadObject(); |
649 | // initialize the cache |
|
650 | try |
|
651 | { |
|
652 | 0 | if (region != null) |
653 | { |
|
654 | 0 | setRegion(region); |
655 | } |
|
656 | } |
|
657 | 0 | catch (Exception e) |
658 | { |
|
659 | 0 | log.error("Cache could not be initialized for region '" |
660 | + region + "' after deserialization"); |
|
661 | 0 | } |
662 | 0 | } |
663 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |