killbill-memoizeit

Details

diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
index ba71e6f..8bb42d5 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -28,15 +28,12 @@ import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import org.skife.jdbi.v2.IDBI;
-
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.ProductCategory;
-import org.killbill.clock.Clock;
 import org.killbill.billing.entitlement.AccountEventsStreams;
 import org.killbill.billing.entitlement.EventsStream;
 import org.killbill.billing.entitlement.api.BlockingState;
@@ -51,10 +48,13 @@ import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseTransition;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.clock.Clock;
+import org.skife.jdbi.v2.IDBI;
 
 import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
@@ -172,13 +172,7 @@ public class EventsStreamBuilder {
         for (final UUID bundleId : subscriptions.keySet()) {
             final SubscriptionBaseBundle bundle = bundlesPerId.get(bundleId);
             final List<SubscriptionBase> allSubscriptionsForBundle = subscriptions.get(bundleId);
-            final SubscriptionBase baseSubscription = Iterables.<SubscriptionBase>tryFind(allSubscriptionsForBundle,
-                                                                                          new Predicate<SubscriptionBase>() {
-                                                                                              @Override
-                                                                                              public boolean apply(final SubscriptionBase input) {
-                                                                                                  return ProductCategory.BASE.equals(input.getLastActiveProduct().getCategory());
-                                                                                              }
-                                                                                          }).orNull();
+            final SubscriptionBase baseSubscription = findBaseSubscription(allSubscriptionsForBundle);
             final List<BlockingState> bundleBlockingStates = Objects.firstNonNull(blockingStatesPerBundle.get(bundleId), ImmutableList.<BlockingState>of());
 
             if (entitlementsPerBundle.get(bundleId) == null) {
@@ -230,13 +224,7 @@ public class EventsStreamBuilder {
             subscription = subscriptionInternalApi.getSubscriptionFromId(entitlementId, internalTenantContext);
             bundle = subscriptionInternalApi.getBundleFromId(subscription.getBundleId(), internalTenantContext);
             allSubscriptionsForBundle = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), null, internalTenantContext);
-            baseSubscription = Iterables.<SubscriptionBase>tryFind(allSubscriptionsForBundle,
-                                                                   new Predicate<SubscriptionBase>() {
-                                                                       @Override
-                                                                       public boolean apply(final SubscriptionBase input) {
-                                                                           return ProductCategory.BASE.equals(input.getLastActiveProduct().getCategory());
-                                                                       }
-                                                                   }).orNull(); // null for standalone subscriptions
+            baseSubscription = findBaseSubscription(allSubscriptionsForBundle);
         } catch (SubscriptionBaseApiException e) {
             throw new EntitlementApiException(e);
         }
@@ -341,4 +329,18 @@ public class EventsStreamBuilder {
                                        internalTenantContext,
                                        clock.getUTCNow());
     }
+
+    private SubscriptionBase findBaseSubscription(final Iterable<SubscriptionBase> subscriptions) {
+        return Iterables.<SubscriptionBase>tryFind(subscriptions,
+                                                   new Predicate<SubscriptionBase>() {
+                                                       @Override
+                                                       public boolean apply(final SubscriptionBase input) {
+                                                           final List<SubscriptionBaseTransition> allTransitions = input.getAllTransitions();
+                                                           return !allTransitions.isEmpty() &&
+                                                                  allTransitions.get(0).getNextPlan() != null &&
+                                                                  allTransitions.get(0).getNextPlan().getProduct() != null &&
+                                                                  ProductCategory.BASE.equals(allTransitions.get(0).getNextPlan().getProduct().getCategory());
+                                                       }
+                                                   }).orNull(); // null for standalone subscriptions
+    }
 }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
index 5036c03..24e5eb3 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/SubscriptionJson.java
@@ -309,11 +309,12 @@ public class SubscriptionJson extends JsonBase {
     public SubscriptionJson(final Subscription subscription, @Nullable final AccountAuditLogs accountAuditLogs) {
         super(toAuditLogJson(accountAuditLogs == null ? null : accountAuditLogs.getAuditLogsForSubscription(subscription.getId())));
         this.startDate = subscription.getEffectiveStartDate();
-        this.productName = subscription.getLastActiveProduct().getName();
-        this.productCategory = subscription.getLastActiveProductCategory().name();
-        this.billingPeriod = subscription.getLastActivePlan().getRecurringBillingPeriod().toString();
-        this.phaseType = subscription.getLastActivePhase().getPhaseType().toString();
-        this.priceList = subscription.getLastActivePriceList().getName();
+        // last* fields can be null if the subscription starts in the future
+        this.productName = subscription.getLastActiveProduct() == null ? null : subscription.getLastActiveProduct().getName();
+        this.productCategory = subscription.getLastActiveProductCategory() == null ? null : subscription.getLastActiveProductCategory().name();
+        this.billingPeriod = subscription.getLastActivePlan() == null ? null : subscription.getLastActivePlan().getRecurringBillingPeriod().toString();
+        this.phaseType = subscription.getLastActivePhase() == null ? null : subscription.getLastActivePhase().getPhaseType().toString();
+        this.priceList = subscription.getLastActivePriceList() == null ? null : subscription.getLastActivePriceList().getName();
         this.state = subscription.getState().name();
         this.sourceType = subscription.getSourceType().name();
         this.cancelledDate = subscription.getEffectiveEndDate();
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
index d59430a..99bc820 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/applicator/OverdueStateApplicator.java
@@ -320,6 +320,7 @@ public class OverdueStateApplicator {
                                                                                                               new Predicate<Entitlement>() {
                                                                                                                   @Override
                                                                                                                   public boolean apply(final Entitlement entitlement) {
+                                                                                                                      // Note: this would miss add-ons created in the future. We should expose a new API to do something similar to EventsStreamBuilder#findBaseSubscription
                                                                                                                       return !ProductCategory.ADD_ON.equals(entitlement.getLastActiveProductCategory());
                                                                                                                   }
                                                                                                               });
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
index c0844da..9688a17 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBase.java
@@ -267,7 +267,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
             return data.getPreviousPlan().getProduct();
         } else {
             final Plan currentPlan = getCurrentPlan();
-            // currentPlan can be null when playing with the clock
+            // currentPlan can be null when playing with the clock (subscription created in the future)
             return currentPlan == null ? null : currentPlan.getProduct();
         }
     }
@@ -289,7 +289,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
             return data.getPreviousPlan().getProduct().getCategory();
         } else {
             final Plan currentPlan = getCurrentPlan();
-            // currentPlan can be null when playing with the clock
+            // currentPlan can be null when playing with the clock (subscription created in the future)
             return currentPlan == null ? null : currentPlan.getProduct().getCategory();
         }
     }
@@ -321,7 +321,7 @@ public class DefaultSubscriptionBase extends EntityBase implements SubscriptionB
             return data.getPreviousPlan().getRecurringBillingPeriod();
         } else {
             final Plan currentPlan = getCurrentPlan();
-            // currentPlan can be null when playing with the clock
+            // currentPlan can be null when playing with the clock (subscription created in the future)
             return currentPlan.getRecurringBillingPeriod();
         }
     }