%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 473821 2006-11-11 22:37:25Z 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.remove(); |
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 | * 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 | 0 | Persistent oldOm = null; |
239 | 0 | if (cache != null) |
240 | { |
|
241 | try |
|
242 | { |
|
243 | 0 | synchronized (this) |
244 | { |
|
245 | 0 | oldOm = (Persistent) cache.get(key); |
246 | 0 | cache.remove(key); |
247 | 0 | } |
248 | } |
|
249 | 0 | catch (CacheException ce) |
250 | { |
|
251 | 0 | throw new TorqueException |
252 | ("Could not remove from cache due to internal JCS error", |
|
253 | ce); |
|
254 | 0 | } |
255 | } |
|
256 | 0 | 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 | 0 | ObjectKey key = om.getPrimaryKey(); |
272 | 0 | 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 | 0 | if (getOMClass() != null && !getOMClass().isInstance(om)) |
289 | { |
|
290 | 0 | throw new TorqueException(om + "; class=" + om.getClass().getName() |
291 | + "; id=" + om.getPrimaryKey() + " cannot be cached with " |
|
292 | + getOMClass().getName() + " objects"); |
|
293 | } |
|
294 | ||
295 | 0 | Persistent oldOm = null; |
296 | 0 | if (cache != null) |
297 | { |
|
298 | try |
|
299 | { |
|
300 | 0 | synchronized (this) |
301 | { |
|
302 | 0 | oldOm = (Persistent) cache.get(key); |
303 | 0 | cache.put(key, om); |
304 | 0 | } |
305 | } |
|
306 | 0 | catch (CacheException ce) |
307 | { |
|
308 | 0 | throw new TorqueException |
309 | ("Could not cache due to internal JCS error", ce); |
|
310 | 0 | } |
311 | } |
|
312 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | List oms = null; |
366 | 0 | if (ids != null && ids.size() > 0) |
367 | { |
|
368 | // start a new list where we will replace the id's with om's |
|
369 | 0 | oms = new ArrayList(ids); |
370 | 0 | List newIds = new ArrayList(ids.size()); |
371 | 0 | for (int i = 0; i < ids.size(); i++) |
372 | { |
|
373 | 0 | ObjectKey key = (ObjectKey) ids.get(i); |
374 | 0 | Persistent om = null; |
375 | 0 | if (fromCache) |
376 | { |
|
377 | 0 | om = cacheGet(key); |
378 | } |
|
379 | 0 | if (om == null) |
380 | { |
|
381 | 0 | newIds.add(key); |
382 | 0 | } |
383 | else |
|
384 | { |
|
385 | 0 | oms.set(i, om); |
386 | } |
|
387 | } |
|
388 | ||
389 | 0 | if (newIds.size() > 0) |
390 | { |
|
391 | 0 | List newOms = retrieveStoredOMs(newIds); |
392 | 0 | for (int i = 0; i < oms.size(); i++) |
393 | { |
|
394 | 0 | if (oms.get(i) instanceof ObjectKey) |
395 | { |
|
396 | 0 | for (int j = newOms.size() - 1; j >= 0; j--) |
397 | { |
|
398 | 0 | Persistent om = (Persistent) newOms.get(j); |
399 | 0 | if (om.getPrimaryKey().equals(oms.get(i))) |
400 | { |
|
401 | // replace the id with the om and add the om |
|
402 | // to the cache |
|
403 | 0 | oms.set(i, om); |
404 | 0 | newOms.remove(j); |
405 | 0 | if (fromCache) |
406 | { |
|
407 | 0 | putInstanceImpl(om); |
408 | 0 | } |
409 | break; |
|
410 | } |
|
411 | } |
|
412 | } |
|
413 | } |
|
414 | } |
|
415 | } |
|
416 | 0 | 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 | 0 | 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 | 0 | this.region = v; |
452 | try |
|
453 | { |
|
454 | 0 | if (Torque.getConfiguration().getBoolean(Torque.CACHE_KEY, false)) |
455 | { |
|
456 | 0 | cache = JCS.getInstance(getRegion()); |
457 | 0 | mrCache = new MethodResultCache(cache); |
458 | 0 | } |
459 | else |
|
460 | { |
|
461 | 0 | mrCache = new NoOpMethodResultCache(cache); |
462 | } |
|
463 | } |
|
464 | 0 | catch (CacheException e) |
465 | { |
|
466 | 0 | throw new TorqueException("Cache could not be initialized", e); |
467 | 0 | } |
468 | ||
469 | 0 | if (cache == null) |
470 | { |
|
471 | 0 | log.info("Cache could not be initialized for region: " + v); |
472 | } |
|
473 | 0 | } |
474 | ||
475 | /** |
|
476 | * @return The cache instance. |
|
477 | */ |
|
478 | public MethodResultCache getMethodResultCache() |
|
479 | { |
|
480 | 0 | if (isNew) |
481 | { |
|
482 | 0 | synchronized (this) |
483 | { |
|
484 | 0 | if (isNew) |
485 | { |
|
486 | 0 | registerAsListener(); |
487 | 0 | isNew = false; |
488 | } |
|
489 | 0 | } |
490 | } |
|
491 | 0 | 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 | 0 | } |
501 | ||
502 | /** |
|
503 | * |
|
504 | * @param listener A new listener for cache events. |
|
505 | */ |
|
506 | public void addCacheListenerImpl(CacheListener listener) |
|
507 | { |
|
508 | 0 | List keys = listener.getInterestedFields(); |
509 | 0 | Iterator i = keys.iterator(); |
510 | 0 | while (i.hasNext()) |
511 | { |
|
512 | 0 | String key = (String) i.next(); |
513 | // Peer.column names are the fields |
|
514 | 0 | if (validFields != null && validFields.containsKey(key)) |
515 | { |
|
516 | 0 | List listeners = (List) listenersMap.get(key); |
517 | 0 | if (listeners == null) |
518 | { |
|
519 | 0 | listeners = createSubsetList(key); |
520 | } |
|
521 | ||
522 | 0 | boolean isNew = true; |
523 | 0 | Iterator j = listeners.iterator(); |
524 | 0 | while (j.hasNext()) |
525 | { |
|
526 | 0 | Object listener2 = |
527 | ((WeakReference) j.next()).get(); |
|
528 | 0 | if (listener2 == null) |
529 | { |
|
530 | // do a little cleanup while checking for dupes |
|
531 | // not thread-safe, not likely to be many nulls |
|
532 | // but should revisit |
|
533 | //j.remove(); |
|
534 | 0 | } |
535 | 0 | else if (listener2 == listener) |
536 | { |
|
537 | 0 | isNew = false; |
538 | 0 | break; |
539 | } |
|
540 | 0 | } |
541 | 0 | if (isNew) |
542 | { |
|
543 | 0 | listeners.add(new WeakReference(listener)); |
544 | } |
|
545 | } |
|
546 | 0 | } |
547 | 0 | } |
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 | 0 | FastArrayList list = null; |
557 | 0 | if (listenersMap.containsKey(key)) |
558 | { |
|
559 | 0 | list = (FastArrayList) listenersMap.get(key); |
560 | 0 | } |
561 | else |
|
562 | { |
|
563 | 0 | list = new FastArrayList(); |
564 | 0 | list.setFast(true); |
565 | 0 | listenersMap.put(key, list); |
566 | } |
|
567 | 0 | 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 | 0 | if (listeners != null) |
580 | { |
|
581 | 0 | synchronized (listeners) |
582 | { |
|
583 | 0 | Iterator i = listeners.iterator(); |
584 | 0 | while (i.hasNext()) |
585 | { |
|
586 | 0 | CacheListener listener = (CacheListener) |
587 | ((WeakReference) i.next()).get(); |
|
588 | 0 | if (listener == null) |
589 | { |
|
590 | // remove reference as its object was cleared |
|
591 | 0 | i.remove(); |
592 | 0 | } |
593 | else |
|
594 | { |
|
595 | 0 | if (oldOm == null) |
596 | { |
|
597 | // object was added |
|
598 | 0 | listener.addedObject(om); |
599 | 0 | } |
600 | else |
|
601 | { |
|
602 | // object was refreshed |
|
603 | 0 | listener.refreshedObject(om); |
604 | } |
|
605 | } |
|
606 | 0 | } |
607 | 0 | } |
608 | } |
|
609 | 0 | } |
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 | 0 | out.defaultWriteObject(); |
622 | 0 | } |
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 | 0 | in.defaultReadObject(); |
635 | // initialize the cache |
|
636 | try |
|
637 | { |
|
638 | 0 | if (region != null) |
639 | { |
|
640 | 0 | setRegion(region); |
641 | } |
|
642 | } |
|
643 | 0 | catch (Exception e) |
644 | { |
|
645 | 0 | log.error("Cache could not be initialized for region '" |
646 | + region + "' after deserialization"); |
|
647 | 0 | } |
648 | 0 | } |
649 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |