[CXF-5802] Fix a condition introduced with the removeCache call where if two 
proxies are using the same cache, closing one (or having it GC'd) would cause 
the cache to become invalid.

Conflicts:
        
rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/EHCacheTokenStore.java
        
systests/ws-security/src/test/java/org/apache/cxf/systest/ws/cache/CachingTest.java


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/35377167
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/35377167
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/35377167

Branch: refs/heads/2.7.x-fixes
Commit: 353771670f917714c8370a5bf45c298649622c69
Parents: a25efba
Author: Daniel Kulp <[email protected]>
Authored: Mon Jun 30 12:29:00 2014 -0400
Committer: Daniel Kulp <[email protected]>
Committed: Mon Jun 30 13:17:06 2014 -0400

----------------------------------------------------------------------
 .../security/tokenstore/EHCacheTokenStore.java  | 42 ++++++++++++++++++--
 .../cxf/systest/ws/cache/CachingTest.java       | 38 +++++++++++-------
 2 files changed, 62 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/35377167/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/EHCacheTokenStore.java
----------------------------------------------------------------------
diff --git 
a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/EHCacheTokenStore.java
 
b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/EHCacheTokenStore.java
index 3ee6c84..4270934 100644
--- 
a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/EHCacheTokenStore.java
+++ 
b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/EHCacheTokenStore.java
@@ -26,11 +26,13 @@ import java.util.Collection;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import net.sf.ehcache.Cache;
 import net.sf.ehcache.CacheManager;
 import net.sf.ehcache.Ehcache;
 import net.sf.ehcache.Element;
+import net.sf.ehcache.Status;
 import net.sf.ehcache.config.CacheConfiguration;
 
 import org.apache.cxf.Bus;
@@ -44,7 +46,6 @@ import org.apache.cxf.ws.security.cache.EHCacheManagerHolder;
  * and the max TTL is 12 hours.
  */
 public class EHCacheTokenStore implements TokenStore, Closeable, 
BusLifeCycleListener {
-
     public static final long DEFAULT_TTL = 3600L;
     public static final long MAX_TTL = DEFAULT_TTL * 12L;
     
@@ -64,13 +65,34 @@ public class EHCacheTokenStore implements TokenStore, 
Closeable, BusLifeCycleLis
         CacheConfiguration cc = 
EHCacheManagerHolder.getCacheConfiguration(key, cacheManager)
             .overflowToDisk(false); //tokens not writable
         
-        Ehcache newCache = new Cache(cc);
+        Cache newCache = new RefCountCache(cc);
         cache = cacheManager.addCacheIfAbsent(newCache);
+        synchronized (cache) {
+            if (cache.getStatus() != Status.STATUS_ALIVE) {
+                cache = cacheManager.addCacheIfAbsent(newCache);
+            }
+            if (cache instanceof RefCountCache) {
+                ((RefCountCache)cache).incrementAndGet();
+            }
+        }
         
         // Set the TimeToLive value from the CacheConfiguration
         ttl = cc.getTimeToLiveSeconds();
     }
     
+    private static class RefCountCache extends Cache {
+        AtomicInteger count = new AtomicInteger();
+        public RefCountCache(CacheConfiguration cc) {
+            super(cc);
+        }
+        public int incrementAndGet() {
+            return count.incrementAndGet();
+        }
+        public int decrementAndGet() {
+            return count.decrementAndGet();
+        }
+    }
+    
     /**
      * Set a new (default) TTL value in seconds
      * @param newTtl a new (default) TTL value in seconds
@@ -114,13 +136,16 @@ public class EHCacheTokenStore implements TokenStore, 
Closeable, BusLifeCycleLis
     }
     
     public void remove(String identifier) {
-        if (!StringUtils.isEmpty(identifier) && 
cache.isKeyInCache(identifier)) {
+        if (cache != null && !StringUtils.isEmpty(identifier) && 
cache.isKeyInCache(identifier)) {
             cache.remove(identifier);
         }
     }
 
     @SuppressWarnings("unchecked")
     public Collection<String> getTokenIdentifiers() {
+        if (cache == null) {
+            return null;
+        }
         return cache.getKeysWithExpiryCheck();
     }
     
@@ -138,6 +163,9 @@ public class EHCacheTokenStore implements TokenStore, 
Closeable, BusLifeCycleLis
     }
     
     public SecurityToken getToken(String identifier) {
+        if (cache == null) {
+            return null;
+        }
         Element element = cache.get(identifier);
         if (element != null && !cache.isExpired(element)) {
             return (SecurityToken)element.getObjectValue();
@@ -179,7 +207,13 @@ public class EHCacheTokenStore implements TokenStore, 
Closeable, BusLifeCycleLis
         if (cacheManager != null) {
             // this step is especially important for global shared cache 
manager
             if (cache != null) {
-                cacheManager.removeCache(cache.getName());
+                synchronized (cache) {
+                    if (cache instanceof RefCountCache) {
+                        if (((RefCountCache)cache).decrementAndGet() == 0) {
+                            cacheManager.removeCache(cache.getName());
+                        }
+                    }
+                }                
             }
             
             EHCacheManagerHolder.releaseCacheManger(cacheManager);

http://git-wip-us.apache.org/repos/asf/cxf/blob/35377167/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/cache/CachingTest.java
----------------------------------------------------------------------
diff --git 
a/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/cache/CachingTest.java
 
b/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/cache/CachingTest.java
index c324e07..d3c73e8 100644
--- 
a/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/cache/CachingTest.java
+++ 
b/systests/ws-security/src/test/java/org/apache/cxf/systest/ws/cache/CachingTest.java
@@ -90,24 +90,32 @@ public class CachingTest extends 
AbstractBusClientServerTestBase {
             );
         assertNotNull(tokenStore);
         // We expect 1 token
-        assertEquals(tokenStore.getTokenIdentifiers().size(), 1);
+        assertEquals(1, tokenStore.getTokenIdentifiers().size());
+        
+        
         
         // Second invocation
-        port = service.getPort(portQName, DoubleItPortType.class);
-        updateAddressPort(port, PORT);
+        DoubleItPortType port2 = service.getPort(portQName, 
DoubleItPortType.class);
+        updateAddressPort(port2, test.getPort());
         
-        port.doubleIt(35);
+        port2.doubleIt(35);
 
-        client = ClientProxy.getClient(port);
+        client = ClientProxy.getClient(port2);
         tokenStore = 
             (TokenStore)client.getEndpoint().getEndpointInfo().getProperty(
                 SecurityConstants.TOKEN_STORE_CACHE_INSTANCE
             );
+
         assertNotNull(tokenStore);
         // There should now be 2 tokens as both proxies share the same 
TokenStore
-        assertEquals(tokenStore.getTokenIdentifiers().size(), 2);
+        assertEquals(2, tokenStore.getTokenIdentifiers().size());
         
         ((java.io.Closeable)port).close();
+        //port2 is still holding onto the cache, thus, this should still be 4
+        assertEquals(4, tokenStore.getTokenIdentifiers().size());       
+        ((java.io.Closeable)port2).close();
+        //port2 is now closed, this should be null
+        assertNull(tokenStore.getTokenIdentifiers());       
         bus.shutdown(true);
     }
     
@@ -146,31 +154,33 @@ public class CachingTest extends 
AbstractBusClientServerTestBase {
             );
         assertNotNull(tokenStore);
         // We expect 1 token
-        assertEquals(tokenStore.getTokenIdentifiers().size(), 1);
+        assertEquals(1, tokenStore.getTokenIdentifiers().size());
+        
         
         // Second invocation
-        port = service.getPort(portQName, DoubleItPortType.class);
-        updateAddressPort(port, PORT);
+        DoubleItPortType port2 = service.getPort(portQName, 
DoubleItPortType.class);
+        updateAddressPort(port2, test.getPort());
         
-        ((BindingProvider)port).getRequestContext().put(
+        ((BindingProvider)port2).getRequestContext().put(
             SecurityConstants.CACHE_IDENTIFIER, "proxy2"
         );
-        ((BindingProvider)port).getRequestContext().put(
+        ((BindingProvider)port2).getRequestContext().put(
             SecurityConstants.CACHE_CONFIG_FILE, "client/per-proxy-cache.xml"
         );
         
-        port.doubleIt(35);
+        port2.doubleIt(35);
 
-        client = ClientProxy.getClient(port);
+        client = ClientProxy.getClient(port2);
         tokenStore = 
             (TokenStore)client.getEndpoint().getEndpointInfo().getProperty(
                 SecurityConstants.TOKEN_STORE_CACHE_INSTANCE
             );
         assertNotNull(tokenStore);
         // We expect 1 token
-        assertEquals(tokenStore.getTokenIdentifiers().size(), 1);
+        assertEquals(1, tokenStore.getTokenIdentifiers().size());
         
         ((java.io.Closeable)port).close();
+        ((java.io.Closeable)port2).close();
         bus.shutdown(true);
     }
     

Reply via email to