Package org.fjank.jcache

Source Code of org.fjank.jcache.CacheImpl

/*   Open Source Java Caching Service
*    Copyright (C) 2002 Frank Karlstr�m
*    This library is free software; you can redistribute it and/or
*    modify it under the terms of the GNU Lesser General Public
*    License as published by the Free Software Foundation; either
*    version 2.1 of the License, or (at your option) any later version.
*
*    This library is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*    Lesser General Public License for more details.
*
*    You should have received a copy of the GNU Lesser General Public
*    License along with this library; if not, write to the Free Software
*    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*    The author can be contacted by email: fjankk@sourceforge.net
*/
package org.fjank.jcache;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.ReferenceQueue;
import java.net.InetAddress;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import javax.util.jcache.Attributes;
import javax.util.jcache.CacheAttributes;
import javax.util.jcache.CacheException;
import javax.util.jcache.CacheLogger;
import javax.util.jcache.CacheNotAvailableException;
import javax.util.jcache.DiskCacheException;
import javax.util.jcache.NullObjectNameException;
import javax.util.jcache.ObjectExistsException;
import javax.util.jcache.RegionNotFoundException;
import org.fjank.jcache.distribution.DistributionEngine;
import org.fjank.jcache.persistence.DiskCache;

/**
* Contains several usefull methods for configuring, administering and
* monitoring the Cache. Is final to avoid subclassing to further lock down
* the singleton pattern.
*
* @author Frank Karlstr�m
*
* @todo fix Singleton serialization, Multiple classloaders, and JVM
*       destroy/reload.
*/
public final class CacheImpl implements javax.util.jcache.Cache {
    private DistributionEngine distributionEngine;
    /** this is the actuall cache instance in this JVM */
    private static CacheImpl _singleton;
    /** the disk cache implementation */
    private DiskCache diskCache;
    /** a boolean indication wether this cache is ready or not. */
    private boolean ready;
    /** the CacheAttributes for this cache */
    private CacheAttributes attributes;
    /** the version of this cache */
    private float version;
    /** the user defined regions. */
    private final Map userRegions = new HashMap();
    /**
     * a ReferenceQueue. all objects which users either destroys, or quit
     * using,  ends up in this queue. (The GC sends them here)
     */
    private final ReferenceQueue refQueue = new ReferenceQueue();
    /**
     * starts the service Threads wich sweeps the cache to remove invalid
     * objects.
     */
    //2004/09-FB
    //private CacheSweeper sweeper;
    /**
     * A thread pool for running potential long-running background tasks.
     */
    private final JCacheExecutorPool execPool = new JCacheExecutorPool();

    /**
     * private constructor to implement the singleton pattern.
     */
    private CacheImpl() {
    }

    /**
     * Gets an instance of the Cache class. Is synchronized to avoid several
     * Threads to create multiple instances of this class wich MUST be a
     * singleton.
     *
     * @param init a boolean inication wether to actually performe the
     *        initialization or not
     *
     * @return A Cache instance global for this JVM.
     *
     * @throws CacheNotAvailableException if the Cache is not available.
     */
    public static synchronized CacheImpl getCache(final boolean init) {
        if (_singleton == null) {
            _singleton = new CacheImpl();
            if (init) {
                _singleton.open(null);
            }
        }
        return _singleton;
    }

    static CacheImpl getCache() {
        return _singleton;
    }
    /**
     * Gets the default region in this cache.,
     *
     * @return the default region in this cache.
     */
    public CacheRegion getRegion() {
        return CacheRegion.getRegion();
    }

    /**
     * Gets the named region in this cache.,
     *
     * @param name the name of the region to get.
     *
     * @return the named region.
     */
    public CacheRegion getRegion(final Object name) {
        return (CacheRegion) this.userRegions.get(name);
    }

    /**
     * Adds the specified region.
     *
     * @param name the name of the region to add.
     * @param attributes the attributes for the new region.
     *
     * @throws ObjectExistsException if the name already exists in the cache.
     * @throws NullObjectNameException if the region is attempted initialized
     *         with <CODE>null</CODE> as name.
     */
    void addRegion(final String name, final Attributes attributes) throws ObjectExistsException, NullObjectNameException {
        if (name == null) {
            throw new NullObjectNameException("A region cannot be created with null as its name.");
        }
        if ("".equals(name)) {
            throw new NullObjectNameException("A region cannot be created with an empty string as its name.");
        }
        if (userRegions.containsKey(name)) {
            throw new ObjectExistsException("The object " + name + " already exists in the cache.");
        }
        userRegions.put(name, new CacheRegion(name, new AttributesImpl(attributes)));
    }

    /**
     * Will invalidate all objects in the named region and the named region.
     *
     * @param name the name of the region to destroy.
     *
     *
     * @see #destroy()
     */
    public void destroyRegion(final Object name) {
        CacheRegion localRegion = (CacheRegion) userRegions.get(name);
        userRegions.remove(localRegion.getName());
        localRegion.destroy();
    }

    /**
     * initializes the cache, allocates space for metadata and starts the
     * service threads. The cache is a process wide service, so it can only be
     * initialized once per process. Subsequent init calls are ignored.
     *
     * @param attributes2 contains configuration information to initialize the
     *        cache system.
     *
     * @throws CacheNotAvailableException if the cache is not available.
     */
    public void init(final CacheAttributes attributes) throws CacheNotAvailableException{
        synchronized (_singleton) {
            if (!ready) {
                this.attributes = attributes;
                attributes.registerCache(this);
                startServiceThreads();
                if (attributes.getDiskPath() != null) {
                     try {
                        diskCache = new DiskCache(attributes);
                    } catch (DiskCacheException e) {
                        throw new CacheNotAvailableException(e);
                    }
                }
                if (attributes.isDistributed()) {
                    distributionEngine = DistributionEngine.getInstance(this);
                }
                ready = true;
            }
        }
    }

    /**
     * starts the service Thread(s) which sweeps the cache to  remove invalid
     * objects.
     */
    private void startServiceThreads() {
    //2004/09-FB
        CacheSweeper.getInstance().startSweeper();
    }

    /**
     * stops the service Threads which sweeps the cache to remove invalid
     * objects.
     */
    private void stopServiceThreads() {
    //2004/09-FB
    CacheSweeper.getInstance().stopSweeper();
        CacheSweeper.removeInstance();
    }

    /**
     * will create a CacheAttributes object based on the values in a Java
     * properties file, then call the method init. The properties file opened
     * is called jcache.properties If this method is called, the init() method
     * is not neccessary to call.
     *
     * @throws CacheNotAvailableException if the cache is not available.
     */
    public void open() throws CacheNotAvailableException {
        open(null);
    }

    /**
     * tries to initialize a CacheLogger frorm the named class.
     *
     * @param logger the name of the class to initialize.
     *
     * @return a CacheLogger instance of the class with the specified name.
     *
     * @throws CacheException if exceptions occur during initialization.
     */
    private CacheLogger parseLogger(final String logger) throws CacheException {
        try {
            return (CacheLogger) Class.forName(logger).newInstance();
        } catch (ClassNotFoundException e) {
            throw new CacheException("The CacheLogger " + logger + " could not be found.");
        } catch (InstantiationException e) {
            throw new CacheException("The CacheLogger " + logger + " could not be loaded.");
        } catch (IllegalAccessException e) {
            throw new CacheException("The CacheLogger " + logger + " is appearently not a CacheLogger.");
        }
    }

    /**
     * returns an int describing the CacheLoggerSeverity
     *
     * @param logSeverity a String representation of the log severity.
     *
     * @return an int describing the CacheLoggerSeverity
     */
    private int parseLogSeverity(final String logSeverity) {
        try {
            java.lang.reflect.Field[] fields = CacheLogger.class.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                if (fields[i].getName().equals(logSeverity)) {
                    return fields[i].getInt(null);
                }
            }
        } catch (IllegalAccessException e) {
            ;
        }
        return CacheLogger.DEFAULT;
    }

    /**
     * parser the addresses into a List of URLs.
     *
     * @param addresses the lilst of addresses to parse.
     *
     * @return a List of URLS
     */
    private java.util.List parseAddresses(final String addresses) {
        java.util.ArrayList returnValue = new java.util.ArrayList();
        java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(addresses, ",");
        while (tokenizer.hasMoreTokens()) {
            try {
                returnValue.add(new java.net.URL("http://" + tokenizer.nextToken()));
            } catch (java.net.MalformedURLException e) {
                e.printStackTrace();
            }
        }
        return returnValue;
    }

    /**
     * will create a CacheAttributes object based on the values in a Java
     * properties file, then call the method init. If this method is called,
     * the init() method is not neccessary to call.
     *
     * @param configFile the name of the configuration file.
     *
     * @throws CacheNotAvailableException if the cache is not available.
     */
    public void open(final String configFile) {
        InputStream in = null;
        try {
            synchronized (_singleton) {
                if (!ready) {
                    Properties properties = new Properties();
                    if (configFile == null) {
                        in = getClass().getClassLoader().getResourceAsStream("./jcache.properties");
                    } else {
                        in = new FileInputStream(configFile);
                    }
                    if (in == null) {
                        properties = new Properties();
                    } else {
                        properties.load(in);
                        in.close();
                    }
                    //convert the properties to legal values, use default if no props available.
                    boolean distribute = Boolean.valueOf(properties.getProperty("distribute", "false")).booleanValue();
                    String logFileName = properties.getProperty("logFileName", "jcache.log");
                    int cleanInterval = 30;
                    try {
                        cleanInterval = Integer.parseInt(properties.getProperty("cleanInterval", "30"), 10);
                    } catch (NumberFormatException e) {
                    }
                    int diskSize = 10;
                    try {
                        diskSize = Integer.parseInt(properties.getProperty("diskSize", "10"), 10);
                    } catch (NumberFormatException e) {
                    }
                    String diskPath = properties.getProperty("diskPath");
                    float version = (float) 1.0;
                    try {
                        version = Float.parseFloat(properties.getProperty("version", "1.0"));
                    } catch (NumberFormatException e) {
                    }
                    int maxObjects = 5000;
                    try {
                        maxObjects = Integer.parseInt(properties.getProperty("maxObjects", "5000"), 10);
                    } catch (NumberFormatException e) {
                    }
                    int maxSize = -1;
                    try {
                        maxSize = Integer.parseInt(properties.getProperty("maxSize", "1"), 10);
                    } catch (NumberFormatException e) {
                    }
                    int logSeverity = parseLogSeverity(properties.getProperty("logSeverity", "DEFAULT"));
                    CacheLogger logger = new DefaultCacheLogger();
                    try {
                        logger = parseLogger(properties.getProperty("logger", "org.fjank.jcache.DefaultCacheLogger"));
                        logger.setSeverity(logSeverity);
                    } catch (CacheException e) {
                        //ugh.
                        e.printStackTrace();
                    }
                    List addresses = parseAddresses(properties.getProperty("discoveryAddress", "localhost:12345"));
                    //is now parsed, create and populate the CacheAttributes.
                    CacheAttributes attributes = CacheAttributes.getDefaultCacheAttributes();
                    if (!distribute) {
                        attributes.setLocal();
                    }
                    attributes.setDefaultLogFileName(logFileName);
                    attributes.setCleanInterval(cleanInterval);
                    attributes.setDiskCacheSize(diskSize);
                    attributes.setDiskPath(diskPath);
                    attributes.setMaxObjects(maxObjects);
                    attributes.setMemoryCacheSize(maxSize);
                    this.version = version;
                    attributes.setLogger(logger);
                    Iterator iter = addresses.iterator();
                    while (iter.hasNext()) {
                        URL url = (URL) iter.next();
                        attributes.addCacheAddr(InetAddress.getByName(url.getHost()), url.getPort());
                    }
                    init(attributes);
                }
            }
        } catch (IOException e) {
            throw new IllegalStateException("Error loading configuration from properties file. Caused by:" + e.getMessage());
        } catch (CacheNotAvailableException e) {
            throw new IllegalStateException("The cache was not available. "+e.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                    throw new IllegalStateException("Failed to close stream to properties file. Caused by:" + e1.getMessage());
                }
            }
        }
    }

    /**
     * will mark the cache as "not ready" and shutdown the cache. Marking the
     * cache as "not ready" will prevent any threads from accessing the Cache
     * during shutdown. If the cache is distributed, close will unregister
     * with the distributed caching system. The method should be called as a
     * part of process termination.
     */
    public void close() {
        synchronized (this) {
            this.ready = false;
            stopServiceThreads();
            if (diskCache != null) {
                diskCache.close();
            }
        }
    }

    /**
     * will mark all objects in the cache, both disk and memory, as invalid,
     * forcing objects to be reloaded. All processes sharing the disk cache
     * are notified when the cache is flushed.
     *
     * @throws CacheException if an error occurs.
     */
    public void flush() throws CacheException {
        flushMemory();
        flushDisk();
    }

    /**
     * will mark all objects in the cache as invalid, forcing objects to be
     * reloaded. Flushing the memory cache will also invalidate memory objects
     * spooled to disk. Objects that are only cached on disk will not be
     * affected.
     *
     * @throws CacheException if an error occurs.
     */
    public void flushMemory() throws CacheException {
        Iterator iter = userRegions.keySet().iterator();
        while (iter.hasNext()) {
            Object name = iter.next();
            CacheRegion reg = (CacheRegion) userRegions.get(name);
            reg.invalidate();
        }
        CacheRegion.getRegion().invalidate();
    }

    /**
     * wll mark all objects in the cache as invalid, forcing objects to be
     * reloaded. Flushing the disk cache will also invalidate memory objects
     * spooled to disk. All processes sharing the disk cache are notified when
     * the cache is flushed.
     *
     * @throws CacheException if an error occurs.
     */
    public void flushDisk() throws CacheException {
        if (this.diskCache == null) {
            return;
        }
        diskCache.removeAll();
    }

    /**
     * returns the current version of the cache.
     *
     * @return the current version of the cache.
     */
    public float getVersion() {
        return version;
    }

    /**
     * returns true if the cache has been initialized and not closed, false
     * otherwise.
     *
     * @return true if the cache has been initialized and not closed, false
     *         otherwise.
     */
    public boolean isReady() {
        return this.ready;
    }

    /**
     * returns true if the cache is currently in distributed mode, that it is
     * distributing updates and invalidates within the site, false if all
     * cache actions are local only.
     *
     * @return true if the cache is currently in distributed mode, that it is
     *         distributing updates and invalidates within the site, false if
     *         all cache actions are local only.
     */
    public boolean isDistributed() {
        return this.attributes.isDistributed();
    }

    /**
     * will return an Enumeration of CacheObjectInfo objects describing the
     * objects in all regions in the cache. CacheObjectInfo will include
     * information such as the object name, the type, what group it is
     * associated with, the reference count, the expiration time if any and
     * object attributes.
     *
     * @return an Enumeration of CacheObjectInfo objects.
     *
     *@todo add feature to list objects in the disk cache.
     */
    public Enumeration listCacheObjects() {
        Vector temp = new Vector();
        addNamedCacheObjects(temp, CacheRegion.getRegion());
        Iterator iter = userRegions.keySet().iterator();
        while (iter.hasNext()) {
            addNamedCacheObjects(temp, (CacheRegion) userRegions.get(iter.next()));
        }
        return temp.elements();
    }

    /**
     * adds all objects in the region to the vector.
     * will also add from all the groups, if any.
     * @param vector the vector to add all objects to.
     * @param region the region to add from.
     */
    private void addNamedCacheObjects(final Vector vector, final CacheRegion region) {
        recurseObjects(vector, region);
    }

    private void recurseObjects(final Vector vector, final CacheGroup group) {
        Map objects = group.weakReferenceObjects;
        Iterator iter = objects.keySet().iterator();
        while (iter.hasNext()) {
            vector.add(new CacheObjectInfoImpl((CacheObject) objects.get(iter.next())));
        }
        Map groups = group.getGroups();
        for (Iterator iterator = groups.keySet().iterator(); iterator.hasNext();) {
            Object key = iterator.next();
            CacheGroup tmp = (CacheGroup) groups.get(key);
            recurseObjects(vector, tmp);
        }
    }

    /**
     * will return an Enumeration of CacheObjectInfo objects describing the
     * objects in the specified in the cache. CacheObjectInfo will include
     * information such as the object name, the type, what group it is
     * associated with, the reference count, the expiration time if any and
     * object attributes.
     *
     * @param region the region to get the Enumeration for.
     *
     * @return an Enumeration of CacheObjectInfo objects.
     *
     * @throws RegionNotFoundException if the named region os not present in
     *         the cache.
     *
     *@todo add feature to list objects in the disk cache.
     */
    public Enumeration listCacheObjects(final String region) throws RegionNotFoundException {
        Vector temp = new Vector();
        if (region == null) {
            throw new RegionNotFoundException("The regionName cannot be null.");
        }
        if (!userRegions.containsKey(region)) {
            throw new RegionNotFoundException("The region " + region + " is not present in the cache.");
        }
        addNamedCacheObjects(temp, (CacheRegion) userRegions.get(region));
        return temp.elements();
    }

    /**
     * returns the current attributes of the cache including the cache version
     * number, wether the cache is local or distributed, the maximum number of
     * objects in the cache, the disk cache location, and the disk cache size.
     *
     * @return the current attributes of this cache.
     *
     * @throws CacheNotAvailableException if the cache is not ready.
     */
    public CacheAttributes getAttributes(){
        return this.attributes;
    }

    /**
     * sets the log severity of the cache system. This determines wich messages
     * the cache formats and logs into the log destination. Severity's are
     * defined in the CacheLogger class.
     *
     * @param severity the severity level to set
     */
    public void setLogSeverity(final int severity) {
        this.attributes.getLogger().setSeverity(severity);
    }

    /**
     * returns a String representation of this Cache.
     *
     * @return a String representation of this Cache.
     */
    public String toString() {
        return "Fjanks FKache version " + getVersion() + " running in " + (isDistributed() ? "distributed" : "local") + " mode is " + (isReady() ? "" : "not ")
                + "ready.";
    }

    /**
     * gets the diskCache
     *
     * @return the diskCache
     */
    DiskCache getDiskCache() {
        return diskCache;
    }

    /**
     * gets the ReferenceQueue.
     *
     * @return the ReferenceQueue.
     */
    public ReferenceQueue getReferenceQueue() {
        return refQueue;
    }

    /**
     * @return
     */
    public JCacheExecutorPool getExecPool() {
        return execPool;
    }

    /**Returns an Iterator over the user defined regions.
     * @return an Iterator over the user defined regions.
     */
    Iterator userRegionNames() {
        return userRegions.keySet().iterator();
    }
    public DistributionEngine getDistributionEngine() {
        return distributionEngine;
    }
}
TOP

Related Classes of org.fjank.jcache.CacheImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.