You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@turbine.apache.org by se...@apache.org on 2006/06/27 02:55:23 UTC

svn commit: r417323 - in /jakarta/turbine/fulcrum/trunk/cache: ./ src/java/org/apache/fulcrum/cache/ src/java/org/apache/fulcrum/cache/impl/ src/test/ src/test/org/apache/fulcrum/cache/ xdocs/

Author: seade
Date: Mon Jun 26 17:55:22 2006
New Revision: 417323

URL: http://svn.apache.org/viewvc?rev=417323&view=rev
Log:
TRB-17: Added new JCSCacheService.  Thanks to Thomas Vandahl.

Added:
    jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/impl/JCSCacheService.java
    jakarta/turbine/fulcrum/trunk/cache/src/test/cache.ccf
    jakarta/turbine/fulcrum/trunk/cache/src/test/org/apache/fulcrum/cache/JCSCacheTest.java
Modified:
    jakarta/turbine/fulcrum/trunk/cache/maven.xml
    jakarta/turbine/fulcrum/trunk/cache/project.xml
    jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/CachedObject.java
    jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/RefreshableCachedObject.java
    jakarta/turbine/fulcrum/trunk/cache/src/test/TestComponentConfig.xml
    jakarta/turbine/fulcrum/trunk/cache/src/test/TestRoleConfig.xml
    jakarta/turbine/fulcrum/trunk/cache/xdocs/changes.xml
    jakarta/turbine/fulcrum/trunk/cache/xdocs/index.xml

Modified: jakarta/turbine/fulcrum/trunk/cache/maven.xml
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/maven.xml?rev=417323&r1=417322&r2=417323&view=diff
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/maven.xml (original)
+++ jakarta/turbine/fulcrum/trunk/cache/maven.xml Mon Jun 26 17:55:22 2006
@@ -1,7 +1,15 @@
-<project default="jar:jar" xmlns:maven="jelly:maven" xmlns:j="jelly:core" xmlns:util="jelly:util">
+<project default="jar:jar" xmlns:maven="jelly:maven" xmlns:j="jelly:core" xmlns:util="jelly:util" xmlns:ant="jelly:ant">
 
   <preGoal name="site">
     <attainGoal name="maven-jcoverage-plugin:deregister"/>
+  </preGoal>
+
+  <preGoal name="test:test">
+    <ant:copy todir="${maven.test.dest}">
+      <ant:fileset dir="${basedir}/src/test">
+        <ant:include name="*.ccf" />
+      </ant:fileset>
+    </ant:copy>
   </preGoal>
 
 </project>

Modified: jakarta/turbine/fulcrum/trunk/cache/project.xml
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/project.xml?rev=417323&r1=417322&r2=417323&view=diff
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/project.xml (original)
+++ jakarta/turbine/fulcrum/trunk/cache/project.xml Mon Jun 26 17:55:22 2006
@@ -3,7 +3,7 @@
   <extend>${basedir}/../project.xml</extend>
   <id>fulcrum-cache</id>
   <name>Fulcrum Cache Component</name>
-  <currentVersion>1.0.5</currentVersion>
+  <currentVersion>1.1-dev</currentVersion>
   <versions>
     <version>
       <id>1.0.5</id>
@@ -18,26 +18,49 @@
       <version>3.1</version>
       <url>http://jakarta.apache.org/commons/collections.html</url>
     </dependency>
-    
+
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>2.1</version>
+    </dependency> 
+
     <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
       <version>1.0.3</version>
     </dependency> 
-          
+
+  <!--  For a more recent release of JCS:
+    <dependency>
+      <groupId>concurrent</groupId>
+      <artifactId>concurrent</artifactId>
+      <version>1.0</version>
+      <type>jar</type>
+    </dependency>
+  -->
+
     <dependency>
       <groupId>ehcache</groupId>
       <artifactId>ehcache</artifactId>
       <version>1.2beta4</version>
     </dependency>         
-    
+
+    <dependency>
+      <groupId>jcs</groupId>
+      <artifactId>jcs</artifactId>
+      <!-- version>1.2.7.7</version -->
+      <version>20030822.182132</version>
+      <type>jar</type>
+    </dependency>
+
     <!--  Needed only for testing -->
     <dependency>
       <groupId>fulcrum</groupId>
       <artifactId>fulcrum-testcontainer</artifactId>
       <version>1.0.5</version>
     </dependency>
-    
+
     <dependency>
       <groupId>fulcrum</groupId>
       <artifactId>fulcrum-yaafi</artifactId>
@@ -61,9 +84,6 @@
     <report>maven-jxr-plugin</report>      
     <report>maven-junit-report-plugin</report>
 
-    
-    
   </reports> 
 
 </project>
-

Modified: jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/CachedObject.java
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/CachedObject.java?rev=417323&r1=417322&r2=417323&view=diff
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/CachedObject.java (original)
+++ jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/CachedObject.java Mon Jun 26 17:55:22 2006
@@ -1,8 +1,7 @@
 package org.apache.fulcrum.cache;
 
-
 /*
- * Copyright 2001-2004 The Apache Software Foundation.
+ * Copyright 2001-2006 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.
@@ -17,7 +16,6 @@
  * limitations under the License.
  */
 
-
 import java.io.Serializable;
 
 /**
@@ -35,6 +33,11 @@
 public class CachedObject
     implements Serializable
 {
+
+    /**
+     * Serialization key
+     */
+    private static final long serialVersionUID = -9107764093769042092L;
 
     /** Cache the object with the Default TTL */
     public static final int DEFAULT = 0;

Modified: jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/RefreshableCachedObject.java
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/RefreshableCachedObject.java?rev=417323&r1=417322&r2=417323&view=diff
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/RefreshableCachedObject.java (original)
+++ jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/RefreshableCachedObject.java Mon Jun 26 17:55:22 2006
@@ -1,8 +1,7 @@
 package org.apache.fulcrum.cache;
 
-
 /*
- * Copyright 2001-2004 The Apache Software Foundation.
+ * Copyright 2001-2006 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.
@@ -17,7 +16,6 @@
  * limitations under the License.
  */
 
-
 /**
  * The idea of the RefreshableCachedObject is that, rather than
  * removing items from the cache when they become stale, we'll tell them to
@@ -37,6 +35,11 @@
 public class RefreshableCachedObject
     extends CachedObject
 {
+
+    /**
+     * Serialization key
+     */
+    private static final long serialVersionUID = 4072572956381768087L;
 
     /**
      * How long to wait before removing an untouched object from the cache.

Added: jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/impl/JCSCacheService.java
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/impl/JCSCacheService.java?rev=417323&view=auto
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/impl/JCSCacheService.java (added)
+++ jakarta/turbine/fulcrum/trunk/cache/src/java/org/apache/fulcrum/cache/impl/JCSCacheService.java Mon Jun 26 17:55:22 2006
@@ -0,0 +1,370 @@
+package org.apache.fulcrum.cache.impl;
+
+/*
+ * Copyright 2006 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.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.avalon.framework.activity.Disposable;
+import org.apache.avalon.framework.activity.Initializable;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.fulcrum.cache.CachedObject;
+import org.apache.fulcrum.cache.GlobalCacheService;
+import org.apache.fulcrum.cache.ObjectExpiredException;
+import org.apache.fulcrum.cache.RefreshableCachedObject;
+import org.apache.jcs.JCS;
+import org.apache.jcs.access.exception.CacheException;
+import org.apache.jcs.engine.ElementAttributes;
+
+/**
+ * Default implementation of JCSCacheService
+ *
+ * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
+ * @version $Id:$
+ */
+public class JCSCacheService
+    extends AbstractLogEnabled
+    implements GlobalCacheService, Runnable, Configurable, Disposable, Initializable, ThreadSafe
+{
+    /**
+     * Cache check frequency in Millis (1000 Millis = 1 second).
+     * Value must be > 0.
+     * Default = 5 seconds
+     */
+    public static final long DEFAULT_CACHE_CHECK_FREQUENCY = 5000; // 5 seconds
+
+    /**
+     * cacheCheckFrequency (default - 5 seconds)
+     */
+    private long cacheCheckFrequency;
+    
+    /**
+     * Instance of the JCS cache
+     */
+    private JCS cacheManager;
+
+    /**
+     * JCS region to use
+     */
+    private String region;
+
+    /**
+     * Path name of the JCS configuration file
+     */
+    private String configFile;
+
+    /**
+     * Constant value which provides a group name
+     */
+    private static String group = "default_group";
+
+    /** thread for refreshing stale items in the cache */
+    private Thread refreshing;
+
+    /** flag to stop the housekeeping thread when the component is disposed. */
+    private boolean continueThread;
+
+    // ---------------- Avalon Lifecycle Methods ---------------------
+
+    /**
+     * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
+     */
+    public void configure(Configuration config) throws ConfigurationException
+    {
+        cacheCheckFrequency = config.getChild("cacheCheckFrequency").getValueAsLong(DEFAULT_CACHE_CHECK_FREQUENCY);
+        region = config.getChild("region").getValue("fulcrum");
+        configFile = config.getChild("configurationFile").getValue("/cache.ccf");
+    }
+
+    /**
+     * @see org.apache.avalon.framework.activity.Initializable#initialize()
+     */
+    public void initialize() throws Exception
+    {
+        JCS.setConfigFilename(configFile);
+        cacheManager = JCS.getInstance(region);
+
+        // Start housekeeping thread.
+        continueThread = true;
+        refreshing = new Thread(this);
+
+        // Indicate that this is a system thread. JVM will quit only when 
+        // there are no more active user threads. Settings threads spawned
+        // internally by Turbine as daemons allows commandline applications
+        // using Turbine to terminate in an orderly manner.
+        refreshing.setDaemon(true);
+        refreshing.setName("JCSCacheService Refreshing");
+        refreshing.start();
+
+        getLogger().debug("JCSCacheService started.");
+    }
+
+    /**
+     * @see org.apache.avalon.framework.activity.Disposable#dispose()
+     */
+    public void dispose()
+    {
+        try
+        {
+            cacheManager.remove();
+        }
+        catch (CacheException e)
+        {
+            // do nothing
+        }
+
+        continueThread = false;
+        refreshing.interrupt();
+
+        cacheManager = null;
+        getLogger().debug("JCSCacheService stopped.");
+    }
+
+    /**
+     * @see org.apache.fulcrum.cache.GlobalCacheService#getObject(java.lang.String)
+     */
+    public CachedObject getObject(String id) throws ObjectExpiredException
+    {
+        CachedObject obj = (CachedObject) cacheManager.getFromGroup(id, group);
+
+        if (obj == null)
+        {
+            // Not in the cache.
+            throw new ObjectExpiredException();
+        }
+
+        if (obj.isStale())
+        {
+            if (obj instanceof RefreshableCachedObject)
+            {
+                RefreshableCachedObject rco = (RefreshableCachedObject) obj;
+                if (rco.isUntouched())
+                {
+                    // Do not refresh an object that has exceeded TimeToLive
+                    removeObject(id);
+                    throw new ObjectExpiredException();
+                }
+
+                // Refresh Object
+                rco.refresh();
+                if (rco.isStale())
+                {
+                    // Object is Expired, remove it from cache.
+                    removeObject(id);
+                    throw new ObjectExpiredException();
+                }
+            }
+            else
+            {
+                // Expired.
+                removeObject(id);
+                throw new ObjectExpiredException();
+            }
+        }
+
+        if (obj instanceof RefreshableCachedObject)
+        {
+            // notify it that it's being accessed.
+            RefreshableCachedObject rco = (RefreshableCachedObject) obj;
+            rco.touch();
+        }
+
+        return obj;
+    }
+
+    /**
+     * @see org.apache.fulcrum.cache.GlobalCacheService#addObject(java.lang.String, org.apache.fulcrum.cache.CachedObject)
+     */
+    public void addObject(String id, CachedObject o)
+    {
+        try
+        {
+            if (!(o.getContents() instanceof Serializable))
+            {
+                getLogger().warn("Object with id [" + id + "] is not serializable. Expect problems with auxiliary caches.");
+            }
+            
+            ElementAttributes attrib = (ElementAttributes)cacheManager.getElementAttributes();
+
+            if (o instanceof RefreshableCachedObject)
+            {
+                attrib.setIsEternal(true);
+            }
+            else
+            {
+                attrib.setIsEternal(false);
+                attrib.setMaxLifeSeconds((o.getExpires() + 500)/ 1000);
+            }
+
+            attrib.setLastAccessTimeNow();
+            
+            // I know this is not nice, but setCreateTime() is missing and the fields are public ...  
+            attrib.createTime = System.currentTimeMillis();
+            cacheManager.putInGroup(id, group, o, attrib);
+        }
+        catch (CacheException e)
+        {
+            getLogger().error("Could not add object " + id + " to cache", e);
+        }
+    }
+
+    /**
+     * @see org.apache.fulcrum.cache.GlobalCacheService#removeObject(java.lang.String)
+     */
+    public void removeObject(String id)
+    {
+        cacheManager.remove(id, group);
+    }
+
+    /**
+     * @see org.apache.fulcrum.cache.GlobalCacheService#getKeys()
+     */
+    public List getKeys()
+    {
+        ArrayList keys = new ArrayList();
+
+        keys.addAll(cacheManager.getGroupKeys(group));
+        return keys;
+    }
+
+    /**
+     * @see org.apache.fulcrum.cache.GlobalCacheService#getCachedObjects()
+     */
+    public List getCachedObjects()
+    {
+        ArrayList values = new ArrayList();
+
+        for (Iterator i = cacheManager.getGroupKeys(group).iterator(); i.hasNext();)
+        {
+            Object o = cacheManager.getFromGroup(i.next(), group);
+            if (o != null)
+            {
+                values.add(o);
+            }
+        }
+
+        return values;
+    }
+
+    /**
+     * Circle through the cache and refresh stale objects.  Frequency
+     * is determined by the cacheCheckFrequency property.
+     */
+    public void run()
+    {
+        while (continueThread)
+        {
+            // Sleep for amount of time set in cacheCheckFrequency -
+            // default = 5 seconds.
+            try
+            {
+                Thread.sleep(cacheCheckFrequency);
+            }
+            catch (InterruptedException exc)
+            {
+                if (!continueThread) return;
+            }
+            
+            for (Iterator i = cacheManager.getGroupKeys(group).iterator(); i.hasNext();)
+            {
+                String key= (String)i.next();
+                CachedObject o = (CachedObject)cacheManager.getFromGroup(key, group);
+                if (o == null)
+                {
+                    removeObject(key);
+                }
+                else
+                {
+                    if (o instanceof RefreshableCachedObject)
+                    {
+                        RefreshableCachedObject rco = (RefreshableCachedObject) o;
+                        if (rco.isUntouched())
+                        {
+                            cacheManager.remove(key, group);
+                        }
+                        else if (rco.isStale())
+                        {
+                            rco.refresh();
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @see org.apache.fulcrum.cache.GlobalCacheService#getCacheSize()
+     */
+    public int getCacheSize() throws IOException
+    {
+        // This is evil!
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ObjectOutputStream out = new ObjectOutputStream(baos);
+        Set keys = cacheManager.getGroupKeys(group);
+
+        for (Iterator i = keys.iterator(); i.hasNext();)
+        {
+            out.writeObject(cacheManager.getFromGroup(i.next(), group));
+        }
+
+        out.flush();
+
+        //
+        // Subtract 4 bytes from the length, because the serialization
+        // magic number (2 bytes) and version number (2 bytes) are
+        // both written to the stream before the object
+        //
+        int objectsize = baos.toByteArray().length - 4 * keys.size();
+        return objectsize;
+    }
+
+    /**
+     * @see org.apache.fulcrum.cache.GlobalCacheService#getNumberOfObjects()
+     */
+    public int getNumberOfObjects()
+    {
+        int count = 0;
+        
+        for (Iterator i = cacheManager.getGroupKeys(group).iterator();  i.hasNext();)
+        {
+            if (cacheManager.getFromGroup(i.next(), group) != null) 
+            {
+                count++;
+            }
+        }
+
+        return count;
+    }
+
+    /**
+     * @see org.apache.fulcrum.cache.GlobalCacheService#flushCache()
+     */
+    public void flushCache()
+    {
+        cacheManager.invalidateGroup(group);
+    }
+}

Modified: jakarta/turbine/fulcrum/trunk/cache/src/test/TestComponentConfig.xml
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/src/test/TestComponentConfig.xml?rev=417323&r1=417322&r2=417323&view=diff
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/src/test/TestComponentConfig.xml (original)
+++ jakarta/turbine/fulcrum/trunk/cache/src/test/TestComponentConfig.xml Mon Jun 26 17:55:22 2006
@@ -1,4 +1,9 @@
 <componentConfig>
     <cache cacheInitialSize="20" cacheCheckFrequency="5"/>
     <ehcache/>
+    <jcscache>
+    	<cacheCheckFrequency>5000</cacheCheckFrequency>
+        <region>Cache_Test</region>
+        <configurationFile>/cache.ccf</configurationFile>
+    </jcscache>
 </componentConfig>

Modified: jakarta/turbine/fulcrum/trunk/cache/src/test/TestRoleConfig.xml
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/src/test/TestRoleConfig.xml?rev=417323&r1=417322&r2=417323&view=diff
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/src/test/TestRoleConfig.xml (original)
+++ jakarta/turbine/fulcrum/trunk/cache/src/test/TestRoleConfig.xml Mon Jun 26 17:55:22 2006
@@ -9,4 +9,9 @@
         name="org.apache.fulcrum.cache.EHCacheService"
         shorthand="ehcache"
         default-class="org.apache.fulcrum.cache.impl.DefaultEHCacheService"/>        
+
+    <role
+        name="org.apache.fulcrum.cache.GlobalCacheService_JCS"
+        shorthand="jcscache"
+        default-class="org.apache.fulcrum.cache.impl.JCSCacheService"/>
 </role-list>

Added: jakarta/turbine/fulcrum/trunk/cache/src/test/cache.ccf
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/src/test/cache.ccf?rev=417323&view=auto
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/src/test/cache.ccf (added)
+++ jakarta/turbine/fulcrum/trunk/cache/src/test/cache.ccf Mon Jun 26 17:55:22 2006
@@ -0,0 +1,11 @@
+# Java Caching System configuration
+
+jcs.default=
+jcs.default.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
+jcs.default.cacheattributes.MaxObjects=100
+jcs.default.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache
+
+jcs.region.Cache_Test=
+jcs.region.Cache_Test.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
+jcs.region.Cache_Test.cacheattributes.MaxObjects=100
+jcs.region.Cache_Test.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache

Added: jakarta/turbine/fulcrum/trunk/cache/src/test/org/apache/fulcrum/cache/JCSCacheTest.java
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/src/test/org/apache/fulcrum/cache/JCSCacheTest.java?rev=417323&view=auto
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/src/test/org/apache/fulcrum/cache/JCSCacheTest.java (added)
+++ jakarta/turbine/fulcrum/trunk/cache/src/test/org/apache/fulcrum/cache/JCSCacheTest.java Mon Jun 26 17:55:22 2006
@@ -0,0 +1,579 @@
+package org.apache.fulcrum.cache;
+
+/*
+ * Copyright 2006 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.
+ */
+
+// Cactus and Junit imports
+
+import java.util.ConcurrentModificationException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.avalon.framework.component.ComponentException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fulcrum.testcontainer.BaseUnitTest;
+
+/**
+ * JCSCacheTest
+ *
+ * @author <a href="tv@apache.org">Thomas Vandahl</a>
+ * @version $Id:$
+ */
+public class JCSCacheTest extends BaseUnitTest
+{
+    private GlobalCacheService globalCache = null;
+    private static final String cacheKey = "CacheKey";
+    private static final String cacheKey_2 = "CacheKey_2";
+    public static final String SKIP_TESTS_KEY = "fulcrum.cache.skip.long.tests";
+    private static final Log LOG = LogFactory.getLog(JCSCacheTest.class);
+
+    /**
+     * Defines the testcase name for JUnit.
+     *
+     * @param name the testcase's name.
+     */
+    public JCSCacheTest(String name)
+    {
+        super(name);
+    }
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        try
+        {
+            globalCache = (GlobalCacheService) this.lookup(GlobalCacheService.ROLE + "_JCS");
+        }
+        catch (ComponentException e)
+        {
+            e.printStackTrace();
+            fail(e.getMessage());
+        }
+    }
+
+    /**
+     * Simple test that verify an object can be created and deleted.
+     *
+     * @throws Exception
+     */
+    public void testSimpleAddGetCacheObject() throws Exception
+    {
+        String testString = new String("This is a test");
+        Object retrievedObject = null;
+        CachedObject cacheObject1 = null;
+        // Create object
+        cacheObject1 = new CachedObject(testString);
+        assertNotNull("Failed to create a cachable object 1", cacheObject1);
+        // Add object to cache
+        globalCache.addObject(cacheKey, cacheObject1);
+        // Get object from cache
+        retrievedObject = globalCache.getObject(cacheKey);
+        assertNotNull("Did not retrieved a cached object 1", retrievedObject);
+        assertTrue("Did not retrieved a correct, expected cached object 1", retrievedObject == cacheObject1);
+        // Remove object from cache
+        globalCache.removeObject(cacheKey);
+        // Verify object removed from cache
+        retrievedObject = null;
+        cacheObject1 = null;
+        try
+        {
+            retrievedObject = globalCache.getObject(cacheKey);
+            assertNull(
+                "Retrieved the deleted cached object 1 and did not get expected ObjectExpiredException",
+                retrievedObject);
+            assertNotNull("Did not get expected ObjectExpiredException retrieving a deleted object", retrievedObject);
+        }
+        catch (ObjectExpiredException e)
+        {
+            assertNull(
+                "Retrieved the deleted cached object 1, but caught expected ObjectExpiredException exception",
+                retrievedObject);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        // Remove object from cache that does NOT exist in the cache
+        globalCache.removeObject(cacheKey);
+    }
+
+    /**
+     * Simple test that adds, retrieves, and deletes 2 object.
+     *
+     * @throws Exception
+     */
+    public void test2ObjectAddGetCachedObject() throws Exception
+    {
+        String testString = new String("This is a test");
+        Object retrievedObject = null;
+        CachedObject cacheObject1 = null;
+        CachedObject cacheObject2 = null;
+        // Create and add Object #1
+        cacheObject1 = new CachedObject(testString);
+        assertNotNull("Failed to create a cachable object 1", cacheObject1);
+        globalCache.addObject(cacheKey, cacheObject1);
+        retrievedObject = globalCache.getObject(cacheKey);
+        assertNotNull("Did not retrieved a cached object 1", retrievedObject);
+        assertEquals("Did not retrieved correct cached object", cacheObject1, retrievedObject);
+        // Create and add Object #2
+        cacheObject2 = new CachedObject(testString);
+        assertNotNull("Failed to create a cachable object 2", cacheObject2);
+        globalCache.addObject(cacheKey_2, cacheObject2);
+        retrievedObject = globalCache.getObject(cacheKey_2);
+        assertNotNull("Did not retrieved a cached object 2", retrievedObject);
+        assertEquals("Did not retrieved correct cached object 2", cacheObject2, retrievedObject);
+        // Get object #1
+        retrievedObject = globalCache.getObject(cacheKey);
+        assertNotNull("Did not retrieved a cached object 1. Attempt #2", retrievedObject);
+        assertEquals("Did not retrieved correct cached object 1. Attempt #2", cacheObject1, retrievedObject);
+        // Get object #1
+        retrievedObject = globalCache.getObject(cacheKey);
+        assertNotNull("Did not retrieved a cached object 1. Attempt #3", retrievedObject);
+        assertEquals("Did not retrieved correct cached object 1. Attempt #3", cacheObject1, retrievedObject);
+        // Get object #2
+        retrievedObject = globalCache.getObject(cacheKey_2);
+        assertNotNull("Did not retrieved a cached object 2. Attempt #2", retrievedObject);
+        assertEquals("Did not retrieved correct cached object 2 Attempt #2", cacheObject2, retrievedObject);
+        // Remove objects
+        globalCache.removeObject(cacheKey);
+        globalCache.removeObject(cacheKey_2);
+    }
+
+    /**
+     * Verify that an object will throw the ObjectExpiredException
+     * when it now longer exists in cache.
+     *
+     * @throws Exception
+     */
+    public void testObjectExpiration() throws Exception
+    {
+        String testString = new String("This is a test");
+        Object retrievedObject = null;
+        CachedObject cacheObject = null;
+        // Create and add Object that expires in 1000 millis (1 second)
+        cacheObject = new CachedObject(testString, 1000);
+        assertNotNull("Failed to create a cachable object", cacheObject);
+        long addTime = System.currentTimeMillis();
+        globalCache.addObject(cacheKey, cacheObject);
+        // Try to get un-expired object
+        try
+        {
+            retrievedObject = null;
+            retrievedObject = globalCache.getObject(cacheKey);
+            assertNotNull("Did not retrieved a cached object", retrievedObject);
+            assertEquals("Did not retrieved correct cached object", cacheObject, retrievedObject);
+        }
+        catch (ObjectExpiredException e)
+        {
+            assertTrue("Object expired early ( " + (System.currentTimeMillis() - addTime) + " millis)", false);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        // Sleep 1500 Millis (1.5 seconds)
+        Thread.sleep(1500);
+        // Try to get expired object
+        try
+        {
+            retrievedObject = null;
+            retrievedObject = globalCache.getObject(cacheKey);
+            assertNull(
+                "Retrieved the expired cached object  and did not get expected ObjectExpiredException",
+                retrievedObject);
+            assertNotNull("Did not get expected ObjectExpiredException retrieving an expired object", retrievedObject);
+        }
+        catch (ObjectExpiredException e)
+        {
+            assertNull(
+                "Retrieved the expired cached object, but caught expected ObjectExpiredException exception",
+                retrievedObject);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        // Remove objects
+        globalCache.removeObject(cacheKey);
+    }
+
+    /**
+     * Verify the all object will be flushed from the cache.
+     *
+     * This test can take server minutes.
+     *
+     * @throws Exception
+     */
+    public void testCacheFlush() throws Exception
+    {
+        String testString = new String("This is a test");
+        CachedObject cacheObject = null;
+        // Create and add Object that expires in 1 turbine Refresh + 1 millis
+        cacheObject = new CachedObject(testString, (getCacheRefresh() * 5) + 1);
+        assertNotNull("Failed to create a cachable object", cacheObject);
+        globalCache.addObject(cacheKey, cacheObject);
+        // 1 Refresh
+        Thread.sleep(getCacheRefresh() + 1);
+        assertTrue("No object in cache before flush", (0 < globalCache.getNumberOfObjects()));
+        // Flush Cache
+        globalCache.flushCache();
+        // Wait 15 seconds, 3 Refresh
+        Thread.sleep((getCacheRefresh() * 2) + 1);
+        assertEquals("After refresh", 0, globalCache.getNumberOfObjects());
+        // Remove objects
+        globalCache.removeObject(cacheKey);
+    }
+
+    /**
+     * Verify the Cache count is correct.
+     *
+     * @throws Exception
+     */
+    public void testObjectCount() throws Exception
+    {
+        assertNotNull("Could not retrive cache service.", globalCache);
+        // Create and add Object that expires in 1.5 turbine Refresh
+        long expireTime = getCacheRefresh() + getCacheRefresh() / 2;
+        CachedObject cacheObject = new CachedObject("This is a test", expireTime);
+        assertNotNull("Failed to create a cachable object", cacheObject);
+        globalCache.addObject("testObjectCount", cacheObject);
+        assertEquals("After adding 1 Object", 1, globalCache.getNumberOfObjects());
+        // Wait until we're passed 1 refresh, but not half way.
+        Thread.sleep(getCacheRefresh() + getCacheRefresh() / 3);
+        assertEquals("After one refresh", 1, globalCache.getNumberOfObjects());
+        // Wait until we're passed 2 more refreshes
+        Thread.sleep((getCacheRefresh() * 2) + getCacheRefresh() / 3);
+        assertEquals("After three refreshes", 0, globalCache.getNumberOfObjects());
+    }
+
+    /**
+     * Verfy a refreshable object will refreshed in the following cases:
+     * - The object is retrieved via getObject an it is stale.
+     * - The object is determied to be stale during a cache
+     *   refresh
+     *
+     * This test can take serveral minutes.
+     *
+     * @throws Exception
+     */
+    public void testRefreshableObject() throws Exception
+    {
+        Object retrievedObject = null;
+        RefreshableCachedObject cacheObject = null;
+        // Create and add Object that expires in TEST_EXPIRETIME millis.
+        cacheObject = new RefreshableCachedObject(new RefreshableObject(), getTestExpireTime());
+        assertNotNull("Failed to create a cachable object", cacheObject);
+        long addTime = System.currentTimeMillis();
+        globalCache.addObject("refreshableObject", cacheObject);
+        // Try to get un-expired object
+        try
+        {
+            retrievedObject = null;
+            retrievedObject = globalCache.getObject("refreshableObject");
+            assertNotNull("Did not retrieved a cached object", retrievedObject);
+            assertEquals("Did not retrieved correct cached object", cacheObject, retrievedObject);
+        }
+        catch (ObjectExpiredException e)
+        {
+            assertTrue("Object expired early ( " + (System.currentTimeMillis() - addTime) + " millis)", false);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        // Wait 1 Turbine cache refresh + 1 second.
+        Thread.sleep(getTestExpireTime() + 1000);
+        // Try to get expired object
+        try
+        {
+            retrievedObject = null;
+            retrievedObject = globalCache.getObject("refreshableObject");
+            assertNotNull("Did not retrieved a cached object, after sleep", retrievedObject);
+            assertNotNull(
+                "Cached object has no contents, after sleep.",
+                ((RefreshableCachedObject) retrievedObject).getContents());
+            assertTrue(
+                "Object did not refresh.",
+                (((RefreshableObject) ((RefreshableCachedObject) retrievedObject).getContents()).getRefreshCount()
+                    > 0));
+        }
+        catch (ObjectExpiredException e)
+        {
+            assertTrue(
+                "Received unexpected ObjectExpiredException exception "
+                    + "when retrieving refreshable object after ( "
+                    + (System.currentTimeMillis() - addTime)
+                    + " millis)",
+                false);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        // See if object will expires (testing every second for 100 seconds.  It should not!
+        for (int i = 0; i < 100; i++)
+        {
+            Thread.sleep(1000); // Sleep 0.5 seconds
+            // Try to get expired object
+            try
+            {
+                retrievedObject = null;
+                retrievedObject = globalCache.getObject("refreshableObject");
+                assertNotNull("Did not retrieved a cached object, after sleep", retrievedObject);
+                assertNotNull(
+                    "Cached object has no contents, after sleep.",
+                    ((RefreshableCachedObject) retrievedObject).getContents());
+                assertTrue(
+                    "Object did not refresh.",
+                    (((RefreshableObject) ((RefreshableCachedObject) retrievedObject).getContents()).getRefreshCount()
+                        > 0));
+            }
+            catch (ObjectExpiredException e)
+            {
+                assertTrue(
+                    "Received unexpected ObjectExpiredException exception "
+                        + "when retrieving refreshable object after ( "
+                        + (System.currentTimeMillis() - addTime)
+                        + " millis)",
+                    false);
+            }
+            catch (Exception e)
+            {
+                throw e;
+            }
+        }
+        // Remove objects
+        globalCache.removeObject(cacheKey);
+    }
+
+    /**
+     * Verify a cached object will be delete after it has been
+     * untouched beyond it's TimeToLive.
+     *
+     * This test can take serveral minutes.
+     *
+     * @throws Exception
+     */
+    public void testRefreshableTimeToLive() throws Exception
+    {
+        String skipTestsProperty = System.getProperty(SKIP_TESTS_KEY,"false");
+        LOG.info("What is the value of the skipTestsProperty:" + skipTestsProperty);
+        if(Boolean.valueOf(skipTestsProperty).booleanValue()){
+            LOG.warn("Skipping testRefreshableTimeToLive test due to property " + SKIP_TESTS_KEY + " being true.");
+            return;
+        }
+        else {
+            LOG.warn("Running testRefreshableTimeToLive test due to property " + SKIP_TESTS_KEY + " being false.");
+        }
+
+        Object retrievedObject = null;
+        RefreshableCachedObject cacheObject = null;
+        // Create and add Object that expires in TEST_EXPIRETIME millis.
+        cacheObject = new RefreshableCachedObject(new RefreshableObject(), getTestExpireTime());
+        assertNotNull("Failed to create a cachable object", cacheObject);
+        cacheObject.setTTL(getTestExpireTime());
+        // Verify TimeToLive was set
+        assertEquals("Returned TimeToLive", getTestExpireTime(), cacheObject.getTTL());
+        // Add object to Cache
+        long addTime = System.currentTimeMillis();
+        globalCache.addObject(cacheKey, cacheObject);
+        // Try to get un-expired object
+        try
+        {
+            retrievedObject = null;
+            retrievedObject = globalCache.getObject(cacheKey);
+            assertNotNull("Did not retrieved a cached object", retrievedObject);
+            assertEquals("Did not retrieved correct cached object", cacheObject, retrievedObject);
+        }
+        catch (ObjectExpiredException e)
+        {
+            fail("Object expired early ( " + (System.currentTimeMillis() - addTime) + " millis)");
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        // Wait long enough to allow object to expire, but do not exceed TTL
+        long timeout = getTestExpireTime() - 0000;
+        Thread.sleep(timeout);
+        // Try to get expired object
+        try
+        {
+            retrievedObject = null;
+            retrievedObject = globalCache.getObject(cacheKey);
+            assertNotNull("Did not retrieve a cached object, after sleep", retrievedObject);
+            assertNotNull(
+                "Cached object has no contents, after sleep.",
+                ((RefreshableCachedObject) retrievedObject).getContents());
+                /*
+                 * @todo this is not working for some reason
+
+            assertTrue(
+                "Object did not refresh.",
+                (((RefreshableObject) ((RefreshableCachedObject) retrievedObject).getContents()).getRefreshCount()
+                    > 0));
+                    */
+        }
+        catch (ObjectExpiredException e)
+        {
+            assertTrue(
+                "Received unexpected ObjectExpiredException exception "
+                    + "when retrieving refreshable object after ( "
+                    + (System.currentTimeMillis() - addTime)
+                    + " millis)",
+                false);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+        // Wait long enough to allow object to expire and exceed TTL
+        Thread.sleep(getTestExpireTime() + 5000);
+        // Try to get expired object
+        try
+        {
+            retrievedObject = null;
+            retrievedObject = globalCache.getObject(cacheKey);
+            assertNull("Retrieved a cached object, after exceeding TimeToLive", retrievedObject);
+        }
+        catch (ObjectExpiredException e)
+        {
+            assertNull(
+                "Retrieved the expired cached object, but caught expected ObjectExpiredException exception",
+                retrievedObject);
+        }
+        catch (Exception e)
+        {
+            throw e;
+        }
+    }
+
+    /**
+     * Test that we can get a list of the keys in the cache
+     */
+    public void testCacheGetKeyList() {
+        globalCache.flushCache();
+        globalCache.addObject("date1", new CachedObject(new Date()));
+        globalCache.addObject("date2", new CachedObject(new Date()));
+        globalCache.addObject("date3", new CachedObject(new Date()));
+        assertTrue("Did not get key list back.", (globalCache.getKeys() != null));
+        List keys = globalCache.getKeys();
+        for (Iterator itr = keys.iterator(); itr.hasNext();) {
+            Object key = itr.next();
+            assertTrue("Key was not an instance of String.", (key instanceof String));
+        }
+
+    }
+
+    /**
+     * Test that we can get a list of the keys in the cache
+     */
+    public void testCacheGetCachedObjects() {
+        globalCache.flushCache();
+        globalCache.addObject("date1", new CachedObject(new Date()));
+        globalCache.addObject("date2", new CachedObject(new Date()));
+        globalCache.addObject("date3", new CachedObject(new Date()));
+        assertTrue("Did not get object list back.", (globalCache.getCachedObjects() != null));
+        List objects = globalCache.getCachedObjects();
+        for (Iterator itr = objects.iterator(); itr.hasNext();) {
+            Object obj = itr.next();
+            assertNotNull("Object was null.", obj);
+            assertTrue("Object was not an instance of CachedObject", (obj instanceof CachedObject));
+        }
+    }
+
+    /**
+     * Test that the retrieved list is safe from
+     * ConcurrentModificationException's being thrown if the cache
+     * is updated while we are iterating over the List.
+     */
+    public void testCacheModification() {
+        globalCache.flushCache();
+        globalCache.addObject("date1", new CachedObject(new Date()));
+        globalCache.addObject("date2", new CachedObject(new Date()));
+        globalCache.addObject("date3", new CachedObject(new Date()));
+        assertTrue("Did not get key list back.", (globalCache.getKeys() != null));
+        List keys = globalCache.getKeys();
+        try {
+            for (Iterator itr = keys.iterator(); itr.hasNext();) {
+                Object key = itr.next();
+                globalCache.addObject("date4", new CachedObject(new Date()));
+            }
+        } catch (ConcurrentModificationException cme)
+        {
+            fail("Caught ConcurrentModificationException adding to cache.");
+        }
+        List objects = globalCache.getCachedObjects();
+        try {
+            for (Iterator itr = objects.iterator(); itr.hasNext();) {
+                Object obj = itr.next();
+                globalCache.addObject("date4", new CachedObject(new Date()));
+            }
+        } catch (ConcurrentModificationException cme)
+        {
+            fail("Caught ConcurrentModificationException adding to cache.");
+        }
+    }
+
+    /**
+     * Down cast the interface to the concreate object in order to grab the
+     * cache check frequency.
+     *
+     * @return the refresh requency in milliseconds
+     */
+    private long getCacheRefresh()
+    {
+        return 5*1000;
+    }
+
+    /**
+     * How long until it expires
+     *
+     * @return the cache refresh plus 1000.
+     */
+    private long getTestExpireTime()
+    {
+        return getCacheRefresh() + 1000;
+    }
+
+    /**
+     * Simple object that can be refreshed
+     */
+    class RefreshableObject implements Refreshable
+    {
+        private int refreshCount = 0;
+
+        /**
+         * Increment the refresh counter
+         */
+        public void refresh()
+        {
+            this.refreshCount++;
+        }
+
+        /**
+         * Reutrn the number of time this object has been refreshed
+         *
+         * @return Number of times refresh() has been called
+         */
+        public int getRefreshCount()
+        {
+            return this.refreshCount;
+        }
+    }
+}

Modified: jakarta/turbine/fulcrum/trunk/cache/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/xdocs/changes.xml?rev=417323&r1=417322&r2=417323&view=diff
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/xdocs/changes.xml (original)
+++ jakarta/turbine/fulcrum/trunk/cache/xdocs/changes.xml Mon Jun 26 17:55:22 2006
@@ -6,7 +6,10 @@
   </properties>
 
   <body>
-    <release version="1.1" date="">
+    <release version="1.1" date="in Subversion">
+      <action type="update" dev="seade" issue="TRB-17" due-to="Thomas Vandahl">
+        Added new JCSCacheService.  Thanks to Thomas Vandahl.
+      </action>
      <action dev="epugh" type="add">
         Add new EHCacheService based on EHCache from http://ehcache.sourceforge.net/
       </action>    

Modified: jakarta/turbine/fulcrum/trunk/cache/xdocs/index.xml
URL: http://svn.apache.org/viewvc/jakarta/turbine/fulcrum/trunk/cache/xdocs/index.xml?rev=417323&r1=417322&r2=417323&view=diff
==============================================================================
--- jakarta/turbine/fulcrum/trunk/cache/xdocs/index.xml (original)
+++ jakarta/turbine/fulcrum/trunk/cache/xdocs/index.xml Mon Jun 26 17:55:22 2006
@@ -4,36 +4,67 @@
 
   <properties>
     <title>Cache Component</title>
-    <author email="epugh@upstate.com">Eric PUgh</author>
+    <author email="epugh@upstate.com">Eric Pugh</author>
+    <author email="tv@apache.org">Thomas Vandahl</author>
   </properties>
 
   <body>
 
   <section name="Overview">
     <p>
-      <b>NEW:</b> There are now two caches, GlobalCacheService and EHCacheService.  GlobalCacheService
-      was built on internal foundations and EHCacheService was built on the EHCache project from
-      <a href="http://ehcache.sourceforge.net/">ehcache.sourceforge.net</a>
+      There are three cache implementations
+      <ul>
+      	<li>GlobalCacheService,</li>
+      	<li>EHCacheService (built on the EHCache project from
+      		<a href="http://ehcache.sourceforge.net/">ehcache.sourceforge.net</a>) and</li>
+      	<li>JCSCacheService (built on the <a href="http://jakarta.apache.org/jcs/">Java Caching System</a>,
+      		which was originally a part of Turbine)</li>
+      </ul>
     </p>
+  </section>
+  <section name="GlobalCacheService" id="GlobalCacheService">
     <p>
      This Service functions as a Global Cache.  A global cache is a good
      place to store items that you may need to access often but don't
      necessarily need (or want) to fetch from the database everytime.  A
      good example would be a look up table of States that you store in a
-     database and use throughout your application.  Since information
+     database and use throughout your application. Since information
      about States doesn't change very often, you could store this
      information in the Global Cache and decrease the overhead of
      hitting the database everytime you need State information.
-   </p>
+    </p>
       
     <p>
       It is written 
       for use in Turbine but it can be used in any container compatible 
       with Avalon's ECM container.
-    </p>    
+    </p>
+
+    <p>
+      First, here is the role configuration.
+    </p>
+
+    <source>
+    <![CDATA[
+        <role
+            name="org.apache.fulcrum.cache.GlobalCacheService"
+            shorthand="cache"
+            default-class="org.apache.fulcrum.cache.DefaultGlobalCacheService"/>
+    ]]>
+    </source>
+
+      <p>
+        And here is the configuration:
+      </p>
+    <source>
+
+    <![CDATA[
+        <cache cacheInitialSize="20" cacheCheckFrequency="5"/>
+    ]]>
+    </source>
   </section>
   
-<section name="Configuration for EHCacheService">
+  <section name="EHCacheService" id="EHCacheService">
 
     <p>
       First, here is the role configuration.
@@ -62,8 +93,14 @@
 
   </section>
     
-<section name="Configuration for GlobalCacheService">
+  <section name="JCSCacheService" id="JCSCacheService">
 
+	<p>
+	  The JCS cache service implements the interface <code>GlobalCacheService</code> and thus can
+	  serve as a drop-in replacement for <code>DefaultGlobalCacheService</code>. However it is 
+	  possible to configure the cache behaviour in much more detail to provide disk caches or lateral TCP
+	  caches for example.
+	</p>
     <p>
       First, here is the role configuration.
     </p>
@@ -72,8 +109,8 @@
     <![CDATA[
         <role
             name="org.apache.fulcrum.cache.GlobalCacheService"
-            shorthand="cache"
-            default-class="org.apache.fulcrum.cache.DefaultGlobalCacheService"/>
+            shorthand="jcscache"
+            default-class="org.apache.fulcrum.cache.impl.JCSCacheService"/>
     ]]>
     </source>
 
@@ -83,10 +120,22 @@
     <source>
 
     <![CDATA[
-        <cache cacheInitialSize="20" cacheCheckFrequency="5"/>
+        <jcscache>
+	    	<cacheCheckFrequency>5000</cacheCheckFrequency>
+        	<region>fulcrum</region>
+        	<configurationFile>/cache.ccf</configurationFile>
+        </jcscache>
     ]]>
     </source>
-
+    <p>
+      The region parameter is the name of the cache region to use when caching objects. It defaults to
+      <code>fulcrum</code>. JCS will store the objects in a group named <code>default_group</code> in that
+      region. The configuration file parameter gives the location of the JCS configuration file. Please
+      note that JCS uses a class loader to read this file, so make sure this path is part of your classpath.
+      The default values of all configuration settings are shown in the example. 
+      See <a href="http://jakarta.apache.org/jcs/">the JCS site</a> for more information about configuring
+      JCS.
+    </p>
   </section>
 
   <section name="Usage">
@@ -126,7 +175,8 @@
     <p>
     You can also place an expiration time on your objects so the Service will
     automatically remove them when they expire. If you don't specify an expiration
-    time, the Service uses 5 seconds. To see an example, look at the 
+    time, the DefaultGlobalCacheService uses 5 seconds. For JCS this value depends on values set
+    in the cache configuration file. To see an example, look at the 
     test case <a href="xref-test/org/apache/fulcrum/cache/CacheTest.html">CacheTest</a>
     </p>
 



---------------------------------------------------------------------
To unsubscribe, e-mail: turbine-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: turbine-dev-help@jakarta.apache.org