org.apache.ojb.broker.cache.CacheDistributor.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.ojb.broker.cache.CacheDistributor.java

Source

package org.apache.ojb.broker.cache;

/* Copyright 2003-2005 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.ojb.broker.Identity;
import org.apache.ojb.broker.OJBRuntimeException;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.metadata.ObjectCacheDescriptor;
import org.apache.ojb.broker.util.ClassHelper;
import org.apache.ojb.broker.util.configuration.impl.OjbConfigurator;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;

/**
 * A intern used {@link AbstractMetaCache} implementation acting
 * as distributor of <code>ObjectCache</code> implementations declared
 * in configuration metadata.
 * <p/>
 * Reads the name of the used ObjectCache implementation
 * <br/>
 * a) from class-descriptor, or if not found
 * <br/>
 * b) from jdbc-connection-descriptor, or if not found
 * <br/>
 * use a given standard ObjectCache implementation (given by
 * constructor argument).
 * </p>
 *
 * @author Matthew Baird  (mattbaird@yahoo.com)
 * @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
 * @version $Id: CacheDistributor.java,v 1.7.2.4 2005/12/21 22:24:15 tomdz Exp $
 */
class CacheDistributor implements ObjectCacheInternal {
    private static Logger log = LoggerFactory.getLogger(CacheDistributor.class);
    private static final String DESCRIPTOR_BASED_CACHES = "descriptorBasedCaches";
    public static final String CACHE_EXCLUDES_STRING = "cacheExcludes";
    private static final String DELIMITER_FOR_EXCLUDE = ",";
    private static final ObjectCacheInternal DUMMY_CACHE = new ObjectCacheInternalWrapper(
            new ObjectCacheEmptyImpl(null, null));

    /**
     * map, represents used cache implementations
     */
    private Map caches = new HashMap();
    private List excludedPackages;

    private final PersistenceBroker broker;
    /**
     * If <code>true</code> the class name of the object is used
     * to find a per class {@link ObjectCache} implementation.
     * If set <code>false</code> the {@link ObjectCacheDescriptor}
     * instance is used as key to find a per class ObjectCache.
     */
    private boolean descriptorBasedCaches;

    /**
     * public Default Constructor
     */
    public CacheDistributor(final PersistenceBroker broker) {
        this.broker = broker;
        this.descriptorBasedCaches = OjbConfigurator.getInstance().getConfigurationFor(null)
                .getBoolean(DESCRIPTOR_BASED_CACHES, false);
        String exclude = broker.serviceConnectionManager().getConnectionDescriptor()
                .getAttribute(CACHE_EXCLUDES_STRING);
        if (exclude != null) {
            exclude = exclude.trim();
            if (exclude.length() > 0) {
                excludedPackages = createExcludedPackagesList(exclude);
                log.info("Packages to exclude from caching: " + excludedPackages);
            }
        }
    }

    public void cache(Identity oid, Object obj) {
        getCache(oid.getObjectsTopLevelClass()).cache(oid, obj);
    }

    /**
     * @see ObjectCacheInternal#cacheIfNew(org.apache.ojb.broker.Identity, Object)
     */
    public boolean cacheIfNew(Identity oid, Object obj) {
        return getCache(oid.getObjectsTopLevelClass()).cacheIfNew(oid, obj);
    }

    public Object lookup(Identity oid) {
        return getCache(oid.getObjectsTopLevelClass()).lookup(oid);
    }

    public void remove(Identity oid) {
        getCache(oid.getObjectsTopLevelClass()).remove(oid);
    }

    public void clear() {
        synchronized (caches) {
            Iterator it = caches.values().iterator();
            ObjectCache oc = null;
            while (it.hasNext()) {
                oc = (ObjectCache) it.next();
                try {
                    oc.clear();
                } catch (Exception e) {
                    log.error("Error while call method 'clear()' on '" + oc + "'", e);
                }
            }
        }
    }

    public void doInternalCache(Identity oid, Object obj, int type) {
        getCache(oid.getObjectsTopLevelClass()).doInternalCache(oid, obj, type);
    }

    public ObjectCacheInternal getCache(Class targetClass) {
        /*
        the priorities to find an ObjectCache for a specific object are:
        1. try to find a cache defined per class
        2. try to find a cache defined per jdbc-connection-descriptor
        */
        boolean useConnectionLevelCache = false;
        ObjectCacheInternal retval = null;
        /*
        first search in class-descriptor, then in jdbc-connection-descriptor
        for ObjectCacheDescriptor.
        */
        ObjectCacheDescriptor ocd = searchInClassDescriptor(targetClass);
        if (ocd == null) {
            ocd = searchInJdbcConnectionDescriptor();
            useConnectionLevelCache = true;
        }
        if (ocd == null) {
            throw new OJBRuntimeException("No object cache descriptor found for " + targetClass + ", using PBKey "
                    + broker.getPBKey()
                    + ". Please set a cache descriptor in jdbc-connection-descriptor or in class-descriptor");
        } else {
            // use a class-descriptor level cache
            if (!useConnectionLevelCache) {
                if (!descriptorBasedCaches) {
                    synchronized (caches) {
                        retval = lookupCache(targetClass);

                        if (retval == null) {
                            if (log.isEnabledFor(Logger.INFO)) {
                                String eol = SystemUtils.LINE_SEPARATOR;
                                log.info(eol + "<====" + eol + "Setup new object cache instance on CLASS LEVEL for"
                                        + eol + "PersistenceBroker: " + broker + eol + "descriptorBasedCache: "
                                        + descriptorBasedCaches + eol + "Class: " + targetClass + eol
                                        + "ObjectCache: " + ocd + eol + "====>");
                            }
                            retval = prepareAndAddCache(targetClass, ocd);
                        }
                    }
                } else {
                    synchronized (caches) {
                        retval = lookupCache(ocd);

                        if (retval == null) {
                            if (log.isEnabledFor(Logger.INFO)) {
                                String eol = SystemUtils.LINE_SEPARATOR;
                                log.info(eol + "<====" + eol + "Setup new object cache instance on CLASS LEVEL for"
                                        + eol + "PersistenceBroker: " + broker + eol + "descriptorBasedCache: "
                                        + descriptorBasedCaches + eol + "class: " + targetClass + eol
                                        + "ObjectCache: " + ocd + eol + "====>");
                            }
                            retval = prepareAndAddCache(ocd, ocd);
                        }
                    }
                }
            }
            // use a jdbc-connection-descriptor level cache
            else {
                if (isExcluded(targetClass)) {
                    if (log.isDebugEnabled())
                        log.debug("Class '" + targetClass.getName() + "' is excluded from being cached");
                    retval = DUMMY_CACHE;
                } else {
                    String jcdAlias = broker.serviceConnectionManager().getConnectionDescriptor().getJcdAlias();
                    synchronized (caches) {
                        retval = lookupCache(jcdAlias);

                        if (retval == null) {
                            if (log.isEnabledFor(Logger.INFO)) {
                                String eol = SystemUtils.LINE_SEPARATOR;
                                log.info(eol + "<====" + eol
                                        + "Setup new object cache instance on CONNECTION LEVEL for" + eol
                                        + "PersistenceBroker: " + broker + eol + "descriptorBasedCache: "
                                        + descriptorBasedCaches + eol + "Connection jcdAlias: " + jcdAlias + eol
                                        + "Calling class: " + targetClass + "ObjectCache: " + ocd + eol + "====>");
                            }
                            retval = prepareAndAddCache(jcdAlias, ocd);
                        }
                    }
                }
            }
        }
        return retval;
    }

    private ObjectCacheInternal prepareAndAddCache(Object key, ObjectCacheDescriptor ocd) {
        ObjectCacheInternal cache;
        // before the synchronize method lock this,
        // another thread maybe added same key
        if ((cache = lookupCache(key)) != null) {
            log.info("Key '" + key + "' was already in use no need to create the ObjectCache instance again");
        } else {
            if (log.isDebugEnabled())
                log.debug("Create new ObjectCache implementation for " + key);
            try {
                ObjectCache temp = (ObjectCache) ClassHelper.newInstance(ocd.getObjectCache(),
                        new Class[] { PersistenceBroker.class, Properties.class },
                        new Object[] { broker, ocd.getConfigurationProperties() });
                if (temp instanceof ObjectCacheInternal) {
                    cache = (ObjectCacheInternal) temp;
                } else {
                    log.info("Specified cache " + ocd.getObjectCache() + " does not implement "
                            + ObjectCacheInternal.class + " and will be wrapped by a helper class");
                    cache = new ObjectCacheInternalWrapper(temp);
                }
            } catch (Exception e) {
                log.error("Can not create ObjectCache instance using class " + ocd.getObjectCache(), e);
                throw new OJBRuntimeException(e);
            }
            caches.put(key, cache);
        }
        return cache;
    }

    private ObjectCacheInternal lookupCache(Object key) {
        return (ObjectCacheInternal) caches.get(key);
    }

    private List createExcludedPackagesList(String theList) {
        StringTokenizer tok = new StringTokenizer(theList, DELIMITER_FOR_EXCLUDE);
        String token = null;
        ArrayList result = new ArrayList();
        while (tok.hasMoreTokens()) {
            token = tok.nextToken().trim();
            if (token.length() > 0)
                result.add(token);
        }
        return result;
    }

    private boolean isExcluded(Class targetClass) {
        if (excludedPackages != null) {
            String name = targetClass.getName();
            for (int i = 0; i < excludedPackages.size(); i++) {
                String exclude = (String) excludedPackages.get(i);
                if (name.startsWith(exclude)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Try to lookup {@link ObjectCacheDescriptor} in
     * {@link org.apache.ojb.broker.metadata.ClassDescriptor}.
     *
     * @param targetClass
     * @return Returns the found {@link ObjectCacheDescriptor} or <code>null</code>
     *         if none was found.
     */
    protected ObjectCacheDescriptor searchInClassDescriptor(Class targetClass) {
        return targetClass != null ? broker.getClassDescriptor(targetClass).getObjectCacheDescriptor() : null;
    }

    /**
     * Lookup {@link ObjectCacheDescriptor} in
     * {@link org.apache.ojb.broker.metadata.JdbcConnectionDescriptor}.
     *
     * @return Returns the found {@link ObjectCacheDescriptor} or <code>null</code>
     *         if none was found.
     */
    protected ObjectCacheDescriptor searchInJdbcConnectionDescriptor() {
        return broker.serviceConnectionManager().getConnectionDescriptor().getObjectCacheDescriptor();
    }

    public String toString() {
        ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.DEFAULT_STYLE);
        return buf.append("Associated PB", broker).append("Mapped caches", caches).toString();
    }

    //=================================================
    // inner class
    //=================================================
    /**
     * Wrapper class used to make existing {@link ObjectCache} implementations work
     * with {@link ObjectCacheInternal}.
     */
    static final class ObjectCacheInternalWrapper implements ObjectCacheInternal {
        ObjectCache cache = null;

        public ObjectCacheInternalWrapper(ObjectCache cache) {
            this.cache = cache;
        }

        public void doInternalCache(Identity oid, Object obj, int type) {
            cache(oid, obj);
        }

        public void doInternalClear() {
            // noop
        }

        public boolean contains(Identity oid) {
            return cache.lookup(oid) != null;
        }

        public void cache(Identity oid, Object obj) {
            cache.cache(oid, obj);
        }

        public boolean cacheIfNew(Identity oid, Object obj) {
            cache.cache(oid, obj);
            return true;
        }

        public Object lookup(Identity oid) {
            return cache.lookup(oid);
        }

        public void remove(Identity oid) {
            cache.remove(oid);
        }

        public void clear() {
            cache.clear();
        }
    }

}