/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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. */ package org.apache.cocoon.components.store.impl; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.Arrays; import java.util.Enumeration; import java.util.Properties; import org.apache.avalon.framework.activity.Disposable; import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.logger.AbstractLogEnabled; import org.apache.avalon.framework.parameters.ParameterException; import org.apache.avalon.framework.parameters.Parameterizable; import org.apache.avalon.framework.parameters.Parameters; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.avalon.framework.service.Serviceable; import org.apache.avalon.framework.thread.ThreadSafe; import org.apache.cocoon.Constants; import org.apache.cocoon.util.IOUtils; import org.apache.commons.collections.iterators.IteratorEnumeration; import org.apache.excalibur.store.Store; import org.apache.excalibur.store.StoreJanitor; import org.apache.jcs.access.GroupCacheAccess; import org.apache.jcs.access.exception.CacheException; import org.apache.jcs.engine.control.CompositeCache; import org.apache.jcs.engine.control.CompositeCacheManager; import org.apache.jcs.engine.memory.MemoryCache; /** * This is the default store implementation based on JCS * http://jakarta.apache.org/jcs/BasicJCSConfiguration.html * * @version $Id$ */ public class JCSDefaultStore extends AbstractLogEnabled implements Store, Contextualizable, Parameterizable, Initializable, Disposable, ThreadSafe, Serviceable { /** The JCS configuration properties */ protected Properties properties; /** The JCS region name */ protected String region; /** JCS Cache manager */ private CompositeCacheManager cacheManager; /** The Java Cache System object */ private JCSCacheAccess jcs; /** The location of the JCS default properties file */ private static final String DEFAULT_PROPERTIES = "org/apache/cocoon/components/store/default.ccf"; /** The context containing the work and the cache directory */ private Context context; /** Service Manager */ private ServiceManager manager; /** Store janitor */ private StoreJanitor janitor; /* (non-Javadoc) * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) */ public void contextualize(Context aContext) throws ContextException { this.context = aContext; } /* (non-Javadoc) * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) */ public void service(ServiceManager aManager) throws ServiceException { this.manager = aManager; this.janitor = (StoreJanitor)this.manager.lookup(StoreJanitor.ROLE); } /* (non-Javadoc) * @see org.apache.avalon.framework.parameters.Parameterizable#parameterize(org.apache.avalon.framework.parameters.Parameters) */ public void parameterize(Parameters parameters) throws ParameterException { // TODO describe options this.region = parameters.getParameter("region-name","main"); Properties defaults = new Properties(); try { String defaultsFile = this.getDefaultPropertiesFile(); if (defaultsFile != null) { defaults.load(Thread.currentThread().getContextClassLoader(). getResourceAsStream(defaultsFile)); } } catch (IOException e) { throw new ParameterException("Failure loading cache defaults",e); } this.properties = new Properties(defaults); String[] names = parameters.getNames(); for (int i = 0; i < names.length; i++) { if (names[i].startsWith("jcs.")) { this.properties.put(names[i], parameters.getParameter(names[i])); } } int maxobjects = parameters.getParameterAsInteger("maxobjects", -1); if (maxobjects != -1) { String key = "jcs.region." + region + ".cacheattributes.MaxObjects"; this.properties.setProperty(key, String.valueOf(maxobjects)); } // get the directory to use try { final File workDir = (File) context.get(Constants.CONTEXT_WORK_DIR); if (parameters.getParameterAsBoolean("use-cache-directory", false)) { final File cacheDir = (File) context.get(Constants.CONTEXT_CACHE_DIR); if (getLogger().isDebugEnabled()) { getLogger().debug("Using cache directory: " + cacheDir); } setDirectory(cacheDir); } else if (parameters.getParameterAsBoolean("use-work-directory", false)) { if (getLogger().isDebugEnabled()) { getLogger().debug("Using work directory: " + workDir); } setDirectory(workDir); } else if (parameters.getParameter("directory", null) != null) { String dir = parameters.getParameter("directory"); dir = IOUtils.getContextFilePath(workDir.getPath(), dir); if (getLogger().isDebugEnabled()) { getLogger().debug("Using directory: " + dir); } setDirectory(new File(dir)); } else { if (getLogger().isDebugEnabled()) { getLogger().debug("Using default directory: " + workDir); } setDirectory(workDir); } } catch (ContextException ce) { throw new ParameterException("Unable to get directory information from context.", ce); } catch (IOException e) { throw new ParameterException("Unable to set directory", e); } } /* (non-Javadoc) * @see org.apache.avalon.framework.activity.Initializable#initialize() */ public void initialize() throws Exception { this.cacheManager = CompositeCacheManager.getUnconfiguredInstance(); this.cacheManager.configure(this.properties); this.jcs = new JCSCacheAccess(cacheManager.getCache(region)); this.janitor.register(this); } /* (non-Javadoc) * @see org.apache.avalon.framework.activity.Disposable#dispose() */ public void dispose() { if( this.janitor != null ) { this.janitor.unregister( this ); } if ( this.jcs != null ) { this.jcs.dispose(); this.jcs = null; } if ( this.cacheManager != null ) { this.cacheManager.release(); this.cacheManager = null; } this.properties = null; if ( this.manager != null ) { this.manager.release( this.janitor ); this.janitor = null; this.manager = null; } } protected String getDefaultPropertiesFile() { return DEFAULT_PROPERTIES; } /** * Sets the disk cache location. */ private void setDirectory(final File directory) throws IOException { /* Does directory exist? */ if (!directory.exists()) { /* Create it anew */ if (!directory.mkdirs()) { throw new IOException( "Error creating store directory '" + directory.getAbsolutePath() + "'. "); } } /* Is given file actually a directory? */ if (!directory.isDirectory()) { throw new IOException("'" + directory.getAbsolutePath() + "' is not a directory"); } /* Is directory readable and writable? */ if (!(directory.canRead() && directory.canWrite())) { throw new IOException( "Directory '" + directory.getAbsolutePath() + "' is not readable/writable" ); } this.properties.setProperty("jcs.auxiliary.DC.attributes.DiskPath", directory.getAbsolutePath()); } // ---------------------------------------------------- Store implementation /* (non-Javadoc) * @see org.apache.excalibur.store.Store#get(java.lang.Object) */ public Object get(Object key) { Object value = this.jcs.get(key); if (getLogger().isDebugEnabled()) { if (value != null) { getLogger().debug("Found key: " + key); } else { getLogger().debug("NOT Found key: " + key); } } return value; } /* (non-Javadoc) * @see org.apache.excalibur.store.Store#store(java.lang.Object, java.lang.Object) */ public void store(Object key, Object value) throws IOException { if (getLogger().isDebugEnabled()) { getLogger().debug("Store object " + value + " with key "+ key); } try { this.jcs.put(key, value); } catch (CacheException ce) { getLogger().error("Failure storing object ", ce); } } /* (non-Javadoc) * @see org.apache.excalibur.store.Store#free() */ public void free() { // TODO Find a better way MemoryCache memoryCache = this.cacheManager.getCache(region).getMemoryCache(); Object[] keys = memoryCache.getKeyArray(); if ( keys != null && keys.length > 0 ) { final Object key = keys[0]; try { memoryCache.remove((Serializable)key); } catch (Exception ignore) { } } } /* (non-Javadoc) * @see org.apache.excalibur.store.Store#clear() */ public void clear() { if (getLogger().isDebugEnabled()) { getLogger().debug("Clearing the store"); } try { this.jcs.remove(); } catch (CacheException ce) { getLogger().error("Failure clearing store", ce); } } /* (non-Javadoc) * @see org.apache.excalibur.store.Store#remove(java.lang.Object) */ public void remove(Object key) { if (getLogger().isDebugEnabled()) { getLogger().debug("Removing item " + key); } try { this.jcs.remove(key); } catch (CacheException ce) { getLogger().error("Failure removing object", ce); } } /* (non-Javadoc) * @see org.apache.excalibur.store.Store#containsKey(java.lang.Object) */ public boolean containsKey(Object key) { return this.jcs.get(key) != null; } /* (non-Javadoc) * @see org.apache.excalibur.store.Store#keys() */ public Enumeration keys() { // TODO Find a better way final MemoryCache memoryCache = this.cacheManager.getCache(region).getMemoryCache(); final Object[] keys = memoryCache.getKeyArray(); return new IteratorEnumeration(Arrays.asList(keys).iterator()); //return new IteratorEnumeration(this.jcs.getGroupKeys("").iterator()); } /* (non-Javadoc) * @see org.apache.excalibur.store.Store#size() */ public int size() { // TODO Find a better way MemoryCache memoryCache = this.cacheManager.getCache(region).getMemoryCache(); return memoryCache.getSize(); //return this.jcs.getSize(); } private static class JCSCacheAccess extends GroupCacheAccess { private JCSCacheAccess(CompositeCache cacheControl) { super(cacheControl); } /*private int getSize() { return super.cacheControl.getSize(); }*/ } }