killbill-memoizeit

Details

diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
index 5f70d68..6628404 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BillCycleDayCalculator.java
@@ -19,6 +19,7 @@
 package org.killbill.billing.junction.plumbing.billing;
 
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
@@ -60,7 +61,11 @@ public class BillCycleDayCalculator {
         this.subscriptionApi = subscriptionApi;
     }
 
-    protected int calculateBcd(final ImmutableAccountData account, final int accountBillCycleDayLocal, final UUID bundleId, final SubscriptionBase subscription, final EffectiveSubscriptionInternalEvent transition, final InternalCallContext context)
+    private interface BCDAlignmentComputation {
+        public Integer compute() throws AccountApiException, SubscriptionBaseApiException, CatalogApiException;
+    }
+
+    protected int calculateBcd(final ImmutableAccountData account, final int accountBillCycleDayLocal, final UUID bundleId, final SubscriptionBase subscription, final EffectiveSubscriptionInternalEvent transition, final Map<UUID, Integer> bcdCache, final InternalCallContext context)
             throws CatalogApiException, AccountApiException, SubscriptionBaseApiException {
 
         final Catalog catalog = catalogService.getFullCatalog(context);
@@ -91,28 +96,49 @@ public class BillCycleDayCalculator {
                                        phase.getPhaseType()),
                 transition.getRequestedTransitionTime());
 
-        return calculateBcdForAlignment(account, accountBillCycleDayLocal, subscription, alignment, bundleId, catalog, plan, context);
+        return calculateBcdForAlignment(account, accountBillCycleDayLocal, subscription, alignment, bundleId, catalog, plan, bcdCache, context);
     }
 
     @VisibleForTesting
     int calculateBcdForAlignment(final ImmutableAccountData account, final int accountBillCycleDayLocal, final SubscriptionBase subscription, final BillingAlignment alignment, final UUID bundleId,
-                                 final Catalog catalog, final Plan plan, final InternalCallContext context) throws AccountApiException, SubscriptionBaseApiException, CatalogApiException {
-        int result = 0;
+                                 final Catalog catalog, final Plan plan, final Map<UUID, Integer> bcdCache, final InternalCallContext context) throws AccountApiException, SubscriptionBaseApiException, CatalogApiException {
+        Integer result = 0;
+        final BCDAlignmentComputation callback;
         switch (alignment) {
             case ACCOUNT:
-                result = accountBillCycleDayLocal != 0 ? accountBillCycleDayLocal : calculateBcdFromSubscription(subscription, plan, account, catalog, context);
+                 callback = new BCDAlignmentComputation() {
+                     @Override
+                     public Integer compute() throws AccountApiException, SubscriptionBaseApiException, CatalogApiException {
+                         return accountBillCycleDayLocal != 0 ? accountBillCycleDayLocal : calculateBcdFromSubscription(subscription, plan, account, catalog, context);
+                     }
+                 };
+                result = computeOrRetrieveBCDForAlignment(account.getId(), bcdCache, callback);
                 break;
+
             case BUNDLE:
-                final SubscriptionBase baseSub = subscriptionApi.getBaseSubscription(bundleId, context);
-                Plan basePlan = baseSub.getCurrentPlan();
-                if (basePlan == null) {
-                    // The BP has been cancelled
-                    basePlan = baseSub.getLastActivePlan();
-                }
-                result = calculateBcdFromSubscription(baseSub, basePlan, account, catalog, context);
+                callback = new BCDAlignmentComputation() {
+                    @Override
+                    public Integer compute() throws AccountApiException, SubscriptionBaseApiException, CatalogApiException {
+                        final SubscriptionBase baseSub = subscriptionApi.getBaseSubscription(bundleId, context);
+                        Plan basePlan = baseSub.getCurrentPlan();
+                        if (basePlan == null) {
+                            // The BP has been cancelled
+                            basePlan = baseSub.getLastActivePlan();
+                        }
+                        return calculateBcdFromSubscription(baseSub, basePlan, account, catalog, context);
+                    }
+                };
+                result = computeOrRetrieveBCDForAlignment(bundleId, bcdCache, callback);
                 break;
+
             case SUBSCRIPTION:
-                result = calculateBcdFromSubscription(subscription, plan, account, catalog, context);
+                callback = new BCDAlignmentComputation() {
+                    @Override
+                    public Integer compute() throws AccountApiException, SubscriptionBaseApiException, CatalogApiException {
+                        return calculateBcdFromSubscription(subscription, plan, account, catalog, context);
+                    }
+                };
+                result = computeOrRetrieveBCDForAlignment(subscription.getId(), bcdCache, callback);
                 break;
         }
 
@@ -123,6 +149,16 @@ public class BillCycleDayCalculator {
         return result;
     }
 
+
+    int computeOrRetrieveBCDForAlignment(final UUID objectId, final Map<UUID, Integer> bcdCache, final BCDAlignmentComputation callback) throws AccountApiException, CatalogApiException, SubscriptionBaseApiException {
+        Integer result = bcdCache.get(objectId);
+        if (result == null) {
+            result = callback.compute();
+            bcdCache.put(objectId, result);
+        }
+        return result;
+    }
+
     @VisibleForTesting
     int calculateBcdFromSubscription(final SubscriptionBase subscription, final Plan plan, final ImmutableAccountData account, final Catalog catalog, final InternalCallContext context)
             throws AccountApiException, CatalogApiException {
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index ff1e3f4..c67962c 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -18,8 +18,10 @@
 
 package org.killbill.billing.junction.plumbing.billing;
 
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.SortedSet;
 import java.util.UUID;
@@ -170,6 +172,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
         // If dryRun is specified, we don't want to to update the account BCD value, so we initialize the flag updatedAccountBCD to true
         boolean updatedAccountBCD = dryRunMode;
 
+        final Map<UUID, Integer> bcdCache =new HashMap<UUID, Integer>();
 
         int currentAccountBCD = accountApi.getBCD(account.getId(), context);
         for (final SubscriptionBase subscription : subscriptions) {
@@ -191,7 +194,7 @@ public class DefaultInternalBillingApi implements BillingInternalApi {
             }
 
             for (final EffectiveSubscriptionInternalEvent transition : billingTransitions) {
-                final int bcdLocal = bcdCalculator.calculateBcd(account, currentAccountBCD, bundleId, subscription, transition, context);
+                final int bcdLocal = bcdCalculator.calculateBcd(account, currentAccountBCD, bundleId, subscription, transition, bcdCache, context);
 
                 if (currentAccountBCD == 0 && !updatedAccountBCD) {
                     accountApi.updateBCD(account.getExternalKey(), bcdLocal, context);
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java
index adef503..aa0d2e8 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBillCycleDayCalculator.java
@@ -16,6 +16,7 @@
 
 package org.killbill.billing.junction.plumbing.billing;
 
+import java.util.HashMap;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
@@ -63,7 +64,7 @@ public class TestBillCycleDayCalculator extends JunctionTestSuiteNoDB {
         final ImmutableAccountData account = Mockito.mock(ImmutableAccountData.class);
         Mockito.when(account.getTimeZone()).thenReturn(accountTimeZone);
         final Integer billCycleDayLocal = billCycleDayCalculator.calculateBcdForAlignment(account, 0, subscription, BillingAlignment.BUNDLE, bundle.getId(),
-                                                                                          catalog, null, internalCallContext);
+                                                                                          catalog, null, new HashMap<UUID, Integer>(), internalCallContext);
 
         Assert.assertEquals(billCycleDayLocal, (Integer) expectedBCDUTC);
     }