001: package org.apache.ojb.broker.cache;
002:
003: /* Copyright 2003-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.util.ArrayList;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.Map;
023: import java.util.Properties;
024: import java.util.StringTokenizer;
025:
026: import org.apache.commons.lang.SystemUtils;
027: import org.apache.commons.lang.builder.ToStringBuilder;
028: import org.apache.commons.lang.builder.ToStringStyle;
029: import org.apache.ojb.broker.Identity;
030: import org.apache.ojb.broker.OJBRuntimeException;
031: import org.apache.ojb.broker.PersistenceBroker;
032: import org.apache.ojb.broker.metadata.ObjectCacheDescriptor;
033: import org.apache.ojb.broker.util.ClassHelper;
034: import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
035: import org.apache.ojb.broker.util.logging.Logger;
036: import org.apache.ojb.broker.util.logging.LoggerFactory;
037:
038: /**
039: * A intern used {@link AbstractMetaCache} implementation acting
040: * as distributor of <code>ObjectCache</code> implementations declared
041: * in configuration metadata.
042: * <p/>
043: * Reads the name of the used ObjectCache implementation
044: * <br/>
045: * a) from class-descriptor, or if not found
046: * <br/>
047: * b) from jdbc-connection-descriptor, or if not found
048: * <br/>
049: * use a given standard ObjectCache implementation (given by
050: * constructor argument).
051: * </p>
052: *
053: * @author Matthew Baird (mattbaird@yahoo.com)
054: * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
055: * @version $Id: CacheDistributor.java,v 1.7.2.4 2005/12/21 22:24:15 tomdz Exp $
056: */
057: class CacheDistributor implements ObjectCacheInternal {
058: private static Logger log = LoggerFactory
059: .getLogger(CacheDistributor.class);
060: private static final String DESCRIPTOR_BASED_CACHES = "descriptorBasedCaches";
061: public static final String CACHE_EXCLUDES_STRING = "cacheExcludes";
062: private static final String DELIMITER_FOR_EXCLUDE = ",";
063: private static final ObjectCacheInternal DUMMY_CACHE = new ObjectCacheInternalWrapper(
064: new ObjectCacheEmptyImpl(null, null));
065:
066: /**
067: * map, represents used cache implementations
068: */
069: private Map caches = new HashMap();
070: private List excludedPackages;
071:
072: private final PersistenceBroker broker;
073: /**
074: * If <code>true</code> the class name of the object is used
075: * to find a per class {@link ObjectCache} implementation.
076: * If set <code>false</code> the {@link ObjectCacheDescriptor}
077: * instance is used as key to find a per class ObjectCache.
078: */
079: private boolean descriptorBasedCaches;
080:
081: /**
082: * public Default Constructor
083: */
084: public CacheDistributor(final PersistenceBroker broker) {
085: this .broker = broker;
086: this .descriptorBasedCaches = OjbConfigurator.getInstance()
087: .getConfigurationFor(null).getBoolean(
088: DESCRIPTOR_BASED_CACHES, false);
089: String exclude = broker.serviceConnectionManager()
090: .getConnectionDescriptor().getAttribute(
091: CACHE_EXCLUDES_STRING);
092: if (exclude != null) {
093: exclude = exclude.trim();
094: if (exclude.length() > 0) {
095: excludedPackages = createExcludedPackagesList(exclude);
096: log.info("Packages to exclude from caching: "
097: + excludedPackages);
098: }
099: }
100: }
101:
102: public void cache(Identity oid, Object obj) {
103: getCache(oid.getObjectsTopLevelClass()).cache(oid, obj);
104: }
105:
106: /**
107: * @see ObjectCacheInternal#cacheIfNew(org.apache.ojb.broker.Identity, Object)
108: */
109: public boolean cacheIfNew(Identity oid, Object obj) {
110: return getCache(oid.getObjectsTopLevelClass()).cacheIfNew(oid,
111: obj);
112: }
113:
114: public Object lookup(Identity oid) {
115: return getCache(oid.getObjectsTopLevelClass()).lookup(oid);
116: }
117:
118: public void remove(Identity oid) {
119: getCache(oid.getObjectsTopLevelClass()).remove(oid);
120: }
121:
122: public void clear() {
123: synchronized (caches) {
124: Iterator it = caches.values().iterator();
125: ObjectCache oc = null;
126: while (it.hasNext()) {
127: oc = (ObjectCache) it.next();
128: try {
129: oc.clear();
130: } catch (Exception e) {
131: log.error("Error while call method 'clear()' on '"
132: + oc + "'", e);
133: }
134: }
135: }
136: }
137:
138: public void doInternalCache(Identity oid, Object obj, int type) {
139: getCache(oid.getObjectsTopLevelClass()).doInternalCache(oid,
140: obj, type);
141: }
142:
143: public ObjectCacheInternal getCache(Class targetClass) {
144: /*
145: the priorities to find an ObjectCache for a specific object are:
146: 1. try to find a cache defined per class
147: 2. try to find a cache defined per jdbc-connection-descriptor
148: */
149: boolean useConnectionLevelCache = false;
150: ObjectCacheInternal retval = null;
151: /*
152: first search in class-descriptor, then in jdbc-connection-descriptor
153: for ObjectCacheDescriptor.
154: */
155: ObjectCacheDescriptor ocd = searchInClassDescriptor(targetClass);
156: if (ocd == null) {
157: ocd = searchInJdbcConnectionDescriptor();
158: useConnectionLevelCache = true;
159: }
160: if (ocd == null) {
161: throw new OJBRuntimeException(
162: "No object cache descriptor found for "
163: + targetClass
164: + ", using PBKey "
165: + broker.getPBKey()
166: + ". Please set a cache descriptor in jdbc-connection-descriptor or in class-descriptor");
167: } else {
168: // use a class-descriptor level cache
169: if (!useConnectionLevelCache) {
170: if (!descriptorBasedCaches) {
171: synchronized (caches) {
172: retval = lookupCache(targetClass);
173:
174: if (retval == null) {
175: if (log.isEnabledFor(Logger.INFO)) {
176: String eol = SystemUtils.LINE_SEPARATOR;
177: log
178: .info(eol
179: + "<===="
180: + eol
181: + "Setup new object cache instance on CLASS LEVEL for"
182: + eol
183: + "PersistenceBroker: "
184: + broker
185: + eol
186: + "descriptorBasedCache: "
187: + descriptorBasedCaches
188: + eol + "Class: "
189: + targetClass + eol
190: + "ObjectCache: " + ocd
191: + eol + "====>");
192: }
193: retval = prepareAndAddCache(targetClass,
194: ocd);
195: }
196: }
197: } else {
198: synchronized (caches) {
199: retval = lookupCache(ocd);
200:
201: if (retval == null) {
202: if (log.isEnabledFor(Logger.INFO)) {
203: String eol = SystemUtils.LINE_SEPARATOR;
204: log
205: .info(eol
206: + "<===="
207: + eol
208: + "Setup new object cache instance on CLASS LEVEL for"
209: + eol
210: + "PersistenceBroker: "
211: + broker
212: + eol
213: + "descriptorBasedCache: "
214: + descriptorBasedCaches
215: + eol + "class: "
216: + targetClass + eol
217: + "ObjectCache: " + ocd
218: + eol + "====>");
219: }
220: retval = prepareAndAddCache(ocd, ocd);
221: }
222: }
223: }
224: }
225: // use a jdbc-connection-descriptor level cache
226: else {
227: if (isExcluded(targetClass)) {
228: if (log.isDebugEnabled())
229: log.debug("Class '" + targetClass.getName()
230: + "' is excluded from being cached");
231: retval = DUMMY_CACHE;
232: } else {
233: String jcdAlias = broker.serviceConnectionManager()
234: .getConnectionDescriptor().getJcdAlias();
235: synchronized (caches) {
236: retval = lookupCache(jcdAlias);
237:
238: if (retval == null) {
239: if (log.isEnabledFor(Logger.INFO)) {
240: String eol = SystemUtils.LINE_SEPARATOR;
241: log
242: .info(eol
243: + "<===="
244: + eol
245: + "Setup new object cache instance on CONNECTION LEVEL for"
246: + eol
247: + "PersistenceBroker: "
248: + broker
249: + eol
250: + "descriptorBasedCache: "
251: + descriptorBasedCaches
252: + eol
253: + "Connection jcdAlias: "
254: + jcdAlias + eol
255: + "Calling class: "
256: + targetClass
257: + "ObjectCache: " + ocd
258: + eol + "====>");
259: }
260: retval = prepareAndAddCache(jcdAlias, ocd);
261: }
262: }
263: }
264: }
265: }
266: return retval;
267: }
268:
269: private ObjectCacheInternal prepareAndAddCache(Object key,
270: ObjectCacheDescriptor ocd) {
271: ObjectCacheInternal cache;
272: // before the synchronize method lock this,
273: // another thread maybe added same key
274: if ((cache = lookupCache(key)) != null) {
275: log
276: .info("Key '"
277: + key
278: + "' was already in use no need to create the ObjectCache instance again");
279: } else {
280: if (log.isDebugEnabled())
281: log.debug("Create new ObjectCache implementation for "
282: + key);
283: try {
284: ObjectCache temp = (ObjectCache) ClassHelper
285: .newInstance(ocd.getObjectCache(), new Class[] {
286: PersistenceBroker.class,
287: Properties.class }, new Object[] {
288: broker,
289: ocd.getConfigurationProperties() });
290: if (temp instanceof ObjectCacheInternal) {
291: cache = (ObjectCacheInternal) temp;
292: } else {
293: log.info("Specified cache " + ocd.getObjectCache()
294: + " does not implement "
295: + ObjectCacheInternal.class
296: + " and will be wrapped by a helper class");
297: cache = new ObjectCacheInternalWrapper(temp);
298: }
299: } catch (Exception e) {
300: log.error(
301: "Can not create ObjectCache instance using class "
302: + ocd.getObjectCache(), e);
303: throw new OJBRuntimeException(e);
304: }
305: caches.put(key, cache);
306: }
307: return cache;
308: }
309:
310: private ObjectCacheInternal lookupCache(Object key) {
311: return (ObjectCacheInternal) caches.get(key);
312: }
313:
314: private List createExcludedPackagesList(String theList) {
315: StringTokenizer tok = new StringTokenizer(theList,
316: DELIMITER_FOR_EXCLUDE);
317: String token = null;
318: ArrayList result = new ArrayList();
319: while (tok.hasMoreTokens()) {
320: token = tok.nextToken().trim();
321: if (token.length() > 0)
322: result.add(token);
323: }
324: return result;
325: }
326:
327: private boolean isExcluded(Class targetClass) {
328: if (excludedPackages != null) {
329: String name = targetClass.getName();
330: for (int i = 0; i < excludedPackages.size(); i++) {
331: String exclude = (String) excludedPackages.get(i);
332: if (name.startsWith(exclude)) {
333: return true;
334: }
335: }
336: }
337: return false;
338: }
339:
340: /**
341: * Try to lookup {@link ObjectCacheDescriptor} in
342: * {@link org.apache.ojb.broker.metadata.ClassDescriptor}.
343: *
344: * @param targetClass
345: * @return Returns the found {@link ObjectCacheDescriptor} or <code>null</code>
346: * if none was found.
347: */
348: protected ObjectCacheDescriptor searchInClassDescriptor(
349: Class targetClass) {
350: return targetClass != null ? broker.getClassDescriptor(
351: targetClass).getObjectCacheDescriptor() : null;
352: }
353:
354: /**
355: * Lookup {@link ObjectCacheDescriptor} in
356: * {@link org.apache.ojb.broker.metadata.JdbcConnectionDescriptor}.
357: *
358: * @return Returns the found {@link ObjectCacheDescriptor} or <code>null</code>
359: * if none was found.
360: */
361: protected ObjectCacheDescriptor searchInJdbcConnectionDescriptor() {
362: return broker.serviceConnectionManager()
363: .getConnectionDescriptor().getObjectCacheDescriptor();
364: }
365:
366: public String toString() {
367: ToStringBuilder buf = new ToStringBuilder(this ,
368: ToStringStyle.DEFAULT_STYLE);
369: return buf.append("Associated PB", broker).append(
370: "Mapped caches", caches).toString();
371: }
372:
373: //=================================================
374: // inner class
375: //=================================================
376: /**
377: * Wrapper class used to make existing {@link ObjectCache} implementations work
378: * with {@link ObjectCacheInternal}.
379: */
380: static final class ObjectCacheInternalWrapper implements
381: ObjectCacheInternal {
382: ObjectCache cache = null;
383:
384: public ObjectCacheInternalWrapper(ObjectCache cache) {
385: this .cache = cache;
386: }
387:
388: public void doInternalCache(Identity oid, Object obj, int type) {
389: cache(oid, obj);
390: }
391:
392: public void doInternalClear() {
393: // noop
394: }
395:
396: public boolean contains(Identity oid) {
397: return cache.lookup(oid) != null;
398: }
399:
400: public void cache(Identity oid, Object obj) {
401: cache.cache(oid, obj);
402: }
403:
404: public boolean cacheIfNew(Identity oid, Object obj) {
405: cache.cache(oid, obj);
406: return true;
407: }
408:
409: public Object lookup(Identity oid) {
410: return cache.lookup(oid);
411: }
412:
413: public void remove(Identity oid) {
414: cache.remove(oid);
415: }
416:
417: public void clear() {
418: cache.clear();
419: }
420: }
421:
422: }
|