killbill-uncached

Fixes #235 (Completes e75130bba3d1d10c1e60f656056f0cf84d623a6c) Rework

1/7/2015 6:59:21 PM

Changes

Details

diff --git a/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java b/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java
index 071f8ed..6480aea 100644
--- a/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java
@@ -21,6 +21,7 @@ import java.util.List;
 import java.util.Locale;
 
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 
 public interface TenantInternalApi {
 
@@ -28,9 +29,11 @@ public interface TenantInternalApi {
         public void invalidateCache(InternalTenantContext tenantContext);
     }
 
-    public List<String> getTenantCatalogs(InternalTenantContext tenantContext, CacheInvalidationCallback cacheInvalidationCallback);
+    public void initializeCacheInvalidationCallback(final TenantKey key, final CacheInvalidationCallback cacheInvalidationCallback);
 
-    public String getTenantOverdueConfig(InternalTenantContext tenantContext, CacheInvalidationCallback cacheInvalidationCallback);
+    public List<String> getTenantCatalogs(InternalTenantContext tenantContext);
+
+    public String getTenantOverdueConfig(InternalTenantContext tenantContext);
 
     public String getInvoiceTemplate(Locale locale, InternalTenantContext tenantContext);
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java
new file mode 100644
index 0000000..eef2e24
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 Groupon, Inc
+ * Copyright 2015 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.catalog.caching;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CatalogCacheInvalidationCallback implements CacheInvalidationCallback {
+
+    private final Logger logger = LoggerFactory.getLogger(CatalogCacheInvalidationCallback.class);
+
+    private final CatalogCache catalogCache;
+
+    @Inject
+    public CatalogCacheInvalidationCallback(final CatalogCache catalogCache) {
+        this.catalogCache = catalogCache;
+    }
+
+    @Override
+    public void invalidateCache(final InternalTenantContext tenantContext) {
+        logger.info("Invalidate catalog cache for tenant " + tenantContext.getTenantRecordId());
+        catalogCache.clearCatalog(tenantContext);
+    }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
index 256b8e2..acfac0c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
@@ -33,9 +33,13 @@ import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.cache.CacheLoaderArgument;
 import org.killbill.billing.util.cache.TenantCatalogCacheLoader.LoaderCallback;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class EhCacheCatalogCache implements CatalogCache {
 
+    private final Logger logger = LoggerFactory.getLogger(EhCacheCatalogCache.class);
+
     private final CacheController cacheController;
     private final VersionedCatalogLoader loader;
     private final CacheLoaderArgument cacheLoaderArgument;
@@ -89,11 +93,6 @@ public class EhCacheCatalogCache implements CatalogCache {
             public Object loadCatalog(final List<String> catalogXMLs) throws CatalogApiException {
                 return loader.load(catalogXMLs);
             }
-
-            @Override
-            public void invalidateCache(final InternalTenantContext tenantContext) {
-                parentCache.clearCatalog(tenantContext);
-            }
         };
         final Object[] args = new Object[1];
         args[0]= loaderCallback;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
index 0497e9d..053122c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultCatalogService.java
@@ -18,15 +18,22 @@
 
 package org.killbill.billing.catalog;
 
+import javax.inject.Named;
+
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.StaticCatalog;
 import org.killbill.billing.catalog.caching.CatalogCache;
+import org.killbill.billing.catalog.caching.CatalogCacheInvalidationCallback;
+import org.killbill.billing.catalog.glue.CatalogModule;
 import org.killbill.billing.platform.api.KillbillService;
 import org.killbill.billing.platform.api.LifecycleHandlerType;
 import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
+import org.killbill.billing.tenant.api.TenantInternalApi;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.util.config.CatalogConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -41,14 +48,21 @@ public class DefaultCatalogService implements KillbillService, CatalogService {
     private final CatalogConfig config;
     private boolean isInitialized;
 
+    private final TenantInternalApi tenantInternalApi;
+
     private final CatalogCache catalogCache;
+    private final CacheInvalidationCallback cacheInvalidationCallback;
 
     @Inject
     public DefaultCatalogService(final CatalogConfig config,
-                                 final CatalogCache catalogCache) {
+                                 final TenantInternalApi tenantInternalApi,
+                                 final CatalogCache catalogCache,
+                                 @Named(CatalogModule.CATALOG_INVALIDATION_CALLBACK) final CacheInvalidationCallback cacheInvalidationCallback) {
         this.config = config;
-        this.isInitialized = false;
         this.catalogCache = catalogCache;
+        this.cacheInvalidationCallback = cacheInvalidationCallback;
+        this.tenantInternalApi = tenantInternalApi;
+        this.isInitialized = false;
     }
 
     @LifecycleHandlerType(LifecycleLevel.LOAD_CATALOG)
@@ -67,7 +81,12 @@ public class DefaultCatalogService implements KillbillService, CatalogService {
         }
     }
 
-    @Override
+    @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
+    public synchronized void initialize() throws ServiceException {
+        tenantInternalApi.initializeCacheInvalidationCallback(TenantKey.CATALOG, cacheInvalidationCallback);
+    }
+
+        @Override
     public String getName() {
         return CATALOG_SERVICE_NAME;
     }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
index 2198ce6..7118e5f 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
@@ -23,16 +23,22 @@ import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.CatalogUserApi;
 import org.killbill.billing.catalog.api.user.DefaultCatalogUserApi;
 import org.killbill.billing.catalog.caching.CatalogCache;
+import org.killbill.billing.catalog.caching.CatalogCacheInvalidationCallback;
 import org.killbill.billing.catalog.caching.EhCacheCatalogCache;
 import org.killbill.billing.catalog.io.CatalogLoader;
 import org.killbill.billing.catalog.io.VersionedCatalogLoader;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.util.config.CatalogConfig;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.skife.config.ConfigurationObjectFactory;
 
+import com.google.inject.name.Names;
+
 public class CatalogModule extends KillBillModule {
 
+    public static final String CATALOG_INVALIDATION_CALLBACK = "CatalogInvalidationCallback";
+
     public CatalogModule(final KillbillConfigSource configSource) {
         super(configSource);
     }
@@ -51,8 +57,9 @@ public class CatalogModule extends KillBillModule {
         bind(CatalogUserApi.class).to(DefaultCatalogUserApi.class).asEagerSingleton();
     }
 
-    public void installOverdueConfigCache() {
+    public void installCatalogConfigCache() {
         bind(CatalogCache.class).to(EhCacheCatalogCache.class).asEagerSingleton();
+        bind(CacheInvalidationCallback.class).annotatedWith(Names.named(CATALOG_INVALIDATION_CALLBACK)).to(CatalogCacheInvalidationCallback.class).asEagerSingleton();
     }
 
     @Override
@@ -60,6 +67,6 @@ public class CatalogModule extends KillBillModule {
         installConfig();
         installCatalog();
         installCatalogUserApi();
-        installOverdueConfigCache();
+        installCatalogConfigCache();
     }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
index 5e0706e..336f691 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
@@ -57,7 +57,7 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         final InternalTenantContext tenantContext = Mockito.mock(InternalTenantContext.class);
         Mockito.when(tenantContext.getTenantRecordId()).thenReturn(0L);
         catalogCache.loadDefaultCatalog(null);
-        Mockito.when(tenantInternalApi.getTenantCatalogs(tenantContext, cacheInvalidationCallback)).thenReturn(ImmutableList.<String>of());
+        Mockito.when(tenantInternalApi.getTenantCatalogs(tenantContext)).thenReturn(ImmutableList.<String>of());
         catalogCache.getCatalog(internalCallContext);
     }
 
@@ -71,7 +71,7 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         Mockito.when(tenantContext.getTenantRecordId()).thenReturn(0L);
 
         catalogCache.loadDefaultCatalog(Resources.getResource("SpyCarBasic.xml").toExternalForm());
-        Mockito.when(tenantInternalApi.getTenantCatalogs(tenantContext, cacheInvalidationCallback)).thenReturn(ImmutableList.<String>of());
+        Mockito.when(tenantInternalApi.getTenantCatalogs(tenantContext)).thenReturn(ImmutableList.<String>of());
         VersionedCatalog result = catalogCache.getCatalog(internalCallContext);
         Assert.assertNotNull(result);
         final DefaultProduct[] products = result.getProducts(clock.getUTCNow());
@@ -89,7 +89,7 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         final InternalTenantContext tenantContext = Mockito.mock(InternalTenantContext.class);
         Mockito.when(tenantContext.getTenantRecordId()).thenReturn(99L);
 
-        Mockito.when(tenantInternalApi.getTenantCatalogs(Mockito.any(InternalTenantContext.class), Mockito.any(CacheInvalidationCallback.class))).thenReturn(ImmutableList.<String>of());
+        Mockito.when(tenantInternalApi.getTenantCatalogs(Mockito.any(InternalTenantContext.class))).thenReturn(ImmutableList.<String>of());
         VersionedCatalog result = catalogCache.getCatalog(tenantContext);
         Assert.assertNotNull(result);
         final DefaultProduct[] products = result.getProducts(clock.getUTCNow());
@@ -113,13 +113,13 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         final InternalTenantContext tenantContext = Mockito.mock(InternalTenantContext.class);
         Mockito.when(tenantContext.getTenantRecordId()).thenReturn(156L);
 
-        Mockito.when(tenantInternalApi.getTenantCatalogs(Mockito.any(InternalTenantContext.class), Mockito.any(CacheInvalidationCallback.class))).thenReturn(ImmutableList.<String>of(catalogXML));
+        Mockito.when(tenantInternalApi.getTenantCatalogs(Mockito.any(InternalTenantContext.class))).thenReturn(ImmutableList.<String>of(catalogXML));
         VersionedCatalog result = catalogCache.getCatalog(tenantContext);
         Assert.assertNotNull(result);
         final DefaultProduct[] products = result.getProducts(clock.getUTCNow());
         Assert.assertEquals(products.length, 6);
 
-        Mockito.when(tenantInternalApi.getTenantCatalogs(tenantContext, cacheInvalidationCallback)).thenThrow(RuntimeException.class);
+        Mockito.when(tenantInternalApi.getTenantCatalogs(tenantContext)).thenThrow(RuntimeException.class);
 
         VersionedCatalog result2 = catalogCache.getCatalog(tenantContext);
         Assert.assertNotNull(result2);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
index 00d82b5..9b84483 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
@@ -18,6 +18,7 @@ package org.killbill.billing.catalog;
 
 import org.killbill.billing.GuicyKillbillTestSuiteNoDB;
 import org.killbill.billing.catalog.caching.CatalogCache;
+import org.killbill.billing.catalog.caching.CatalogCacheInvalidationCallback;
 import org.killbill.billing.catalog.glue.TestCatalogModuleNoDB;
 import org.killbill.billing.catalog.io.VersionedCatalogLoader;
 import org.killbill.billing.tenant.api.TenantInternalApi;
@@ -42,6 +43,9 @@ public abstract class CatalogTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected CatalogCache catalogCache;
 
+    @Inject
+    protected CatalogCacheInvalidationCallback cacheInvalidationCallback;
+
     @BeforeClass(groups = "fast")
     protected void beforeClass() throws Exception {
         final Injector injector = Guice.createInjector(new TestCatalogModuleNoDB(configSource));
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java
index 59a632c..41b66fa 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockCatalogService.java
@@ -26,7 +26,7 @@ public class MockCatalogService extends DefaultCatalogService {
     private final MockCatalog catalog;
 
     public MockCatalogService(final MockCatalog catalog, final CacheControllerDispatcher cacheControllerDispatcher) {
-        super(null, null);
+        super(null, null, null, null);
         this.catalog = catalog;
     }
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java
index 47e7b7d..08a434b 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogService.java
@@ -36,7 +36,7 @@ public class TestCatalogService extends CatalogTestSuiteNoDB {
                 return "file:src/test/resources/versionedCatalog";
             }
 
-        }, catalogCache);
+        }, tenantInternalApi, catalogCache, cacheInvalidationCallback);
         service.loadCatalog();
         Assert.assertNotNull(service.getFullCatalog(internalCallContext));
         Assert.assertEquals(service.getFullCatalog(internalCallContext).getCatalogName(), "WeaponsHireSmall");
@@ -50,7 +50,7 @@ public class TestCatalogService extends CatalogTestSuiteNoDB {
                 return "file:src/test/resources/WeaponsHire.xml";
             }
 
-        },  catalogCache);
+        },  tenantInternalApi, catalogCache, cacheInvalidationCallback);
         service.loadCatalog();
         Assert.assertNotNull(service.getFullCatalog(internalCallContext));
         Assert.assertEquals(service.getFullCatalog(internalCallContext).getCatalogName(), "Firearms");
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/caching/EhCacheOverdueConfigCache.java b/overdue/src/main/java/org/killbill/billing/overdue/caching/EhCacheOverdueConfigCache.java
index 5c7542d..dc998c9 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/caching/EhCacheOverdueConfigCache.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/caching/EhCacheOverdueConfigCache.java
@@ -27,7 +27,6 @@ import javax.inject.Inject;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.overdue.api.OverdueApiException;
 import org.killbill.billing.overdue.api.OverdueConfig;
 import org.killbill.billing.overdue.config.DefaultOverdueConfig;
@@ -53,7 +52,7 @@ public class EhCacheOverdueConfigCache implements OverdueConfigCache {
     @Inject
     public EhCacheOverdueConfigCache(final CacheControllerDispatcher cacheControllerDispatcher) {
         this.cacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_OVERDUE_CONFIG);
-        this.cacheLoaderArgument = initializeCacheLoaderArgument(this);
+        this.cacheLoaderArgument = initializeCacheLoaderArgument();
     }
 
     @Override
@@ -107,7 +106,7 @@ public class EhCacheOverdueConfigCache implements OverdueConfigCache {
         cacheController.remove(tenantContext);
     }
 
-    private CacheLoaderArgument initializeCacheLoaderArgument(final EhCacheOverdueConfigCache parentCache) {
+    private CacheLoaderArgument initializeCacheLoaderArgument() {
         final LoaderCallback loaderCallback = new LoaderCallback() {
             @Override
             public Object loadCatalog(final String catalogXMLs) throws OverdueApiException {
@@ -121,11 +120,6 @@ public class EhCacheOverdueConfigCache implements OverdueConfigCache {
                     throw new OverdueApiException(ErrorCode.OVERDUE_INVALID_FOR_TENANT, "Problem encountered loading overdue config ", e);
                 }
             }
-
-            @Override
-            public void invalidateCache(final InternalTenantContext tenantContext) {
-                parentCache.clearOverdueConfig(tenantContext);
-            }
         };
         final Object[] args = new Object[1];
         args[0] = loaderCallback;
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java b/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java
new file mode 100644
index 0000000..c16ae9f
--- /dev/null
+++ b/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 Groupon, Inc
+ * Copyright 2015 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.overdue.caching;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+
+public class OverdueCacheInvalidationCallback implements CacheInvalidationCallback {
+
+    private static final Logger logger = LoggerFactory.getLogger(OverdueCacheInvalidationCallback.class);
+
+    private final OverdueConfigCache overdueConfigCache;
+
+    @Inject
+    public OverdueCacheInvalidationCallback(final OverdueConfigCache overdueConfigCache) {
+        this.overdueConfigCache = overdueConfigCache;
+    }
+
+    @Override
+    public void invalidateCache(final InternalTenantContext tenantContext) {
+        logger.info("Invalidate overdue cache for tenant " + tenantContext.getTenantRecordId());
+        overdueConfigCache.clearOverdueConfig(tenantContext);
+    }
+}
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java b/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java
index c79179c..9c613e5 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/glue/DefaultOverdueModule.java
@@ -29,6 +29,7 @@ import org.killbill.billing.overdue.applicator.OverdueEmailGenerator;
 import org.killbill.billing.overdue.applicator.formatters.DefaultOverdueEmailFormatterFactory;
 import org.killbill.billing.overdue.applicator.formatters.OverdueEmailFormatterFactory;
 import org.killbill.billing.overdue.caching.EhCacheOverdueConfigCache;
+import org.killbill.billing.overdue.caching.OverdueCacheInvalidationCallback;
 import org.killbill.billing.overdue.caching.OverdueConfigCache;
 import org.killbill.billing.overdue.listener.OverdueListener;
 import org.killbill.billing.overdue.notification.OverdueAsyncBusNotifier;
@@ -40,6 +41,7 @@ import org.killbill.billing.overdue.notification.OverduePoster;
 import org.killbill.billing.overdue.service.DefaultOverdueService;
 import org.killbill.billing.overdue.wrapper.OverdueWrapperFactory;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.util.glue.KillBillModule;
 import org.skife.config.ConfigurationObjectFactory;
 
@@ -47,6 +49,7 @@ import com.google.inject.name.Names;
 
 public class DefaultOverdueModule extends KillBillModule implements OverdueModule {
 
+    public static final String OVERDUE_INVALIDATION_CALLBACK = "overdueInvalidationCallback";
     public static final String OVERDUE_NOTIFIER_CHECK_NAMED = "overdueNotifierCheck";
     public static final String OVERDUE_NOTIFIER_ASYNC_BUS_NAMED = "overdueNotifierAsyncBus";
 
@@ -98,5 +101,6 @@ public class DefaultOverdueModule extends KillBillModule implements OverdueModul
 
     public void installOverdueConfigCache() {
         bind(OverdueConfigCache.class).to(EhCacheOverdueConfigCache.class).asEagerSingleton();
+        bind(CacheInvalidationCallback.class).annotatedWith(Names.named(OVERDUE_INVALIDATION_CALLBACK)).to(OverdueCacheInvalidationCallback.class).asEagerSingleton();
     }
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java b/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java
index d7f37b7..011eb4f 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/service/DefaultOverdueService.java
@@ -18,9 +18,6 @@
 
 package org.killbill.billing.overdue.service;
 
-import java.net.URI;
-import java.net.URISyntaxException;
-
 import javax.inject.Named;
 
 import org.killbill.billing.callcontext.InternalTenantContext;
@@ -29,18 +26,16 @@ import org.killbill.billing.overdue.OverdueProperties;
 import org.killbill.billing.overdue.OverdueService;
 import org.killbill.billing.overdue.api.OverdueApiException;
 import org.killbill.billing.overdue.api.OverdueConfig;
-import org.killbill.billing.overdue.caching.EhCacheOverdueConfigCache;
 import org.killbill.billing.overdue.caching.OverdueConfigCache;
-import org.killbill.billing.overdue.config.DefaultOverdueConfig;
 import org.killbill.billing.overdue.glue.DefaultOverdueModule;
 import org.killbill.billing.overdue.listener.OverdueListener;
 import org.killbill.billing.overdue.notification.OverdueNotifier;
 import org.killbill.billing.platform.api.LifecycleHandlerType;
 import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
-import org.killbill.billing.util.cache.Cachable.CacheType;
-import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.killbill.billing.tenant.api.TenantInternalApi;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.bus.api.PersistentBus.EventBusException;
-import org.killbill.xmlloader.XMLLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -59,8 +54,9 @@ public class DefaultOverdueService implements OverdueService {
     private final OverdueListener listener;
 
     private final OverdueConfigCache overdueConfigCache;
+    private final CacheInvalidationCallback overdueCacheInvalidationCallback;
 
-    private DefaultOverdueConfig overdueConfig;
+    private final TenantInternalApi tenantInternalApi;
     private boolean isConfigLoaded;
 
     @Inject
@@ -69,7 +65,9 @@ public class DefaultOverdueService implements OverdueService {
                                  @Named(DefaultOverdueModule.OVERDUE_NOTIFIER_ASYNC_BUS_NAMED) final OverdueNotifier asyncNotifier,
                                  final BusService busService,
                                  final OverdueListener listener,
-                                 final OverdueConfigCache overdueConfigCache) {
+                                 final OverdueConfigCache overdueConfigCache,
+                                 @Named(DefaultOverdueModule.OVERDUE_INVALIDATION_CALLBACK) final CacheInvalidationCallback overdueCacheInvalidationCallback,
+                                 final TenantInternalApi tenantInternalApi) {
         this.properties = properties;
         this.checkNotifier = checkNotifier;
         this.asyncNotifier = asyncNotifier;
@@ -77,6 +75,8 @@ public class DefaultOverdueService implements OverdueService {
         this.listener = listener;
         this.isConfigLoaded = false;
         this.overdueConfigCache = overdueConfigCache;
+        this.overdueCacheInvalidationCallback = overdueCacheInvalidationCallback;
+        this.tenantInternalApi = tenantInternalApi;
     }
 
     @Override
@@ -102,6 +102,7 @@ public class DefaultOverdueService implements OverdueService {
         registerForBus();
         checkNotifier.initialize();
         asyncNotifier.initialize();
+        tenantInternalApi.initializeCacheInvalidationCallback(TenantKey.OVERDUE_CONFIG, overdueCacheInvalidationCallback);
     }
 
     private void registerForBus() {
diff --git a/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java b/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java
index 0284e6d..d858aa1 100644
--- a/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java
+++ b/overdue/src/test/java/org/killbill/billing/overdue/glue/TestOverdueModule.java
@@ -27,8 +27,10 @@ import org.killbill.billing.overdue.TestOverdueHelper;
 import org.killbill.billing.overdue.applicator.OverdueBusListenerTester;
 import org.killbill.billing.overdue.caching.EhCacheOverdueConfigCache;
 import org.killbill.billing.overdue.caching.MockOverdueConfigCache;
+import org.killbill.billing.overdue.caching.OverdueCacheInvalidationCallback;
 import org.killbill.billing.overdue.caching.OverdueConfigCache;
 import org.killbill.billing.platform.api.KillbillConfigSource;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.util.email.EmailModule;
 import org.killbill.billing.util.email.templates.TemplateModule;
 import org.killbill.billing.util.glue.AuditModule;
@@ -36,6 +38,8 @@ import org.killbill.billing.util.glue.CacheModule;
 import org.killbill.billing.util.glue.CallContextModule;
 import org.killbill.billing.util.glue.CustomFieldModule;
 
+import com.google.inject.name.Names;
+
 public class TestOverdueModule extends DefaultOverdueModule {
 
     public TestOverdueModule(final KillbillConfigSource configSource) {
@@ -68,6 +72,7 @@ public class TestOverdueModule extends DefaultOverdueModule {
 
     public void installOverdueConfigCache() {
         bind(OverdueConfigCache.class).to(MockOverdueConfigCache.class).asEagerSingleton();
+        bind(CacheInvalidationCallback.class).annotatedWith(Names.named(OVERDUE_INVALIDATION_CALLBACK)).to(OverdueCacheInvalidationCallback.class).asEagerSingleton();
     }
 
 }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java
index 579b153..ee33163 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantInternalApi.java
@@ -24,7 +24,6 @@ import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.tenant.api.TenantCacheInvalidation.CacheInvalidationKey;
 import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.killbill.billing.tenant.dao.TenantDao;
 import org.killbill.billing.tenant.glue.DefaultTenantModule;
@@ -32,14 +31,14 @@ import org.killbill.billing.util.LocaleUtils;
 
 /**
  * This is the private API which is used to extract per tenant objects (catalog, overdue, invoice templates, ..)
- * <p>
+ * <p/>
  * Some of these per tenant objects are cached at a higher level in their respective modules (catalog, overdue) to
  * avoid reconstructing the object state from the xml definition each time. As a result, the module also registers
  * a callback which is used for the cache invalidation when the state changes and the operation occurred on a remote node.
  * For those objects, the private api is called from the module.
- * <p>
+ * <p/>
  * Some others (invoice templates,...) are not cached (yet) and so the logic is simpler.
- * <p>
+ * <p/>
  * The api can only be used to retrieve objects where no caching is required.
  */
 public class DefaultTenantInternalApi implements TenantInternalApi {
@@ -47,7 +46,6 @@ public class DefaultTenantInternalApi implements TenantInternalApi {
     private final TenantDao tenantDao;
     private final TenantCacheInvalidation tenantCacheInvalidation;
 
-
     @Inject
     public DefaultTenantInternalApi(@Named(DefaultTenantModule.NO_CACHING_TENANT) final TenantDao tenantDao,
                                     final TenantCacheInvalidation tenantCacheInvalidation) {
@@ -56,14 +54,17 @@ public class DefaultTenantInternalApi implements TenantInternalApi {
     }
 
     @Override
-    public List<String> getTenantCatalogs(final InternalTenantContext tenantContext, final CacheInvalidationCallback cacheInvalidationCallback) {
-        tenantCacheInvalidation.registerCallback(new CacheInvalidationKey(tenantContext.getTenantRecordId(), TenantKey.CATALOG), cacheInvalidationCallback);
+    public void initializeCacheInvalidationCallback(final TenantKey key, final CacheInvalidationCallback cacheInvalidationCallback) {
+        tenantCacheInvalidation.registerCallback(key, cacheInvalidationCallback);
+    }
+
+    @Override
+    public List<String> getTenantCatalogs(final InternalTenantContext tenantContext) {
         return tenantDao.getTenantValueForKey(TenantKey.CATALOG.toString(), tenantContext);
     }
 
     @Override
-    public String getTenantOverdueConfig(final InternalTenantContext tenantContext, final CacheInvalidationCallback cacheInvalidationCallback) {
-        tenantCacheInvalidation.registerCallback(new CacheInvalidationKey(tenantContext.getTenantRecordId(), TenantKey.OVERDUE_CONFIG), cacheInvalidationCallback);
+    public String getTenantOverdueConfig(final InternalTenantContext tenantContext) {
         final List<String> values = tenantDao.getTenantValueForKey(TenantKey.OVERDUE_CONFIG.toString(), tenantContext);
         return getUniqueValue(values, "overdue config", tenantContext);
     }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
index ec4ef07..d686fa8 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
@@ -50,9 +50,10 @@ import org.slf4j.LoggerFactory;
 public class TenantCacheInvalidation {
 
     private final static int TERMINATION_TIMEOUT_SEC = 5;
+
     private static final Logger logger = LoggerFactory.getLogger(TenantCacheInvalidation.class);
 
-    private final Map<CacheInvalidationKey, CacheInvalidationCallback> cache;
+    private final Map<TenantKey, CacheInvalidationCallback> cache;
     private final TenantBroadcastDao broadcastDao;
     private final ScheduledExecutorService tenantExecutor;
     private final TenantConfig tenantConfig;
@@ -63,7 +64,7 @@ public class TenantCacheInvalidation {
     public TenantCacheInvalidation(@Named(DefaultTenantModule.NO_CACHING_TENANT) final TenantBroadcastDao broadcastDao,
                                    @Named(DefaultTenantModule.TENANT_EXECUTOR_NAMED) final ScheduledExecutorService tenantExecutor,
                                    final TenantConfig tenantConfig) {
-        this.cache = new HashMap<CacheInvalidationKey, CacheInvalidationCallback>();
+        this.cache = new HashMap<TenantKey, CacheInvalidationCallback>();
         this.broadcastDao = broadcastDao;
         this.tenantExecutor = tenantExecutor;
         this.tenantConfig = tenantConfig;
@@ -106,13 +107,13 @@ public class TenantCacheInvalidation {
         }
     }
 
-    public void registerCallback(final CacheInvalidationKey key, final CacheInvalidationCallback value) {
+    public void registerCallback(final TenantKey key, final CacheInvalidationCallback value) {
         if (!cache.containsKey(key)) {
             cache.put(key, value);
         }
     }
 
-    public CacheInvalidationCallback getCacheInvalidation(final CacheInvalidationKey key) {
+    public CacheInvalidationCallback getCacheInvalidation(final TenantKey key) {
         return cache.get(key);
     }
 
@@ -150,59 +151,15 @@ public class TenantCacheInvalidation {
                     return;
                 }
 
-                final CacheInvalidationKey key = new CacheInvalidationKey(cur.getTenantRecordId(), TenantKey.valueOf(cur.getType()));
-                final CacheInvalidationCallback callback = parent.getCacheInvalidation(key);
+                final CacheInvalidationCallback callback = parent.getCacheInvalidation(TenantKey.valueOf(cur.getType()));
                 if (callback != null) {
                     final InternalTenantContext tenantContext = new InternalTenantContext(cur.getTenantRecordId(), null);
                     callback.invalidateCache(tenantContext);
+                } else {
+                    logger.warn("Failed to find CacheInvalidationCallback for " + cur.getType());
                 }
                 parent.setLatestRecordIdProcessed(cur.getRecordId());
             }
         }
     }
-
-    public static final class CacheInvalidationKey {
-
-        private final Long tenantRecordId;
-        private final TenantKey type;
-
-        public CacheInvalidationKey(final Long tenantRecordId, final TenantKey type) {
-            this.tenantRecordId = tenantRecordId;
-            this.type = type;
-        }
-
-        public Long getTenantRecordId() {
-            return tenantRecordId;
-        }
-
-        public TenantKey getType() {
-            return type;
-        }
-
-        @Override
-        public boolean equals(final Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (!(o instanceof CacheInvalidationKey)) {
-                return false;
-            }
-
-            final CacheInvalidationKey that = (CacheInvalidationKey) o;
-            if (tenantRecordId != null ? !tenantRecordId.equals(that.tenantRecordId) : that.tenantRecordId != null) {
-                return false;
-            }
-            if (type != that.type) {
-                return false;
-            }
-            return true;
-        }
-
-        @Override
-        public int hashCode() {
-            int result = tenantRecordId != null ? tenantRecordId.hashCode() : 0;
-            result = 31 * result + (type != null ? type.hashCode() : 0);
-            return result;
-        }
-    }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java
index cd82a27..8630e4d 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java
@@ -27,10 +27,14 @@ import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.tenant.api.TenantInternalApi;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @Singleton
 public class TenantCatalogCacheLoader extends BaseCacheLoader {
 
+    private final Logger logger = LoggerFactory.getLogger(TenantCatalogCacheLoader.class);
+
     private final TenantInternalApi tenantApi;
 
     @Inject
@@ -63,11 +67,12 @@ public class TenantCatalogCacheLoader extends BaseCacheLoader {
         }
 
         final LoaderCallback callback = (LoaderCallback) cacheLoaderArgument.getArgs()[0];
-        final List<String> catalogXMLs = tenantApi.getTenantCatalogs(internalTenantContext, (CacheInvalidationCallback) callback);
+        final List<String> catalogXMLs = tenantApi.getTenantCatalogs(internalTenantContext);
         if (catalogXMLs.isEmpty()) {
             return null;
         }
         try {
+            logger.info("Loading catalog cache for tenant " + internalTenantContext.getTenantRecordId());
             return callback.loadCatalog(catalogXMLs);
         } catch (CatalogApiException e) {
             throw new IllegalStateException(String.format("Failed to de-serialize catalog for tenant %s : %s",
@@ -75,7 +80,7 @@ public class TenantCatalogCacheLoader extends BaseCacheLoader {
         }
     }
 
-    public interface LoaderCallback extends CacheInvalidationCallback {
+    public interface LoaderCallback {
         public Object loadCatalog(final List<String> catalogXMLs) throws CatalogApiException;
     }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/cache/TenantOverdueConfigCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/TenantOverdueConfigCacheLoader.java
index edcf7a4..7b53bb8 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/TenantOverdueConfigCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/TenantOverdueConfigCacheLoader.java
@@ -25,10 +25,14 @@ import org.killbill.billing.overdue.api.OverdueApiException;
 import org.killbill.billing.tenant.api.TenantInternalApi;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
 import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @Singleton
 public class TenantOverdueConfigCacheLoader extends BaseCacheLoader {
 
+    private static final Logger logger = LoggerFactory.getLogger(TenantOverdueConfigCacheLoader.class);
+
     private final TenantInternalApi tenantApi;
 
     @Inject
@@ -61,11 +65,13 @@ public class TenantOverdueConfigCacheLoader extends BaseCacheLoader {
         }
 
         final LoaderCallback callback = (LoaderCallback) cacheLoaderArgument.getArgs()[0];
-        final String overdueXML = tenantApi.getTenantOverdueConfig(internalTenantContext, (CacheInvalidationCallback) callback);
+        final String overdueXML = tenantApi.getTenantOverdueConfig(internalTenantContext);
         if (overdueXML == null) {
             return null;
         }
         try {
+            logger.info("Loading overdue cache for tenant " + internalTenantContext.getTenantRecordId());
+
             return callback.loadCatalog(overdueXML);
         } catch (OverdueApiException e) {
             throw new IllegalStateException(String.format("Failed to de-serialize overdue config for tenant %s : %s",
@@ -73,7 +79,7 @@ public class TenantOverdueConfigCacheLoader extends BaseCacheLoader {
         }
     }
 
-    public interface LoaderCallback extends CacheInvalidationCallback {
+    public interface LoaderCallback {
         public Object loadCatalog(final String overdueXML) throws OverdueApiException;
     }
 }
diff --git a/util/src/main/java/org/killbill/billing/util/config/TenantConfig.java b/util/src/main/java/org/killbill/billing/util/config/TenantConfig.java
index 48374c0..5eec621 100644
--- a/util/src/main/java/org/killbill/billing/util/config/TenantConfig.java
+++ b/util/src/main/java/org/killbill/billing/util/config/TenantConfig.java
@@ -26,7 +26,7 @@ public interface TenantConfig extends KillbillConfig {
 
     @Config("org.killbill.tenant.broadcast.rate")
     @Default("5s")
-    @Description("Rate at which janitor tasks are scheduled")
+    @Description("Rate at which tenant broadcast task is scheduled")
     public TimeSpan getTenantBroadcastServiceRunningRate();
 
 }