This is an automated email from the ASF dual-hosted git repository. reta pushed a commit to branch 3.6.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 280b92a504c272925af8af7f4b9dfcfad6cffd5a Author: Andriy Redko <[email protected]> AuthorDate: Sun Jan 19 10:15:18 2025 -0500 CXF-8765: Option to remove Ehcache (#2196) (cherry picked from commit a9053c26036aac80630b030c413900da7df716b1) (cherry picked from commit cddb748c23c4f7a1854dfffc525a4a63edc06ee2) --- rt/rs/security/sso/saml/pom.xml | 7 + .../saml/sso/jcache/JCacheTokenReplayCache.java | 162 ++++++++++++++++++ .../sso/state/jcache/JCacheSPStateManager.java | 165 +++++++++++++++++++ .../saml/src/main/resources/cxf-samlp-ehcache.xml | 18 ++ .../saml/src/main/resources/cxf-samlp-jcache.xml | 74 +++++++++ .../rs/security/saml/sso/SPStateManagerTest.java | 3 +- .../rs/security/saml/sso/TokenReplayCacheTest.java | 43 +++-- rt/ws/security/pom.xml | 7 + .../cache/jcache/CXFJCacheReplayCache.java | 50 ++++++ .../security/cache/jcache/JCacheReplayCache.java | 141 ++++++++++++++++ .../ws/security/tokenstore/TokenStoreFactory.java | 29 +--- .../tokenstore/jcache/JCacheTokenStore.java | 131 +++++++++++++++ .../tokenstore/jcache/JCacheTokenStoreFactory.java | 51 ++++++ .../apache/cxf/ws/security/utils/JCacheUtils.java | 73 +++++++++ .../apache/cxf/ws/security/wss4j/WSS4JUtils.java | 5 + rt/ws/security/src/main/resources/cxf-ehcache.xml | 18 ++ rt/ws/security/src/main/resources/cxf-jcache.xml | 50 ++++++ .../cxf/ws/security/tokenstore/TokenStoreTest.java | 21 ++- .../cxf/sts/cache/jcache/JCacheIdentityCache.java | 182 +++++++++++++++++++++ .../sts-core/src/main/resources/sts-ehcache.xml | 18 ++ .../sts/sts-core/src/main/resources/sts-jcache.xml | 40 +++++ .../sts/cache/jcache/JCacheIdentityCacheTest.java | 37 +++++ .../xkms/cache/jcache/JCacheXKMSClientCache.java | 124 ++++++++++++++ .../apache/cxf/xkms/cache/XKMSClientCacheTest.java | 20 ++- .../systest/jaxrs/BookCxfContinuationStore.java | 2 +- .../apache/cxf/systest/ws/cache/CachingTest.java | 36 ++-- 26 files changed, 1451 insertions(+), 56 deletions(-) diff --git a/rt/rs/security/sso/saml/pom.xml b/rt/rs/security/sso/saml/pom.xml index 1901f263ce..c890ee92df 100644 --- a/rt/rs/security/sso/saml/pom.xml +++ b/rt/rs/security/sso/saml/pom.xml @@ -72,5 +72,12 @@ <version>${cxf.ehcache3.version}</version> <scope>compile</scope> </dependency> + <dependency> + <groupId>org.ehcache.modules</groupId> + <artifactId>ehcache-107</artifactId> + <version>${cxf.ehcache3.version}</version> + <scope>provided</scope> + <optional>true</optional> + </dependency> </dependencies> </project> diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/jcache/JCacheTokenReplayCache.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/jcache/JCacheTokenReplayCache.java new file mode 100644 index 0000000000..e944c36227 --- /dev/null +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/jcache/JCacheTokenReplayCache.java @@ -0,0 +1,162 @@ +/** + * 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.cxf.rs.security.saml.sso.jcache; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.time.Instant; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.Caching; +import javax.cache.configuration.MutableConfiguration; +import javax.cache.spi.CachingProvider; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.jaxrs.utils.ResourceUtils; +import org.apache.cxf.rs.security.saml.sso.TokenReplayCache; +import org.apache.wss4j.common.cache.EHCacheReplayCache; +import org.apache.wss4j.common.cache.EHCacheValue; +import org.apache.wss4j.common.util.Loader; + +/** + * An in-memory JCache based implementation of the TokenReplayCache interface. + * The default TTL is 60 minutes and the max TTL is 12 hours. + */ + +public class JCacheTokenReplayCache implements TokenReplayCache<String> { + + public static final String CACHE_KEY = "cxf.samlp.replay.cache"; + + private static final org.slf4j.Logger LOG = + org.slf4j.LoggerFactory.getLogger(EHCacheReplayCache.class); + private static final String DEFAULT_CONFIG_URL = "/cxf-samlp-jcache.xml"; + + private final Cache<String, EHCacheValue> cache; + private final CacheManager cacheManager; + + public JCacheTokenReplayCache() throws URISyntaxException { + this(DEFAULT_CONFIG_URL, null); + } + + public JCacheTokenReplayCache(Bus bus) throws URISyntaxException { + this(DEFAULT_CONFIG_URL, bus); + } + + public JCacheTokenReplayCache(String configFile) throws URISyntaxException { + this(configFile, null); + } + + public JCacheTokenReplayCache(String configFile, Bus bus) throws URISyntaxException { + if (bus == null) { + bus = BusFactory.getThreadDefaultBus(true); + } + URL configFileURL = null; + try { + configFileURL = + ResourceUtils.getClasspathResourceURL(configFile, JCacheTokenReplayCache.class, bus); + } catch (Exception ex) { + // ignore + } + + final CachingProvider cachingProvider = Caching.getCachingProvider(); + + cacheManager = cachingProvider.getCacheManager( + getConfigFileURL(configFileURL).toURI(), + getClass().getClassLoader()); + + cache = getOrCreate(cacheManager, CACHE_KEY, String.class, EHCacheValue.class); + } + + private static <K, V> Cache<K, V> getOrCreate(CacheManager cacheManager, String name, + Class<K> kclass, Class<V> vclass) { + + final Cache<K, V> cache = cacheManager.getCache(name, kclass, vclass); + if (cache != null) { + return cache; + } + + final MutableConfiguration<K, V> cacheConfiguration = new MutableConfiguration<>(); + cacheConfiguration.setTypes(kclass, vclass); + + return cacheManager.createCache(name, cacheConfiguration); + } + + private URL getConfigFileURL(URL suppliedConfigFileURL) { + if (suppliedConfigFileURL == null) { + //using the default + try { + URL configFileURL = Loader.getResource(DEFAULT_CONFIG_URL); + if (configFileURL == null) { + configFileURL = new URL(DEFAULT_CONFIG_URL); + } + return configFileURL; + } catch (IOException e) { + // Do nothing + LOG.debug(e.getMessage()); + } + } + return suppliedConfigFileURL; + } + + /** + * Add the given identifier to the cache. It will be cached for a default amount of time. + * @param identifier The identifier to be added + */ + public void putId(String identifier) { + putId(identifier, null); + } + + /** + * Add the given identifier to the cache to be cached for the given time + * @param identifier The identifier to be added + * @param expiry A custom expiry time for the identifier. Can be null in which case, the default expiry is used. + */ + public void putId(String identifier, Instant expiry) { + if (identifier == null || "".equals(identifier)) { + return; + } + + cache.put(identifier, new EHCacheValue(identifier, expiry)); + } + + /** + * Return true if the given identifier is contained in the cache + * @param identifier The identifier to check + */ + public boolean contains(String identifier) { + if (cache == null) { + return false; + } + EHCacheValue element = cache.get(identifier); + return element != null; + } + + public synchronized void close() { + if (!cacheManager.isClosed()) { + cacheManager.destroyCache(CACHE_KEY); + cacheManager.close(); + } + } + + +} diff --git a/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/state/jcache/JCacheSPStateManager.java b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/state/jcache/JCacheSPStateManager.java new file mode 100644 index 0000000000..4932a5d0dd --- /dev/null +++ b/rt/rs/security/sso/saml/src/main/java/org/apache/cxf/rs/security/saml/sso/state/jcache/JCacheSPStateManager.java @@ -0,0 +1,165 @@ +/** + * 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.cxf.rs.security.saml.sso.state.jcache; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.Caching; +import javax.cache.configuration.MutableConfiguration; +import javax.cache.spi.CachingProvider; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.jaxrs.utils.ResourceUtils; +import org.apache.cxf.rs.security.saml.sso.state.RequestState; +import org.apache.cxf.rs.security.saml.sso.state.ResponseState; +import org.apache.cxf.rs.security.saml.sso.state.SPStateManager; +import org.apache.wss4j.common.util.Loader; + +/** + * An in-memory JCache based implementation of the SPStateManager interface. + * The default TTL is 5 minutes. + */ +public class JCacheSPStateManager implements SPStateManager { + + public static final String REQUEST_CACHE_KEY = "cxf.samlp.request.state.cache"; + public static final String RESPONSE_CACHE_KEY = "cxf.samlp.response.state.cache"; + private static final String DEFAULT_CONFIG_URL = "/cxf-samlp-jcache.xml"; + private static final org.slf4j.Logger LOG = + org.slf4j.LoggerFactory.getLogger(JCacheSPStateManager.class); + + private final Cache<String, RequestState> requestCache; + private final Cache<String, ResponseState> responseCache; + private final CacheManager cacheManager; + + public JCacheSPStateManager() throws URISyntaxException { + this(DEFAULT_CONFIG_URL, null); + } + + public JCacheSPStateManager(Bus bus) throws URISyntaxException { + this(DEFAULT_CONFIG_URL, bus); + } + + public JCacheSPStateManager(String configFileURL) throws URISyntaxException { + this(configFileURL, null); + } + + public JCacheSPStateManager(String configFile, Bus bus) throws URISyntaxException { + if (bus == null) { + bus = BusFactory.getThreadDefaultBus(true); + } + + URL configFileURL = null; + try { + configFileURL = + ResourceUtils.getClasspathResourceURL(configFile, JCacheSPStateManager.class, bus); + } catch (Exception ex) { + // ignore + } + + final CachingProvider cachingProvider = Caching.getCachingProvider(); + + cacheManager = cachingProvider.getCacheManager( + getConfigFileURL(configFileURL).toURI(), + getClass().getClassLoader()); + + requestCache = getOrCreate(cacheManager, REQUEST_CACHE_KEY, String.class, RequestState.class); + responseCache = getOrCreate(cacheManager, RESPONSE_CACHE_KEY, String.class, ResponseState.class); + } + + private static <K, V> Cache<K, V> getOrCreate(CacheManager cacheManager, String name, + Class<K> kclass, Class<V> vclass) { + + final Cache<K, V> cache = cacheManager.getCache(name, kclass, vclass); + if (cache != null) { + return cache; + } + + final MutableConfiguration<K, V> cacheConfiguration = new MutableConfiguration<>(); + cacheConfiguration.setTypes(kclass, vclass); + + return cacheManager.createCache(name, cacheConfiguration); + } + + private URL getConfigFileURL(URL suppliedConfigFileURL) { + if (suppliedConfigFileURL == null) { + //using the default + try { + URL configFileURL = Loader.getResource(DEFAULT_CONFIG_URL); + if (configFileURL == null) { + configFileURL = new URL(DEFAULT_CONFIG_URL); + } + return configFileURL; + } catch (IOException e) { + // Do nothing + LOG.debug(e.getMessage()); + } + } + return suppliedConfigFileURL; + } + + public ResponseState getResponseState(String securityContextKey) { + return responseCache.get(securityContextKey); + } + + public ResponseState removeResponseState(String securityContextKey) { + ResponseState responseState = getResponseState(securityContextKey); + if (responseState != null) { + responseCache.remove(securityContextKey); + } + return responseState; + } + + public void setResponseState(String securityContextKey, ResponseState state) { + if (securityContextKey == null || "".equals(securityContextKey)) { + return; + } + + responseCache.put(securityContextKey, state); + } + + public void setRequestState(String relayState, RequestState state) { + if (relayState == null || "".equals(relayState)) { + return; + } + + requestCache.put(relayState, state); + } + + public RequestState removeRequestState(String relayState) { + RequestState state = requestCache.get(relayState); + if (state != null) { + requestCache.remove(relayState); + } + return state; + } + + public synchronized void close() throws IOException { + if (!cacheManager.isClosed()) { + cacheManager.destroyCache(REQUEST_CACHE_KEY); + cacheManager.destroyCache(RESPONSE_CACHE_KEY); + cacheManager.close(); + } + } + +} diff --git a/rt/rs/security/sso/saml/src/main/resources/cxf-samlp-ehcache.xml b/rt/rs/security/sso/saml/src/main/resources/cxf-samlp-ehcache.xml index f3b73551e4..3991aa4f91 100644 --- a/rt/rs/security/sso/saml/src/main/resources/cxf-samlp-ehcache.xml +++ b/rt/rs/security/sso/saml/src/main/resources/cxf-samlp-ehcache.xml @@ -1,4 +1,22 @@ <?xml version="1.0"?> +<!-- + 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. +--> <config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://www.ehcache.org/v3' diff --git a/rt/rs/security/sso/saml/src/main/resources/cxf-samlp-jcache.xml b/rt/rs/security/sso/saml/src/main/resources/cxf-samlp-jcache.xml new file mode 100644 index 0000000000..4615649554 --- /dev/null +++ b/rt/rs/security/sso/saml/src/main/resources/cxf-samlp-jcache.xml @@ -0,0 +1,74 @@ +<?xml version="1.0"?> +<!-- + 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. +--> +<config + xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' + xmlns='http://www.ehcache.org/v3' + xmlns:jsr107='http://www.ehcache.org/v3/jsr107' + xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.8.xsd + http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd"> + + <service> + <jsr107:defaults> + <jsr107:cache name="cxf.samlp.response.state.cache" template="cxf.samlp.response.state.cache.template"/> + <jsr107:cache name="cxf.samlp.request.state.cache" template="cxf.samlp.request.state.cache.template"/> + <jsr107:cache name="cxf.samlp.replay.cache" template="cxf.samlp.replay.cache.template"/> + </jsr107:defaults> + </service> + + <persistence directory="${java.io.tmpdir}/ehcache/" /> + + <cache-template name="cxf.samlp.replay.cache.template"> + <key-type>java.lang.String</key-type> + <value-type>org.apache.wss4j.common.cache.EHCacheValue</value-type> + <expiry> + <class>org.apache.wss4j.common.cache.EHCacheExpiry</class> + </expiry> + <resources> + <heap unit="entries">5000</heap> + <disk unit="MB" persistent="false">10</disk> + </resources> + </cache-template> + + <cache-template name="cxf.samlp.request.state.cache.template"> + <key-type>java.lang.String</key-type> + <value-type>org.apache.cxf.rs.security.saml.sso.state.RequestState</value-type> + <expiry> + <ttl unit="seconds">300</ttl> + </expiry> + <resources> + <heap unit="entries">5000</heap> + <disk unit="MB" persistent="false">10</disk> + </resources> + </cache-template> + + <cache-template name="cxf.samlp.response.state.cache.template"> + <key-type>java.lang.String</key-type> + <value-type>org.apache.cxf.rs.security.saml.sso.state.ResponseState</value-type> + <expiry> + <ttl unit="seconds">300</ttl> + </expiry> + <resources> + <heap unit="entries">5000</heap> + <disk unit="MB" persistent="false">10</disk> + </resources> + </cache-template> + +</config> + diff --git a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SPStateManagerTest.java b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SPStateManagerTest.java index a771631e12..587c27237d 100644 --- a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SPStateManagerTest.java +++ b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/SPStateManagerTest.java @@ -30,6 +30,7 @@ import org.apache.cxf.rs.security.saml.sso.state.MemorySPStateManager; import org.apache.cxf.rs.security.saml.sso.state.RequestState; import org.apache.cxf.rs.security.saml.sso.state.ResponseState; import org.apache.cxf.rs.security.saml.sso.state.SPStateManager; +import org.apache.cxf.rs.security.saml.sso.state.jcache.JCacheSPStateManager; import org.junit.After; import org.junit.Before; @@ -55,7 +56,7 @@ public class SPStateManagerTest { @Parameterized.Parameters(name = "{0}") public static Collection<Class<? extends SPStateManager>> data() { - return Arrays.asList(MemorySPStateManager.class, EHCacheSPStateManager.class); + return Arrays.asList(MemorySPStateManager.class, EHCacheSPStateManager.class, JCacheSPStateManager.class); } @Before diff --git a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/TokenReplayCacheTest.java b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/TokenReplayCacheTest.java index 66c8d9ced8..0b3731366b 100644 --- a/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/TokenReplayCacheTest.java +++ b/rt/rs/security/sso/saml/src/test/java/org/apache/cxf/rs/security/saml/sso/TokenReplayCacheTest.java @@ -21,9 +21,15 @@ package org.apache.cxf.rs.security.saml.sso; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Collection; import java.util.UUID; +import org.apache.cxf.rs.security.saml.sso.jcache.JCacheTokenReplayCache; + import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -31,12 +37,23 @@ import static org.junit.Assert.assertTrue; /** * Some unit tests for the TokenReplayCache implementations */ +@RunWith(value = org.junit.runners.Parameterized.class) public class TokenReplayCacheTest { + private final Class<? extends TokenReplayCache<String>> tokenReplayCacheClass; + + public TokenReplayCacheTest(Class<? extends TokenReplayCache<String>> tokenReplayCacheClass) { + this.tokenReplayCacheClass = tokenReplayCacheClass; + } + + @Parameterized.Parameters(name = "{0}") + public static Collection<Class<? extends TokenReplayCache<String>>> data() { + return Arrays.asList(EHCacheTokenReplayCache.class, JCacheTokenReplayCache.class); + } @Test - public void testEhCacheTokenReplayCache() throws Exception { + public void testTokenReplayCacheTokenReplayCache() throws Exception { - TokenReplayCache<String> replayCache = new EHCacheTokenReplayCache(); + TokenReplayCache<String> replayCache = createCache(); testTokenReplayCacheInstance(replayCache); @@ -44,16 +61,16 @@ public class TokenReplayCacheTest { } @Test - public void testEhCacheCloseCacheTwice() throws Exception { - TokenReplayCache<String> replayCache = new EHCacheTokenReplayCache(); + public void testTokenReplayCacheCloseCacheTwice() throws Exception { + TokenReplayCache<String> replayCache = createCache(); replayCache.close(); replayCache.close(); } // No expiry specified so it falls back to the default @Test - public void testEhCacheTokenReplayCacheNoExpirySpecified() throws Exception { - TokenReplayCache<String> replayCache = new EHCacheTokenReplayCache(); + public void testTokenReplayCacheNoExpirySpecified() throws Exception { + TokenReplayCache<String> replayCache = createCache(); String id = UUID.randomUUID().toString(); replayCache.putId(id); @@ -64,8 +81,8 @@ public class TokenReplayCacheTest { // The negative expiry is rejected and it falls back to the default @Test - public void testEhCacheTokenReplayCacheNegativeExpiry() throws Exception { - TokenReplayCache<String> replayCache = new EHCacheTokenReplayCache(); + public void testTokenReplayCacheNegativeExpiry() throws Exception { + TokenReplayCache<String> replayCache = createCache(); String id = UUID.randomUUID().toString(); replayCache.putId(id, Instant.now().minusSeconds(100L)); @@ -76,8 +93,8 @@ public class TokenReplayCacheTest { // The huge expiry is rejected and it falls back to the default @Test - public void testEhCacheTokenReplayCacheHugeExpiry() throws Exception { - TokenReplayCache<String> replayCache = new EHCacheTokenReplayCache(); + public void testTokenReplayCacheHugeExpiry() throws Exception { + TokenReplayCache<String> replayCache = createCache(); String id = UUID.randomUUID().toString(); replayCache.putId(id, Instant.now().plus(14, ChronoUnit.HOURS)); @@ -105,4 +122,8 @@ public class TokenReplayCacheTest { assertFalse(replayCache.contains(id)); } -} \ No newline at end of file + + private TokenReplayCache<String> createCache() throws Exception { + return tokenReplayCacheClass.getDeclaredConstructor().newInstance(); + } +} diff --git a/rt/ws/security/pom.xml b/rt/ws/security/pom.xml index 1733920d56..0605bc452d 100644 --- a/rt/ws/security/pom.xml +++ b/rt/ws/security/pom.xml @@ -96,6 +96,13 @@ <version>${cxf.ehcache3.version}</version> <scope>compile</scope> </dependency> + <dependency> + <groupId>org.ehcache.modules</groupId> + <artifactId>ehcache-107</artifactId> + <version>${cxf.ehcache3.version}</version> + <scope>provided</scope> + <optional>true</optional> + </dependency> <dependency> <groupId>org.apache.wss4j</groupId> <artifactId>wss4j-ws-security-dom</artifactId> diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/jcache/CXFJCacheReplayCache.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/jcache/CXFJCacheReplayCache.java new file mode 100644 index 0000000000..e751ad8f39 --- /dev/null +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/jcache/CXFJCacheReplayCache.java @@ -0,0 +1,50 @@ +/** + * 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.cxf.ws.security.cache.jcache; + +import org.apache.cxf.Bus; +import org.apache.cxf.buslifecycle.BusLifeCycleListener; +import org.apache.cxf.buslifecycle.BusLifeCycleManager; +import org.apache.wss4j.common.ext.WSSecurityException; + +/** + * Wrap the default JCacheReplayCache in a BusLifeCycleListener, to make sure that + * the cache is shutdown correctly. + */ +public class CXFJCacheReplayCache extends JCacheReplayCache implements BusLifeCycleListener { + private final Bus bus; + + public CXFJCacheReplayCache(String key, Bus bus) throws WSSecurityException { + super(key); + this.bus = bus; + if (bus != null) { + bus.getExtension(BusLifeCycleManager.class).registerLifeCycleListener(this); + } + } + + @Override + public void close() { + super.close(); + + if (bus != null) { + bus.getExtension(BusLifeCycleManager.class).unregisterLifeCycleListener(this); + } + } +} diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/jcache/JCacheReplayCache.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/jcache/JCacheReplayCache.java new file mode 100644 index 0000000000..fca66a8551 --- /dev/null +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/cache/jcache/JCacheReplayCache.java @@ -0,0 +1,141 @@ +/** + * 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.cxf.ws.security.cache.jcache; + +import java.time.Instant; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.Caching; +import javax.cache.expiry.Duration; +import javax.cache.expiry.ExpiryPolicy; +import javax.cache.spi.CachingProvider; + +import org.apache.cxf.ws.security.utils.JCacheUtils; +import org.apache.wss4j.common.cache.EHCacheExpiry; +import org.apache.wss4j.common.cache.EHCacheValue; +import org.apache.wss4j.common.cache.ReplayCache; +import org.apache.wss4j.common.ext.WSSecurityException; + +/** + * An in-memory JCache based implementation of the ReplayCache interface. + * The default TTL is 60 minutes and the max TTL is 12 hours. + */ +class JCacheReplayCache implements ReplayCache { + private static final org.slf4j.Logger LOG = + org.slf4j.LoggerFactory.getLogger(JCacheReplayCache.class); + + private final Cache<String, EHCacheValue> cache; + private final CacheManager cacheManager; + private final String key; + + JCacheReplayCache(String key) throws WSSecurityException { + this(key, 10000); + } + + JCacheReplayCache(String key, long heapEntries) throws WSSecurityException { + this.key = Objects.requireNonNull(key); + + // Do some checking on the arguments + try { + final CachingProvider cachingProvider = Caching.getCachingProvider(); + cacheManager = cachingProvider.getCacheManager(); + cache = JCacheUtils.getOrCreate(cacheManager, key, String.class, EHCacheValue.class, + cacheConfiguration -> cacheConfiguration.setExpiryPolicyFactory(() -> new ExpiryPolicy() { + @Override + public Duration getExpiryForCreation() { + return new Duration(TimeUnit.SECONDS, EHCacheExpiry.DEFAULT_TTL); + } + + @Override + public Duration getExpiryForAccess() { + return null; + } + + @Override + public Duration getExpiryForUpdate() { + return null; + } + })); + } catch (Exception ex) { + LOG.error("Error configuring JCacheReplayCache: {}", ex.getMessage()); + throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex, "replayCacheError"); + } + } + + /** + * Add the given identifier to the cache. It will be cached for a default amount of time. + * @param identifier The identifier to be added + */ + public void add(String identifier) { + add(identifier, null); + } + + /** + * Add the given identifier to the cache to be cached for the given time + * @param identifier The identifier to be added + * @param expiry A custom expiry time for the identifier. Can be null in which case, the default expiry is used. + */ + public void add(String identifier, Instant expiry) { + if (identifier == null || identifier.length() == 0) { + return; + } + + cache.put(identifier, new EHCacheValue(identifier, expiry)); + } + + /** + * Return true if the given identifier is contained in the cache + * @param identifier The identifier to check + */ + public boolean contains(String identifier) { + if (cache == null) { + return false; + } + EHCacheValue element = cache.get(identifier); + return element != null; + } + + // Only exposed for testing + EHCacheValue get(String identifier) { + return cache.get(identifier); + } + + @Override + public synchronized void close() { + if (!cacheManager.isClosed()) { + cacheManager.destroyCache(key); + cacheManager.close(); + } + } + + public void initComplete() { + } + + public void preShutdown() { + close(); + } + + public void postShutdown() { + close(); + } +} diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/TokenStoreFactory.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/TokenStoreFactory.java index 728fa3a553..436508d79d 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/TokenStoreFactory.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/TokenStoreFactory.java @@ -20,36 +20,23 @@ package org.apache.cxf.ws.security.tokenstore; import org.apache.cxf.message.Message; +import org.apache.cxf.ws.security.tokenstore.jcache.JCacheTokenStoreFactory; +import org.apache.cxf.ws.security.utils.JCacheUtils; +import org.apache.wss4j.common.cache.WSS4JCacheUtil; /** * An abstract factory to return a TokenStore instance. It returns an EHCacheTokenStoreFactory * if EH-Cache is available. Otherwise it returns a MemoryTokenStoreFactory. */ public abstract class TokenStoreFactory { - - private static boolean ehCacheInstalled; - - static { - try { - Class<?> cacheManagerClass = Class.forName("org.ehcache.CacheManager"); - if (cacheManagerClass != null) { - ehCacheInstalled = true; - } - } catch (Exception e) { - //ignore - } - } - - public static synchronized boolean isEhCacheInstalled() { - return ehCacheInstalled; - } - public static TokenStoreFactory newInstance() { - if (isEhCacheInstalled()) { + if (WSS4JCacheUtil.isEhCacheInstalled()) { return new EHCacheTokenStoreFactory(); + } else if (JCacheUtils.isJCacheInstalled()) { + return new JCacheTokenStoreFactory(); + } else { + return new MemoryTokenStoreFactory(); } - - return new MemoryTokenStoreFactory(); } public abstract TokenStore newTokenStore(String key, Message message) throws TokenStoreException; diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/jcache/JCacheTokenStore.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/jcache/JCacheTokenStore.java new file mode 100644 index 0000000000..a179c6ab30 --- /dev/null +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/jcache/JCacheTokenStore.java @@ -0,0 +1,131 @@ +/** + * 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.cxf.ws.security.tokenstore.jcache; + +import java.io.Closeable; +import java.net.URL; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.Caching; +import javax.cache.spi.CachingProvider; + +import org.apache.cxf.Bus; +import org.apache.cxf.buslifecycle.BusLifeCycleListener; +import org.apache.cxf.buslifecycle.BusLifeCycleManager; +import org.apache.cxf.common.util.StringUtils; +import org.apache.cxf.ws.security.tokenstore.SecurityToken; +import org.apache.cxf.ws.security.tokenstore.TokenStore; +import org.apache.cxf.ws.security.tokenstore.TokenStoreException; +import org.apache.cxf.ws.security.utils.JCacheUtils; + +/** + * An in-memory JCache implementation of the TokenStore interface. The default TTL is 60 minutes + * and the max TTL is 12 hours. + */ +public class JCacheTokenStore implements TokenStore, Closeable, BusLifeCycleListener { + private final Bus bus; + private final Cache<String, SecurityToken> cache; + private final CacheManager cacheManager; + private final String key; + + public JCacheTokenStore(String key, Bus b, URL configFileURL) throws TokenStoreException { + bus = b; + if (bus != null) { + b.getExtension(BusLifeCycleManager.class).registerLifeCycleListener(this); + } + + this.key = key; + try { + // Exclude the endpoint info bit added in TokenStoreUtils when getting the template name + String template = key; + if (template.contains("-")) { + template = key.substring(0, key.lastIndexOf('-')); + } + + final CachingProvider cachingProvider = Caching.getCachingProvider(); + cacheManager = cachingProvider.getCacheManager(configFileURL.toURI(), + SecurityToken.class.getClassLoader()); + cache = JCacheUtils.getOrCreate(cacheManager, key, String.class, SecurityToken.class); + } catch (Exception e) { + throw new TokenStoreException(e); + } + } + + public void add(SecurityToken token) { + if (token != null && !StringUtils.isEmpty(token.getId())) { + cache.put(token.getId(), token); + } + } + + public void add(String identifier, SecurityToken token) { + if (token != null && !StringUtils.isEmpty(identifier)) { + cache.put(identifier, token); + } + } + + public void remove(String identifier) { + if (cache != null && !StringUtils.isEmpty(identifier)) { + cache.remove(identifier); + } + } + + public Collection<String> getTokenIdentifiers() { + if (cache == null) { + return null; + } + + // Not very efficient, but we are only using this method for testing + Set<String> keys = new HashSet<>(); + for (Cache.Entry<String, SecurityToken> entry : cache) { + keys.add(entry.getKey()); + } + + return keys; + } + + public SecurityToken getToken(String identifier) { + if (cache == null) { + return null; + } + return cache.get(identifier); + } + + public synchronized void close() { + if (!cacheManager.isClosed()) { + cacheManager.destroyCache(key); + cacheManager.close(); + } + } + + public void initComplete() { + } + + public void preShutdown() { + close(); + } + + public void postShutdown() { + close(); + } +} diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/jcache/JCacheTokenStoreFactory.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/jcache/JCacheTokenStoreFactory.java new file mode 100644 index 0000000000..039b94912b --- /dev/null +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/tokenstore/jcache/JCacheTokenStoreFactory.java @@ -0,0 +1,51 @@ +/** + * 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.cxf.ws.security.tokenstore.jcache; + +import java.net.URL; + +import org.apache.cxf.message.Message; +import org.apache.cxf.rt.security.utils.SecurityUtils; +import org.apache.cxf.ws.security.SecurityConstants; +import org.apache.cxf.ws.security.tokenstore.TokenStore; +import org.apache.cxf.ws.security.tokenstore.TokenStoreException; +import org.apache.cxf.ws.security.tokenstore.TokenStoreFactory; +import org.apache.wss4j.common.util.Loader; + + +/** + * A factory to return an JCacheTokenStore instance. + */ +public class JCacheTokenStoreFactory extends TokenStoreFactory { + + private static final String DEFAULT_CONFIG_FILE = "cxf-jcache.xml"; + + @Override + public TokenStore newTokenStore(String key, Message message) throws TokenStoreException { + URL configFileURL = SecurityUtils.getConfigFileURL(message, SecurityConstants.CACHE_CONFIG_FILE, + DEFAULT_CONFIG_FILE); + if (configFileURL == null) { + configFileURL = Loader.getResource(this.getClass().getClassLoader(), + DEFAULT_CONFIG_FILE); + } + return new JCacheTokenStore(key, message.getExchange().getBus(), configFileURL); + } + +} diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/utils/JCacheUtils.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/utils/JCacheUtils.java new file mode 100644 index 0000000000..223207695e --- /dev/null +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/utils/JCacheUtils.java @@ -0,0 +1,73 @@ +/** + * 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.cxf.ws.security.utils; + +import java.util.function.Function; +import java.util.logging.Logger; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.configuration.MutableConfiguration; + +import org.apache.cxf.common.logging.LogUtils; + +public final class JCacheUtils { + private static final Logger LOG = LogUtils.getL7dLogger(JCacheUtils.class); + private static final boolean JCACHE_INSTALLED; + + static { + boolean jcacheInstalled = false; + try { + final Class<?> caching = Class.forName("javax.cache.Caching"); + if (caching != null) { + jcacheInstalled = true; + } + } catch (Exception e) { + LOG.fine("No JCache SPIs detected on classpath: " + e.getMessage()); + } + JCACHE_INSTALLED = jcacheInstalled; + } + + private JCacheUtils() { + } + + public static <K, V> Cache<K, V> getOrCreate(CacheManager cacheManager, String name, Class<K> kclass, + Class<V> vclass) { + return getOrCreate(cacheManager, name, kclass, vclass, Function.identity()); + } + + public static <K, V> Cache<K, V> getOrCreate(CacheManager cacheManager, String name, Class<K> kclass, + Class<V> vclass, Function<MutableConfiguration<K, V>, MutableConfiguration<K, V>> customizer) { + + final Cache<K, V> cache = cacheManager.getCache(name, kclass, vclass); + if (cache != null) { + return cache; + } + + MutableConfiguration<K, V> cacheConfiguration = new MutableConfiguration<>(); + cacheConfiguration = customizer.apply(cacheConfiguration.setTypes(kclass, vclass)); + + return cacheManager.createCache(name, cacheConfiguration); + } + + public static boolean isJCacheInstalled() { + return JCACHE_INSTALLED; + } + +} diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JUtils.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JUtils.java index b2eda68c50..df6f1b7f1f 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JUtils.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/WSS4JUtils.java @@ -47,9 +47,11 @@ import org.apache.cxf.rt.security.utils.SecurityUtils; import org.apache.cxf.service.model.EndpointInfo; import org.apache.cxf.ws.security.SecurityConstants; import org.apache.cxf.ws.security.cache.CXFEHCacheReplayCache; +import org.apache.cxf.ws.security.cache.jcache.CXFJCacheReplayCache; import org.apache.cxf.ws.security.tokenstore.SecurityToken; import org.apache.cxf.ws.security.tokenstore.TokenStoreException; import org.apache.cxf.ws.security.tokenstore.TokenStoreUtils; +import org.apache.cxf.ws.security.utils.JCacheUtils; import org.apache.wss4j.common.cache.MemoryReplayCache; import org.apache.wss4j.common.cache.ReplayCache; import org.apache.wss4j.common.cache.WSS4JCacheUtil; @@ -146,6 +148,9 @@ public final class WSS4JUtils { throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex); } replayCache = new CXFEHCacheReplayCache(cacheKey, bus, diskstoreParent); + } else if (JCacheUtils.isJCacheInstalled()) { + Bus bus = message.getExchange().getBus(); + replayCache = new CXFJCacheReplayCache(cacheKey, bus); } else { replayCache = new MemoryReplayCache(); } diff --git a/rt/ws/security/src/main/resources/cxf-ehcache.xml b/rt/ws/security/src/main/resources/cxf-ehcache.xml index eedfa20f13..c8238c712d 100644 --- a/rt/ws/security/src/main/resources/cxf-ehcache.xml +++ b/rt/ws/security/src/main/resources/cxf-ehcache.xml @@ -1,4 +1,22 @@ <?xml version="1.0"?> +<!-- + 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. +--> <config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://www.ehcache.org/v3' diff --git a/rt/ws/security/src/main/resources/cxf-jcache.xml b/rt/ws/security/src/main/resources/cxf-jcache.xml new file mode 100644 index 0000000000..48800ebb4c --- /dev/null +++ b/rt/ws/security/src/main/resources/cxf-jcache.xml @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<!-- + 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. +--> +<config + xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' + xmlns='http://www.ehcache.org/v3' + xmlns:jsr107='http://www.ehcache.org/v3/jsr107' + xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.8.xsd + http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd"> + + <service> + <jsr107:defaults default-template="org.apache.cxf.ws.security.tokenstore.TokenStore.template"> + <jsr107:cache name="org.apache.cxf.ws.security.tokenstore.TokenStore" template="org.apache.cxf.ws.security.tokenstore.TokenStore.template"/> + </jsr107:defaults> + </service> + + <!-- + Security Tokens are not writable to Disk so use a memory only + cache with a bit larger maxEntriesLocalHeap. However, this + cache is per-endpoint so not "too large" to not consume the + entire heap. + --> + <cache-template name="org.apache.cxf.ws.security.tokenstore.TokenStore.template"> + <key-type>java.lang.String</key-type> + <value-type copier="org.ehcache.impl.copy.IdentityCopier">org.apache.cxf.ws.security.tokenstore.SecurityToken</value-type> + <expiry> + <ttl unit="seconds">3600</ttl> + </expiry> + <resources> + <heap unit="entries">10000</heap> + </resources> + </cache-template> + +</config> diff --git a/rt/ws/security/src/test/java/org/apache/cxf/ws/security/tokenstore/TokenStoreTest.java b/rt/ws/security/src/test/java/org/apache/cxf/ws/security/tokenstore/TokenStoreTest.java index 506bb78a67..44707bb147 100644 --- a/rt/ws/security/src/test/java/org/apache/cxf/ws/security/tokenstore/TokenStoreTest.java +++ b/rt/ws/security/src/test/java/org/apache/cxf/ws/security/tokenstore/TokenStoreTest.java @@ -25,11 +25,14 @@ import org.apache.cxf.message.ExchangeImpl; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageImpl; import org.apache.cxf.ws.security.SecurityConstants; +import org.apache.cxf.ws.security.tokenstore.jcache.JCacheTokenStoreFactory; import org.apache.xml.security.utils.ClassLoaderUtils; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -46,14 +49,13 @@ public class TokenStoreTest { @Parameterized.Parameters(name = "{0}") public static Collection<TokenStore> data() throws TokenStoreException { Message message = new MessageImpl(); - message.put( - SecurityConstants.CACHE_CONFIG_FILE, - ClassLoaderUtils.getResource("cxf-ehcache.xml", TokenStoreTest.class) - ); message.setExchange(new ExchangeImpl()); return Arrays.asList( new MemoryTokenStoreFactory().newTokenStore(SecurityConstants.TOKEN_STORE_CACHE_INSTANCE, message), - new EHCacheTokenStoreFactory().newTokenStore(SecurityConstants.TOKEN_STORE_CACHE_INSTANCE, message) + new EHCacheTokenStoreFactory().newTokenStore(SecurityConstants.TOKEN_STORE_CACHE_INSTANCE, + withConfigFile(message, "cxf-ehcache.xml")), + new JCacheTokenStoreFactory().newTokenStore(SecurityConstants.TOKEN_STORE_CACHE_INSTANCE, + withConfigFile(message, "cxf-jcache.xml")) ); } @@ -84,7 +86,7 @@ public class TokenStoreTest { store.add(token1); store.add(token2); store.add(token3); - assertTrue(store.getTokenIdentifiers().size() == 3); + assertThat(store.getTokenIdentifiers(), hasSize(3)); store.remove(token3.getId()); assertNull(store.getToken("test3")); store.remove(token1.getId()); @@ -92,4 +94,11 @@ public class TokenStoreTest { assertTrue(store.getTokenIdentifiers().isEmpty()); } + private static Message withConfigFile(Message message, String configFile) { + message.put( + SecurityConstants.CACHE_CONFIG_FILE, + ClassLoaderUtils.getResource(configFile, TokenStoreTest.class) + ); + return message; + } } diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/cache/jcache/JCacheIdentityCache.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/cache/jcache/JCacheIdentityCache.java new file mode 100644 index 0000000000..cf58dde1ce --- /dev/null +++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/cache/jcache/JCacheIdentityCache.java @@ -0,0 +1,182 @@ +/** + * 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.cxf.sts.cache.jcache; + +import java.io.Closeable; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.Caching; +import javax.cache.spi.CachingProvider; +import javax.management.JMException; +import javax.management.ObjectName; + +import org.apache.cxf.Bus; +import org.apache.cxf.buslifecycle.BusLifeCycleListener; +import org.apache.cxf.buslifecycle.BusLifeCycleManager; +import org.apache.cxf.common.classloader.ClassLoaderUtils; +import org.apache.cxf.common.logging.LogUtils; +import org.apache.cxf.management.InstrumentationManager; +import org.apache.cxf.management.ManagementConstants; +import org.apache.cxf.management.annotation.ManagedOperation; +import org.apache.cxf.management.annotation.ManagedResource; +import org.apache.cxf.resource.ResourceManager; +import org.apache.cxf.sts.IdentityMapper; +import org.apache.cxf.sts.cache.AbstractIdentityCache; +import org.apache.cxf.sts.cache.EHCacheIdentityValue; +import org.apache.cxf.ws.security.tokenstore.TokenStoreFactory; +import org.apache.cxf.ws.security.utils.JCacheUtils; + +/** + * A JCache based cache to cache identities in different realms where + * the relationship is of type FederateIdentity. + */ +@ManagedResource() +public class JCacheIdentityCache extends AbstractIdentityCache + implements Closeable, BusLifeCycleListener { + + private static final Logger LOG = LogUtils.getL7dLogger(JCacheIdentityCache.class); + private static final String KEY = "org.apache.cxf.sts.cache.EHCacheIdentityCache"; + private Cache<String, EHCacheIdentityValue> cache; + private final CacheManager cacheManager; + + + public JCacheIdentityCache( + IdentityMapper identityMapper, Bus b + ) { + this(identityMapper, JCacheIdentityCache.class.getName(), b, null); + } + + public JCacheIdentityCache(IdentityMapper identityMapper, String key, Bus b, URL configFileURL) { + this(Caching.getCachingProvider(), identityMapper, key, b, configFileURL); + } + + JCacheIdentityCache(CachingProvider cachingProvider, IdentityMapper identityMapper, + String key, Bus b, URL configFileURL) { + super(b, identityMapper); + if (b != null) { + b.getExtension(BusLifeCycleManager.class).registerLifeCycleListener(this); + InstrumentationManager im = b.getExtension(InstrumentationManager.class); + if (im != null) { + try { + im.register(this); + } catch (JMException e) { + LOG.log(Level.WARNING, "Registering JCacheIdentityCache failed.", e); + } + } + } + + final URL xmlConfigURL = configFileURL != null ? configFileURL : getDefaultConfigFileURL(); + try { + cacheManager = cachingProvider.getCacheManager(xmlConfigURL.toURI(), getClass().getClassLoader()); + cache = JCacheUtils.getOrCreate(cacheManager, KEY, String.class, EHCacheIdentityValue.class); + } catch (final URISyntaxException ex) { + throw new IllegalStateException("Unable to convert " + xmlConfigURL + " to URI", ex); + } + } + + @Override + public void add(String user, String realm, Map<String, String> identities) { + cache.put(user + "@" + realm, new EHCacheIdentityValue(identities)); + } + + @ManagedOperation() + @Override + public Map<String, String> get(String user, String realm) { + EHCacheIdentityValue value = cache.get(user + "@" + realm); + if (value != null) { + return value.getValue(); + } + + return null; + } + + @Override + public void remove(String user, String realm) { + cache.remove(user + "@" + realm); + } + + @ManagedOperation() + public String getContent() { + return this.cache.toString(); + } + + public synchronized void close() { + if (!cacheManager.isClosed()) { + cacheManager.destroyCache(KEY); + cacheManager.close(); + + if (super.getBus() != null) { + super.getBus().getExtension(BusLifeCycleManager.class).unregisterLifeCycleListener(this); + } + } + } + + public void initComplete() { + } + + public void preShutdown() { + close(); + } + + public void postShutdown() { + close(); + } + + private URL getDefaultConfigFileURL() { + URL url = null; + if (super.getBus() != null) { + ResourceManager rm = super.getBus().getExtension(ResourceManager.class); + url = rm.resolveResource("sts-jcache.xml", URL.class); + } + try { + if (url == null) { + url = ClassLoaderUtils.getResource("sts-jcache.xml", TokenStoreFactory.class); + } + if (url == null) { + url = new URL("sts-jcache.xml"); + } + return url; + } catch (IOException e) { + // Do nothing + } + return null; + } + + public ObjectName getObjectName() throws JMException { + StringBuilder buffer = new StringBuilder(128); + buffer.append(ManagementConstants.DEFAULT_DOMAIN_NAME).append(':'); + if (super.getBus() != null) { + buffer.append( + ManagementConstants.BUS_ID_PROP).append('=').append(super.getBus().getId()).append(','); + } + buffer.append(ManagementConstants.TYPE_PROP).append('=').append("EHCacheIdentityCache").append(','); + buffer.append(ManagementConstants.NAME_PROP).append('=') + .append("EHCacheIdentityCache-").append(System.identityHashCode(this)); + return new ObjectName(buffer.toString()); + } +} + diff --git a/services/sts/sts-core/src/main/resources/sts-ehcache.xml b/services/sts/sts-core/src/main/resources/sts-ehcache.xml index c1a307f44a..b5409540fc 100644 --- a/services/sts/sts-core/src/main/resources/sts-ehcache.xml +++ b/services/sts/sts-core/src/main/resources/sts-ehcache.xml @@ -1,4 +1,22 @@ <?xml version="1.0"?> +<!-- + 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. +--> <config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns='http://www.ehcache.org/v3' diff --git a/services/sts/sts-core/src/main/resources/sts-jcache.xml b/services/sts/sts-core/src/main/resources/sts-jcache.xml new file mode 100644 index 0000000000..e27555b8e7 --- /dev/null +++ b/services/sts/sts-core/src/main/resources/sts-jcache.xml @@ -0,0 +1,40 @@ +<?xml version="1.0"?> +<!-- + 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. +--> +<config + xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' + xmlns='http://www.ehcache.org/v3' + xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.8.xsd"> + + <persistence directory="${java.io.tmpdir}" /> + + <cache alias="org.apache.cxf.sts.cache.EHCacheIdentityCache"> + <key-type>java.lang.String</key-type> + <value-type copier="org.ehcache.impl.copy.IdentityCopier">org.apache.cxf.sts.cache.EHCacheIdentityValue</value-type> + <expiry> + <ttl unit="seconds">3600</ttl> + </expiry> + <resources> + <heap unit="entries">5000</heap> + <disk unit="MB" persistent="false">10</disk> + </resources> + </cache> + +</config> + diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/cache/jcache/JCacheIdentityCacheTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/cache/jcache/JCacheIdentityCacheTest.java new file mode 100644 index 0000000000..c69dee6163 --- /dev/null +++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/cache/jcache/JCacheIdentityCacheTest.java @@ -0,0 +1,37 @@ +/** + * 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.cxf.sts.cache.jcache; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.sts.IdentityMapper; +import org.apache.cxf.sts.cache.AbstractIdentityCache; +import org.apache.cxf.sts.cache.MemoryIdentityCacheTest; +import org.ehcache.jsr107.EhcacheCachingProvider; + +public class JCacheIdentityCacheTest extends MemoryIdentityCacheTest { + + @Override + protected AbstractIdentityCache getIdentityCache(IdentityMapper mapper) { + Bus bus = BusFactory.getDefaultBus(); + return new JCacheIdentityCache(new EhcacheCachingProvider(), mapper, + JCacheIdentityCache.class.getName(), bus, null); + } + +} diff --git a/services/xkms/xkms-client/src/main/java/org/apache/cxf/xkms/cache/jcache/JCacheXKMSClientCache.java b/services/xkms/xkms-client/src/main/java/org/apache/cxf/xkms/cache/jcache/JCacheXKMSClientCache.java new file mode 100644 index 0000000000..c1c078d139 --- /dev/null +++ b/services/xkms/xkms-client/src/main/java/org/apache/cxf/xkms/cache/jcache/JCacheXKMSClientCache.java @@ -0,0 +1,124 @@ +/** + * 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.cxf.xkms.cache.jcache; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +import javax.cache.Cache; +import javax.cache.CacheManager; +import javax.cache.Caching; +import javax.cache.expiry.Duration; +import javax.cache.expiry.ExpiryPolicy; +import javax.cache.spi.CachingProvider; + +import org.apache.cxf.Bus; +import org.apache.cxf.BusFactory; +import org.apache.cxf.buslifecycle.BusLifeCycleListener; +import org.apache.cxf.buslifecycle.BusLifeCycleManager; +import org.apache.cxf.ws.security.utils.JCacheUtils; +import org.apache.cxf.xkms.cache.XKMSCacheToken; +import org.apache.cxf.xkms.cache.XKMSClientCache; +import org.apache.cxf.xkms.cache.XKMSClientCacheException; + +/** + * An in-memory JCache implementation of the XKMSClientCache interface. + */ +public class JCacheXKMSClientCache implements XKMSClientCache, BusLifeCycleListener { + private final Cache<String, XKMSCacheToken> cache; + private final CacheManager cacheManager; + private final Bus bus; + private final String cacheKey; + + public JCacheXKMSClientCache() throws XKMSClientCacheException { + this(null); + } + + public JCacheXKMSClientCache(Bus cxfBus) throws XKMSClientCacheException { + if (cxfBus == null) { + cxfBus = BusFactory.getThreadDefaultBus(true); + } + if (cxfBus != null) { + cxfBus.getExtension(BusLifeCycleManager.class).registerLifeCycleListener(this); + } + + this.bus = cxfBus; + this.cacheKey = UUID.randomUUID().toString(); + + final CachingProvider cachingProvider = Caching.getCachingProvider(); + cacheManager = cachingProvider.getCacheManager(); + + cache = JCacheUtils.getOrCreate(cacheManager, cacheKey, String.class, XKMSCacheToken.class, + cacheConfiguration -> cacheConfiguration.setExpiryPolicyFactory(() -> new ExpiryPolicy() { + @Override + public Duration getExpiryForCreation() { + return new Duration(TimeUnit.SECONDS, 3600); + } + + @Override + public Duration getExpiryForAccess() { + return null; + } + + @Override + public Duration getExpiryForUpdate() { + return new Duration(TimeUnit.SECONDS, 3600); + } + }).setStoreByValue(false)); + + } + + /** + * Store an XKMSCacheToken in the Cache using the given key + */ + public void put(String key, XKMSCacheToken cacheToken) { + cache.put(key, cacheToken); + } + + /** + * Get an XKMSCacheToken from the cache matching the given key. Returns null if there + * is no such XKMSCacheToken in the cache. + */ + public XKMSCacheToken get(String key) { + return cache.get(key); + } + + public synchronized void close() { + if (!cacheManager.isClosed()) { + cacheManager.destroyCache(cacheKey); + cacheManager.close(); + + if (bus != null) { + bus.getExtension(BusLifeCycleManager.class).unregisterLifeCycleListener(this); + } + } + } + + public void initComplete() { + } + + public void preShutdown() { + close(); + } + + public void postShutdown() { + close(); + } +} diff --git a/services/xkms/xkms-client/src/test/java/org/apache/cxf/xkms/cache/XKMSClientCacheTest.java b/services/xkms/xkms-client/src/test/java/org/apache/cxf/xkms/cache/XKMSClientCacheTest.java index e5dcabab6e..b9ea506e4a 100644 --- a/services/xkms/xkms-client/src/test/java/org/apache/cxf/xkms/cache/XKMSClientCacheTest.java +++ b/services/xkms/xkms-client/src/test/java/org/apache/cxf/xkms/cache/XKMSClientCacheTest.java @@ -22,8 +22,14 @@ package org.apache.cxf.xkms.cache; import java.math.BigInteger; import java.security.KeyStore; import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Collection; import org.apache.cxf.common.classloader.ClassLoaderUtils; +import org.apache.cxf.xkms.cache.jcache.JCacheXKMSClientCache; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -33,14 +39,15 @@ import static org.junit.Assert.assertTrue; /** * A test for the XKMSClientCache */ +@RunWith(value = org.junit.runners.Parameterized.class) public class XKMSClientCacheTest { private final XKMSClientCache cache; - private final X509Certificate alice; - private final X509Certificate bob; + private X509Certificate alice; + private X509Certificate bob; - public XKMSClientCacheTest() throws Exception { - cache = new EHCacheXKMSClientCache(); + public XKMSClientCacheTest(XKMSClientCache cache) throws Exception { + this.cache = cache; KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); keystore.load(ClassLoaderUtils.getResourceAsStream("keys/alice.jks", @@ -55,6 +62,11 @@ public class XKMSClientCacheTest { bob = (X509Certificate)keystore.getCertificate("bob"); } + @Parameterized.Parameters(name = "{0}") + public static Collection<XKMSClientCache> cache() throws XKMSClientCacheException { + return Arrays.asList(new EHCacheXKMSClientCache(), new JCacheXKMSClientCache()); + } + @org.junit.Test public void testCache() { assertNotNull(alice); diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookCxfContinuationStore.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookCxfContinuationStore.java index 68d5c5d411..bdc1c8e0e1 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookCxfContinuationStore.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookCxfContinuationStore.java @@ -24,6 +24,7 @@ import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -41,7 +42,6 @@ import org.apache.cxf.jaxrs.ext.MessageContext; import org.apache.cxf.jaxrs.impl.UriInfoImpl; import org.apache.cxf.message.Message; import org.apache.cxf.phase.PhaseInterceptorChain; -import org.ehcache.impl.internal.concurrent.ConcurrentHashMap; @Path("/bookstore") public class BookCxfContinuationStore { 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 360572eaec..fe16ae32d0 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 @@ -20,8 +20,6 @@ package org.apache.cxf.systest.ws.cache; import java.net.URL; -import java.util.Arrays; -import java.util.Collection; import java.util.Random; import javax.xml.namespace.QName; @@ -40,6 +38,8 @@ import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; import org.apache.cxf.ws.security.SecurityConstants; import org.apache.cxf.ws.security.tokenstore.EHCacheTokenStore; import org.apache.cxf.ws.security.tokenstore.TokenStore; +import org.apache.cxf.ws.security.tokenstore.TokenStoreException; +import org.apache.cxf.ws.security.tokenstore.jcache.JCacheTokenStore; import org.example.contract.doubleit.DoubleItPortType; import org.junit.BeforeClass; @@ -59,11 +59,18 @@ public class CachingTest extends AbstractBusClientServerTestBase { private static final String NAMESPACE = "http://www.example.org/contract/DoubleIt"; private static final QName SERVICE_QNAME = new QName(NAMESPACE, "DoubleItService"); + + @FunctionalInterface + private interface TokenStoreCacheFactory { + TokenStore create(String key, Bus b) throws TokenStoreException; + } - final TestParam test; + private final TestParam test; + private final TokenStoreCacheFactory factory; - public CachingTest(TestParam type) { + public CachingTest(TestParam type, TokenStoreCacheFactory factory) { this.test = type; + this.factory = factory; } @BeforeClass @@ -76,12 +83,18 @@ public class CachingTest extends AbstractBusClientServerTestBase { ); } - @Parameters(name = "{0}") - public static Collection<TestParam> data() { - - return Arrays.asList(new TestParam[] {new TestParam(PORT, false), - new TestParam(PORT, true), - }); + @Parameters(name = "{0},{1}") + public static Object[][] data() { + return new Object[][] { + {new TestParam(PORT, false), (TokenStoreCacheFactory) (String key, Bus b) -> + new EHCacheTokenStore(key, b, ClassLoaderUtils.getResource("cxf-ehcache.xml", CachingTest.class))}, + {new TestParam(PORT, false), (TokenStoreCacheFactory) (String key, Bus b) -> + new JCacheTokenStore(key, b, ClassLoaderUtils.getResource("cxf-jcache.xml", CachingTest.class))}, + {new TestParam(PORT, true), (TokenStoreCacheFactory) (String key, Bus b) -> + new EHCacheTokenStore(key, b, ClassLoaderUtils.getResource("cxf-ehcache.xml", CachingTest.class))}, + {new TestParam(PORT, true), (TokenStoreCacheFactory) (String key, Bus b) -> + new JCacheTokenStore(key, b, ClassLoaderUtils.getResource("cxf-jcache.xml", CachingTest.class))} + }; } @org.junit.AfterClass @@ -174,8 +187,7 @@ public class CachingTest extends AbstractBusClientServerTestBase { // Create shared cache String cacheKey = SecurityConstants.TOKEN_STORE_CACHE_INSTANCE + '-' + Math.abs(new Random().nextInt()); - TokenStore tokenStore = new EHCacheTokenStore(cacheKey, bus, - ClassLoaderUtils.getResource("cxf-ehcache.xml", this.getClass())); + TokenStore tokenStore = factory.create(cacheKey, bus); Client client = ClientProxy.getClient(port); client.getEndpoint().getEndpointInfo().setProperty(SecurityConstants.TOKEN_STORE_CACHE_INSTANCE, tokenStore);
