killbill-aplcache

junction: fix bug when computing blocking durations This

11/27/2013 12:47:36 PM

Changes

junction/pom.xml 10(+10 -0)

Details

diff --git a/api/src/main/java/com/ning/billing/junction/BillingEventSet.java b/api/src/main/java/com/ning/billing/junction/BillingEventSet.java
index 8dfca08..237e658 100644
--- a/api/src/main/java/com/ning/billing/junction/BillingEventSet.java
+++ b/api/src/main/java/com/ning/billing/junction/BillingEventSet.java
@@ -25,7 +25,4 @@ public interface BillingEventSet extends SortedSet<BillingEvent> {
     public abstract boolean isAccountAutoInvoiceOff();
 
     public abstract List<UUID> getSubscriptionIdsWithAutoInvoiceOff();
-
-    public boolean isLast(BillingEvent event);
-
 }
diff --git a/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java b/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java
index 0d30cf4..e7b9720 100644
--- a/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java
+++ b/api/src/main/java/com/ning/billing/junction/BlockingInternalApi.java
@@ -28,7 +28,7 @@ public interface BlockingInternalApi {
 
     public BlockingState getBlockingStateForService(UUID blockableId, BlockingStateType blockingStateType, String serviceName, InternalTenantContext context);
 
-    public List<BlockingState> getBlockingAll(UUID blockableId, BlockingStateType blockingStateType, InternalTenantContext context);
+    public List<BlockingState> getBlockingAllForAccount(InternalTenantContext context);
 
     public void setBlockingState(BlockingState state, InternalCallContext context);
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
index 22f7434..2ca84c0 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultInternalBlockingApi.java
@@ -55,8 +55,8 @@ public class DefaultInternalBlockingApi implements BlockingInternalApi {
     }
 
     @Override
-    public List<BlockingState> getBlockingAll(final UUID overdueableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        return dao.getBlockingAll(overdueableId, blockingStateType, context);
+    public List<BlockingState> getBlockingAllForAccount(final InternalTenantContext context) {
+        return dao.getBlockingAllForAccountRecordId(context);
     }
 
     @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
index a154d28..8c96ed6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/ProxyBlockingStateDao.java
@@ -60,21 +60,38 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
 
     private static final Logger log = LoggerFactory.getLogger(ProxyBlockingStateDao.class);
 
+    // Ordering is critical here, especially for Junction
     private static final Ordering<BlockingState> BLOCKING_STATE_ORDERING = Ordering.<BlockingState>from(new Comparator<BlockingState>() {
         @Override
         public int compare(final BlockingState o1, final BlockingState o2) {
-            final int blockableIdComparison = o1.getBlockedId().compareTo(o2.getBlockedId());
-            if (blockableIdComparison == 0) {
-                // effective_date column NOT NULL
-                final int comparison = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
-                if (comparison == 0) {
-                    // Keep a stable ordering for ties
-                    return o1.getCreatedDate().compareTo(o2.getCreatedDate());
+            // effective_date column NOT NULL
+            final int effectiveDateComparison = o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
+            if (effectiveDateComparison != 0) {
+                return effectiveDateComparison;
+            } else {
+                final int blockableIdComparison = o1.getBlockedId().compareTo(o2.getBlockedId());
+                if (blockableIdComparison != 0) {
+                    return blockableIdComparison;
                 } else {
-                    return comparison;
+                    // Same date, same blockable id - make sure billing transitions are respected first (assume block -> clear transitions)
+                    if (!o1.isBlockBilling() && o2.isBlockBilling()) {
+                        return 1;
+                    } else if (o1.isBlockBilling() && !o2.isBlockBilling()) {
+                        return -1;
+                    }
+
+                    // Then respect other blocking states
+                    if ((!o1.isBlockChange() && o2.isBlockChange()) ||
+                        (!o1.isBlockEntitlement() && o2.isBlockEntitlement())) {
+                        return 1;
+                    } else if ((o1.isBlockChange() && !o2.isBlockChange()) ||
+                               (o1.isBlockEntitlement() && !o2.isBlockEntitlement())) {
+                        return -1;
+                    }
+
+                    // Otherwise, just respect the created date
+                    return o1.getCreatedDate().compareTo(o2.getCreatedDate());
                 }
-            } else {
-                return blockableIdComparison;
             }
         }
     });
@@ -204,11 +221,13 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
                     baseSubscriptionsToConsider = ImmutableList.<SubscriptionBase>of(baseSubscription);
                 } else {
                     // blockable id points to a base or standalone subscription, there is nothing to do
-                    return blockingStatesOnDisk;
+                    // Simply return the sorted list
+                    return BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStatesOnDisk);
                 }
             } else {
                 // blockable id points to an account or bundle, in which case there are no extra blocking states to add
-                return blockingStatesOnDisk;
+                // Simply return the sorted list
+                return BLOCKING_STATE_ORDERING.immutableSortedCopy(blockingStatesOnDisk);
             }
         } catch (SubscriptionBaseApiException e) {
             log.error("Error retrieving subscriptions for account record id " + context.getAccountRecordId(), e);
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
index 25067a4..484b8fd 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/dao/BlockingStateSqlDao.sql.stg
@@ -50,7 +50,7 @@ and effective_date \<= :effectiveDate
 and is_active
 <AND_CHECK_TENANT()>
 -- We want the current state, hence the order desc and limit 1
-order by record_id desc
+order by effective_date desc, record_id desc
 limit 1
 ;
 >>
@@ -71,7 +71,7 @@ getBlockingState() ::= <<
          group by service
  ) tmp
  on t.record_id = tmp.record_id
- order by t.record_id asc
+ order by t.effective_date, t.record_id asc
  ;
  >>
 
@@ -84,7 +84,7 @@ where blockable_id = :blockableId
 and service = :service
 and is_active
 <AND_CHECK_TENANT()>
-order by record_id asc
+order by effective_date, record_id asc
 ;
 >>
 
@@ -96,7 +96,7 @@ from
 where blockable_id = :blockableId
 and is_active
 <AND_CHECK_TENANT()>
-order by record_id asc
+order by effective_date, record_id asc
 ;
 >>
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java
index e227e1d..b17e7d8 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/block/TestBlockingApi.java
@@ -90,7 +90,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         blockingInternalApi.setBlockingState(state2, internalCallContext);
         assertListenerStatus();
 
-        final List<BlockingState> blockingAll = blockingInternalApi.getBlockingAll(uuid, BlockingStateType.ACCOUNT, internalCallContext);
+        final List<BlockingState> blockingAll = blockingInternalApi.getBlockingAllForAccount(internalCallContext);
         final List<BlockingState> history = ImmutableList.<BlockingState>copyOf(Collections2.<BlockingState>filter(blockingAll,
                                                                                                                    new Predicate<BlockingState>() {
                                                                                                                        @Override
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
index f2bff3d..3e3c69d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/MockBlockingStateDao.java
@@ -32,6 +32,7 @@ import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.api.EntitlementApiException;
 import com.ning.billing.util.entity.dao.MockEntityDaoBase;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
@@ -39,6 +40,7 @@ import com.google.common.collect.ImmutableList;
 public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDao, BlockingState, EntitlementApiException> implements BlockingStateDao {
 
     private final Map<UUID, List<BlockingState>> blockingStates = new HashMap<UUID, List<BlockingState>>();
+    private final Map<Long, List<BlockingState>> blockingStatesPerAccountRecordId = new HashMap<Long, List<BlockingState>>();
 
     // TODO This mock class should also check that events are past or present except for getBlockingAll
 
@@ -100,7 +102,7 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
 
     @Override
     public List<BlockingState> getBlockingAllForAccountRecordId(final InternalTenantContext context) {
-        throw new UnsupportedOperationException();
+        return Objects.firstNonNull(blockingStatesPerAccountRecordId.get(context.getAccountRecordId()), ImmutableList.<BlockingState>of());
     }
 
     @Override
@@ -109,6 +111,11 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
             blockingStates.put(state.getBlockedId(), new ArrayList<BlockingState>());
         }
         blockingStates.get(state.getBlockedId()).add(state);
+
+        if (blockingStatesPerAccountRecordId.get(context.getAccountRecordId()) == null) {
+            blockingStatesPerAccountRecordId.put(context.getAccountRecordId(), new ArrayList<BlockingState>());
+        }
+        blockingStatesPerAccountRecordId.get(context.getAccountRecordId()).add(state);
     }
 
     @Override
@@ -116,11 +123,8 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
         throw new UnsupportedOperationException();
     }
 
-    public synchronized void setBlockingStates(final UUID blockedId, final List<BlockingState> states) {
-        blockingStates.put(blockedId, states);
-    }
-
     public synchronized void clear() {
         blockingStates.clear();
+        blockingStatesPerAccountRecordId.clear();
     }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
index ed26f65..9300fc7 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/dao/TestDefaultBlockingStateDao.java
@@ -136,12 +136,12 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         Assert.assertEquals(blockingStates5.size(), 2);
         Assert.assertEquals(blockingStates5.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates5.get(0).getStateName(), state);
-        Assert.assertEquals(blockingStates5.get(0).getService(), serviceB);
-        Assert.assertEquals(blockingStates5.get(0).getEffectiveDate(), stateDateTime);
+        Assert.assertEquals(blockingStates5.get(0).getService(), serviceA);
+        Assert.assertEquals(blockingStates5.get(0).getEffectiveDate(), stateDateTime3);
         Assert.assertEquals(blockingStates5.get(1).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates5.get(1).getStateName(), state);
-        Assert.assertEquals(blockingStates5.get(1).getService(), serviceA);
-        Assert.assertEquals(blockingStates5.get(1).getEffectiveDate(), stateDateTime3);
+        Assert.assertEquals(blockingStates5.get(1).getService(), serviceB);
+        Assert.assertEquals(blockingStates5.get(1).getEffectiveDate(), stateDateTime);
 
         // Set a new state for service A
         final DateTime state2DateTime = new DateTime(2013, 12, 6, 10, 11, 12, DateTimeZone.UTC);
@@ -151,12 +151,12 @@ public class TestDefaultBlockingStateDao extends EntitlementTestSuiteWithEmbedde
         Assert.assertEquals(blockingStates6.size(), 3);
         Assert.assertEquals(blockingStates6.get(0).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates6.get(0).getStateName(), state);
-        Assert.assertEquals(blockingStates6.get(0).getService(), serviceB);
-        Assert.assertEquals(blockingStates6.get(0).getEffectiveDate(), stateDateTime);
+        Assert.assertEquals(blockingStates6.get(0).getService(), serviceA);
+        Assert.assertEquals(blockingStates6.get(0).getEffectiveDate(), stateDateTime3);
         Assert.assertEquals(blockingStates6.get(1).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates6.get(1).getStateName(), state);
-        Assert.assertEquals(blockingStates6.get(1).getService(), serviceA);
-        Assert.assertEquals(blockingStates6.get(1).getEffectiveDate(), stateDateTime3);
+        Assert.assertEquals(blockingStates6.get(1).getService(), serviceB);
+        Assert.assertEquals(blockingStates6.get(1).getEffectiveDate(), stateDateTime);
         Assert.assertEquals(blockingStates6.get(2).getBlockedId(), blockableId);
         Assert.assertEquals(blockingStates6.get(2).getStateName(), state2);
         Assert.assertEquals(blockingStates6.get(2).getService(), serviceA);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
index f32427d..6037eeb 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/MockBillingEventSet.java
@@ -36,11 +36,6 @@ public class MockBillingEventSet extends TreeSet<BillingEvent> implements Billin
     }
 
     @Override
-    public boolean isLast(final BillingEvent event) {
-        return event == last();
-    }
-
-    @Override
     public boolean isAccountAutoInvoiceOff() {
         return isAccountInvoiceOff;
     }

junction/pom.xml 10(+10 -0)

diff --git a/junction/pom.xml b/junction/pom.xml
index 71d8d07..3a94722 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -50,6 +50,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>com.jayway.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-api</artifactId>
         </dependency>
@@ -81,6 +86,11 @@
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-subscription</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
             <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
index e2cca24..bc98975 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -25,22 +25,23 @@ import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicLong;
 
+import javax.annotation.Nullable;
+
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
-import com.ning.billing.entitlement.api.BlockingStateType;
-import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
-import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.entitlement.api.BlockingState;
-import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.junction.BillingEvent;
 import com.ning.billing.junction.BillingModeType;
 import com.ning.billing.junction.BlockingInternalApi;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.inject.Inject;
@@ -52,8 +53,9 @@ public class BlockingCalculator {
     private final BlockingInternalApi blockingApi;
 
     protected static class DisabledDuration {
+
         private final DateTime start;
-        private final DateTime end;
+        private DateTime end;
 
         public DisabledDuration(final DateTime start, final DateTime end) {
             this.start = start;
@@ -68,6 +70,9 @@ public class BlockingCalculator {
             return end;
         }
 
+        public void setEnd(final DateTime end) {
+            this.end = end;
+        }
     }
 
     @Inject
@@ -92,11 +97,9 @@ public class BlockingCalculator {
         final SortedSet<BillingEvent> billingEventsToAdd = new TreeSet<BillingEvent>();
         final SortedSet<BillingEvent> billingEventsToRemove = new TreeSet<BillingEvent>();
 
+        final List<BlockingState> blockingEvents = blockingApi.getBlockingAllForAccount(context);
+        final List<DisabledDuration> blockingDurations = createBlockingDurations(blockingEvents);
         for (final UUID bundleId : bundleMap.keySet()) {
-            final List<BlockingState> blockingEvents = blockingApi.getBlockingAll(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, context);
-            blockingEvents.addAll(blockingApi.getBlockingAll(account.getId(), BlockingStateType.ACCOUNT, context));
-            final List<DisabledDuration> blockingDurations = createBlockingDurations(blockingEvents);
-
             for (final SubscriptionBase subscription : bundleMap.get(bundleId)) {
                 billingEventsToAdd.addAll(createNewEvents(blockingDurations, billingEvents, account, subscription));
                 billingEventsToRemove.addAll(eventsToRemove(blockingDurations, billingEvents, subscription));
@@ -252,31 +255,56 @@ public class BlockingCalculator {
         return result;
     }
 
-
     // In ascending order
-    protected List<DisabledDuration> createBlockingDurations(final List<BlockingState> overdueBundleEvents) {
+    protected List<DisabledDuration> createBlockingDurations(final Iterable<BlockingState> overdueBundleEvents) {
         final List<DisabledDuration> result = new ArrayList<BlockingCalculator.DisabledDuration>();
         // Earliest blocking event
         BlockingState first = null;
 
+        int blockedNesting = 0;
+        BlockingState lastOne = null;
         for (final BlockingState e : overdueBundleEvents) {
-            if (e.isBlockBilling() && first == null) {
+            lastOne = e;
+            if (e.isBlockBilling() && blockedNesting == 0) {
                 // First blocking event of contiguous series of blocking events
                 first = e;
-            } else if (first != null && !e.isBlockBilling()) {
-                // End of the interval
-                result.add(new DisabledDuration(first.getEffectiveDate(), e.getEffectiveDate()));
-                first = null;
+                blockedNesting++;
+            } else if (e.isBlockBilling() && blockedNesting > 0) {
+                // Nest blocking states
+                blockedNesting++;
+            } else if (!e.isBlockBilling() && blockedNesting > 0) {
+                blockedNesting--;
+                if (blockedNesting == 0) {
+                    // End of the interval
+                    addDisabledDuration(result, first, e);
+                    first = null;
+                }
             }
         }
 
         if (first != null) { // found a transition to disabled with no terminating event
-            result.add(new DisabledDuration(first.getEffectiveDate(), null));
+            addDisabledDuration(result, first, lastOne.isBlockBilling() ? null : lastOne);
         }
 
         return result;
     }
 
+    private void addDisabledDuration(final List<DisabledDuration> result, final BlockingState firstBlocking, @Nullable final BlockingState firstNonBlocking) {
+        final DisabledDuration lastOne;
+        if (!result.isEmpty()) {
+            lastOne = result.get(result.size() - 1);
+        } else {
+            lastOne = null;
+        }
+
+        final DateTime endDate = firstNonBlocking == null ? null : firstNonBlocking.getEffectiveDate();
+        if (lastOne != null && lastOne.getEnd().compareTo(firstBlocking.getEffectiveDate()) == 0) {
+            lastOne.setEnd(endDate);
+        } else {
+            result.add(new DisabledDuration(firstBlocking.getEffectiveDate(), endDate));
+        }
+    }
+
     @VisibleForTesting
     static AtomicLong getGlobalTotalOrder() {
         return globaltotalOrder;
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
index 03df1e7..c55aaa3 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultBillingEventSet.java
@@ -55,10 +55,6 @@ public class DefaultBillingEventSet extends TreeSet<BillingEvent> implements Sor
         this.subscriptionIdsWithAutoInvoiceOff = subscriptionIdsWithAutoInvoiceOff;
     }
 
-    public boolean isLast(final BillingEvent event) {
-        return last() == event;
-    }
-
     @Override
     public String toString() {
         return "DefaultBillingEventSet [accountAutoInvoiceOff=" + accountAutoInvoiceOff
diff --git a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
index 83ef135..defc439 100644
--- a/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
+++ b/junction/src/main/java/com/ning/billing/junction/plumbing/billing/DefaultInternalBillingApi.java
@@ -28,18 +28,18 @@ import org.slf4j.LoggerFactory;
 import com.ning.billing.ObjectType;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountInternalApi;
 import com.ning.billing.account.api.MutableAccountData;
+import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.CatalogService;
-import com.ning.billing.subscription.api.SubscriptionBase;
-import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
-import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.events.EffectiveSubscriptionInternalEvent;
-import com.ning.billing.account.api.AccountInternalApi;
-import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.junction.BillingEvent;
 import com.ning.billing.junction.BillingEventSet;
 import com.ning.billing.junction.BillingInternalApi;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
 import com.ning.billing.tag.TagInternalApi;
 import com.ning.billing.util.tag.ControlTagType;
 import com.ning.billing.util.tag.Tag;
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
index 0d4307d..b74b247 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModule.java
@@ -18,19 +18,16 @@ package com.ning.billing.junction.glue;
 
 import org.skife.config.ConfigSource;
 
-import com.ning.billing.catalog.MockCatalogModule;
 import com.ning.billing.entitlement.api.svcs.DefaultInternalBlockingApi;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.block.MockBlockingChecker;
 import com.ning.billing.entitlement.dao.BlockingStateDao;
 import com.ning.billing.entitlement.dao.MockBlockingStateDao;
-import com.ning.billing.mock.glue.MockAccountModule;
+import com.ning.billing.junction.BlockingInternalApi;
 import com.ning.billing.mock.glue.MockEntitlementModule;
-import com.ning.billing.mock.glue.MockSubscriptionModule;
 import com.ning.billing.util.glue.CacheModule;
 import com.ning.billing.util.glue.CallContextModule;
 import com.ning.billing.util.glue.MetricsModule;
-import com.ning.billing.junction.BlockingInternalApi;
 
 public class TestJunctionModule extends DefaultJunctionModule {
 
@@ -45,10 +42,6 @@ public class TestJunctionModule extends DefaultJunctionModule {
         install(new MetricsModule());
         install(new CacheModule(configSource));
         install(new CallContextModule());
-        install(new MockAccountModule());
-        install(new MockCatalogModule());
-        install(new MockSubscriptionModule());
-        install(new MockEntitlementModuleForJunction());
     }
 
     public class MockEntitlementModuleForJunction extends MockEntitlementModule {
@@ -67,6 +60,5 @@ public class TestJunctionModule extends DefaultJunctionModule {
         public void installBlockingChecker() {
             bind(BlockingChecker.class).to(MockBlockingChecker.class).asEagerSingleton();
         }
-
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
index faf6939..081d6aa 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleNoDB.java
@@ -20,7 +20,10 @@ import org.skife.config.ConfigSource;
 import org.skife.config.ConfigurationObjectFactory;
 
 import com.ning.billing.GuicyKillbillTestNoDBModule;
+import com.ning.billing.catalog.MockCatalogModule;
+import com.ning.billing.mock.glue.MockAccountModule;
 import com.ning.billing.mock.glue.MockNonEntityDaoModule;
+import com.ning.billing.mock.glue.MockSubscriptionModule;
 import com.ning.billing.mock.glue.MockTagModule;
 import com.ning.billing.notificationq.MockNotificationQueueService;
 import com.ning.billing.notificationq.api.NotificationQueueConfig;
@@ -42,6 +45,10 @@ public class TestJunctionModuleNoDB extends TestJunctionModule {
         install(new GuicyKillbillTestNoDBModule());
         install(new MockNonEntityDaoModule());
         install(new InMemoryBusModule(configSource));
+        install(new MockAccountModule());
+        install(new MockCatalogModule());
+        install(new MockSubscriptionModule());
+        install(new MockEntitlementModuleForJunction());
         install(new MockTagModule());
         installNotificationQueue();
     }
diff --git a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
index ccac19f..32dff2d 100644
--- a/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/glue/TestJunctionModuleWithEmbeddedDB.java
@@ -19,9 +19,17 @@ package com.ning.billing.junction.glue;
 import org.skife.config.ConfigSource;
 
 import com.ning.billing.GuicyKillbillTestWithEmbeddedDBModule;
+import com.ning.billing.account.glue.DefaultAccountModule;
+import com.ning.billing.api.TestApiListener;
+import com.ning.billing.api.TestListenerStatus;
+import com.ning.billing.catalog.glue.CatalogModule;
+import com.ning.billing.entitlement.glue.DefaultEntitlementModule;
+import com.ning.billing.junction.JunctionTestListenerStatus;
+import com.ning.billing.subscription.glue.DefaultSubscriptionModule;
 import com.ning.billing.util.glue.BusModule;
 import com.ning.billing.util.glue.MetricsModule;
 import com.ning.billing.util.glue.NonEntityDaoModule;
+import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
 
 public class TestJunctionModuleWithEmbeddedDB extends TestJunctionModule {
@@ -36,8 +44,16 @@ public class TestJunctionModuleWithEmbeddedDB extends TestJunctionModule {
 
         install(new GuicyKillbillTestWithEmbeddedDBModule());
         install(new NonEntityDaoModule());
+        install(new CatalogModule(configSource));
+        install(new DefaultAccountModule(configSource));
+        install(new DefaultEntitlementModule(configSource));
+        install(new NotificationQueueModule(configSource));
+        install(new DefaultSubscriptionModule(configSource));
         install(new BusModule(configSource));
         install(new MetricsModule());
         install(new TagStoreModule());
+
+        bind(TestListenerStatus.class).to(JunctionTestListenerStatus.class).asEagerSingleton();
+        bind(TestApiListener.class).asEagerSingleton();
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/JunctionTestListenerStatus.java b/junction/src/test/java/com/ning/billing/junction/JunctionTestListenerStatus.java
new file mode 100644
index 0000000..a54976d
--- /dev/null
+++ b/junction/src/test/java/com/ning/billing/junction/JunctionTestListenerStatus.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.junction;
+
+import javax.inject.Inject;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+
+import com.ning.billing.api.TestListenerStatus;
+
+public class JunctionTestListenerStatus implements TestListenerStatus {
+
+    private final Logger log = LoggerFactory.getLogger(JunctionTestListenerStatus.class);
+
+    private boolean isListenerFailed;
+    private String listenerFailedMsg;
+
+    @Inject
+    public JunctionTestListenerStatus() {
+        isListenerFailed = false;
+    }
+
+    @Override
+    public void failed(final String msg) {
+        this.isListenerFailed = true;
+        this.listenerFailedMsg = msg;
+    }
+
+    @Override
+    public void resetTestListenerStatus() {
+        this.isListenerFailed = false;
+        this.listenerFailedMsg = null;
+    }
+
+    public void assertListenerStatus() {
+        if (isListenerFailed) {
+            log.error(listenerFailedMsg);
+            Assert.fail(listenerFailedMsg);
+        }
+    }
+}
\ No newline at end of file
diff --git a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
index 1c989e2..7f98ec3 100644
--- a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -16,34 +16,62 @@
 
 package com.ning.billing.junction;
 
+import java.net.URL;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.BeforeMethod;
 
 import com.ning.billing.GuicyKillbillTestSuiteWithEmbeddedDB;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.api.TestApiListener;
+import com.ning.billing.api.TestListenerStatus;
 import com.ning.billing.bus.api.PersistentBus;
+import com.ning.billing.catalog.DefaultCatalogService;
+import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.clock.ClockMock;
+import com.ning.billing.entitlement.DefaultEntitlementService;
+import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.api.EntitlementApi;
 import com.ning.billing.junction.glue.TestJunctionModuleWithEmbeddedDB;
-import com.ning.billing.junction.plumbing.billing.BlockingCalculator;
-import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.mock.MockAccountBuilder;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
-import com.ning.billing.tag.TagInternalApi;
-import com.ning.billing.util.tag.dao.TagDao;
+import com.ning.billing.subscription.api.SubscriptionBaseService;
+import com.ning.billing.subscription.engine.core.DefaultSubscriptionBaseService;
+import com.ning.billing.util.svcsapi.bus.BusService;
 
 import com.google.inject.Guice;
 import com.google.inject.Inject;
 import com.google.inject.Injector;
+import com.google.inject.Stage;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
 
 public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWithEmbeddedDB {
 
+    protected static final Logger log = LoggerFactory.getLogger(JunctionTestSuiteWithEmbeddedDB.class);
+
+    // Be generous...
+    protected static final Long DELAY = 20000L;
+
     @Inject
-    protected AccountInternalApi accountInternalApi;
+    protected AccountUserApi accountApi;
     @Inject
-    protected BillingInternalApi billingInternalApi;
+    protected BlockingInternalApi blockingInternalApi;
     @Inject
-    protected BlockingCalculator blockingCalculator;
+    protected EntitlementApi entitlementApi;
     @Inject
-    protected BlockingInternalApi blockingInternalApi;
+    protected BillingInternalApi billingInternalApi;
     @Inject
     protected CatalogService catalogService;
     @Inject
@@ -51,24 +79,143 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
     @Inject
     protected PersistentBus bus;
     @Inject
-    protected TagDao tagDao;
+    protected TestApiListener testListener;
+    @Inject
+    protected TestListenerStatus testListenerStatus;
+    @Inject
+    protected BusService busService;
     @Inject
-    protected TagInternalApi tagInternalApi;
+    protected SubscriptionBaseService subscriptionBaseService;
+    @Inject
+    protected EntitlementService entitlementService;
+
+    protected Catalog catalog;
+
+    private void loadSystemPropertiesFromClasspath(final String resource) {
+        final URL url = JunctionTestSuiteWithEmbeddedDB.class.getResource(resource);
+        Assert.assertNotNull(url);
+
+        configSource.merge(url);
+    }
 
     @BeforeClass(groups = "slow")
     protected void beforeClass() throws Exception {
-        final Injector injector = Guice.createInjector(new TestJunctionModuleWithEmbeddedDB(configSource));
+        loadSystemPropertiesFromClasspath("/junction.properties");
+        final Injector injector = Guice.createInjector(Stage.PRODUCTION, new TestJunctionModuleWithEmbeddedDB(configSource));
         injector.injectMembers(this);
     }
 
     @BeforeMethod(groups = "slow")
     public void beforeMethod() throws Exception {
         super.beforeMethod();
-        bus.start();
+        startTestFamework();
+        this.catalog = initCatalog(catalogService);
+
+        // Make sure we start with a clean state
+        assertListenerStatus();
     }
 
     @AfterMethod(groups = "slow")
-    public void afterMethod() {
-        bus.stop();
+    public void afterMethod() throws Exception {
+        // Make sure we finish in a clean state
+        assertListenerStatus();
+
+        stopTestFramework();
+    }
+
+    private Catalog initCatalog(final CatalogService catalogService) throws Exception {
+        ((DefaultCatalogService) catalogService).loadCatalog();
+        final Catalog catalog = catalogService.getFullCatalog();
+        assertNotNull(catalog);
+        return catalog;
+    }
+
+    private void startTestFamework() throws Exception {
+        log.debug("STARTING TEST FRAMEWORK");
+
+        resetTestListener(testListener, testListenerStatus);
+
+        resetClockToStartOfTest(clock);
+
+        startBusAndRegisterListener(busService, testListener);
+
+        restartSubscriptionService(subscriptionBaseService);
+        restartEntitlementService(entitlementService);
+
+        log.debug("STARTED TEST FRAMEWORK");
+    }
+
+    private void stopTestFramework() throws Exception {
+        log.debug("STOPPING TEST FRAMEWORK");
+        stopBusAndUnregisterListener(busService, testListener);
+        stopSubscriptionService(subscriptionBaseService);
+        stopEntitlementService(entitlementService);
+        log.debug("STOPPED TEST FRAMEWORK");
+    }
+
+    private void resetTestListener(final TestApiListener testListener, final TestListenerStatus testListenerStatus) {
+        // RESET LIST OF EXPECTED EVENTS
+        if (testListener != null) {
+            testListener.reset();
+            testListenerStatus.resetTestListenerStatus();
+        }
+    }
+
+    private void resetClockToStartOfTest(final ClockMock clock) {
+        clock.resetDeltaFromReality();
+
+        // Date at which all tests start-- we create the date object here after the system properties which set the JVM in UTC have been set.
+        final DateTime testStartDate = new DateTime(2012, 5, 7, 0, 3, 42, 0);
+        clock.setDeltaFromReality(testStartDate.getMillis() - clock.getUTCNow().getMillis());
+    }
+
+    private void startBusAndRegisterListener(final BusService busService, final TestApiListener testListener) throws Exception {
+        busService.getBus().start();
+        busService.getBus().register(testListener);
+    }
+
+    private void restartSubscriptionService(final SubscriptionBaseService subscriptionBaseService) {
+        // START NOTIFICATION QUEUE FOR SUBSCRIPTION
+        ((DefaultSubscriptionBaseService) subscriptionBaseService).initialize();
+        ((DefaultSubscriptionBaseService) subscriptionBaseService).start();
+    }
+
+    private void restartEntitlementService(final EntitlementService entitlementService) {
+        // START NOTIFICATION QUEUE FOR ENTITLEMENT
+        ((DefaultEntitlementService) entitlementService).initialize();
+        ((DefaultEntitlementService) entitlementService).start();
+    }
+
+    private void stopBusAndUnregisterListener(final BusService busService, final TestApiListener testListener) throws Exception {
+        busService.getBus().unregister(testListener);
+        busService.getBus().stop();
+    }
+
+    private void stopSubscriptionService(final SubscriptionBaseService subscriptionBaseService) throws Exception {
+        ((DefaultSubscriptionBaseService) subscriptionBaseService).stop();
+    }
+
+    private void stopEntitlementService(final EntitlementService entitlementService) throws Exception {
+        ((DefaultEntitlementService) entitlementService).stop();
+    }
+
+    protected AccountData getAccountData(final int billingDay) {
+        return new MockAccountBuilder().name(UUID.randomUUID().toString().substring(1, 8))
+                                       .firstNameLength(6)
+                                       .email(UUID.randomUUID().toString().substring(1, 8))
+                                       .phone(UUID.randomUUID().toString().substring(1, 8))
+                                       .migrated(false)
+                                       .isNotifiedForInvoices(false)
+                                       .externalKey(UUID.randomUUID().toString().substring(1, 8))
+                                       .billingCycleDayLocal(billingDay)
+                                       .currency(Currency.USD)
+                                       .paymentMethodId(UUID.randomUUID())
+                                       .timeZone(DateTimeZone.UTC)
+                                       .build();
+    }
+
+    protected void assertListenerStatus() {
+        assertTrue(testListener.isCompleted(DELAY));
+        ((JunctionTestListenerStatus) testListenerStatus).assertListenerStatus();
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
index cd4ac59..c2354e9 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBillingApi.java
@@ -181,11 +181,9 @@ public class TestBillingApi extends JunctionTestSuiteNoDB {
 
         final Account account = createAccount(32);
 
-        final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
-        blockingStates.add(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE,  DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
-        blockingStates.add(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+        blockingStateDao.setBlockingState(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE,  DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)), clock, internalCallContext);
+        blockingStateDao.setBlockingState(new DefaultBlockingState(bunId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)), clock, internalCallContext);
 
-        ((MockBlockingStateDao) blockingStateDao).setBlockingStates(bunId, blockingStates);
         final SortedSet<BillingEvent> events = billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext);
 
         Assert.assertEquals(events.size(), 3);
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
index 409ce5f..dbf0294 100644
--- a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -16,6 +16,22 @@
 
 package com.ning.billing.junction.plumbing.billing;
 
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.mockito.Mockito;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
 import com.ning.billing.account.api.Account;
 import com.ning.billing.catalog.MockPlan;
 import com.ning.billing.catalog.MockPlanPhase;
@@ -23,31 +39,16 @@ import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.dao.MockBlockingStateDao;
-import com.ning.billing.junction.JunctionTestSuiteNoDB;
-import com.ning.billing.entitlement.api.BlockingState;
-import com.ning.billing.junction.plumbing.billing.BlockingCalculator.DisabledDuration;
-import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
-import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.junction.BillingEvent;
 import com.ning.billing.junction.BillingModeType;
 import com.ning.billing.junction.DefaultBlockingState;
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.LocalDate;
-import org.mockito.Mockito;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.UUID;
+import com.ning.billing.junction.JunctionTestSuiteNoDB;
+import com.ning.billing.junction.plumbing.billing.BlockingCalculator.DisabledDuration;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -114,9 +115,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
         final List<BlockingState> blockingStates = new ArrayList<BlockingState>();
         blockingStates.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now));
-        blockingStates.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE,  "test", false, false, false, now.plusDays(2)));
+        blockingStates.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
 
-        setBlockingStates(bundleId1, blockingStates);
+        setBlockingStates(blockingStates);
 
         blockingCalculator.insertBlockingEvents(billingEvents, internalCallContext);
 
@@ -516,7 +517,6 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertNotNull(minus5andAHalf);
         assertEquals(minus5andAHalf.getEffectiveDate(), now.minusDays(6));
 
-
     }
 
     protected BillingEvent createRealEvent(final DateTime effectiveDate, final SubscriptionBase subscription) {
@@ -538,9 +538,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         final DateTimeZone tz = DateTimeZone.UTC;
 
         return new DefaultBillingEvent(account, subscription, effectiveDate, plan, planPhase,
-                fixedPrice, recurringPrice, currency,
-                billingPeriod, billCycleDay, billingModeType,
-                description, totalOrdering, type, tz);
+                                       fixedPrice, recurringPrice, currency,
+                                       billingPeriod, billCycleDay, billingModeType,
+                                       description, totalOrdering, type, tz);
     }
 
     @Test(groups = "fast")
@@ -610,7 +610,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
         public MockBillingEvent() {
             super(account, subscription1, clock.getUTCNow(), null, null, BigDecimal.ZERO, BigDecimal.TEN, Currency.USD, BillingPeriod.ANNUAL,
-                    4, BillingModeType.IN_ADVANCE, "", 3L, SubscriptionBaseTransitionType.CREATE, DateTimeZone.UTC);
+                  4, BillingModeType.IN_ADVANCE, "", 3L, SubscriptionBaseTransitionType.CREATE, DateTimeZone.UTC);
         }
     }
 
@@ -642,9 +642,10 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
     public void testCreateDisablePairs() {
         List<BlockingState> blockingEvents;
         final UUID ovdId = UUID.randomUUID();
+        final UUID ovdId2 = UUID.randomUUID();
         final DateTime now = clock.getUTCNow();
 
-        //simple events open clear -> disabled
+        // Simple events open clear -> disabled
         blockingEvents = new ArrayList<BlockingState>();
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -655,7 +656,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertEquals(pairs.get(0).getStart(), now.plusDays(1));
         assertNull(pairs.get(0).getEnd());
 
-        //simple events closed clear -> disabled
+        // Simple events closed clear -> disabled
         blockingEvents = new ArrayList<BlockingState>();
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -668,7 +669,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertNotNull(pairs.get(0).getEnd());
         assertEquals(pairs.get(0).getEnd(), now.plusDays(2));
 
-        //simple BUNDLE events closed clear -> disabled
+        // Simple BUNDLE events closed clear -> disabled
         blockingEvents = new ArrayList<BlockingState>();
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -681,7 +682,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertNotNull(pairs.get(0).getEnd());
         assertEquals(pairs.get(0).getEnd(), now.plusDays(2));
 
-        //two or more disableds in a row
+        // Two or more disabled in a row
         blockingEvents = new ArrayList<BlockingState>();
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
@@ -708,6 +709,30 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertEquals(pairs.get(0).getStart(), now.plusDays(1));
         assertNotNull(pairs.get(0).getEnd());
         assertEquals(pairs.get(0).getEnd(), now.plusDays(4));
+
+        // Verify ordering at the same effective date doesn't matter. This is to work around nondeterministic ordering
+        // behavior in ProxyBlockingStateDao#BLOCKING_STATE_ORDERING. See also TestDefaultInternalBillingApi.
+        blockingEvents = new ArrayList<BlockingState>();
+        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
+        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+        blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(2)));
+        blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(3)));
+
+        pairs = blockingCalculator.createBlockingDurations(blockingEvents);
+        assertEquals(pairs.size(), 1);
+        assertEquals(pairs.get(0).getStart(), now.plusDays(1));
+        assertEquals(pairs.get(0).getEnd(), now.plusDays(3));
+
+        blockingEvents = new ArrayList<BlockingState>();
+        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(1)));
+        blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, now.plusDays(2)));
+        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(2)));
+        blockingEvents.add(new DefaultBlockingState(ovdId2, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, now.plusDays(3)));
+
+        pairs = blockingCalculator.createBlockingDurations(blockingEvents);
+        assertEquals(pairs.size(), 1);
+        assertEquals(pairs.get(0).getStart(), now.plusDays(1));
+        assertEquals(pairs.get(0).getEnd(), now.plusDays(3));
     }
 
     @Test(groups = "fast")
@@ -728,7 +753,7 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
         blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
 
-        setBlockingStates(bundleId1, blockingEvents);
+        setBlockingStates(blockingEvents);
 
         blockingCalculator.insertBlockingEvents(billingEvents, internalCallContext);
 
@@ -746,7 +771,9 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         assertEquals(events.get(4).getTransitionType(), SubscriptionBaseTransitionType.CHANGE);
     }
 
-    private void setBlockingStates(final UUID blockedId, final List<BlockingState> blockingStates) {
-        ((MockBlockingStateDao) blockingStateDao).setBlockingStates(blockedId, blockingStates);
+    private void setBlockingStates(final List<BlockingState> blockingStates) {
+        for (final BlockingState blockingState : blockingStates) {
+            blockingStateDao.setBlockingState(blockingState, clock, internalCallContext);
+        }
     }
 }
diff --git a/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
new file mode 100644
index 0000000..99ed652
--- /dev/null
+++ b/junction/src/test/java/com/ning/billing/junction/plumbing/billing/TestDefaultInternalBillingApi.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.junction.plumbing.billing;
+
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.api.BlockingStateType;
+import com.ning.billing.entitlement.api.DefaultEntitlementApi;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.junction.BillingEvent;
+import com.ning.billing.junction.DefaultBlockingState;
+import com.ning.billing.junction.JunctionTestSuiteWithEmbeddedDB;
+import com.ning.billing.subscription.api.SubscriptionBase;
+import com.ning.billing.subscription.api.SubscriptionBaseTransitionType;
+
+import com.google.common.collect.ImmutableList;
+
+public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbeddedDB {
+
+    // See https://github.com/killbill/killbill/issues/123
+    //
+    // Pierre, why do we have invocationCount > 0 here?
+    //
+    // This test will exercise ProxyBlockingStateDao#BLOCKING_STATE_ORDERING - unfortunately, for some reason,
+    // the ordering doesn't seem deterministic. In some scenarii,
+    // BlockingState(idA, effectiveDate1, BLOCK), BlockingState(idA, effectiveDate2, CLEAR), BlockingState(idB, effectiveDate2, BLOCK), BlockingState(idB, effectiveDate3, CLEAR)
+    // is ordered
+    // BlockingState(idA, effectiveDate1, BLOCK), BlockingState(idB, effectiveDate2, BLOCK), BlockingState(idA, effectiveDate2, CLEAR), BlockingState(idB, effectiveDate3, CLEAR)
+    // The code BlockingCalculator#createBlockingDurations has been updated to support it, but we still want to make sure it actually works in both scenarii
+    // (invocationCount = 10 will trigger both usecases in my testing).
+    @Test(groups = "slow", description = "Check blocking states with same effective date are correctly handled", invocationCount = 10)
+    public void testBlockingStatesWithSameEffectiveDate() throws Exception {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.MONTHLY, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+        final Entitlement entitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), initialDate, callContext);
+        final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(entitlement.getId(), internalCallContext);
+        assertListenerStatus();
+
+        final DateTime block1Date = clock.getUTCNow();
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+        final DefaultBlockingState state1 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block1Date);
+        blockingInternalApi.setBlockingState(state1, internalCallContext);
+        // Same date, we'll order by record id asc
+        final DefaultBlockingState state2 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block1Date);
+        blockingInternalApi.setBlockingState(state2, internalCallContext);
+        assertListenerStatus();
+
+        clock.addDays(5);
+
+        final DateTime block2Date = clock.getUTCNow();
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+        final DefaultBlockingState state3 = new DefaultBlockingState(entitlement.getBundleId(),
+                                                                     BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block2Date);
+        blockingInternalApi.setBlockingState(state3, internalCallContext);
+        // Same date, we'll order by record id asc
+        final DefaultBlockingState state4 = new DefaultBlockingState(entitlement.getBundleId(),
+                                                                     BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR,
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block2Date);
+        blockingInternalApi.setBlockingState(state4, internalCallContext);
+        assertListenerStatus();
+
+        final DateTime block3Date = block2Date.plusDays(3);
+
+        // Pass the phase
+        testListener.pushExpectedEvent(NextEvent.PHASE);
+        clock.addDays(50);
+        assertListenerStatus();
+
+        final DateTime block4Date = clock.getUTCNow();
+        final DateTime block5Date = block4Date.plusDays(3);
+        // Only one event on the bus (for state5)
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        // Insert the clear state first, to make sure the order in which we insert blocking states doesn't matter
+        // Since we are already in an ENT_STATE_CLEAR state for service ENTITLEMENT_SERVICE_NAME, we need to use a different
+        // state name to simulate this behavior (otherwise, by design, this event won't be created)
+        final DefaultBlockingState state6 = new DefaultBlockingState(entitlement.getBundleId(),
+                                                                     BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR + "-something",
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block5Date);
+        blockingInternalApi.setBlockingState(state6, internalCallContext);
+        final DefaultBlockingState state5 = new DefaultBlockingState(entitlement.getBundleId(),
+                                                                     BlockingStateType.SUBSCRIPTION_BUNDLE,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something",
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block4Date);
+        blockingInternalApi.setBlockingState(state5, internalCallContext);
+        assertListenerStatus();
+
+        // Now, add back blocking states at an earlier date, for a different blockable id, to make sure the effective
+        // date ordering is correctly respected when computing blocking durations
+        testListener.pushExpectedEvents(NextEvent.BLOCK, NextEvent.BLOCK);
+        final DefaultBlockingState state7 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_BLOCKED + "-something2",
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     true,
+                                                                     true,
+                                                                     true,
+                                                                     block3Date);
+        blockingInternalApi.setBlockingState(state7, internalCallContext);
+        final DefaultBlockingState state8 = new DefaultBlockingState(account.getId(),
+                                                                     BlockingStateType.ACCOUNT,
+                                                                     DefaultEntitlementApi.ENT_STATE_CLEAR + "-something2",
+                                                                     EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                     false,
+                                                                     false,
+                                                                     false,
+                                                                     block4Date);
+        blockingInternalApi.setBlockingState(state8, internalCallContext);
+        assertListenerStatus();
+
+        // Advance for state6 to be active
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        clock.addDays(5);
+        assertListenerStatus();
+
+        // Expected blocking durations:
+        // * 2013-08-07 to 2013-08-07 (block1Date)
+        // * 2013-08-12 to 2013-08-12 (block2Date)
+        // * 2013-08-15 to 2013-10-04 [2013-08-15 to 2013-10-01 (block3Date -> block4Date) and 2013-10-01 to 2013-10-04 (block4Date -> block5Date)]
+        final List<BillingEvent> events = ImmutableList.<BillingEvent>copyOf(billingInternalApi.getBillingEventsForAccountAndUpdateAccountBCD(account.getId(), internalCallContext));
+        Assert.assertEquals(events.size(), 7);
+        Assert.assertEquals(events.get(0).getTransitionType(), SubscriptionBaseTransitionType.CREATE);
+        Assert.assertEquals(events.get(0).getEffectiveDate(), subscription.getStartDate());
+        Assert.assertEquals(events.get(1).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+        Assert.assertEquals(events.get(1).getEffectiveDate(), block1Date);
+        Assert.assertEquals(events.get(2).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
+        Assert.assertEquals(events.get(2).getEffectiveDate(), block1Date);
+        Assert.assertEquals(events.get(3).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+        Assert.assertEquals(events.get(3).getEffectiveDate(), block2Date);
+        Assert.assertEquals(events.get(4).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
+        Assert.assertEquals(events.get(4).getEffectiveDate(), block2Date);
+        Assert.assertEquals(events.get(5).getTransitionType(), SubscriptionBaseTransitionType.START_BILLING_DISABLED);
+        Assert.assertEquals(events.get(5).getEffectiveDate(), block3Date);
+        Assert.assertEquals(events.get(6).getTransitionType(), SubscriptionBaseTransitionType.END_BILLING_DISABLED);
+        Assert.assertEquals(events.get(6).getEffectiveDate(), block5Date);
+    }
+}
diff --git a/junction/src/test/resources/catalog.xml b/junction/src/test/resources/catalog.xml
new file mode 100644
index 0000000..973afd0
--- /dev/null
+++ b/junction/src/test/resources/catalog.xml
@@ -0,0 +1,828 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- ~ Copyright 2010-2011 Ning, Inc. ~ ~ Ning 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. -->
+
+<!-- Use cases covered so far: Tiered Product (Pistol/Shotgun/Assault-Rifle) 
+	Multiple changeEvent plan policies Multiple PlanAlignment (see below, trial 
+	add-on alignments and rescue discount package) Product transition rules Add 
+	on (Scopes, Hoster) Multi-pack addon (Extra-Ammo) Addon Trial aligned to 
+	base plan (holster-monthly-regular) Addon Trial aligned to creation (holster-monthly-special) 
+	Rescue discount package (assault-rifle-annual-rescue) Plan phase with a reccurring 
+	and a one off (refurbish-maintenance) Phan with more than 2 phase (gunclub 
+	discount plans) Use Cases to do: Tiered Add On Riskfree period -->
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+	<effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
+	<catalogName>Firearms</catalogName>
+
+	<currencies>
+		<currency>USD</currency>
+		<currency>EUR</currency>
+		<currency>GBP</currency>
+	</currencies>
+
+	<products>
+		<product name="Pistol">
+			<category>BASE</category>
+		</product>
+		<product name="Shotgun">
+			<category>BASE</category>
+			<available>
+				<addonProduct>Telescopic-Scope</addonProduct>
+				<addonProduct>Laser-Scope</addonProduct>
+			</available>
+		</product>
+		<product name="Assault-Rifle">
+			<category>BASE</category>
+			<included>
+				<addonProduct>Telescopic-Scope</addonProduct>
+			</included>
+			<available>
+				<addonProduct>Laser-Scope</addonProduct>
+			</available>
+		</product>
+		<product name="Telescopic-Scope">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Laser-Scope">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Holster">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Extra-Ammo">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Refurbish-Maintenance">
+			<category>ADD_ON</category>
+		</product>
+	</products>
+
+	<rules>
+		<changePolicy>
+			<changePolicyCase>
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+                       <changePolicyCase>
+                                <toPriceList>rescue</toPriceList>
+                                <policy>END_OF_TERM</policy>
+                        </changePolicyCase>
+			<changePolicyCase>
+				<toProduct>Assault-Rifle</toProduct>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase>
+				<fromProduct>Pistol</fromProduct>
+				<toProduct>Shotgun</toProduct>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase>
+				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
+				<toBillingPeriod>ANNUAL</toBillingPeriod>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase>
+				<fromBillingPeriod>ANNUAL</fromBillingPeriod>
+				<toBillingPeriod>MONTHLY</toBillingPeriod>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+		</changePolicy>
+		<changeAlignment>
+			<changeAlignmentCase>
+				<toPriceList>rescue</toPriceList>
+				<alignment>CHANGE_OF_PLAN</alignment>
+			</changeAlignmentCase>
+			<changeAlignmentCase>
+				<fromPriceList>rescue</fromPriceList>
+				<toPriceList>rescue</toPriceList>
+				<alignment>CHANGE_OF_PRICELIST</alignment>
+			</changeAlignmentCase>
+			<changeAlignmentCase>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</changeAlignmentCase>
+		</changeAlignment>
+		<cancelPolicy>
+			<cancelPolicyCase>
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</cancelPolicyCase>
+			<cancelPolicyCase>
+				<policy>END_OF_TERM</policy>
+			</cancelPolicyCase>
+		</cancelPolicy>
+		<createAlignment>
+			<createAlignmentCase>
+				<product>Laser-Scope</product>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</createAlignmentCase>
+			<createAlignmentCase>
+				<alignment>START_OF_BUNDLE</alignment>
+			</createAlignmentCase>
+		</createAlignment>
+		<billingAlignment>
+			<billingAlignmentCase>
+				<productCategory>ADD_ON</productCategory>
+				<alignment>BUNDLE</alignment>
+			</billingAlignmentCase>
+			<billingAlignmentCase>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<alignment>SUBSCRIPTION</alignment>
+			</billingAlignmentCase>
+			<billingAlignmentCase>
+				<alignment>ACCOUNT</alignment>
+			</billingAlignmentCase>
+		</billingAlignment>
+		<priceList>
+			<priceListCase>
+				<fromPriceList>rescue</fromPriceList>
+				<toPriceList>DEFAULT</toPriceList>
+			</priceListCase>
+		</priceList>
+	</rules>
+
+	<plans>
+		<plan name="pistol-monthly">
+			<product>Pistol</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice> <!-- empty price implies $0 -->
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>GBP</currency>
+						<value>29.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>29.95</value>
+					</price>
+					<price>
+						<currency>USD</currency>
+						<value>29.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="shotgun-monthly">
+			<product>Shotgun</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice></fixedPrice>
+					<!-- no price implies $0 -->
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+					<number>-1</number>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>249.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>149.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>169.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-monthly">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>599.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>349.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>399.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="pistol-annual">
+			<product>Pistol</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="shotgun-annual">
+			<product>Shotgun</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>2399.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>1499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>1699.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-annual">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>5999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>3499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>3999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="pistol-annual-gunclub-discount">
+			<product>Pistol</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>6</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>9.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>9.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>9.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="shotgun-annual-gunclub-discount">
+			<product>Shotgun</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>6</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>19.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>49.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>69.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>2399.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>1499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>1699.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-annual-gunclub-discount">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>6</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>99.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>99.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>99.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>5999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>3499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>3999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="laser-scope-monthly">
+			<product>Laser-Scope</product>
+			<initialPhases>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>1</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>999.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>499.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>999.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>1999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>1499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>1999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="telescopic-scope-monthly">
+			<product>Telescopic-Scope</product>
+			<initialPhases>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>1</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>399.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>299.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>399.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="extra-ammo-monthly">
+			<product>Extra-Ammo</product>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+			<plansAllowedInBundle>-1</plansAllowedInBundle> <!-- arbitrary number of these (multipack) -->
+		</plan>
+		<plan name="holster-monthly-regular">
+			<product>Holster</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="holster-monthly-special">
+			<product>Holster</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-annual-rescue">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>YEARS</unit>
+						<number>1</number>
+					</duration>
+					<billingPeriod>ANNUAL</billingPeriod>
+					<recurringPrice>
+						<price>
+							<currency>USD</currency>
+							<value>5999.95</value>
+						</price>
+						<price>
+							<currency>EUR</currency>
+							<value>3499.95</value>
+						</price>
+						<price>
+							<currency>GBP</currency>
+							<value>3999.95</value>
+						</price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>5999.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>3499.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>3999.95</value>
+					</price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="refurbish-maintenance">
+			<product>Refurbish-Maintenance</product>
+			<finalPhase type="FIXEDTERM">
+				<duration>
+					<unit>MONTHS</unit>
+					<number>12</number>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price>
+						<currency>USD</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>199.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>199.95</value>
+					</price>
+				</recurringPrice>
+				<fixedPrice>
+					<price>
+						<currency>USD</currency>
+						<value>599.95</value>
+					</price>
+					<price>
+						<currency>EUR</currency>
+						<value>599.95</value>
+					</price>
+					<price>
+						<currency>GBP</currency>
+						<value>599.95</value>
+					</price>
+				</fixedPrice>
+			</finalPhase>
+		</plan>
+	</plans>
+	<priceLists>
+		<defaultPriceList name="DEFAULT">
+			<plans>
+				<plan>pistol-monthly</plan>
+				<plan>shotgun-monthly</plan>
+				<plan>assault-rifle-monthly</plan>
+				<plan>pistol-annual</plan>
+				<plan>shotgun-annual</plan>
+				<plan>assault-rifle-annual</plan>
+				<plan>laser-scope-monthly</plan>
+				<plan>telescopic-scope-monthly</plan>
+				<plan>extra-ammo-monthly</plan>
+				<plan>holster-monthly-regular</plan>
+				<plan>refurbish-maintenance</plan>
+			</plans>
+		</defaultPriceList>
+		<childPriceList name="gunclubDiscount">
+			<plans>
+				<plan>pistol-monthly</plan>
+				<plan>shotgun-monthly</plan>
+				<plan>assault-rifle-monthly</plan>
+				<plan>pistol-annual-gunclub-discount</plan>
+				<plan>shotgun-annual-gunclub-discount</plan>
+				<plan>assault-rifle-annual-gunclub-discount</plan>
+				<plan>holster-monthly-special</plan>
+			</plans>
+		</childPriceList>
+		<childPriceList name="rescue">
+			<plans>
+				<plan>assault-rifle-annual-rescue</plan>
+			</plans>
+		</childPriceList>
+	</priceLists>
+
+</catalog>
diff --git a/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java b/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java
index 12625ab..1d4bf63 100644
--- a/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java
+++ b/overdue/src/test/java/com/ning/billing/overdue/glue/ApplicatorMockJunctionModule.java
@@ -54,7 +54,7 @@ public class ApplicatorMockJunctionModule extends AbstractModule {
         }
 
         @Override
-        public List<BlockingState> getBlockingAll(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
+        public List<BlockingState> getBlockingAllForAccount(final InternalTenantContext context) {
             throw new UnsupportedOperationException();
         }