Merge branch commons-jcs-2.2.x back into trunk

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/jcs/trunk@1840421 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunner.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunner.java
index fe82a3b..3b8beed 100644
--- a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunner.java
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RegistryKeepAliveRunner.java
@@ -20,6 +20,7 @@
  */
 
 import java.rmi.Naming;
+import java.rmi.Remote;
 import java.rmi.RemoteException;
 import java.rmi.registry.Registry;
 
@@ -41,12 +42,18 @@
     /** The URL of the service to look for. */
     private String namingURL;
 
+    /** The service name. */
+    private String serviceName;
+
     /** the port on which to start the registry */
     private int registryPort;
 
     /** An optional event logger */
     private ICacheEventLogger cacheEventLogger;
 
+    /** the registry */
+    private Registry registry;
+
     /**
      * @param registryHost - Hostname of the registry
      * @param registryPort - the port on which to start the registry
@@ -55,6 +62,7 @@
     public RegistryKeepAliveRunner( String registryHost, int registryPort, String serviceName )
     {
         this.namingURL = RemoteUtils.getNamingURL(registryHost, registryPort, serviceName);
+        this.serviceName = serviceName;
         this.registryPort = registryPort;
     }
 
@@ -104,41 +112,42 @@
             {
                 cacheEventLogger.logError( "RegistryKeepAliveRunner", "Naming.lookup", message + ":" + ex.getMessage() );
             }
-            createAndRegister( namingURL );
+            createAndRegister( serviceName );
         }
     }
 
     /**
      * Creates the registry and registers the server.
      * <p>
-     * @param registry
+     * @param serviceName the service name
      */
-    protected void createAndRegister( String registry )
+    protected void createAndRegister( String serviceName )
     {
-        createReqistry( registry );
-        registerServer( registry );
+        createReqistry( serviceName );
+        registerServer( serviceName );
     }
 
     /**
      * Try to create the registry. Log errors
      * <p>
-     * @param registry
+     * @param serviceName the service name
      */
-    protected void createReqistry( String registry )
+    protected void createReqistry( String serviceName )
     {
-        Registry reg = RemoteUtils.createRegistry(registryPort);
+        // TODO: Refactor method signature. This is ugly but required to keep the binary API compatibility
+        this.registry = RemoteUtils.createRegistry(registryPort);
 
         if ( cacheEventLogger != null )
         {
-            if (reg != null)
+            if (this.registry != null)
             {
                 cacheEventLogger.logApplicationEvent( "RegistryKeepAliveRunner", "createRegistry",
-                        "Successfully created registry [" + registry + "]." );
+                        "Successfully created registry [" + serviceName + "]." );
             }
             else
             {
                 cacheEventLogger.logError( "RegistryKeepAliveRunner", "createRegistry",
-                        "Could not start registry [" + registry + "]." );
+                        "Could not start registry [" + serviceName + "]." );
             }
         }
     }
@@ -146,15 +155,22 @@
     /**
      * Try to rebind the server.
      * <p>
-     * @param registry
+     * @param serviceName the service name
      */
-    protected void registerServer( String registry )
+    protected void registerServer( String serviceName )
     {
         try
         {
             // try to rebind anyway
-            RemoteCacheServerFactory.registerServer( registry, RemoteCacheServerFactory.getRemoteCacheServer() );
-            String message = "Successfully rebound server to registry [" + registry + "].";
+            Remote server = RemoteCacheServerFactory.getRemoteCacheServer();
+
+            if ( server == null )
+            {
+                throw new RemoteException( "Cannot register the server until it is created." );
+            }
+
+            this.registry.rebind( serviceName, server );
+            String message = "Successfully rebound server to registry [" + serviceName + "].";
             if ( cacheEventLogger != null )
             {
                 cacheEventLogger.logApplicationEvent( "RegistryKeepAliveRunner", "registerServer", message );
@@ -166,7 +182,7 @@
         }
         catch ( RemoteException e )
         {
-            String message = "Could not rebind server to registry [" + registry + "].";
+            String message = "Could not rebind server to registry [" + serviceName + "].";
             log.error( message, e );
             if ( cacheEventLogger != null )
             {
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributes.java
index 351f5ac..45a0a72 100644
--- a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributes.java
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerAttributes.java
@@ -143,6 +143,7 @@
      * Should we start the registry
      * <p>
      * @param startRegistry the startRegistry to set
+     * @deprecated Always true, to be removed
      */
     @Override
     public void setStartRegistry( boolean startRegistry )
@@ -154,6 +155,7 @@
      * Should we start the registry
      * <p>
      * @return the startRegistry
+     * @deprecated Always true, to be removed
      */
     @Override
     public boolean isStartRegistry()
@@ -201,7 +203,6 @@
         buf.append( "\n allowClusterGet = [" + this.isAllowClusterGet() + "]" );
         buf.append( "\n configFileName = [" + this.getConfigFileName() + "]" );
         buf.append( "\n rmiSocketFactoryTimeoutMillis = [" + this.getRmiSocketFactoryTimeoutMillis() + "]" );
-        buf.append( "\n startRegistry = [" + this.isStartRegistry() + "]" );
         buf.append( "\n useRegistryKeepAlive = [" + this.isUseRegistryKeepAlive() + "]" );
         buf.append( "\n registryKeepAliveDelayMillis = [" + this.getRegistryKeepAliveDelayMillis() + "]" );
         buf.append( "\n eventQueueType = [" + this.getEventQueueType() + "]" );
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactory.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactory.java
index e0c616e..60a0fa0 100644
--- a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactory.java
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/RemoteCacheServerFactory.java
@@ -149,13 +149,10 @@
             remoteCacheServer.setCacheEventLogger( cacheEventLogger );
 
             // START THE REGISTRY
-            if (rcsa.isStartRegistry())
-            {
-            	registry = RemoteUtils.createRegistry(port);
-            }
+        	registry = RemoteUtils.createRegistry(port);
 
             // REGISTER THE SERVER
-            registerServer( RemoteUtils.getNamingURL(host, port, serviceName), remoteCacheServer );
+            registerServer( serviceName, remoteCacheServer );
 
             // KEEP THE REGISTRY ALIVE
             if ( rcsa.isUseRegistryKeepAlive() )
@@ -227,11 +224,11 @@
      * Registers the server with the registry. I broke this off because we might want to have code
      * that will restart a dead registry. It will need to rebind the server.
      * <p>
-     * @param namingURL
-     * @param server
+     * @param serviceName the name of the service
+     * @param server the server object to bind
      * @throws RemoteException
      */
-    protected static void registerServer( String namingURL, Remote server )
+    protected static void registerServer(String serviceName, Remote server )
         throws RemoteException
     {
         if ( server == null )
@@ -239,20 +236,17 @@
             throw new RemoteException( "Cannot register the server until it is created." );
         }
 
-        if ( log.isInfoEnabled() )
+        if ( registry == null )
         {
-            log.info( "Binding server to " + namingURL );
+            throw new RemoteException( "Cannot register the server: Registry is null." );
         }
 
-        try
+        if ( log.isInfoEnabled() )
         {
-            Naming.rebind( namingURL, server );
+            log.info( "Binding server to " + serviceName );
         }
-        catch ( MalformedURLException ex )
-        {
-            // impossible case.
-            throw new IllegalArgumentException( ex.getMessage() + "; url=" + namingURL );
-        }
+
+        registry.rebind( serviceName, server );
     }
 
     /**
diff --git a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServerAttributes.java b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServerAttributes.java
index 1c8500d..5817e2d 100644
--- a/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServerAttributes.java
+++ b/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/remote/server/behavior/IRemoteCacheServerAttributes.java
@@ -89,6 +89,7 @@
      * Should we start the registry
      * <p>
      * @param startRegistry the startRegistry to set
+     * @deprecated Always true, to be removed
      */
     void setStartRegistry( boolean startRegistry );
 
@@ -96,6 +97,7 @@
      * Should we start the registry
      * <p>
      * @return the startRegistry
+     * @deprecated Always true, to be removed
      */
     boolean isStartRegistry();
 
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/ExpiryAwareCache.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/ExpiryAwareCache.java
new file mode 100644
index 0000000..d4691ec
--- /dev/null
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/ExpiryAwareCache.java
@@ -0,0 +1,61 @@
+/*
+ * 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.commons.jcs.jcache;
+
+import java.util.Arrays;
+import java.util.Map;
+
+import javax.cache.Cache;
+import javax.cache.configuration.CacheEntryListenerConfiguration;
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.EventType;
+
+import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+
+// allows us to plug some lifecycle callbacks on the core cache without impacting too much the core
+public class ExpiryAwareCache<A, B> extends CompositeCache<A, B>
+{
+    private Map<CacheEntryListenerConfiguration<A, B>, JCSListener<A, B>> listeners;
+    private Cache<A, B> cacheRef;
+
+    ExpiryAwareCache(final ICompositeCacheAttributes cattr, final IElementAttributes attr)
+    {
+        super(cattr, attr);
+    }
+
+    @Override
+    protected void doExpires(final ICacheElement<A, B> element)
+    {
+        super.doExpires(element);
+        for (final JCSListener<A, B> listener : listeners.values())
+        {
+            listener.onExpired(Arrays.<CacheEntryEvent<? extends A, ? extends B>> asList(new JCSCacheEntryEvent<A, B>(
+                    cacheRef, EventType.REMOVED, null, element.getKey(), element.getVal())));
+        }
+    }
+
+    void init(final Cache<A, B> cache, final Map<CacheEntryListenerConfiguration<A, B>, JCSListener<A, B>> listeners)
+    {
+        this.cacheRef = cache;
+        this.listeners = listeners;
+    }
+}
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCache.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCache.java
index f5e13d6..baa1ff5 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCache.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCache.java
@@ -23,7 +23,6 @@
 import org.apache.commons.jcs.engine.behavior.ICacheElement;
 import org.apache.commons.jcs.engine.behavior.IElementAttributes;
 import org.apache.commons.jcs.engine.behavior.IElementSerializer;
-import org.apache.commons.jcs.engine.control.CompositeCache;
 import org.apache.commons.jcs.jcache.jmx.JCSCacheMXBean;
 import org.apache.commons.jcs.jcache.jmx.JCSCacheStatisticsMXBean;
 import org.apache.commons.jcs.jcache.jmx.JMXs;
@@ -71,7 +70,7 @@
 // TODO: configure serializer
 public class JCSCache<K, V> implements Cache<K, V>
 {
-    private final CompositeCache<K, V> delegate;
+    private final ExpiryAwareCache<K, V> delegate;
     private final JCSCachingManager manager;
     private final JCSConfiguration<K, V> config;
     private final CacheLoader<K, V> loader;
@@ -89,7 +88,7 @@
 
     public JCSCache(final ClassLoader classLoader, final JCSCachingManager mgr,
                     final String cacheName, final JCSConfiguration<K, V> configuration,
-                    final Properties properties, final CompositeCache<K, V> cache)
+                    final Properties properties, final ExpiryAwareCache<K, V> cache)
     {
         manager = mgr;
 
@@ -153,6 +152,7 @@
         {
             listeners.put(listener, new JCSListener<K, V>(listener));
         }
+        delegate.init(this, listeners);
 
         statistics.setActive(config.isStatisticsEnabled());
 
@@ -307,7 +307,7 @@
                 }
                 else
                 {
-                    expires(key);
+                    forceExpires(key);
                 }
             }
         }
@@ -401,7 +401,7 @@
         {
             if (!created)
             {
-                expires(key);
+                forceExpires(key);
             }
         }
     }
@@ -411,7 +411,7 @@
         return duration == null || !duration.isZero();
     }
 
-    private void expires(final K cacheKey)
+    private void forceExpires(final K cacheKey)
     {
         final ICacheElement<K, V> elt = delegate.get(cacheKey);
         delegate.remove(cacheKey);
@@ -549,7 +549,7 @@
             final Duration expiryForAccess = expiryPolicy.getExpiryForAccess();
             if (!isNotZero(expiryForAccess))
             {
-                expires(key);
+                forceExpires(key);
             }
             else if (expiryForAccess != null && (!elt.getElementAttributes().getIsEternal() || !expiryForAccess.isEternal()))
             {
diff --git a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingManager.java b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingManager.java
index 2357005..c764eb3 100644
--- a/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingManager.java
+++ b/commons-jcs-jcache/src/main/java/org/apache/commons/jcs/jcache/JCSCachingManager.java
@@ -18,6 +18,10 @@
  */
 package org.apache.commons.jcs.jcache;
 
+import org.apache.commons.jcs.engine.behavior.ICompositeCacheAttributes;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.CompositeCache;
+import org.apache.commons.jcs.engine.control.CompositeCacheConfigurator;
 import org.apache.commons.jcs.engine.control.CompositeCacheManager;
 import org.apache.commons.jcs.jcache.lang.Subsitutor;
 import org.apache.commons.jcs.jcache.proxy.ClassLoaderAwareCache;
@@ -65,6 +69,19 @@
             return new InternalManager();
         }
 
+        protected CompositeCacheConfigurator newConfigurator()
+        {
+            return new CompositeCacheConfigurator()
+            {
+                @Override
+                protected <K, V> CompositeCache<K, V> newCache(
+                        final ICompositeCacheAttributes cca, final IElementAttributes ea)
+                {
+                    return new ExpiryAwareCache<K, V>( cca, ea );
+                }
+            };
+        }
+
         @Override // needed to call it from JCSCachingManager
         protected void initialize() {
             super.initialize();
@@ -197,7 +214,7 @@
                             loader, this, cacheName,
                             new JCSConfiguration/*<K, V>*/(configuration, keyType, valueType),
                             properties,
-                            delegate.getCache(cacheName)));
+                            ExpiryAwareCache.class.cast(delegate.getCache(cacheName))));
             caches.putIfAbsent(cacheName, cache);
         }
         else
diff --git a/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/ExpiryListenerTest.java b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/ExpiryListenerTest.java
new file mode 100644
index 0000000..1ac2cc1
--- /dev/null
+++ b/commons-jcs-jcache/src/test/java/org/apache/commons/jcs/jcache/ExpiryListenerTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.commons.jcs.jcache;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
+import javax.cache.configuration.FactoryBuilder;
+import javax.cache.configuration.MutableCacheEntryListenerConfiguration;
+import javax.cache.configuration.MutableConfiguration;
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.CacheEntryExpiredListener;
+import javax.cache.event.CacheEntryListenerException;
+import javax.cache.expiry.CreatedExpiryPolicy;
+import javax.cache.expiry.Duration;
+import javax.cache.expiry.ExpiryPolicy;
+import javax.cache.spi.CachingProvider;
+
+import org.junit.Test;
+
+public class ExpiryListenerTest {
+
+    @Test
+    public void listener() throws InterruptedException {
+        final CachingProvider cachingProvider = Caching.getCachingProvider();
+        final CacheManager cacheManager = cachingProvider.getCacheManager();
+        final CacheEntryExpiredListenerImpl listener = new CacheEntryExpiredListenerImpl();
+        cacheManager.createCache("default", new MutableConfiguration<String, String>()
+                .setExpiryPolicyFactory(new FactoryBuilder.SingletonFactory<ExpiryPolicy>(
+                        new CreatedExpiryPolicy(new Duration(TimeUnit.MILLISECONDS, 1))))
+                .addCacheEntryListenerConfiguration(new MutableCacheEntryListenerConfiguration<String, String>(
+                        FactoryBuilder.factoryOf(listener),
+                        null, false, false
+                )));
+        final Cache<String, String> cache = cacheManager.getCache("default");
+        assertFalse(cache.containsKey("foo"));
+        cache.put("foo", "bar");
+        Thread.sleep(10);
+        assertFalse(cache.containsKey("foo"));
+        cachingProvider.close();
+        assertEquals(1, listener.events.size());
+    }
+
+    private static class CacheEntryExpiredListenerImpl implements CacheEntryExpiredListener<String, String>, Serializable {
+        private final Collection<CacheEntryEvent<? extends String, ? extends String>> events =
+                new ArrayList<CacheEntryEvent<? extends String, ? extends String>>();
+
+        @Override
+        public void onExpired(final Iterable<CacheEntryEvent<? extends String, ? extends String>> cacheEntryEvents)
+                throws CacheEntryListenerException {
+            for (final CacheEntryEvent<? extends String, ? extends String> cacheEntryEvent : cacheEntryEvents) {
+                events.add(cacheEntryEvent);
+            }
+        }
+    }
+}