killbill-uncached

Details

diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 3e1dd16..1eb38a9 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -16,15 +16,12 @@
       <option name="REPORT_VARIABLES" value="true" />
       <option name="REPORT_PARAMETERS" value="true" />
     </inspection_tool>
-    <inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
-      <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
-      <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
-    </inspection_tool>
     <inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true">
       <option name="m_requireAnnotationsFirst" value="true" />
     </inspection_tool>
     <inspection_tool class="RedundantTypeArguments" enabled="false" level="WARNING" enabled_by_default="false" />
     <inspection_tool class="SizeReplaceableByIsEmpty" enabled="true" level="WARNING" enabled_by_default="true" />
+    <inspection_tool class="StaticPseudoFunctionalStyleMethod" enabled="false" level="WARNING" enabled_by_default="false" />
     <inspection_tool class="TryWithIdenticalCatches" enabled="false" level="WARNING" enabled_by_default="false" />
     <inspection_tool class="TypeMayBeWeakened" enabled="true" level="WARNING" enabled_by_default="true">
       <option name="useRighthandTypeAsWeakestTypeInAssignments" value="true" />
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/DefaultCatalogCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/DefaultCatalogCache.java
index 02d5d9d..067bd95 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/DefaultCatalogCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/DefaultCatalogCache.java
@@ -18,6 +18,7 @@
 package org.killbill.billing.catalog.caching;
 
 import java.util.List;
+import java.util.Set;
 
 import javax.inject.Inject;
 
@@ -147,7 +148,8 @@ public class DefaultCatalogCache implements CatalogCache {
 
     private DefaultVersionedCatalog getCatalogFromPlugins(final InternalTenantContext internalTenantContext) throws CatalogApiException {
         final TenantContext tenantContext = internalCallContextFactory.createTenantContext(internalTenantContext);
-        for (final String service : pluginRegistry.getAllServices()) {
+        final Set<String> allServices = pluginRegistry.getAllServices();
+        for (final String service : allServices) {
             final CatalogPluginApi plugin = pluginRegistry.getServiceForName(service);
 
             //
@@ -158,9 +160,9 @@ public class DefaultCatalogCache implements CatalogCache {
             // (e.g deleted Plans...), then multiple versions must be returned.
             //
             final DateTime latestCatalogUpdatedDate = plugin.getLatestCatalogVersion(ImmutableList.<PluginProperty>of(), tenantContext);
-            // A null latestCatalogUpdatedDate by passing caching, by fetching full catalog from plugin below (compatibility mode with 0.18.x or non optimized plugin api mode)
-            //
-            if (latestCatalogUpdatedDate != null) {
+            // A null latestCatalogUpdatedDate bypasses caching, by fetching full catalog from plugin below (compatibility mode with 0.18.x or non optimized plugin api mode)
+            final boolean cacheable = latestCatalogUpdatedDate != null;
+            if (cacheable) {
                 final DefaultVersionedCatalog tenantCatalog = cacheController.get(internalTenantContext.getTenantRecordId(), cacheLoaderArgument);
                 if (tenantCatalog != null) {
                     initializeCatalog(tenantCatalog);
@@ -174,10 +176,19 @@ public class DefaultCatalogCache implements CatalogCache {
             final VersionedPluginCatalog pluginCatalog = plugin.getVersionedPluginCatalog(ImmutableList.<PluginProperty>of(), tenantContext);
             // First plugin that gets something (for that tenant) returns it
             if (pluginCatalog != null) {
-                logger.info("Returning catalog from plugin {} on tenant {} ", service, internalTenantContext.getTenantRecordId());
+                // The log entry is only interesting if there are multiple plugins
+                if (allServices.size() > 1) {
+                    logger.info("Returning catalog from plugin {} on tenant {} ", service, internalTenantContext.getTenantRecordId());
+                }
+
                 final DefaultVersionedCatalog resolvedPluginCatalog = versionedCatalogMapper.toVersionedCatalog(pluginCatalog, internalTenantContext);
+
+                // Always clear the cache for safety
                 cacheController.remove(internalTenantContext.getTenantRecordId());
-                cacheController.putIfAbsent(internalTenantContext.getTenantRecordId(), resolvedPluginCatalog);
+                if (cacheable) {
+                    cacheController.putIfAbsent(internalTenantContext.getTenantRecordId(), resolvedPluginCatalog);
+                }
+
                 return resolvedPluginCatalog;
             }
         }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUnit.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUnit.java
index 6bd7330..f2f7d67 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUnit.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUnit.java
@@ -106,12 +106,15 @@ public class DefaultUnit extends ValidatingConfig<StandaloneCatalog> implements 
     @Override
     public void writeExternal(final ObjectOutput out) throws IOException {
         out.writeUTF(name);
-        out.writeUTF(prettyName);
+        out.writeBoolean(prettyName != null);
+        if (prettyName != null) {
+            out.writeUTF(prettyName);
+        }
     }
 
     @Override
     public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
         this.name = in.readUTF();
-        this.prettyName = in.readUTF();
+        this.prettyName = in.readBoolean() ? in.readUTF() : null;
     }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/RuntimeExceptionMapper.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/RuntimeExceptionMapper.java
index 5117e1f..f8cbbaa 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/RuntimeExceptionMapper.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/mappers/RuntimeExceptionMapper.java
@@ -1,7 +1,9 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
+ * Copyright 2014-2018 Groupon, Inc
+ * Copyright 2014-2018 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:
  *
@@ -42,9 +44,7 @@ public class RuntimeExceptionMapper extends ExceptionMapperBase implements Excep
     @Override
     public Response toResponse(final RuntimeException exception) {
         if (exception instanceof NullPointerException) {
-            // Assume bad payload
-            exception.printStackTrace();
-            log.warn("Exception : " + exception.getMessage());
+            log.warn("Unexpected NullPointerException", exception);
             return buildBadRequestResponse(exception, uriInfo);
         } else if (exception instanceof WebApplicationException) {
             // e.g. com.sun.jersey.api.NotFoundException
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 ce32de0..a99d0be 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
@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Preconditions;
 
-// Build the abstraction layer between EhCache and Kill Bill
+// Build the abstraction layer between JCache and Kill Bill
 public class CacheControllerDispatcherProvider implements Provider<CacheControllerDispatcher> {
 
     private static final Logger logger = LoggerFactory.getLogger(CacheControllerDispatcherProvider.class);
@@ -56,13 +56,13 @@ public class CacheControllerDispatcherProvider implements Provider<CacheControll
 
             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());
+                logger.warn("Cache for cacheName='{}' not configured", cacheLoader.getCacheType().getCacheName());
                 continue;
             }
             Preconditions.checkState(!cache.isClosed(), "Cache '%s' should not be closed", cacheType.getCacheName());
 
-            final CacheController<Object, Object> ehCacheBasedCacheController = new KillBillCacheController<Object, Object>(cache, cacheLoader);
-            cacheControllers.put(cacheType, ehCacheBasedCacheController);
+            final CacheController<Object, Object> killBillCacheController = new KillBillCacheController<Object, Object>(cache, cacheLoader);
+            cacheControllers.put(cacheType, killBillCacheController);
         }
 
         return new CacheControllerDispatcher(cacheControllers);