killbill-aplcache

entitlement: Add support for older versions of data where ENT_STATE_START

2/25/2016 4:00:41 PM

Details

diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
index 37db289..429eafe 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/BlockingStateOrdering.java
@@ -24,11 +24,14 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import org.joda.time.LocalDate;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Plan;
@@ -40,8 +43,13 @@ import org.killbill.billing.entitlement.block.BlockingChecker.BlockingAggregator
 import org.killbill.billing.entitlement.block.DefaultBlockingChecker.DefaultBlockingAggregator;
 import org.killbill.billing.junction.DefaultBlockingState;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
 
 // Given an event stream (across one or multiple entitlements), insert the blocking events at the right place
 public class BlockingStateOrdering extends EntitlementOrderingBase {
@@ -52,9 +60,12 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
 
     public static void insertSorted(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
         INSTANCE.computeEvents(entitlements, internalTenantContext, inputAndOutputResult);
+
     }
 
     private void computeEvents(final Iterable<Entitlement> entitlements, final InternalTenantContext internalTenantContext, final LinkedList<SubscriptionEvent> inputAndOutputResult) {
+
+
         final Collection<UUID> allEntitlementUUIDs = new HashSet<UUID>();
         final Collection<BlockingState> blockingStates = new LinkedList<BlockingState>();
         for (final Entitlement entitlement : entitlements) {
@@ -63,16 +74,19 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
             blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getBlockingStates());
         }
 
+        final SupportForOlderVersionThan_0_17_X backwardCompatibleContext = new SupportForOlderVersionThan_0_17_X(inputAndOutputResult, blockingStates);
+
         // Trust the incoming ordering here: blocking states were sorted using ProxyBlockingStateDao#sortedCopy
         for (final BlockingState currentBlockingState : blockingStates) {
             final List<SubscriptionEvent> outputNewEvents = new ArrayList<SubscriptionEvent>();
-            final int index = insertFromBlockingEvent(allEntitlementUUIDs, currentBlockingState, inputAndOutputResult, internalTenantContext, outputNewEvents);
+            final int index = insertFromBlockingEvent(allEntitlementUUIDs, currentBlockingState, inputAndOutputResult, backwardCompatibleContext, internalTenantContext, outputNewEvents);
             insertAfterIndex(inputAndOutputResult, outputNewEvents, index);
         }
+        backwardCompatibleContext.addMissing_START_ENTITLEMENT(inputAndOutputResult, internalTenantContext);
     }
 
     // Returns the index and the newEvents generated from the incoming blocking state event. Those new events will all be created for the same effectiveDate and should be ordered.
-    private int insertFromBlockingEvent(final Collection<UUID> allEntitlementUUIDs, final BlockingState currentBlockingState, final List<SubscriptionEvent> inputExistingEvents, final InternalTenantContext internalTenantContext, final Collection<SubscriptionEvent> outputNewEvents) {
+    private int insertFromBlockingEvent(final Collection<UUID> allEntitlementUUIDs, final BlockingState currentBlockingState, final List<SubscriptionEvent> inputExistingEvents, final SupportForOlderVersionThan_0_17_X backwardCompatibleContext, final InternalTenantContext internalTenantContext, final Collection<SubscriptionEvent> outputNewEvents) {
         // Keep the current state per entitlement
         final Map<UUID, TargetState> targetStates = new HashMap<UUID, TargetState>();
         for (final UUID cur : allEntitlementUUIDs) {
@@ -104,6 +118,10 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
                     curTargetState.setEntitlementStopped();
                     break;
                 case START_BILLING:
+                    // For older subscriptions we miss the START_ENTITLEMENT (the START_BILLING marks both start of billing and entitlement)
+                    if (backwardCompatibleContext.isOlderEntitlement(cur.getEntitlementId())) {
+                        curTargetState.setEntitlementStarted();
+                    }
                     curTargetState.setBillingStarted();
                     break;
                 case PAUSE_BILLING:
@@ -343,7 +361,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
                                                                               bs.getStateName(),
                                                                               bs.getService(),
                                                                               bs.isBlockChange(),
-                                                                              (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
+                                                                              (bs.isBlockEntitlement() && isEntitlementStarted &&  !isEntitlementStopped),
                                                                               (bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
                                                                               bs.getEffectiveDate());
 
@@ -375,7 +393,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
             }
             final BlockingAggregator stateAfter = getState();
 
-            final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
+            final boolean shouldResumeEntitlement = isEntitlementStarted &&  !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
             if (shouldResumeEntitlement) {
                 result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
             }
@@ -384,7 +402,7 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
                 result.add(SubscriptionEventType.RESUME_BILLING);
             }
 
-            final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
+            final boolean shouldBlockEntitlement = isEntitlementStarted &&  !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
             if (shouldBlockEntitlement) {
                 result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
             }
@@ -407,4 +425,85 @@ public class BlockingStateOrdering extends EntitlementOrderingBase {
             return aggrBefore;
         }
     }
+
+    //
+    // The logic to add the missing START_ENTITLEMENT for older subscriptions is contained in this class. When we want/need to drop backward compatibility we can
+    // simply drop this class and where it is called.
+    //
+    private static class SupportForOlderVersionThan_0_17_X {
+
+        private final Set<UUID> olderEntitlementSet;
+
+        public SupportForOlderVersionThan_0_17_X(final List<SubscriptionEvent> initialEntitlementEvents, final Collection<BlockingState> blockingStates) {
+            this.olderEntitlementSet = computeOlderEntitlementSet(initialEntitlementEvents, blockingStates);
+        }
+
+        public boolean isOlderEntitlement(final UUID entitlementId) {
+            return olderEntitlementSet.contains(entitlementId);
+        }
+
+        public void addMissing_START_ENTITLEMENT(final LinkedList<SubscriptionEvent> inputAndOutputResult, final InternalTenantContext internalTenantContext) {
+
+            // Insert missing START_ENTITLEMENT right before START_BILLING (same event as START_BILLING but with different type=START_ENTITLEMENT to be compatible with old code)
+            final ListIterator<SubscriptionEvent> it = inputAndOutputResult.listIterator();
+            while (it.hasNext()) {
+                final SubscriptionEvent cur = it.next();
+                if (cur.getSubscriptionEventType() == SubscriptionEventType.START_BILLING && olderEntitlementSet.contains(cur.getEntitlementId())) {
+                    final SubscriptionEvent newEntitlementStartEvent = new DefaultSubscriptionEvent(cur.getId(),
+                                                                                                    cur.getEntitlementId(),
+                                                                                                    internalTenantContext.toUTCDateTime(cur.getEffectiveDate()),
+                                                                                                    SubscriptionEventType.START_ENTITLEMENT,
+                                                                                                    false,
+                                                                                                    false,
+                                                                                                    DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                                                    SubscriptionEventType.START_ENTITLEMENT.toString(),
+                                                                                                    cur.getPrevProduct(),
+                                                                                                    cur.getPrevPlan(),
+                                                                                                    cur.getPrevPhase(),
+                                                                                                    cur.getPrevPriceList(),
+                                                                                                    cur.getPrevBillingPeriod(),
+                                                                                                    cur.getNextProduct(),
+                                                                                                    cur.getNextPlan(),
+                                                                                                    cur.getNextPhase(),
+                                                                                                    cur.getNextPriceList(),
+                                                                                                    cur.getNextBillingPeriod(),
+                                                                                                    internalTenantContext.toUTCDateTime(cur.getEffectiveDate()),
+                                                                                                    internalTenantContext);
+                    it.previous();
+                    it.add(newEntitlementStartEvent);
+                    it.next();
+                }
+            }
+        }
+
+        private Set<UUID> computeOlderEntitlementSet(final List<SubscriptionEvent> initialEntitlementEvents, final Collection<BlockingState> blockingStates) {
+
+            final Set<UUID> START_BILLING_entitlementIdSet = ImmutableSet.copyOf(Iterables.transform(Iterables.filter(initialEntitlementEvents, new Predicate<SubscriptionEvent>() {
+                @Override
+                public boolean apply(final SubscriptionEvent input) {
+                    return input.getSubscriptionEventType() == SubscriptionEventType.START_BILLING;
+                }
+            }), new Function<SubscriptionEvent, UUID>() {
+                @Override
+                public UUID apply(final SubscriptionEvent input) {
+                    return input.getEntitlementId();
+                }
+            }));
+
+            final Set<UUID> ENT_STATE_START_entitlementIdSet = ImmutableSet.copyOf(Iterables.transform(Iterables.filter(blockingStates, new Predicate<BlockingState>() {
+                @Override
+                public boolean apply(final BlockingState input) {
+                    return input.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME) && input.getStateName().equals(DefaultEntitlementApi.ENT_STATE_START);
+                }
+            }), new Function<BlockingState, UUID>() {
+                @Override
+                public UUID apply(final BlockingState input) {
+                    return input.getBlockedId();
+                }
+            }));
+
+            return Sets.<UUID>difference(START_BILLING_entitlementIdSet, ENT_STATE_START_entitlementIdSet);
+        }
+    }
+
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
index 9d5513e..9283363 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/SubscriptionEventOrdering.java
@@ -24,7 +24,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
-import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.DefaultEntitlementService;
 import org.killbill.billing.subscription.api.SubscriptionBase;
@@ -80,7 +79,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
             for (final SubscriptionBaseTransition tr : baseTransitions) {
                 final List<SubscriptionEventType> eventTypes = toEventTypes(tr.getTransitionType());
                 for (final SubscriptionEventType eventType : eventTypes) {
-                    final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, base.getStartDate(), internalTenantContext);
+                    final SubscriptionEvent event = toSubscriptionEvent(tr, eventType, internalTenantContext);
                     insertSubscriptionEvent(event, result);
                 }
             }
@@ -147,7 +146,7 @@ public class SubscriptionEventOrdering extends EntitlementOrderingBase {
         result.add(index, event);
     }
 
-    private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTime referenceTime, final InternalTenantContext internalTenantContext) {
+    private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final InternalTenantContext internalTenantContext) {
         return new DefaultSubscriptionEvent(in.getId(),
                                             in.getSubscriptionId(),
                                             in.getEffectiveTransitionTime(),
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
index 7220143..c4dcbd3 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionBundleTimeline.java
@@ -265,6 +265,16 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testOneSimpleEntitlement() throws CatalogApiException {
+        testOneSimpleEntitlementImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testOneSimpleEntitlementWithRegression() throws CatalogApiException {
+        testOneSimpleEntitlementImpl(true);
+    }
+
+
+    private void testOneSimpleEntitlementImpl(boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -283,10 +293,12 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-        blockingStates.add(bsCreate);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+            blockingStates.add(bsCreate);
+        }
 
 
         effectiveDate = effectiveDate.plusDays(30);
@@ -336,8 +348,18 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         assertNull(events.get(3).getNextPhase());
     }
 
+
+
     @Test(groups = "fast")
     public void testCancelBundleBeforeSubscription() throws CatalogApiException {
+        testCancelBundleBeforeSubscriptionImpl(false);
+    }
+    @Test(groups = "fast")
+    public void testCancelBundleBeforeSubscriptionWithRegression() throws CatalogApiException {
+        testCancelBundleBeforeSubscriptionImpl(true);
+    }
+
+    private void testCancelBundleBeforeSubscriptionImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -355,10 +377,12 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-        blockingStates.add(bsCreate);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+            blockingStates.add(bsCreate);
+        }
 
         // Block the bundle before the subscription
         effectiveDate = effectiveDate.plusDays(15);
@@ -411,6 +435,16 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
     public void testOneEntitlementWithPauseResume() throws CatalogApiException {
+        testOneEntitlementWithPauseResumeImpl(false);
+    }
+
+    @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/135")
+    public void testOneEntitlementWithPauseResumeWithRegression() throws CatalogApiException {
+        testOneEntitlementWithPauseResumeImpl(true);
+    }
+
+
+    private void testOneEntitlementWithPauseResumeImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -428,11 +462,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+
+            blockingStates.add(bsCreate);
+        }
 
-        blockingStates.add(bsCreate);
 
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
@@ -544,6 +581,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/147 and https://github.com/killbill/killbill/issues/148")
     public void testOneEntitlementWithOverduePauseThenCancel() throws CatalogApiException {
+        testOneEntitlementWithOverduePauseThenCancelImpl(false);
+    }
+
+    public void testOneEntitlementWithOverduePauseThenCancelWithRegression() throws CatalogApiException {
+        testOneEntitlementWithOverduePauseThenCancelImpl(true);
+    }
+
+    private void testOneEntitlementWithOverduePauseThenCancelImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -561,12 +606,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
-        blockingStates.add(bsCreate);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
 
+            blockingStates.add(bsCreate);
+        }
 
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
@@ -699,6 +745,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testOneEntitlementWithInitialBlockingState() throws CatalogApiException {
+        testOneEntitlementWithInitialBlockingStateImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testOneEntitlementWithInitialBlockingStateWithRegression() throws CatalogApiException {
+        testOneEntitlementWithInitialBlockingStateImpl(true);
+    }
+
+    private void testOneEntitlementWithInitialBlockingStateImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -723,12 +778,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
-        blockingStates.add(bsCreate);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
 
+            blockingStates.add(bsCreate);
+        }
 
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
@@ -791,6 +847,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testOneEntitlementWithBlockingStatesSubscription() throws CatalogApiException {
+        testOneEntitlementWithBlockingStatesSubscriptionImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testOneEntitlementWithBlockingStatesSubscriptionWithRegression() throws CatalogApiException {
+        testOneEntitlementWithBlockingStatesSubscriptionImpl(true);
+    }
+
+    private void testOneEntitlementWithBlockingStatesSubscriptionImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -808,12 +873,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
-        blockingStates.add(bsCreate);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
 
+            blockingStates.add(bsCreate);
+        }
 
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
@@ -885,6 +951,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testWithMultipleEntitlements() throws CatalogApiException {
+        testWithMultipleEntitlementsImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testWithMultipleEntitlementsWithRegression() throws CatalogApiException {
+        testWithMultipleEntitlementsImpl(true);
+    }
+
+    private void testWithMultipleEntitlementsImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -904,23 +979,26 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition ent1Tr1 = createTransition(entitlementId1, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial1");
         allTransitions1.add(ent1Tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId1, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
-        blockingStates.add(bsCreate);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId1, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
 
+            blockingStates.add(bsCreate);
+        }
 
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
         final SubscriptionBaseTransition ent2Tr1 = createTransition(entitlementId2, EventType.API_USER, ApiEventType.TRANSFER, requestedDate, effectiveDate, clock.getUTCNow(), null, "phase2");
         allTransitions2.add(ent2Tr1);
 
-        final BlockingState bsCreate2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId2, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
+        if (!regressionFlagForOlderVersionThan_0_17_X ) {
+            final BlockingState bsCreate2 = new DefaultBlockingState(UUID.randomUUID(), entitlementId2, BlockingStateType.SUBSCRIPTION,
+                                                                     DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
 
-        blockingStates.add(bsCreate2);
+            blockingStates.add(bsCreate2);
+        }
 
         effectiveDate = effectiveDate.plusDays(15);
         clock.addDays(15);
@@ -1022,6 +1100,15 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast")
     public void testWithOverdueOffline() throws CatalogApiException {
+        testWithOverdueOfflineImpl(false);
+    }
+
+    @Test(groups = "fast")
+    public void testWithOverdueOfflineWithRegression() throws CatalogApiException {
+        testWithOverdueOfflineImpl(true);
+    }
+
+    private void testWithOverdueOfflineImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1040,12 +1127,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
-        blockingStates.add(bsCreate);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
 
+            blockingStates.add(bsCreate);
+        }
 
         effectiveDate = effectiveDate.plusDays(30);
         clock.addDays(30);
@@ -1142,6 +1230,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/134")
     public void testRemoveOverlappingBlockingStates() throws CatalogApiException {
+        testRemoveOverlappingBlockingStatesImpl(false);
+    }
+
+    public void testRemoveOverlappingBlockingStatesWithRegression() throws CatalogApiException {
+        testRemoveOverlappingBlockingStatesImpl(true);
+    }
+
+    public void testRemoveOverlappingBlockingStatesImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1159,12 +1255,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
-        blockingStates.add(bsCreate);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
 
+            blockingStates.add(bsCreate);
+        }
 
         // Overlapping ENT_STATE_BLOCKED - should merge
         effectiveDate = effectiveDate.plusDays(5);
@@ -1233,6 +1330,14 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
 
     @Test(groups = "fast", description = "Test for https://github.com/killbill/killbill/issues/149")
     public void testVariousBlockingStatesAtTheSameEffectiveDate() throws CatalogApiException {
+        testVariousBlockingStatesAtTheSameEffectiveDateImpl(false);
+    }
+
+    public void testVariousBlockingStatesAtTheSameEffectiveDateWithRegression() throws CatalogApiException {
+        testVariousBlockingStatesAtTheSameEffectiveDateImpl(true);
+    }
+
+    private void testVariousBlockingStatesAtTheSameEffectiveDateImpl(final boolean regressionFlagForOlderVersionThan_0_17_X) throws CatalogApiException {
         clock.setDay(new LocalDate(2013, 1, 1));
 
         final DateTimeZone accountTimeZone = DateTimeZone.UTC;
@@ -1250,12 +1355,13 @@ public class TestDefaultSubscriptionBundleTimeline extends EntitlementTestSuiteN
         final SubscriptionBaseTransition tr1 = createTransition(entitlementId, EventType.API_USER, ApiEventType.CREATE, requestedDate, effectiveDate, clock.getUTCNow(), null, "trial");
         allTransitions.add(tr1);
 
-        final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
-                                                                DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
-
-        blockingStates.add(bsCreate);
+        if (!regressionFlagForOlderVersionThan_0_17_X) {
+            final BlockingState bsCreate = new DefaultBlockingState(UUID.randomUUID(), entitlementId, BlockingStateType.SUBSCRIPTION,
+                                                                    DefaultEntitlementApi.ENT_STATE_START, DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                    false, false, false, effectiveDate, clock.getUTCNow(), clock.getUTCNow(), 0L);
 
+            blockingStates.add(bsCreate);
+        }
 
         // 2013-02-10
         effectiveDate = effectiveDate.plusDays(40);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
new file mode 100644
index 0000000..4b772e3
--- /dev/null
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestRegessionSubscriptionApi.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014-2016 Groupon, Inc
+ * Copyright 2014-2016 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.entitlement.api;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.ErrorCode;
+import org.killbill.billing.OrderingType;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountApiException;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.catalog.api.BillingActionPolicy;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.entitlement.EntitlementService;
+import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementActionPolicy;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.api.AuditLevel;
+import org.killbill.billing.util.audit.AuditLog;
+import org.killbill.billing.util.audit.ChangeType;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+public class TestRegessionSubscriptionApi extends EntitlementTestSuiteWithEmbeddedDB {
+
+    @Test(groups = "slow", description = "Verify behavior with or without ENT_STARTED event works as expected")
+    public void testRegressionForNew_ENT_STARTED_event() throws Exception {
+
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        // Start the entitlement yesterday (does not m,ake sense, but we want to check later different of behavior)
+        final LocalDate entitlementEffectiveDate = initialDate.minusDays(1);
+
+        final Account account = createAccount(getAccountData(7));
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        testListener.pushExpectedEvents(NextEvent.CREATE, NextEvent.BLOCK);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, UUID.randomUUID().toString(), null, entitlementEffectiveDate, null, ImmutableList.<PluginProperty>of(), callContext);
+        // Because of the BlockingState event ENT_STARTED, the entitlement date should be correctly set
+        Assert.assertEquals(entitlement.getEffectiveStartDate(), entitlementEffectiveDate);
+
+        final List<SubscriptionBundle> bundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
+        Assert.assertEquals(bundles.size(), 1);
+        subscriptionBundleChecker(bundles, initialDate, entitlement, 0);
+
+        // Let's do some surgery and inactivate the ENT_STARTED BlockingState
+        final List<BlockingState> blockingStates = blockingStateDao.getBlockingState(entitlement.getId(), BlockingStateType.SUBSCRIPTION, clock.getUTCNow(), internalCallContext);
+        assertEquals(blockingStates.size(), 1);
+        assertEquals(blockingStates.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_START);
+        blockingStateDao.unactiveBlockingState(blockingStates.get(0).getId(), internalCallContext);
+
+        final Entitlement oldSchoolEntitlement = entitlementApi.getEntitlementForId(entitlement.getId(), callContext);
+        // Because the ENT_STARTED BlockingState has been invalidated, the startDate should now default to the billingDate
+        Assert.assertEquals(oldSchoolEntitlement.getEffectiveStartDate(), initialDate);
+
+        final List<SubscriptionBundle> oldSchoolBundles = subscriptionApi.getSubscriptionBundlesForAccountId(account.getId(), callContext);
+        Assert.assertEquals(oldSchoolBundles.size(), 1);
+        subscriptionBundleChecker(oldSchoolBundles, initialDate, oldSchoolEntitlement, 0);
+    }
+
+    private void subscriptionBundleChecker(final List<SubscriptionBundle> bundles, final LocalDate billingStartDate, final Entitlement entitlement, final int idx) {
+        Assert.assertEquals(bundles.get(idx).getId(), entitlement.getBundleId());
+        Assert.assertEquals(bundles.get(idx).getSubscriptions().size(), 1);
+        Assert.assertEquals(bundles.get(idx).getSubscriptions().get(0).getId(), entitlement.getId());
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().size(), 3);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getEffectiveDate(), entitlement.getEffectiveStartDate());
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(0).getSubscriptionEventType(), SubscriptionEventType.START_ENTITLEMENT);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getEffectiveDate(), billingStartDate);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(1).getSubscriptionEventType(), SubscriptionEventType.START_BILLING);
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getEffectiveDate(), new LocalDate(2013, 9, 6));
+        Assert.assertEquals(bundles.get(idx).getTimeline().getSubscriptionEvents().get(2).getSubscriptionEventType(), SubscriptionEventType.PHASE);
+    }
+
+}