killbill-memoizeit
Changes
profiles/killbill/pom.xml 8(+4 -4)
profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillJdbcTenantRealmProvider.java 12(+3 -9)
profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java 36(+5 -31)
util/pom.xml 20(+12 -8)
util/src/main/resources/ehcache.xml 243(+24 -219)
Details
profiles/killbill/pom.xml 8(+4 -4)
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index 1e98233..deb1bf0 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -172,10 +172,6 @@
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
- <artifactId>shiro-ehcache</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.shiro</groupId>
<artifactId>shiro-guice</artifactId>
</dependency>
<dependency>
@@ -228,6 +224,10 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.ehcache.integrations.shiro</groupId>
+ <artifactId>shiro-ehcache3</artifactId>
+ </dependency>
+ <dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<scope>runtime</scope>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillJdbcTenantRealmProvider.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillJdbcTenantRealmProvider.java
index d53b571..5774818 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillJdbcTenantRealmProvider.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillbillJdbcTenantRealmProvider.java
@@ -1,6 +1,6 @@
/*
- * Copyright 2014-2015 Groupon, Inc
- * Copyright 2014-2015 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project 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
@@ -23,7 +23,6 @@ import javax.sql.DataSource;
import org.apache.shiro.cache.CacheManager;
import org.killbill.billing.server.security.KillbillJdbcTenantRealm;
import org.killbill.billing.util.config.definition.SecurityConfig;
-import org.killbill.billing.util.glue.ShiroEhCacheInstrumentor;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -32,14 +31,12 @@ public class KillbillJdbcTenantRealmProvider implements Provider<KillbillJdbcTen
private final SecurityConfig securityConfig;
private final CacheManager cacheManager;
- private final ShiroEhCacheInstrumentor ehCacheInstrumentor;
private final DataSource dataSource;
@Inject
- public KillbillJdbcTenantRealmProvider(final SecurityConfig securityConfig, final CacheManager cacheManager, final ShiroEhCacheInstrumentor ehCacheInstrumentor, @Named(KillbillPlatformModule.SHIRO_DATA_SOURCE_ID_NAMED) final DataSource dataSource) {
+ public KillbillJdbcTenantRealmProvider(final SecurityConfig securityConfig, final CacheManager cacheManager, @Named(KillbillPlatformModule.SHIRO_DATA_SOURCE_ID_NAMED) final DataSource dataSource) {
this.securityConfig = securityConfig;
this.cacheManager = cacheManager;
- this.ehCacheInstrumentor = ehCacheInstrumentor;
this.dataSource = dataSource;
}
@@ -52,9 +49,6 @@ public class KillbillJdbcTenantRealmProvider implements Provider<KillbillJdbcTen
// automatically configured with the EhCache manager (see EhCacheManagerProvider)
killbillJdbcTenantRealm.setCacheManager(cacheManager);
- // Instrument the cache
- ehCacheInstrumentor.instrument(killbillJdbcTenantRealm);
-
return killbillJdbcTenantRealm;
}
}
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java
index a623ff3..4d8f5c1 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/modules/KillBillShiroWebModule.java
@@ -1,7 +1,7 @@
/*
* Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
* The Billing Project 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
@@ -27,9 +27,7 @@ import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.authc.pam.ModularRealmAuthenticatorWith540;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.guice.web.ShiroWebModuleWith435;
-import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
-import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.WebSecurityManager;
@@ -39,20 +37,17 @@ import org.killbill.billing.jaxrs.resources.JaxrsResource;
import org.killbill.billing.server.security.FirstSuccessfulStrategyWith540;
import org.killbill.billing.server.security.KillbillJdbcTenantRealm;
import org.killbill.billing.util.config.definition.RbacConfig;
-import org.killbill.billing.util.glue.EhCacheManagerProvider;
+import org.killbill.billing.util.glue.EhcacheShiroManagerProvider;
import org.killbill.billing.util.glue.IniRealmProvider;
import org.killbill.billing.util.glue.JDBCSessionDaoProvider;
import org.killbill.billing.util.glue.KillBillShiroModule;
-import org.killbill.billing.util.glue.ShiroEhCacheInstrumentor;
import org.killbill.billing.util.security.shiro.dao.JDBCSessionDao;
import org.killbill.billing.util.security.shiro.realm.KillBillJdbcRealm;
import org.killbill.billing.util.security.shiro.realm.KillBillJndiLdapRealm;
import org.skife.config.ConfigSource;
import org.skife.config.ConfigurationObjectFactory;
-import com.google.inject.Inject;
import com.google.inject.Key;
-import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.AnnotatedBindingBuilder;
import com.google.inject.matcher.AbstractMatcher;
@@ -73,16 +68,9 @@ public class KillBillShiroWebModule extends ShiroWebModuleWith435 {
}
@Override
- public void configure() {
- super.configure();
-
- bind(ShiroEhCacheInstrumentor.class).asEagerSingleton();
- }
-
- @Override
protected void configureShiroWeb() {
// Magic provider to configure the cache manager
- bind(CacheManager.class).toProvider(EhCacheManagerProvider.class).asEagerSingleton();
+ bind(CacheManager.class).toProvider(EhcacheShiroManagerProvider.class).asEagerSingleton();
configureShiroForRBAC();
@@ -106,7 +94,7 @@ public class KillBillShiroWebModule extends ShiroWebModuleWith435 {
return Matchers.subclassesOf(WebSecurityManager.class).matches(o.getRawType());
}
},
- new DefaultWebSecurityManagerTypeListener(getProvider(ShiroEhCacheInstrumentor.class)));
+ new DefaultWebSecurityManagerTypeListener());
if (KillBillShiroModule.isRBACEnabled()) {
addFilterChain(JaxrsResource.PREFIX + "/**", Key.get(CorsBasicHttpAuthenticationFilter.class));
@@ -143,30 +131,16 @@ public class KillBillShiroWebModule extends ShiroWebModuleWith435 {
private static final class DefaultWebSecurityManagerTypeListener implements TypeListener {
- private final Provider<ShiroEhCacheInstrumentor> instrumentorProvider;
-
- @Inject
- public DefaultWebSecurityManagerTypeListener(final Provider<ShiroEhCacheInstrumentor> instrumentorProvider) {
- this.instrumentorProvider = instrumentorProvider;
- }
-
@Override
public <I> void hear(final TypeLiteral<I> typeLiteral, final TypeEncounter<I> typeEncounter) {
typeEncounter.register(new InjectionListener<I>() {
@Override
public void afterInjection(final Object o) {
- final ShiroEhCacheInstrumentor ehCacheInstrumentor = instrumentorProvider.get();
- ehCacheInstrumentor.instrument(CachingSessionDAO.ACTIVE_SESSION_CACHE_NAME);
-
final DefaultWebSecurityManager webSecurityManager = (DefaultWebSecurityManager) o;
if (webSecurityManager.getAuthenticator() instanceof ModularRealmAuthenticator) {
final ModularRealmAuthenticator authenticator = (ModularRealmAuthenticator) webSecurityManager.getAuthenticator();
authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategyWith540());
webSecurityManager.setAuthenticator(new ModularRealmAuthenticatorWith540(authenticator));
-
- for (final Realm realm : webSecurityManager.getRealms()) {
- ehCacheInstrumentor.instrument(realm);
- }
}
}
});
util/pom.xml 20(+12 -8)
diff --git a/util/pom.xml b/util/pom.xml
index 7743895..1a4974b 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -104,7 +104,11 @@
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
- <artifactId>metrics-ehcache</artifactId>
+ <artifactId>metrics-jcache</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.cache</groupId>
+ <artifactId>cache-api</artifactId>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
@@ -126,10 +130,6 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>net.sf.ehcache</groupId>
- <artifactId>ehcache</artifactId>
- </dependency>
- <dependency>
<groupId>org.antlr</groupId>
<artifactId>stringtemplate</artifactId>
</dependency>
@@ -143,11 +143,15 @@
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
- <artifactId>shiro-ehcache</artifactId>
+ <artifactId>shiro-guice</artifactId>
</dependency>
<dependency>
- <groupId>org.apache.shiro</groupId>
- <artifactId>shiro-guice</artifactId>
+ <groupId>org.ehcache</groupId>
+ <artifactId>ehcache</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ehcache.integrations.shiro</groupId>
+ <artifactId>shiro-ehcache3</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
diff --git a/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java b/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java
index 302176c..7d9ce76 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/CacheControllerDispatcherProvider.java
@@ -22,6 +22,8 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
+import javax.cache.Cache;
+import javax.cache.CacheManager;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -29,9 +31,6 @@ import org.killbill.billing.util.cache.Cachable.CacheType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import net.sf.ehcache.CacheManager;
-import net.sf.ehcache.Ehcache;
-
// Build the abstraction layer between EhCache and Kill Bill
public class CacheControllerDispatcherProvider implements Provider<CacheControllerDispatcher> {
@@ -53,7 +52,7 @@ public class CacheControllerDispatcherProvider implements Provider<CacheControll
for (final BaseCacheLoader cacheLoader : cacheLoaders) {
final CacheType cacheType = cacheLoader.getCacheType();
- final Ehcache cache = cacheManager.getEhcache(cacheType.getCacheName());
+ final Cache cache = cacheManager.getCache(cacheType.getCacheName(), cacheType.getKeyType(), cacheType.getValueType());
if (cache == null) {
logger.warn("Cache for cacheName='{}' not configured - check your ehcache.xml", cacheLoader.getCacheType().getCacheName());
continue;
diff --git a/util/src/main/java/org/killbill/billing/util/cache/EhCacheBasedCacheController.java b/util/src/main/java/org/killbill/billing/util/cache/EhCacheBasedCacheController.java
index 6372e3b..92e8e42 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/EhCacheBasedCacheController.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/EhCacheBasedCacheController.java
@@ -18,76 +18,73 @@
package org.killbill.billing.util.cache;
-import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+
+import javax.cache.Cache;
+import javax.cache.Cache.Entry;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
-import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.Element;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
public class EhCacheBasedCacheController<K, V> implements CacheController<K, V> {
private static final Logger logger = LoggerFactory.getLogger(EhCacheBasedCacheController.class);
- private final Ehcache cache;
+ private final Cache<K, V> cache;
private final BaseCacheLoader<K, V> baseCacheLoader;
- public EhCacheBasedCacheController(final Ehcache cache, final BaseCacheLoader<K, V> baseCacheLoader) {
+ public EhCacheBasedCacheController(final Cache<K, V> cache, final BaseCacheLoader<K, V> baseCacheLoader) {
this.cache = cache;
this.baseCacheLoader = baseCacheLoader;
}
@Override
public List<K> getKeys() {
- return cache.getKeys();
+ final Iterable<K> kIterable = Iterables.<Entry<K, V>, K>transform(cache,
+ new Function<Entry<K, V>, K>() {
+ @Override
+ public K apply(final Entry<K, V> input) {
+ return input.getKey();
+ }
+ });
+ return ImmutableList.<K>copyOf(kIterable);
}
@Override
public boolean isKeyInCache(final K key) {
- return cache.isKeyInCache(key);
+ return cache.containsKey(key);
}
@Override
public V get(final K key, final CacheLoaderArgument cacheLoaderArgument) {
- checkKey(key);
-
final V value;
if (!isKeyInCache(key)) {
value = computeAndCacheValue(key, cacheLoaderArgument);
} else {
- final Element element = cache.get(key);
- if (element == null) {
- value = null;
- } else if (element.isExpired()) {
- value = computeAndCacheValue(key, cacheLoaderArgument);
- } else {
- value = (V) element.getObjectValue();
- }
+ value = cache.get(key);
}
if (value == null || value.equals(BaseCacheLoader.EMPTY_VALUE_PLACEHOLDER)) {
return null;
} else {
- checkValue(value);
return value;
}
}
@Override
public void putIfAbsent(final K key, final V value) {
- checkKey(key);
- checkValue(value);
- cache.putIfAbsent(new Element(key, value));
+ cache.putIfAbsent(key, value);
}
@Override
public boolean remove(final K key) {
- checkKey(key);
if (isKeyInCache(key)) {
cache.remove(key);
return true;
@@ -98,7 +95,7 @@ public class EhCacheBasedCacheController<K, V> implements CacheController<K, V>
@Override
public void remove(final Function<K, Boolean> keyMatcher) {
- final Collection<K> toRemove = new HashSet<K>();
+ final Set<K> toRemove = new HashSet<K>();
for (final Object key : getKeys()) {
if (keyMatcher.apply((K) key) == Boolean.TRUE) {
toRemove.add((K) key);
@@ -109,12 +106,12 @@ public class EhCacheBasedCacheController<K, V> implements CacheController<K, V>
@Override
public void removeAll() {
- cache.removeAll();
+ cache.clear();
}
@Override
public int size() {
- return cache.getSize();
+ return Iterables.<Cache.Entry<K, V>>size(cache);
}
@Override
@@ -123,8 +120,6 @@ public class EhCacheBasedCacheController<K, V> implements CacheController<K, V>
}
private V computeAndCacheValue(final K key, final CacheLoaderArgument cacheLoaderArgument) {
- checkKey(key);
-
final V value;
try {
value = baseCacheLoader.compute(key, cacheLoaderArgument);
@@ -137,29 +132,9 @@ public class EhCacheBasedCacheController<K, V> implements CacheController<K, V>
return null;
}
- checkValue(value);
-
// Race condition, we may compute it for nothing
- cache.putIfAbsent(new Element(key, value));
+ cache.putIfAbsent(key, value);
return value;
}
-
- private void checkKey(final K keyObject) {
- if (keyObject == null) {
- throw new NullPointerException();
- }
- if (!getCacheType().getKeyType().isAssignableFrom(keyObject.getClass())) {
- throw new ClassCastException("Invalid key type, expected : " + getCacheType().getKeyType().getName() + " but was : " + keyObject.getClass().getName());
- }
- }
-
- private void checkValue(final V valueObject) {
- if (valueObject == null) {
- throw new NullPointerException();
- }
- if (!getCacheType().getValueType().isAssignableFrom(valueObject.getClass())) {
- throw new ClassCastException("Invalid value type, expected : " + getCacheType().getValueType().getName() + " but was : " + valueObject.getClass().getName());
- }
- }
}
diff --git a/util/src/main/java/org/killbill/billing/util/glue/CacheModule.java b/util/src/main/java/org/killbill/billing/util/glue/CacheModule.java
index 4ea728c..d4ddf25 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/CacheModule.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/CacheModule.java
@@ -18,6 +18,8 @@
package org.killbill.billing.util.glue;
+import javax.cache.CacheManager;
+
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.util.cache.AccountBCDCacheLoader;
import org.killbill.billing.util.cache.AccountRecordIdCacheLoader;
@@ -26,7 +28,6 @@ import org.killbill.billing.util.cache.AuditLogViaHistoryCacheLoader;
import org.killbill.billing.util.cache.BaseCacheLoader;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.cache.CacheControllerDispatcherProvider;
-import org.killbill.billing.util.cache.EhCacheCacheManagerProvider;
import org.killbill.billing.util.cache.ImmutableAccountCacheLoader;
import org.killbill.billing.util.cache.ObjectIdCacheLoader;
import org.killbill.billing.util.cache.OverriddenPlanCacheLoader;
@@ -42,7 +43,6 @@ import org.killbill.billing.util.config.definition.EhCacheConfig;
import org.skife.config.ConfigurationObjectFactory;
import com.google.inject.multibindings.Multibinder;
-import net.sf.ehcache.CacheManager;
public class CacheModule extends KillBillModule {
@@ -56,7 +56,7 @@ public class CacheModule extends KillBillModule {
bind(EhCacheConfig.class).toInstance(config);
// EhCache specifics
- bind(CacheManager.class).toProvider(EhCacheCacheManagerProvider.class).asEagerSingleton();
+ bind(CacheManager.class).toProvider(Eh107CacheManagerProvider.class).asEagerSingleton();
// Kill Bill generic cache dispatcher
bind(CacheControllerDispatcher.class).toProvider(CacheControllerDispatcherProvider.class).asEagerSingleton();
diff --git a/util/src/main/java/org/killbill/billing/util/glue/CacheProviderBase.java b/util/src/main/java/org/killbill/billing/util/glue/CacheProviderBase.java
new file mode 100644
index 0000000..24f2dbf
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/glue/CacheProviderBase.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
+ *
+ * The Billing Project 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.killbill.billing.util.glue;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import javax.cache.Cache;
+import javax.cache.CacheManager;
+import javax.cache.configuration.MutableConfiguration;
+
+import org.killbill.billing.util.config.definition.EhCacheConfig;
+import org.killbill.xmlloader.UriAccessor;
+
+import com.codahale.metrics.Metric;
+import com.codahale.metrics.MetricFilter;
+import com.codahale.metrics.MetricRegistry;
+import com.codahale.metrics.jcache.JCacheGaugeSet;
+
+abstract class CacheProviderBase {
+
+ private static final String PROP_METRIC_REG_JCACHE_STATISTICS = "jcache.statistics";
+
+ private final MetricRegistry metricRegistry;
+
+ final URL xmlConfigurationURL;
+
+ CacheProviderBase(final MetricRegistry metricRegistry, final EhCacheConfig cacheConfig) {
+ this.metricRegistry = metricRegistry;
+
+ try {
+ xmlConfigurationURL = UriAccessor.toURL(cacheConfig.getCacheConfigLocation());
+ } catch (final IOException e) {
+ throw new RuntimeException(e);
+ } catch (final URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ <K, V> Cache<K, V> createCache(final CacheManager cacheManager, final String cacheName, final Class<K> keyType, final Class<V> valueType) {
+ // Make sure we start from a clean state - this is mainly useful for tests
+ cacheManager.destroyCache(cacheName);
+
+ // All other configuration options come from the ehcache.xml
+ final MutableConfiguration<K, V> configuration = new MutableConfiguration<K, V>().setTypes(keyType, valueType)
+ .setStoreByValue(false); // Store by reference to avoid copying large objects (e.g. catalog)
+ final Cache<K, V> cache = cacheManager.createCache(cacheName, configuration);
+
+ // Re-create the metrics to support dynamically created caches (e.g. for Shiro)
+ metricRegistry.removeMatching(new MetricFilter() {
+ @Override
+ public boolean matches(final String name, final Metric metric) {
+ return name != null && name.startsWith(PROP_METRIC_REG_JCACHE_STATISTICS);
+ }
+ });
+ metricRegistry.register(PROP_METRIC_REG_JCACHE_STATISTICS, new JCacheGaugeSet());
+
+ return cache;
+ }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/glue/EhcacheShiroManagerProvider.java b/util/src/main/java/org/killbill/billing/util/glue/EhcacheShiroManagerProvider.java
new file mode 100644
index 0000000..1af8a98
--- /dev/null
+++ b/util/src/main/java/org/killbill/billing/util/glue/EhcacheShiroManagerProvider.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
+ *
+ * The Billing Project 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.killbill.billing.util.glue;
+
+import java.lang.reflect.Field;
+
+import javax.cache.CacheManager;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheException;
+import org.apache.shiro.mgt.DefaultSecurityManager;
+import org.apache.shiro.mgt.SecurityManager;
+import org.ehcache.integrations.shiro.EhcacheShiro;
+import org.ehcache.integrations.shiro.EhcacheShiroManager;
+import org.killbill.billing.util.config.definition.EhCacheConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.codahale.metrics.MetricRegistry;
+
+public class EhcacheShiroManagerProvider extends CacheProviderBase implements Provider<EhcacheShiroManager> {
+
+ private final SecurityManager securityManager;
+ private final CacheManager eh107CacheManager;
+ private final org.ehcache.CacheManager ehcacheCacheManager;
+
+ @Inject
+ public EhcacheShiroManagerProvider(final SecurityManager securityManager,
+ final CacheManager eh107CacheManager,
+ final MetricRegistry metricRegistry,
+ final EhCacheConfig cacheConfig) {
+ super(metricRegistry, cacheConfig);
+ this.securityManager = securityManager;
+ this.eh107CacheManager = eh107CacheManager;
+ this.ehcacheCacheManager = getEhcacheManager();
+ }
+
+ @Override
+ public EhcacheShiroManager get() {
+ final EhcacheShiroManager shiroEhCacheManager = new EhcacheShiroManagerWrapper(this);
+ // Same EhCache manager instance as the rest of the system
+ shiroEhCacheManager.setCacheManager(ehcacheCacheManager);
+
+ if (securityManager instanceof DefaultSecurityManager) {
+ // For RBAC only (see also KillbillJdbcTenantRealmProvider)
+ ((DefaultSecurityManager) securityManager).setCacheManager(shiroEhCacheManager);
+ }
+
+ return shiroEhCacheManager;
+ }
+
+ // Shiro isn't JCache compatible
+ private org.ehcache.CacheManager getEhcacheManager() {
+ try {
+ final Field f = eh107CacheManager.getClass().getDeclaredField("ehCacheManager");
+ f.setAccessible(true);
+
+ return (org.ehcache.CacheManager) f.get(eh107CacheManager);
+ } catch (final IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (final NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // Custom createCache implementation going through JCache layer to enable stats, etc.
+ private final class EhcacheShiroManagerWrapper extends EhcacheShiroManager {
+
+ private final Logger log = LoggerFactory.getLogger(EhcacheShiroManagerWrapper.class);
+
+ private final EhcacheShiroManagerProvider ehcacheShiroManagerProvider;
+
+ EhcacheShiroManagerWrapper(final EhcacheShiroManagerProvider ehcacheShiroManagerProvider) {
+ this.ehcacheShiroManagerProvider = ehcacheShiroManagerProvider;
+ }
+
+ public <K, V> Cache<K, V> getCache(final String name) throws CacheException {
+ log.trace("Acquiring EhcacheShiro instance named [{}]", name);
+
+ org.ehcache.Cache<Object, Object> cache = getCacheManager().getCache(name, Object.class, Object.class);
+
+ if (cache == null) {
+ log.info("Cache with name {} does not yet exist. Creating now.", name);
+ ehcacheShiroManagerProvider.createCache(eh107CacheManager, name, Object.class, Object.class);
+ cache = getCacheManager().getCache(name, Object.class, Object.class);
+ log.info("Added EhcacheShiro named [{}]", name);
+ } else {
+ log.info("Using existing EhcacheShiro named [{}]", name);
+ }
+
+ return new EhcacheShiro<K, V>(cache);
+ }
+ }
+}
diff --git a/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java b/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java
index 4e48913..bd9ceb2 100644
--- a/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java
+++ b/util/src/main/java/org/killbill/billing/util/glue/KillBillShiroModule.java
@@ -86,7 +86,7 @@ public class KillBillShiroModule extends ShiroModule {
super.bindSecurityManager(bind);
// Magic provider to configure the cache manager
- bind(CacheManager.class).toProvider(EhCacheManagerProvider.class).asEagerSingleton();
+ bind(CacheManager.class).toProvider(EhcacheShiroManagerProvider.class).asEagerSingleton();
}
@Override
util/src/main/resources/ehcache.xml 243(+24 -219)
diff --git a/util/src/main/resources/ehcache.xml b/util/src/main/resources/ehcache.xml
index 61d098d..faf95e5 100644
--- a/util/src/main/resources/ehcache.xml
+++ b/util/src/main/resources/ehcache.xml
@@ -2,8 +2,8 @@
<!--
~ Copyright 2010-2014 Ning, Inc.
- ~ Copyright 2014-2016 Groupon, Inc
- ~ Copyright 2014-2016 The Billing Project, LLC
+ ~ Copyright 2014-2017 Groupon, Inc
+ ~ Copyright 2014-2017 The Billing Project, LLC
~
~ The Billing Project 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
@@ -18,221 +18,26 @@
~ under the License.
-->
-<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:noNamespaceSchemaLocation="ehcache.xsd">
-
- <defaultCache
- maxElementsInMemory="100000"
- maxElementsOnDisk="0"
- eternal="true"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- />
-
- <cache name="record-id"
- maxElementsInMemory="100000"
- maxElementsOnDisk="0"
- eternal="true"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="tenant-record-id"
- maxElementsInMemory="100000"
- maxElementsOnDisk="0"
- eternal="true"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="account-record-id"
- maxElementsInMemory="100000"
- maxElementsOnDisk="0"
- eternal="true"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="object-id"
- maxElementsInMemory="100000"
- maxElementsOnDisk="0"
- eternal="true"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
-
- <cache name="audit-log"
- maxElementsInMemory="500000"
- maxElementsOnDisk="0"
- timeToIdleSeconds="600"
- timeToLiveSeconds="600"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="audit-log-via-history"
- maxElementsInMemory="500000"
- maxElementsOnDisk="0"
- timeToIdleSeconds="600"
- timeToLiveSeconds="600"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="tenant-catalog"
- maxElementsInMemory="1000"
- maxElementsOnDisk="0"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="tenant-overdue-config"
- maxElementsInMemory="1000"
- maxElementsOnDisk="0"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true">
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="tenant-config"
- maxElementsInMemory="1000"
- maxElementsOnDisk="0"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true">
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="tenant-kv"
- maxElementsInMemory="1000"
- maxElementsOnDisk="0"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="overridden-plan"
- maxElementsInMemory="1000"
- maxElementsOnDisk="0"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="account-immutable"
- maxElementsInMemory="1000"
- maxElementsOnDisk="0"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="account-bcd"
- maxElementsInMemory="1000"
- maxElementsOnDisk="0"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
-
- <cache name="tenant"
- maxElementsInMemory="100"
- maxElementsOnDisk="0"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
- <cache name="tenant-payment-state-machine-config"
- maxElementsInMemory="100"
- maxElementsOnDisk="0"
- overflowToDisk="false"
- diskPersistent="false"
- memoryStoreEvictionPolicy="LFU"
- statistics="true"
- >
- <cacheEventListenerFactory
- class="org.killbill.billing.util.cache.ExpirationListenerFactory"
- properties=""/>
- </cache>
-
-</ehcache>
+<ehcache:config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
+ xmlns:ehcache='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.0.xsd
+ http://www.ehcache.org/v3/jsr107
+ http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
+ <ehcache:service>
+ <jsr107:defaults default-template="defaultCacheConfiguration" enable-management="true" enable-statistics="true"/>
+ </ehcache:service>
+
+ <ehcache:cache-template name="defaultCacheConfiguration">
+ <ehcache:expiry>
+ <ehcache:none/>
+ </ehcache:expiry>
+
+ <ehcache:resources>
+ <!-- The maximal number of entries to be held in the Cache, prior to eviction starting -->
+ <ehcache:heap unit="entries">100000</ehcache:heap>
+ </ehcache:resources>
+ </ehcache:cache-template>
+</ehcache:config>
diff --git a/util/src/test/java/org/killbill/billing/util/security/TestPermissionAnnotationMethodInterceptor.java b/util/src/test/java/org/killbill/billing/util/security/TestPermissionAnnotationMethodInterceptor.java
index 566518d..c4a8c52 100644
--- a/util/src/test/java/org/killbill/billing/util/security/TestPermissionAnnotationMethodInterceptor.java
+++ b/util/src/test/java/org/killbill/billing/util/security/TestPermissionAnnotationMethodInterceptor.java
@@ -1,7 +1,9 @@
/*
* Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2017 Groupon, Inc
+ * Copyright 2014-2017 The Billing Project, LLC
*
- * Ning licenses this file to you under the Apache License, version 2.0
+ * The Billing Project 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:
*
@@ -20,6 +22,13 @@ import javax.inject.Singleton;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthenticatedException;
+import org.killbill.billing.security.Permission;
+import org.killbill.billing.security.RequiresPermissions;
+import org.killbill.billing.tenant.api.TenantInternalApi;
+import org.killbill.billing.util.UtilTestSuiteNoDB;
+import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.glue.CacheModule;
+import org.killbill.billing.util.glue.KillBillShiroAopModule;
import org.killbill.billing.util.glue.TestSecurityModuleNoDB;
import org.killbill.billing.util.glue.TestUtilModuleNoDB.ShiroModuleNoDB;
import org.mockito.Mockito;
@@ -27,18 +36,10 @@ import org.skife.jdbi.v2.IDBI;
import org.testng.Assert;
import org.testng.annotations.Test;
-import org.killbill.billing.security.Permission;
-import org.killbill.billing.security.RequiresPermissions;
-import org.killbill.billing.util.UtilTestSuiteNoDB;
-import org.killbill.billing.util.glue.KillBillShiroAopModule;
-import org.killbill.billing.util.glue.KillBillShiroModule;
-import org.killbill.billing.util.glue.SecurityModule;
-
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Stage;
-import net.sf.ehcache.CacheManager;
public class TestPermissionAnnotationMethodInterceptor extends UtilTestSuiteNoDB {
@@ -73,16 +74,18 @@ public class TestPermissionAnnotationMethodInterceptor extends UtilTestSuiteNoDB
// Now, verify the interception works
configureShiro();
- // Shutdown the cache manager to avoid duplicate exceptions
- CacheManager.getInstance().shutdown();
+
final Injector injector = Guice.createInjector(Stage.PRODUCTION,
new ShiroModuleNoDB(configSource),
new KillBillShiroAopModule(),
new TestSecurityModuleNoDB(configSource),
+ new CacheModule(configSource),
new AbstractModule() {
@Override
protected void configure() {
bind(IDBI.class).toInstance(Mockito.mock(IDBI.class));
+ bind(TenantInternalApi.class).toInstance(Mockito.mock(TenantInternalApi.class));
+ bind(NonEntityDao.class).toInstance(Mockito.mock(NonEntityDao.class));
}
});
final AopTester aopedTester = injector.getInstance(AopTester.class);
@@ -101,17 +104,19 @@ public class TestPermissionAnnotationMethodInterceptor extends UtilTestSuiteNoDB
// Now, verify the interception works
configureShiro();
- // Shutdown the cache manager to avoid duplicate exceptions
- CacheManager.getInstance().shutdown();
+
final Injector injector = Guice.createInjector(Stage.PRODUCTION,
new ShiroModuleNoDB(configSource),
new KillBillShiroAopModule(),
new TestSecurityModuleNoDB(configSource),
+ new CacheModule(configSource),
new AbstractModule() {
@Override
public void configure() {
bind(IDBI.class).toInstance(Mockito.mock(IDBI.class));
bind(IAopTester.class).to(AopTesterImpl.class).asEagerSingleton();
+ bind(TenantInternalApi.class).toInstance(Mockito.mock(TenantInternalApi.class));
+ bind(NonEntityDao.class).toInstance(Mockito.mock(NonEntityDao.class));
}
});
final IAopTester aopedTester = injector.getInstance(IAopTester.class);