killbill-memoizeit

Changes

account/pom.xml 2(+1 -1)

api/pom.xml 2(+1 -1)

beatrix/pom.xml 2(+1 -1)

catalog/pom.xml 2(+1 -1)

currency/pom.xml 2(+1 -1)

invoice/pom.xml 2(+1 -1)

jaxrs/pom.xml 2(+1 -1)

junction/pom.xml 2(+1 -1)

osgi/pom.xml 2(+1 -1)

overdue/pom.xml 2(+1 -1)

payment/pom.xml 2(+1 -1)

pom.xml 2(+1 -1)

server/pom.xml 2(+1 -1)

tenant/pom.xml 2(+1 -1)

usage/pom.xml 2(+1 -1)

util/pom.xml 2(+1 -1)

Details

account/pom.xml 2(+1 -1)

diff --git a/account/pom.xml b/account/pom.xml
index a66537e..f63d185 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>

api/pom.xml 2(+1 -1)

diff --git a/api/pom.xml b/api/pom.xml
index fed08a0..31ad402 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-internal-api</artifactId>
diff --git a/api/src/main/java/com/ning/billing/entitlement/AccountEntitlements.java b/api/src/main/java/com/ning/billing/entitlement/AccountEntitlements.java
new file mode 100644
index 0000000..3a192e9
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/AccountEntitlements.java
@@ -0,0 +1,37 @@
+/*
+ * 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.entitlement;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+// Wrapper object to save on DAO calls
+public interface AccountEntitlements {
+
+    public Account getAccount();
+
+    // Map bundle id -> bundle
+    public Map<UUID, SubscriptionBaseBundle> getBundles();
+
+    // Map bundle id -> entitlements
+    public Map<UUID, Collection<Entitlement>> getEntitlements();
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/AccountEventsStreams.java b/api/src/main/java/com/ning/billing/entitlement/AccountEventsStreams.java
new file mode 100644
index 0000000..94e7817
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/AccountEventsStreams.java
@@ -0,0 +1,36 @@
+/*
+ * 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.entitlement;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+// Wrapper object to save on DAO calls
+public interface AccountEventsStreams {
+
+    public Account getAccount();
+
+    // Map bundle id -> bundle
+    public Map<UUID, SubscriptionBaseBundle> getBundles();
+
+    // Map bundle id -> events streams
+    public Map<UUID, Collection<EventsStream>> getEventsStreams();
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/EntitlementInternalApi.java b/api/src/main/java/com/ning/billing/entitlement/EntitlementInternalApi.java
new file mode 100644
index 0000000..44953ea
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/EntitlementInternalApi.java
@@ -0,0 +1,27 @@
+/*
+ * 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.entitlement;
+
+import java.util.UUID;
+
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.util.callcontext.TenantContext;
+
+public interface EntitlementInternalApi {
+
+    public AccountEntitlements getAllEntitlementsForAccountId(UUID accountId, TenantContext context) throws EntitlementApiException;
+}
diff --git a/api/src/main/java/com/ning/billing/entitlement/EventsStream.java b/api/src/main/java/com/ning/billing/entitlement/EventsStream.java
new file mode 100644
index 0000000..fc57b74
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/EventsStream.java
@@ -0,0 +1,74 @@
+/*
+ * 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.entitlement;
+
+import java.util.Collection;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.entitlement.api.BlockingState;
+import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
+import com.ning.billing.subscription.api.SubscriptionBase;
+
+public interface EventsStream {
+
+    UUID getAccountId();
+
+    DateTimeZone getAccountTimeZone();
+
+    UUID getBundleId();
+
+    String getBundleExternalKey();
+
+    UUID getEntitlementId();
+
+    EntitlementState getEntitlementState();
+
+    LocalDate getEntitlementEffectiveEndDate();
+
+    SubscriptionBase getSubscription();
+
+    SubscriptionBase getBaseSubscription();
+
+    boolean isEntitlementActive();
+
+    boolean isBlockChange();
+
+    boolean isEntitlementCancelled();
+
+    boolean isSubscriptionCancelled();
+
+    Collection<BlockingState> getCurrentSubscriptionEntitlementBlockingStatesForServices();
+
+    Collection<BlockingState> getPendingEntitlementCancellationEvents();
+
+    Collection<BlockingState> getSubscriptionEntitlementStates();
+
+    Collection<BlockingState> getBundleEntitlementStates();
+
+    Collection<BlockingState> getAccountEntitlementStates();
+
+    Collection<BlockingState> computeAddonsBlockingStatesForNextSubscriptionBaseEvent(DateTime effectiveDate);
+
+    Collection<BlockingState> computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
+
+    InternalTenantContext getInternalTenantContext();
+}
diff --git a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
index 456fb17..bd21d81 100644
--- a/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
+++ b/api/src/main/java/com/ning/billing/glue/EntitlementModule.java
@@ -17,12 +17,15 @@
 package com.ning.billing.glue;
 
 public interface EntitlementModule {
+
     public void installBlockingStateDao();
 
     public void installBlockingApi();
 
     public void installEntitlementApi();
 
+    public void installEntitlementInternalApi();
+
     public void installSubscriptionApi();
 
     public void installBlockingChecker();
diff --git a/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java b/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java
index a400b68..65185eb 100644
--- a/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java
+++ b/api/src/main/java/com/ning/billing/glue/SubscriptionModule.java
@@ -27,5 +27,4 @@ public interface SubscriptionModule {
     public void installSubscriptionInternalApi();
 
     public void installSubscriptionTimelineApi();
-
 }

beatrix/pom.xml 2(+1 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index caa12bb..5518181 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>

catalog/pom.xml 2(+1 -1)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 011fe1d..fd8864e 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>

currency/pom.xml 2(+1 -1)

diff --git a/currency/pom.xml b/currency/pom.xml
index e3e6379..003458b 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
 
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 1540e00..7e53f2f 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
index 983381d..ac479f7 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlement.java
@@ -21,10 +21,10 @@ import java.util.Collection;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.joda.time.LocalDate;
 
 import com.ning.billing.ErrorCode;
-import com.ning.billing.account.api.Account;
 import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.catalog.api.BillingActionPolicy;
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -36,12 +36,12 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.clock.Clock;
 import com.ning.billing.entitlement.DefaultEntitlementService;
 import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.dao.BlockingStateDao;
 import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
 import com.ning.billing.entitlement.engine.core.EntitlementNotificationKeyAction;
 import com.ning.billing.entitlement.engine.core.EntitlementUtils;
-import com.ning.billing.entitlement.engine.core.EventsStream;
 import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
 import com.ning.billing.entity.EntityBase;
 import com.ning.billing.junction.DefaultBlockingState;
@@ -88,7 +88,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                               final SubscriptionBaseInternalApi subscriptionInternalApi, final BlockingChecker checker,
                               final NotificationQueueService notificationQueueService, final EntitlementUtils entitlementUtils,
                               final EntitlementDateHelper dateHelper, final Clock clock, final InternalCallContextFactory internalCallContextFactory) {
-        super(eventsStream.getSubscription().getId(), eventsStream.getSubscription().getCreatedDate(), eventsStream.getSubscription().getUpdatedDate());
+        super(eventsStream.getEntitlementId(), eventsStream.getSubscription().getCreatedDate(), eventsStream.getSubscription().getUpdatedDate());
         this.eventsStreamBuilder = eventsStreamBuilder;
         this.eventsStream = eventsStream;
         this.dateHelper = dateHelper;
@@ -116,18 +116,22 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
              in.getInternalCallContextFactory());
     }
 
-    public Account getAccount() {
-        return eventsStream.getAccount();
-    }
-
     public EventsStream getEventsStream() {
         return eventsStream;
     }
 
+    public DateTimeZone getAccountTimeZone() {
+        return eventsStream.getAccountTimeZone();
+    }
+
     public SubscriptionBase getSubscriptionBase() {
         return eventsStream.getSubscription();
     }
 
+    public SubscriptionBase getBaseSubscription() {
+        return eventsStream.getBaseSubscription();
+    }
+
     public EventsStreamBuilder getEventsStreamBuilder() {
         return eventsStreamBuilder;
     }
@@ -170,22 +174,22 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
 
     @Override
     public UUID getBaseEntitlementId() {
-        return eventsStream.getSubscription().getId();
+        return eventsStream.getEntitlementId();
     }
 
     @Override
     public UUID getBundleId() {
-        return eventsStream.getSubscription().getBundleId();
+        return eventsStream.getBundleId();
     }
 
     @Override
     public UUID getAccountId() {
-        return eventsStream.getAccount().getId();
+        return eventsStream.getAccountId();
     }
 
     @Override
     public String getExternalKey() {
-        return eventsStream.getBundle().getExternalKey();
+        return eventsStream.getBundleExternalKey();
     }
 
     @Override
@@ -200,7 +204,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
 
     @Override
     public LocalDate getEffectiveStartDate() {
-        return new LocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccount().getTimeZone());
+        return new LocalDate(getSubscriptionBase().getStartDate(), eventsStream.getAccountTimeZone());
     }
 
     @Override
@@ -319,7 +323,7 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         // (we don't want an entitlement cancel date one second or so after the subscription cancel date or add-ons cancellations
         // computations won't work).
         final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-        final LocalDate effectiveLocalDate = new LocalDate(localCancelDate, eventsStream.getAccount().getTimeZone());
+        final LocalDate effectiveLocalDate = new LocalDate(localCancelDate, eventsStream.getAccountTimeZone());
         final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(effectiveLocalDate, getSubscriptionBase().getStartDate(), contextWithValidAccountRecordId);
 
         try {
@@ -341,10 +345,10 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
         final LocalDate cancellationDate;
         switch (entitlementPolicy) {
             case IMMEDIATE:
-                cancellationDate = new LocalDate(clock.getUTCNow(), eventsStream.getAccount().getTimeZone());
+                cancellationDate = new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
                 break;
             case END_OF_TERM:
-                cancellationDate = getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccount().getTimeZone()) : new LocalDate(clock.getUTCNow(), eventsStream.getAccount().getTimeZone());
+                cancellationDate = getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), eventsStream.getAccountTimeZone()) : new LocalDate(clock.getUTCNow(), eventsStream.getAccountTimeZone());
                 break;
             default:
                 throw new RuntimeException("Unsupported policy " + entitlementPolicy);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
index 5be1c0e..dfd9cd6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultEntitlementApi.java
@@ -17,9 +17,7 @@
 package com.ning.billing.entitlement.api;
 
 import java.io.IOException;
-import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 
 import javax.inject.Inject;
@@ -41,15 +39,16 @@ import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.BillingActionPolicy;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.AccountEventsStreams;
 import com.ning.billing.entitlement.DefaultEntitlementService;
 import com.ning.billing.entitlement.EntitlementService;
 import com.ning.billing.entitlement.EntitlementTransitionType;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.dao.BlockingStateDao;
 import com.ning.billing.entitlement.engine.core.EntitlementNotificationKey;
 import com.ning.billing.entitlement.engine.core.EntitlementNotificationKeyAction;
 import com.ning.billing.entitlement.engine.core.EntitlementUtils;
-import com.ning.billing.entitlement.engine.core.EventsStream;
 import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
 import com.ning.billing.junction.DefaultBlockingState;
 import com.ning.billing.notificationq.api.NotificationEvent;
@@ -141,8 +140,8 @@ public class DefaultEntitlementApi implements EntitlementApi {
         }
 
         // Check the base entitlement state is not blocked
-        if (eventsStreamForBaseSubscription.getCurrentBlockingAggregator().isBlockChange()) {
-            throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getSubscription().getId().toString()));
+        if (eventsStreamForBaseSubscription.isBlockChange()) {
+            throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
         }
 
         final DateTime requestedDate = dateHelper.fromLocalDateAndReferenceTime(effectiveDate, eventsStreamForBaseSubscription.getSubscription().getStartDate(), eventsStreamForBaseSubscription.getInternalTenantContext());
@@ -184,8 +183,21 @@ public class DefaultEntitlementApi implements EntitlementApi {
 
     @Override
     public List<Entitlement> getAllEntitlementsForBundle(final UUID bundleId, final TenantContext tenantContext) throws EntitlementApiException {
-        final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(tenantContext);
-        return getAllEntitlementsForBundle(subscriptionInternalApi.getSubscriptionsForBundle(bundleId, context), tenantContext);
+        final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(tenantContext);
+        final UUID accountId;
+        try {
+            accountId = subscriptionInternalApi.getBundleFromId(bundleId, internalContext).getAccountId();
+        } catch (SubscriptionBaseApiException e) {
+            throw new EntitlementApiException(e);
+        }
+
+        return ImmutableList.<Entitlement>copyOf(Iterables.<Entitlement>filter(getAllEntitlementsForAccountId(accountId, tenantContext),
+                                                                               new Predicate<Entitlement>() {
+                                                                                   @Override
+                                                                                   public boolean apply(final Entitlement input) {
+                                                                                       return bundleId.equals(input.getBundleId());
+                                                                                   }
+                                                                               }));
     }
 
     @Override
@@ -202,29 +214,20 @@ public class DefaultEntitlementApi implements EntitlementApi {
 
     @Override
     public List<Entitlement> getAllEntitlementsForAccountId(final UUID accountId, final TenantContext tenantContext) throws EntitlementApiException {
+        final EntitlementApi entitlementApi = this;
         final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
-        final Map<UUID, List<SubscriptionBase>> subscriptionsPerBundle = subscriptionInternalApi.getSubscriptionsForAccount(context);
-
-        final List<Entitlement> result = new LinkedList<Entitlement>();
-        for (final UUID bundleId : subscriptionsPerBundle.keySet()) {
-            final List<Entitlement> entitlements = getAllEntitlementsForBundle(subscriptionsPerBundle.get(bundleId), tenantContext);
-            result.addAll(entitlements);
-        }
-        return result;
-    }
 
-    private List<Entitlement> getAllEntitlementsForBundle(final List<SubscriptionBase> subscriptions, final TenantContext context) {
-        return Lists.transform(subscriptions,
-                               new Function<SubscriptionBase, Entitlement>() {
-                                   @Override
-                                   public Entitlement apply(final SubscriptionBase input) {
-                                       try {
-                                           return getEntitlementForId(input.getId(), context);
-                                       } catch (EntitlementApiException e) {
-                                           throw new RuntimeException("Failed to extract blocking state for subscription " + input.getId().toString());
-                                       }
-                                   }
-                               });
+        final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(context);
+        final List<EventsStream> eventsStreams = ImmutableList.<EventsStream>copyOf(Iterables.<EventsStream>concat(accountEventsStreams.getEventsStreams().values()));
+        return Lists.<EventsStream, Entitlement>transform(eventsStreams,
+                                                          new Function<EventsStream, Entitlement>() {
+                                                              @Override
+                                                              public Entitlement apply(final EventsStream eventsStream) {
+                                                                  return new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi,
+                                                                                                blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+                                                                                                entitlementUtils, dateHelper, clock, internalCallContextFactory);
+                                                              }
+                                                          });
     }
 
     @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java
index 25842b7..3938659 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscription.java
@@ -16,29 +16,32 @@
 
 package com.ning.billing.entitlement.api;
 
-import java.util.List;
+import java.util.Collection;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
 public class DefaultSubscription extends DefaultEntitlement implements Subscription {
 
-    private final List<BlockingState> blockingStates;
+    private final Collection<BlockingState> currentSubscriptionBlockingStatesForServices;
 
-    DefaultSubscription(final DefaultEntitlement entitlement, final List<BlockingState> blockingStates) {
+    DefaultSubscription(final DefaultEntitlement entitlement) {
         super(entitlement);
-        this.blockingStates = blockingStates;
+        this.currentSubscriptionBlockingStatesForServices = eventsStream.getCurrentSubscriptionEntitlementBlockingStatesForServices();
     }
 
     @Override
     public LocalDate getBillingStartDate() {
-        return new LocalDate(getSubscriptionBase().getStartDate(), getAccount().getTimeZone());
+        return new LocalDate(getSubscriptionBase().getStartDate(), getAccountTimeZone());
     }
 
     @Override
     public LocalDate getBillingEndDate() {
         final DateTime futureOrCurrentEndDateForSubscription = getSubscriptionBase().getEndDate() != null ? getSubscriptionBase().getEndDate() : getSubscriptionBase().getFutureEndDate();
-        final DateTime futureOrCurrentEndDateForBaseSubscription = getEventsStream().getBaseSubscription().getEndDate() != null ? getEventsStream().getBaseSubscription().getEndDate() : getEventsStream().getBaseSubscription().getFutureEndDate();
+        final DateTime futureOrCurrentEndDateForBaseSubscription = getBaseSubscription().getEndDate() != null ? getBaseSubscription().getEndDate() : getBaseSubscription().getFutureEndDate();
 
         final DateTime futureOrCurrentEndDate;
         if (futureOrCurrentEndDateForBaseSubscription != null && futureOrCurrentEndDateForBaseSubscription.isBefore(futureOrCurrentEndDateForSubscription)) {
@@ -47,25 +50,27 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
             futureOrCurrentEndDate = futureOrCurrentEndDateForSubscription;
         }
 
-        return futureOrCurrentEndDate != null ? new LocalDate(futureOrCurrentEndDate, getAccount().getTimeZone()) : null;
+        return futureOrCurrentEndDate != null ? new LocalDate(futureOrCurrentEndDate, getAccountTimeZone()) : null;
     }
 
     @Override
     public LocalDate getChargedThroughDate() {
-        return getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), getAccount().getTimeZone()) : null;
+        return getSubscriptionBase().getChargedThroughDate() != null ? new LocalDate(getSubscriptionBase().getChargedThroughDate(), getAccountTimeZone()) : null;
     }
 
     @Override
     public String getCurrentStateForService(final String serviceName) {
-
-        if (blockingStates == null) {
+        if (currentSubscriptionBlockingStatesForServices == null) {
             return null;
+        } else {
+            final BlockingState blockingState = Iterables.<BlockingState>tryFind(currentSubscriptionBlockingStatesForServices,
+                                                                                 new Predicate<BlockingState>() {
+                                                                                     @Override
+                                                                                     public boolean apply(final BlockingState input) {
+                                                                                         return serviceName.equals(input.getService());
+                                                                                     }
+                                                                                 }).orNull();
+            return blockingState == null ? null : blockingState.getService();
         }
-        for (BlockingState cur : blockingStates) {
-            if (cur.getService().equals(serviceName)) {
-                return cur.getStateName();
-            }
-        }
-        return null;
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
index 850317f..d3b480e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionApi.java
@@ -17,11 +17,10 @@
 package com.ning.billing.entitlement.api;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.inject.Inject;
@@ -29,217 +28,184 @@ import javax.inject.Inject;
 import org.joda.time.DateTimeZone;
 
 import com.ning.billing.ErrorCode;
-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.ObjectType;
 import com.ning.billing.callcontext.InternalTenantContext;
-import com.ning.billing.clock.Clock;
-import com.ning.billing.entitlement.block.BlockingChecker;
-import com.ning.billing.entitlement.dao.BlockingStateDao;
+import com.ning.billing.entitlement.AccountEntitlements;
+import com.ning.billing.entitlement.EntitlementInternalApi;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+import com.ning.billing.util.cache.Cachable.CacheType;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.customfield.ShouldntHappenException;
+import com.ning.billing.util.dao.NonEntityDao;
 
-import com.google.common.base.Function;
+import com.google.common.base.Optional;
 import com.google.common.base.Predicate;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.ListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 public class DefaultSubscriptionApi implements SubscriptionApi {
 
+    private final EntitlementInternalApi entitlementInternalApi;
     private final SubscriptionBaseInternalApi subscriptionInternalApi;
-    private final EntitlementApi entitlementApi;
-    private final BlockingChecker checker;
-    private final BlockingStateDao blockingStateDao;
-    private final EntitlementDateHelper dateHelper;
-    private final Clock clock;
     private final InternalCallContextFactory internalCallContextFactory;
-    private final AccountInternalApi accountApi;
+    private final NonEntityDao nonEntityDao;
+    private final CacheControllerDispatcher cacheControllerDispatcher;
 
     @Inject
-    public DefaultSubscriptionApi(final SubscriptionBaseInternalApi subscriptionInternalApi, final EntitlementApi entitlementApi, final BlockingChecker checker, final BlockingStateDao blockingStateDao, final AccountInternalApi accountApi, final Clock clock, final InternalCallContextFactory internalCallContextFactory) {
+    public DefaultSubscriptionApi(final EntitlementInternalApi entitlementInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
+                                  final InternalCallContextFactory internalCallContextFactory, final NonEntityDao nonEntityDao, final CacheControllerDispatcher cacheControllerDispatcher) {
+        this.entitlementInternalApi = entitlementInternalApi;
         this.subscriptionInternalApi = subscriptionInternalApi;
-        this.entitlementApi = entitlementApi;
-        this.accountApi = accountApi;
-        this.checker = checker;
-        this.blockingStateDao = blockingStateDao;
-        this.dateHelper = new EntitlementDateHelper(accountApi, clock);
-        ;
-        this.clock = clock;
         this.internalCallContextFactory = internalCallContextFactory;
+        this.nonEntityDao = nonEntityDao;
+        this.cacheControllerDispatcher = cacheControllerDispatcher;
     }
 
     @Override
     public Subscription getSubscriptionForEntitlementId(final UUID entitlementId, final TenantContext context) throws SubscriptionApiException {
+        final Long accountRecordId = nonEntityDao.retrieveAccountRecordIdFromObject(entitlementId, ObjectType.SUBSCRIPTION, cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
+        final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT);
+
+        // Retrieve entitlements
+        final AccountEntitlements accountEntitlements;
         try {
-            final Entitlement entitlement = entitlementApi.getEntitlementForId(entitlementId, context);
-            final InternalTenantContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalTenantContext(entitlement.getAccountId(), context);
-            return fromEntitlement(entitlement, contextWithValidAccountRecordId);
+            accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, context);
         } catch (EntitlementApiException e) {
             throw new SubscriptionApiException(e);
         }
+
+        // Build subscriptions
+        final Iterable<Subscription> accountSubscriptions = Iterables.<Subscription>concat(buildSubscriptionsFromEntitlements(accountEntitlements).values());
+
+        return Iterables.<Subscription>find(accountSubscriptions,
+                                            new Predicate<Subscription>() {
+                                                @Override
+                                                public boolean apply(final Subscription subscription) {
+                                                    return subscription.getId().equals(entitlementId);
+                                                }
+                                            });
     }
 
     @Override
     public SubscriptionBundle getSubscriptionBundle(final UUID bundleId, final TenantContext context) throws SubscriptionApiException {
-
-        try {
-
-            final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForBundle(bundleId, context);
-            if (entitlements.isEmpty()) {
-                throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_ID, bundleId);
-            }
-            return getSubscriptionBundleFromEntitlements(bundleId, entitlements, context);
-        } catch (EntitlementApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (SubscriptionBaseApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
+        final Long accountRecordId = nonEntityDao.retrieveAccountRecordIdFromObject(bundleId, ObjectType.BUNDLE, cacheControllerDispatcher.getCacheController(CacheType.ACCOUNT_RECORD_ID));
+        final UUID accountId = nonEntityDao.retrieveIdFromObject(accountRecordId, ObjectType.ACCOUNT);
+
+        final Optional<SubscriptionBundle> bundleOptional = Iterables.<SubscriptionBundle>tryFind(getSubscriptionBundlesForAccount(accountId, context),
+                                                                                                  new Predicate<SubscriptionBundle>() {
+                                                                                                      @Override
+                                                                                                      public boolean apply(final SubscriptionBundle bundle) {
+                                                                                                          return bundle.getId().equals(bundleId);
+                                                                                                      }
+                                                                                                  });
+        if (!bundleOptional.isPresent()) {
+            throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_ID, bundleId);
+        } else {
+            return bundleOptional.get();
         }
     }
 
-
     @Override
     public List<SubscriptionBundle> getSubscriptionBundlesForAccountIdAndExternalKey(final UUID accountId, final String externalKey, final TenantContext context) throws SubscriptionApiException {
-
-        try {
-
-            final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountIdAndExternalKey(accountId, externalKey, context);
-            if (entitlements.isEmpty()) {
-                throw new SubscriptionApiException(ErrorCode.SUB_GET_INVALID_BUNDLE_KEY, externalKey);
-            }
-            return getSubscriptionBundles(entitlements, context);
-        } catch (EntitlementApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (SubscriptionBaseApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
-        }
+        return ImmutableList.<SubscriptionBundle>copyOf(Iterables.<SubscriptionBundle>filter(getSubscriptionBundlesForAccount(accountId, context),
+                                                                                             new Predicate<SubscriptionBundle>() {
+                                                                                                 @Override
+                                                                                                 public boolean apply(final SubscriptionBundle bundle) {
+                                                                                                     return bundle.getExternalKey().equals(externalKey);
+                                                                                                 }
+                                                                                             }));
     }
 
     @Override
-    public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey,
-                                                                        final TenantContext context) throws SubscriptionApiException {
-
+    public SubscriptionBundle getActiveSubscriptionBundleForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
         final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
         try {
             final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getActiveBundleForKey(externalKey, internalContext);
-            final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(baseBundle.getId(), context);
-
-            return getSubscriptionBundleFromEntitlements(baseBundle.getId(), allEntitlementsForBundle, context);
+            return getSubscriptionBundle(baseBundle.getId(), context);
         } catch (SubscriptionBaseApiException e) {
             throw new SubscriptionApiException(e);
-        } catch (EntitlementApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
         }
     }
 
     @Override
     public List<SubscriptionBundle> getSubscriptionBundlesForExternalKey(final String externalKey, final TenantContext context) throws SubscriptionApiException {
-
         final InternalTenantContext internalContext = internalCallContextFactory.createInternalTenantContext(context);
-        try {
-            final List<SubscriptionBaseBundle> baseBundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
-            final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
-            for (SubscriptionBaseBundle cur : baseBundles) {
-                final List<Entitlement> allEntitlementsForBundle = entitlementApi.getAllEntitlementsForBundle(cur.getId(), context);
-                final SubscriptionBundle bundle = getSubscriptionBundleFromEntitlements(cur.getId(), allEntitlementsForBundle, context);
-                result.add(bundle);
-            }
-            return result;
-        } catch (SubscriptionBaseApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (EntitlementApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
+        final List<SubscriptionBaseBundle> baseBundles = subscriptionInternalApi.getBundlesForKey(externalKey, internalContext);
+
+        final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(baseBundles.size());
+        for (final SubscriptionBaseBundle cur : baseBundles) {
+            final SubscriptionBundle bundle = getSubscriptionBundle(cur.getId(), context);
+            result.add(bundle);
         }
+
+        return result;
     }
 
     @Override
-    public List<SubscriptionBundle> getSubscriptionBundlesForAccountId(final UUID accountId,
-                                                                       final TenantContext context) throws SubscriptionApiException {
-        try {
+    public List<SubscriptionBundle> getSubscriptionBundlesForAccountId(final UUID accountId, final TenantContext context) throws SubscriptionApiException {
+        return getSubscriptionBundlesForAccount(accountId, context);
+    }
 
-            final List<Entitlement> entitlements = entitlementApi.getAllEntitlementsForAccountId(accountId, context);
-            if (entitlements.isEmpty()) {
-                return Collections.emptyList();
-            }
-            return getSubscriptionBundles(entitlements, context);
+    private List<SubscriptionBundle> getSubscriptionBundlesForAccount(final UUID accountId, final TenantContext tenantContext) throws SubscriptionApiException {
+        // Retrieve entitlements
+        final AccountEntitlements accountEntitlements;
+        try {
+            accountEntitlements = entitlementInternalApi.getAllEntitlementsForAccountId(accountId, tenantContext);
         } catch (EntitlementApiException e) {
             throw new SubscriptionApiException(e);
-        } catch (SubscriptionBaseApiException e) {
-            throw new SubscriptionApiException(e);
-        } catch (AccountApiException e) {
-            throw new SubscriptionApiException(e);
         }
-    }
 
-    private List<SubscriptionBundle> getSubscriptionBundles(final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException, AccountApiException {
-        final ListMultimap<UUID, Entitlement> perBundleEntitlements = LinkedListMultimap.create();
-        for (Entitlement cur : entitlements) {
-            perBundleEntitlements.put(cur.getBundleId(), cur);
+        // Build subscriptions
+        final Map<UUID, List<Subscription>> subscriptionsPerBundle = buildSubscriptionsFromEntitlements(accountEntitlements);
+
+        final DateTimeZone accountTimeZone = accountEntitlements.getAccount().getTimeZone();
+
+        // Build subscription bundles
+        final List<SubscriptionBundle> bundles = new LinkedList<SubscriptionBundle>();
+        for (final UUID bundleId : subscriptionsPerBundle.keySet()) {
+            final List<Subscription> subscriptionsForBundle = subscriptionsPerBundle.get(bundleId);
+            final String externalKey = subscriptionsForBundle.get(0).getExternalKey();
+
+            final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone,
+                                                                                              accountId,
+                                                                                              bundleId,
+                                                                                              externalKey,
+                                                                                              accountEntitlements.getEntitlements().get(bundleId));
+
+            final SubscriptionBaseBundle baseBundle = accountEntitlements.getBundles().get(bundleId);
+            final SubscriptionBundle subscriptionBundle = new DefaultSubscriptionBundle(bundleId,
+                                                                                        accountId,
+                                                                                        externalKey,
+                                                                                        subscriptionsForBundle,
+                                                                                        timeline,
+                                                                                        baseBundle.getOriginalCreatedDate(),
+                                                                                        baseBundle.getCreatedDate(),
+                                                                                        baseBundle.getUpdatedDate());
+            bundles.add(subscriptionBundle);
         }
 
-        final List<SubscriptionBundle> result = new ArrayList<SubscriptionBundle>(perBundleEntitlements.keySet().size());
-        for (UUID bundleId : perBundleEntitlements.keySet()) {
-            final List<Entitlement> allEntitlementsForBundle = perBundleEntitlements.get(bundleId);
-            final SubscriptionBundle bundle = getSubscriptionBundleFromEntitlements(bundleId, allEntitlementsForBundle, context);
-            result.add(bundle);
-        }
-        return result;
+        return bundles;
     }
 
-    private Subscription fromEntitlement(final Entitlement entitlement, final InternalTenantContext internalTenantContext) {
-
-        final List<BlockingState> states = blockingStateDao.getBlockingState(entitlement.getId(), BlockingStateType.SUBSCRIPTION, internalTenantContext);
-        final Subscription result = new DefaultSubscription((DefaultEntitlement) entitlement, states);
-        return result;
-    }
-
-    private SubscriptionBundle getSubscriptionBundleFromEntitlements(final UUID bundleId, final List<Entitlement> entitlements, final TenantContext context) throws SubscriptionBaseApiException, AccountApiException {
-        final InternalTenantContext internalTenantContext = internalCallContextFactory.createInternalTenantContext(context);
-        final SubscriptionBaseBundle baseBundle = subscriptionInternalApi.getBundleFromId(bundleId, internalTenantContext);
-        final List<Subscription> subscriptions = new ArrayList<Subscription>(entitlements.size());
-        subscriptions.addAll(Collections2.transform(entitlements, new Function<Entitlement, Subscription>() {
-            @Override
-            public Subscription apply(final Entitlement input) {
-                return fromEntitlement(input, internalTenantContext);
+    private Map<UUID, List<Subscription>> buildSubscriptionsFromEntitlements(final AccountEntitlements accountEntitlements) {
+        final Map<UUID, List<Subscription>> subscriptionsPerBundle = new HashMap<UUID, List<Subscription>>();
+        for (final UUID bundleId : accountEntitlements.getEntitlements().keySet()) {
+            if (subscriptionsPerBundle.get(bundleId) == null) {
+                subscriptionsPerBundle.put(bundleId, new LinkedList<Subscription>());
             }
-        }));
-
-        final Account account = accountApi.getAccountById(baseBundle.getAccountId(), internalTenantContext);
-
-        final InternalTenantContext internalTenantContextWithAccountRecordId = internalCallContextFactory.createInternalTenantContext(account.getId(), context);
-
-        final DateTimeZone accountTimeZone = account.getTimeZone();
 
-        final List<BlockingState> allBlockingStatesPerAccountRecordId = blockingStateDao.getBlockingAllForAccountRecordId(internalTenantContextWithAccountRecordId);
-        final Set<UUID> allEntitlementIds = new HashSet<UUID>(Collections2.<Entitlement, UUID>transform(entitlements, new Function<Entitlement, UUID>() {
-            @Override
-            public UUID apply(final Entitlement input) {
-                return input.getId();
+            for (final Entitlement entitlement : accountEntitlements.getEntitlements().get(bundleId)) {
+                if (entitlement instanceof DefaultEntitlement) {
+                    subscriptionsPerBundle.get(bundleId).add(new DefaultSubscription((DefaultEntitlement) entitlement));
+                } else {
+                    throw new ShouldntHappenException("Entitlement should be a DefaultEntitlement instance");
+                }
             }
-        }));
-
-        final List<BlockingState> filteredBlockingStates = new LinkedList<BlockingState>(Collections2.filter(allBlockingStatesPerAccountRecordId, new Predicate<BlockingState>() {
-            @Override
-            public boolean apply(final BlockingState input) {
-                return input.getType() == BlockingStateType.ACCOUNT ||
-                       (input.getType() == BlockingStateType.SUBSCRIPTION_BUNDLE && input.getBlockedId().equals(bundleId)) ||
-                       (input.getType() == BlockingStateType.SUBSCRIPTION && allEntitlementIds.contains(input.getBlockedId()));
-            }
-        }));
-
-        final SubscriptionBundleTimeline timeline = new DefaultSubscriptionBundleTimeline(accountTimeZone, account.getId(), bundleId, baseBundle.getExternalKey(), entitlements, filteredBlockingStates);
-        final DefaultSubscriptionBundle bundle = new DefaultSubscriptionBundle(bundleId, baseBundle.getAccountId(), baseBundle.getExternalKey(), subscriptions, timeline, baseBundle.getOriginalCreatedDate() , baseBundle.getCreatedDate(), baseBundle.getUpdatedDate());
-        return bundle;
+        }
+        return subscriptionsPerBundle;
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
index 796eb06..608e6b6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/DefaultSubscriptionBundleTimeline.java
@@ -21,6 +21,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -56,13 +57,11 @@ import com.google.common.collect.ImmutableList;
 
 public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTimeline {
 
-
     private final Logger logger = LoggerFactory.getLogger(DefaultSubscriptionBundleTimeline.class);
 
     // STEPH This is added to give us confidence the timeline we generate behaves as expected. Could be removed at some point
     private final static String TIMELINE_WARN_LOG = "Sanity Timeline: ";
 
-
     public static final String BILLING_SERVICE_NAME = "billing-service";
     public static final String ENT_BILLING_SERVICE_NAME = "entitlement+billing-service";
 
@@ -71,6 +70,18 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     private final UUID bundleId;
     private final String externalKey;
 
+    public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final Collection<Entitlement> entitlements) {
+        final Collection<BlockingState> blockingStates = new HashSet<BlockingState>();
+        for (final Entitlement entitlement : entitlements) {
+            blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getSubscriptionEntitlementStates());
+            blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getBundleEntitlementStates());
+            blockingStates.addAll(((DefaultEntitlement) entitlement).getEventsStream().getAccountEntitlementStates());
+        }
+        this.accountId = accountId;
+        this.bundleId = bundleId;
+        this.externalKey = externalKey;
+        this.events = computeEvents(entitlements, new LinkedList<BlockingState>(blockingStates), accountTimeZone);
+    }
 
     public DefaultSubscriptionBundleTimeline(final DateTimeZone accountTimeZone, final UUID accountId, final UUID bundleId, final String externalKey, final List<Entitlement> entitlements, List<BlockingState> allBlockingStates) {
         this.accountId = accountId;
@@ -85,7 +96,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     // - base subscription events are already ordered for each Entitlement and so when we reorder at the bundle level we try not to break that initial ordering
     // - blocking state events occur at various level (account, bundle and subscription) so for higher level, we need to dispatch that on each subscription.
     //
-    private List<SubscriptionEvent> computeEvents(final List<Entitlement> entitlements, List<BlockingState> allBlockingStates, final DateTimeZone accountTimeZone) {
+    private List<SubscriptionEvent> computeEvents(final Collection<Entitlement> entitlements, List<BlockingState> allBlockingStates, final DateTimeZone accountTimeZone) {
 
         // Extract ids for all entitlement in the list
         final Set<UUID> allEntitlementUUIDs = new TreeSet(Collections2.transform(entitlements, new Function<Entitlement, UUID>() {
@@ -171,7 +182,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
 
             int currentIndex = i;
             if (shouldSwap) {
-                Collections.swap(events, i, i+1);
+                Collections.swap(events, i, i + 1);
             }
             if (shouldReverseSort) {
                 while (currentIndex >= 1) {
@@ -189,7 +200,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         }
     }
 
-
     private boolean shouldSwap(DefaultSubscriptionEvent cur, DefaultSubscriptionEvent other, boolean isAscending) {
 
         // For a given date, order by subscriptionId, and within subscription by event type
@@ -198,12 +208,11 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                 ((isAscending &&
                   ((idComp > 0) ||
                    (idComp == 0 && cur.getSubscriptionEventType().ordinal() > other.getSubscriptionEventType().ordinal()))) ||
-                (!isAscending &&
-                   ((idComp < 0) ||
-                    (idComp == 0 && cur.getSubscriptionEventType().ordinal() < other.getSubscriptionEventType().ordinal())))));
+                 (!isAscending &&
+                  ((idComp < 0) ||
+                   (idComp == 0 && cur.getSubscriptionEventType().ordinal() < other.getSubscriptionEventType().ordinal())))));
     }
 
-
     private void insertAfterIndex(final LinkedList<SubscriptionEvent> original, final List<SubscriptionEvent> newEvents, int index) {
 
         final boolean firstPosition = (index == -1);
@@ -227,7 +236,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     //
     private int insertFromBlockingEvent(final DateTimeZone accountTimeZone, final Set<UUID> allEntitlementUUIDs, final List<SubscriptionEvent> result, final BlockingState bs, final DateTime bsEffectiveDate, final List<SubscriptionEvent> newEvents) {
 
-
         // Keep the current state per entitlement
         final Map<UUID, TargetState> targetStates = new HashMap<UUID, TargetState>();
         for (UUID cur : allEntitlementUUIDs) {
@@ -325,7 +333,7 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
     }
 
     // Compute the initial stream of events based on the subscription base events
-    private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final List<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
+    private LinkedList<SubscriptionEvent> computeSubscriptionBaseEvents(final Collection<Entitlement> entitlements, final DateTimeZone accountTimeZone) {
         final LinkedList<SubscriptionEvent> result = new LinkedList<SubscriptionEvent>();
         for (final Entitlement cur : entitlements) {
             final SubscriptionBase base = ((DefaultEntitlement) cur).getSubscriptionBase();
@@ -404,7 +412,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         result.add(index, event);
     }
 
-
     private SubscriptionEvent toSubscriptionEvent(final SubscriptionEvent prev, final SubscriptionEvent next, final UUID entitlementId, final BlockingState in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
         return new DefaultSubscriptionEvent(in.getId(),
                                             entitlementId,
@@ -430,7 +437,6 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
                                             accountTimeZone);
     }
 
-
     private SubscriptionEvent toSubscriptionEvent(final SubscriptionBaseTransition in, final SubscriptionEventType eventType, final DateTimeZone accountTimeZone) {
         return new DefaultSubscriptionEvent(in.getId(),
                                             in.getSubscriptionId(),
@@ -526,415 +532,413 @@ public class DefaultSubscriptionBundleTimeline implements SubscriptionBundleTime
         return events;
     }
 
-//
-// Internal class to keep the state associated with each subscription
-//
-private final static class TargetState {
-
-    private boolean isEntitlementStarted;
-    private boolean isEntitlementStopped;
-    private boolean isBillingStarted;
-    private boolean isBillingStopped;
-    private Map<String, BlockingState> perServiceBlockingState;
-
-    public TargetState() {
-        this.isEntitlementStarted = false;
-        this.isEntitlementStopped = false;
-        this.isBillingStarted = false;
-        this.isBillingStopped = false;
-        this.perServiceBlockingState = new HashMap<String, BlockingState>();
-    }
+    //
+    // Internal class to keep the state associated with each subscription
+    //
+    private final static class TargetState {
 
-    public void setEntitlementStarted() {
-        isEntitlementStarted = true;
-    }
+        private boolean isEntitlementStarted;
+        private boolean isEntitlementStopped;
+        private boolean isBillingStarted;
+        private boolean isBillingStopped;
+        private Map<String, BlockingState> perServiceBlockingState;
 
-    public void setEntitlementStopped() {
-        isEntitlementStopped = true;
-    }
+        public TargetState() {
+            this.isEntitlementStarted = false;
+            this.isEntitlementStopped = false;
+            this.isBillingStarted = false;
+            this.isBillingStopped = false;
+            this.perServiceBlockingState = new HashMap<String, BlockingState>();
+        }
 
-    public void setBillingStarted() {
-        isBillingStarted = true;
-    }
+        public void setEntitlementStarted() {
+            isEntitlementStarted = true;
+        }
 
-    public void setBillingStopped() {
-        isBillingStopped = true;
-    }
+        public void setEntitlementStopped() {
+            isEntitlementStopped = true;
+        }
 
-    public void addEntitlementEvent(final SubscriptionEvent e) {
-        final BlockingState converted = new DefaultBlockingState(e.getEntitlementId(), BlockingStateType.SUBSCRIPTION,
-                                                                 e.getServiceStateName(), e.getServiceName(), false, e.isBlockedEntitlement(), e.isBlockedBilling(),
-                                                                 ((DefaultSubscriptionEvent) e).getEffectiveDateTime());
-        perServiceBlockingState.put(converted.getService(), converted);
+        public void setBillingStarted() {
+            isBillingStarted = true;
+        }
 
-    }
+        public void setBillingStopped() {
+            isBillingStopped = true;
+        }
+
+        public void addEntitlementEvent(final SubscriptionEvent e) {
+            final BlockingState converted = new DefaultBlockingState(e.getEntitlementId(), BlockingStateType.SUBSCRIPTION,
+                                                                     e.getServiceStateName(), e.getServiceName(), false, e.isBlockedEntitlement(), e.isBlockedBilling(),
+                                                                     ((DefaultSubscriptionEvent) e).getEffectiveDateTime());
+            perServiceBlockingState.put(converted.getService(), converted);
 
-    //
-    // From the current state of that subscription, compute the effect of the new state based on the incoming blockingState event
-    //
-    private List<SubscriptionEventType> addStateAndReturnEventTypes(final BlockingState bs) {
-
-        // Turn off isBlockedEntitlement and isBlockedBilling if there was not start event
-        final BlockingState fixedBlockingState = new DefaultBlockingState(bs.getBlockedId(),
-                                                                          bs.getType(),
-                                                                          bs.getStateName(),
-                                                                          bs.getService(),
-                                                                          bs.isBlockChange(),
-                                                                          (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
-                                                                          (bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
-                                                                          bs.getEffectiveDate());
-
-        final List<SubscriptionEventType> result = new ArrayList<SubscriptionEventType>(4);
-        if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
-            isEntitlementStopped = true;
-            result.add(SubscriptionEventType.STOP_ENTITLEMENT);
-            return result;
         }
 
         //
-        // We look at the effect of the incoming event for the specific service, and then recompute the state after so we can compare if anything has changed
-        // across all services
+        // From the current state of that subscription, compute the effect of the new state based on the incoming blockingState event
         //
-        final BlockingAggregator stateBefore = getState();
-        perServiceBlockingState.put(fixedBlockingState.getService(), fixedBlockingState);
-        final BlockingAggregator stateAfter = getState();
+        private List<SubscriptionEventType> addStateAndReturnEventTypes(final BlockingState bs) {
+
+            // Turn off isBlockedEntitlement and isBlockedBilling if there was not start event
+            final BlockingState fixedBlockingState = new DefaultBlockingState(bs.getBlockedId(),
+                                                                              bs.getType(),
+                                                                              bs.getStateName(),
+                                                                              bs.getService(),
+                                                                              bs.isBlockChange(),
+                                                                              (bs.isBlockEntitlement() && isEntitlementStarted && !isEntitlementStopped),
+                                                                              (bs.isBlockBilling() && isBillingStarted && !isBillingStopped),
+                                                                              bs.getEffectiveDate());
+
+            final List<SubscriptionEventType> result = new ArrayList<SubscriptionEventType>(4);
+            if (fixedBlockingState.getStateName().equals(DefaultEntitlementApi.ENT_STATE_CANCELLED)) {
+                isEntitlementStopped = true;
+                result.add(SubscriptionEventType.STOP_ENTITLEMENT);
+                return result;
+            }
 
-        final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
-        if (shouldResumeEntitlement) {
-            result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
-        }
-        final boolean shouldResumeBilling = isBillingStarted && !isBillingStopped && stateBefore.isBlockBilling() && !stateAfter.isBlockBilling();
-        if (shouldResumeBilling) {
-            result.add(SubscriptionEventType.RESUME_BILLING);
-        }
+            //
+            // We look at the effect of the incoming event for the specific service, and then recompute the state after so we can compare if anything has changed
+            // across all services
+            //
+            final BlockingAggregator stateBefore = getState();
+            perServiceBlockingState.put(fixedBlockingState.getService(), fixedBlockingState);
+            final BlockingAggregator stateAfter = getState();
 
-        final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
-        if (shouldBlockEntitlement) {
-            result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
-        }
-        final boolean shouldBlockBilling = isBillingStarted && !isBillingStopped && !stateBefore.isBlockBilling() && stateAfter.isBlockBilling();
-        if (shouldBlockBilling) {
-            result.add(SubscriptionEventType.PAUSE_BILLING);
-        }
+            final boolean shouldResumeEntitlement = isEntitlementStarted && !isEntitlementStopped && stateBefore.isBlockEntitlement() && !stateAfter.isBlockEntitlement();
+            if (shouldResumeEntitlement) {
+                result.add(SubscriptionEventType.RESUME_ENTITLEMENT);
+            }
+            final boolean shouldResumeBilling = isBillingStarted && !isBillingStopped && stateBefore.isBlockBilling() && !stateAfter.isBlockBilling();
+            if (shouldResumeBilling) {
+                result.add(SubscriptionEventType.RESUME_BILLING);
+            }
+
+            final boolean shouldBlockEntitlement = isEntitlementStarted && !isEntitlementStopped && !stateBefore.isBlockEntitlement() && stateAfter.isBlockEntitlement();
+            if (shouldBlockEntitlement) {
+                result.add(SubscriptionEventType.PAUSE_ENTITLEMENT);
+            }
+            final boolean shouldBlockBilling = isBillingStarted && !isBillingStopped && !stateBefore.isBlockBilling() && stateAfter.isBlockBilling();
+            if (shouldBlockBilling) {
+                result.add(SubscriptionEventType.PAUSE_BILLING);
+            }
 
-        if (!shouldResumeEntitlement && !shouldBlockEntitlement && !shouldBlockEntitlement && !shouldBlockBilling && !fixedBlockingState.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
-            result.add(SubscriptionEventType.SERVICE_STATE_CHANGE);
+            if (!shouldResumeEntitlement && !shouldBlockEntitlement && !shouldBlockEntitlement && !shouldBlockBilling && !fixedBlockingState.getService().equals(DefaultEntitlementService.ENTITLEMENT_SERVICE_NAME)) {
+                result.add(SubscriptionEventType.SERVICE_STATE_CHANGE);
+            }
+            return result;
         }
-        return result;
-    }
 
-    private BlockingAggregator getState() {
-        final DefaultBlockingAggregator aggrBefore = new DefaultBlockingAggregator();
-        for (BlockingState cur : perServiceBlockingState.values()) {
-            aggrBefore.or(cur);
+        private BlockingAggregator getState() {
+            final DefaultBlockingAggregator aggrBefore = new DefaultBlockingAggregator();
+            for (BlockingState cur : perServiceBlockingState.values()) {
+                aggrBefore.or(cur);
+            }
+            return aggrBefore;
+        }
+    }
+
+    protected static final class DefaultSubscriptionEvent implements SubscriptionEvent {
+
+        private final UUID id;
+        private final UUID entitlementId;
+        private final DateTime effectiveDate;
+        private final DateTime requestedDate;
+        private final SubscriptionEventType eventType;
+        private final boolean isBlockingEntitlement;
+        private final boolean isBlockingBilling;
+        private final String serviceName;
+        private final String serviceStateName;
+        private final Product prevProduct;
+        private final Plan prevPlan;
+        private final PlanPhase prevPlanPhase;
+        private final PriceList prevPriceList;
+        private final BillingPeriod prevBillingPeriod;
+        private final Product nextProduct;
+        private final Plan nextPlan;
+        private final PlanPhase nextPlanPhase;
+        private final PriceList nextPriceList;
+        private final BillingPeriod nextBillingPeriod;
+        private final DateTime createdDate;
+        private final DateTimeZone accountTimeZone;
+
+        public DefaultSubscriptionEvent(final UUID id,
+                                        final UUID entitlementId,
+                                        final DateTime effectiveDate,
+                                        final DateTime requestedDate,
+                                        final SubscriptionEventType eventType,
+                                        final boolean blockingEntitlement,
+                                        final boolean blockingBilling,
+                                        final String serviceName,
+                                        final String serviceStateName,
+                                        final Product prevProduct,
+                                        final Plan prevPlan,
+                                        final PlanPhase prevPlanPhase,
+                                        final PriceList prevPriceList,
+                                        final BillingPeriod prevBillingPeriod,
+                                        final Product nextProduct,
+                                        final Plan nextPlan,
+                                        final PlanPhase nextPlanPhase,
+                                        final PriceList nextPriceList,
+                                        final BillingPeriod nextBillingPeriod,
+                                        final DateTime createDate,
+                                        final DateTimeZone accountTimeZone) {
+            this.id = id;
+            this.entitlementId = entitlementId;
+            this.effectiveDate = effectiveDate;
+            this.requestedDate = requestedDate;
+            this.eventType = eventType;
+            this.isBlockingEntitlement = blockingEntitlement;
+            this.isBlockingBilling = blockingBilling;
+            this.serviceName = serviceName;
+            this.serviceStateName = serviceStateName;
+            this.prevProduct = prevProduct;
+            this.prevPlan = prevPlan;
+            this.prevPlanPhase = prevPlanPhase;
+            this.prevPriceList = prevPriceList;
+            this.prevBillingPeriod = prevBillingPeriod;
+            this.nextProduct = nextProduct;
+            this.nextPlan = nextPlan;
+            this.nextPlanPhase = nextPlanPhase;
+            this.nextPriceList = nextPriceList;
+            this.nextBillingPeriod = nextBillingPeriod;
+            this.createdDate = createDate;
+            this.accountTimeZone = accountTimeZone;
+        }
+
+        private DefaultSubscriptionEvent(DefaultSubscriptionEvent copy, SubscriptionEventType newEventType) {
+            this(copy.getId(),
+                 copy.getEntitlementId(),
+                 copy.getEffectiveDateTime(),
+                 copy.getRequestedDateTime(),
+                 newEventType,
+                 copy.isBlockedEntitlement(),
+                 copy.isBlockedBilling(),
+                 copy.getServiceName(),
+                 copy.getServiceStateName(),
+                 copy.getPrevProduct(),
+                 copy.getPrevPlan(),
+                 copy.getPrevPhase(),
+                 copy.getPrevPriceList(),
+                 copy.getPrevBillingPeriod(),
+                 copy.getNextProduct(),
+                 copy.getNextPlan(),
+                 copy.getNextPhase(),
+                 copy.getNextPriceList(),
+                 copy.getNextBillingPeriod(),
+                 copy.getCreatedDate(),
+                 copy.getAccountTimeZone());
         }
-        return aggrBefore;
-    }
-}
 
+        public DateTimeZone getAccountTimeZone() {
+            return accountTimeZone;
+        }
 
-protected static final class DefaultSubscriptionEvent implements SubscriptionEvent {
-
-    private final UUID id;
-    private final UUID entitlementId;
-    private final DateTime effectiveDate;
-    private final DateTime requestedDate;
-    private final SubscriptionEventType eventType;
-    private final boolean isBlockingEntitlement;
-    private final boolean isBlockingBilling;
-    private final String serviceName;
-    private final String serviceStateName;
-    private final Product prevProduct;
-    private final Plan prevPlan;
-    private final PlanPhase prevPlanPhase;
-    private final PriceList prevPriceList;
-    private final BillingPeriod prevBillingPeriod;
-    private final Product nextProduct;
-    private final Plan nextPlan;
-    private final PlanPhase nextPlanPhase;
-    private final PriceList nextPriceList;
-    private final BillingPeriod nextBillingPeriod;
-    private final DateTime createdDate;
-    private final DateTimeZone accountTimeZone;
-
-
-    public DefaultSubscriptionEvent(final UUID id,
-                                     final UUID entitlementId,
-                                     final DateTime effectiveDate,
-                                     final DateTime requestedDate,
-                                     final SubscriptionEventType eventType,
-                                     final boolean blockingEntitlement,
-                                     final boolean blockingBilling,
-                                     final String serviceName,
-                                     final String serviceStateName,
-                                     final Product prevProduct,
-                                     final Plan prevPlan,
-                                     final PlanPhase prevPlanPhase,
-                                     final PriceList prevPriceList,
-                                     final BillingPeriod prevBillingPeriod,
-                                     final Product nextProduct,
-                                     final Plan nextPlan,
-                                     final PlanPhase nextPlanPhase,
-                                     final PriceList nextPriceList,
-                                     final BillingPeriod nextBillingPeriod,
-                                     final DateTime createDate,
-                                     final DateTimeZone accountTimeZone) {
-        this.id = id;
-        this.entitlementId = entitlementId;
-        this.effectiveDate = effectiveDate;
-        this.requestedDate = requestedDate;
-        this.eventType = eventType;
-        this.isBlockingEntitlement = blockingEntitlement;
-        this.isBlockingBilling = blockingBilling;
-        this.serviceName = serviceName;
-        this.serviceStateName = serviceStateName;
-        this.prevProduct = prevProduct;
-        this.prevPlan = prevPlan;
-        this.prevPlanPhase = prevPlanPhase;
-        this.prevPriceList = prevPriceList;
-        this.prevBillingPeriod = prevBillingPeriod;
-        this.nextProduct = nextProduct;
-        this.nextPlan = nextPlan;
-        this.nextPlanPhase = nextPlanPhase;
-        this.nextPriceList = nextPriceList;
-        this.nextBillingPeriod = nextBillingPeriod;
-        this.createdDate = createDate;
-        this.accountTimeZone = accountTimeZone;
-    }
+        public DateTime getEffectiveDateTime() {
+            return effectiveDate;
+        }
 
-    private DefaultSubscriptionEvent(DefaultSubscriptionEvent copy, SubscriptionEventType newEventType) {
-        this(copy.getId(),
-             copy.getEntitlementId(),
-             copy.getEffectiveDateTime(),
-             copy.getRequestedDateTime(),
-             newEventType,
-             copy.isBlockedEntitlement(),
-             copy.isBlockedBilling(),
-             copy.getServiceName(),
-             copy.getServiceStateName(),
-             copy.getPrevProduct(),
-             copy.getPrevPlan(),
-             copy.getPrevPhase(),
-             copy.getPrevPriceList(),
-             copy.getPrevBillingPeriod(),
-             copy.getNextProduct(),
-             copy.getNextPlan(),
-             copy.getNextPhase(),
-             copy.getNextPriceList(),
-             copy.getNextBillingPeriod(),
-             copy.getCreatedDate(),
-             copy.getAccountTimeZone());
-    }
+        public DateTime getRequestedDateTime() {
+            return requestedDate;
+        }
 
-    public DateTimeZone getAccountTimeZone() {
-        return accountTimeZone;
-    }
+        @Override
+        public UUID getId() {
+            return id;
+        }
 
-    public DateTime getEffectiveDateTime() {
-        return effectiveDate;
-    }
+        @Override
+        public UUID getEntitlementId() {
+            return entitlementId;
+        }
 
-    public DateTime getRequestedDateTime() {
-        return requestedDate;
-    }
+        @Override
+        public LocalDate getEffectiveDate() {
+            return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
+        }
 
-    @Override
-    public UUID getId() {
-        return id;
-    }
+        @Override
+        public LocalDate getRequestedDate() {
+            return requestedDate != null ? new LocalDate(requestedDate, accountTimeZone) : null;
+        }
 
-    @Override
-    public UUID getEntitlementId() {
-        return entitlementId;
-    }
+        @Override
+        public SubscriptionEventType getSubscriptionEventType() {
+            return eventType;
+        }
 
-    @Override
-    public LocalDate getEffectiveDate() {
-        return effectiveDate != null ? new LocalDate(effectiveDate, accountTimeZone) : null;
-    }
+        @Override
+        public boolean isBlockedBilling() {
+            return isBlockingBilling;
+        }
 
-    @Override
-    public LocalDate getRequestedDate() {
-        return requestedDate != null ? new LocalDate(requestedDate, accountTimeZone) : null;
-    }
+        @Override
+        public boolean isBlockedEntitlement() {
+            return isBlockingEntitlement;
+        }
 
-    @Override
-    public SubscriptionEventType getSubscriptionEventType() {
-        return eventType;
-    }
+        @Override
+        public String getServiceName() {
+            return serviceName;
+        }
 
-    @Override
-    public boolean isBlockedBilling() {
-        return isBlockingBilling;
-    }
+        @Override
+        public String getServiceStateName() {
+            return serviceStateName;
+        }
 
-    @Override
-    public boolean isBlockedEntitlement() {
-        return isBlockingEntitlement;
-    }
+        @Override
+        public Product getPrevProduct() {
+            return prevProduct;
+        }
 
-    @Override
-    public String getServiceName() {
-        return serviceName;
-    }
+        @Override
+        public Plan getPrevPlan() {
+            return prevPlan;
+        }
 
-    @Override
-    public String getServiceStateName() {
-        return serviceStateName;
-    }
+        @Override
+        public PlanPhase getPrevPhase() {
+            return prevPlanPhase;
+        }
 
-    @Override
-    public Product getPrevProduct() {
-        return prevProduct;
-    }
+        @Override
+        public PriceList getPrevPriceList() {
+            return prevPriceList;
+        }
 
-    @Override
-    public Plan getPrevPlan() {
-        return prevPlan;
-    }
+        @Override
+        public BillingPeriod getPrevBillingPeriod() {
+            return prevBillingPeriod;
+        }
 
-    @Override
-    public PlanPhase getPrevPhase() {
-        return prevPlanPhase;
-    }
+        @Override
+        public Product getNextProduct() {
+            return nextProduct;
+        }
 
-    @Override
-    public PriceList getPrevPriceList() {
-        return prevPriceList;
-    }
+        @Override
+        public Plan getNextPlan() {
+            return nextPlan;
+        }
 
-    @Override
-    public BillingPeriod getPrevBillingPeriod() {
-        return prevBillingPeriod;
-    }
+        @Override
+        public PlanPhase getNextPhase() {
+            return nextPlanPhase;
+        }
 
-    @Override
-    public Product getNextProduct() {
-        return nextProduct;
-    }
+        @Override
+        public PriceList getNextPriceList() {
+            return nextPriceList;
+        }
 
-    @Override
-    public Plan getNextPlan() {
-        return nextPlan;
-    }
+        @Override
+        public BillingPeriod getNextBillingPeriod() {
+            return nextBillingPeriod;
+        }
 
-    @Override
-    public PlanPhase getNextPhase() {
-        return nextPlanPhase;
-    }
+        public DateTime getCreatedDate() {
+            return createdDate;
+        }
 
-    @Override
-    public PriceList getNextPriceList() {
-        return nextPriceList;
-    }
+        @Override
+        public boolean equals(final Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
 
-    @Override
-    public BillingPeriod getNextBillingPeriod() {
-        return nextBillingPeriod;
-    }
+            final DefaultSubscriptionEvent that = (DefaultSubscriptionEvent) o;
 
-    public DateTime getCreatedDate() {
-        return createdDate;
-    }
+            if (isBlockingBilling != that.isBlockingBilling) {
+                return false;
+            }
+            if (isBlockingEntitlement != that.isBlockingEntitlement) {
+                return false;
+            }
+            if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
+                return false;
+            }
+            if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
+                return false;
+            }
+            if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
+                return false;
+            }
+            if (eventType != that.eventType) {
+                return false;
+            }
+            if (id != null ? !id.equals(that.id) : that.id != null) {
+                return false;
+            }
+            if (nextBillingPeriod != that.nextBillingPeriod) {
+                return false;
+            }
+            if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
+                return false;
+            }
+            if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
+                return false;
+            }
+            if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
+                return false;
+            }
+            if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
+                return false;
+            }
+            if (prevBillingPeriod != that.prevBillingPeriod) {
+                return false;
+            }
+            if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
+                return false;
+            }
+            if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
+                return false;
+            }
+            if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
+                return false;
+            }
+            if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
+                return false;
+            }
+            if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
+                return false;
+            }
+            if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
+                return false;
+            }
+            if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
+                return false;
+            }
 
-    @Override
-    public boolean equals(final Object o) {
-        if (this == o) {
             return true;
         }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        final DefaultSubscriptionEvent that = (DefaultSubscriptionEvent) o;
 
-        if (isBlockingBilling != that.isBlockingBilling) {
-            return false;
-        }
-        if (isBlockingEntitlement != that.isBlockingEntitlement) {
-            return false;
-        }
-        if (createdDate != null ? !createdDate.equals(that.createdDate) : that.createdDate != null) {
-            return false;
-        }
-        if (effectiveDate != null ? !effectiveDate.equals(that.effectiveDate) : that.effectiveDate != null) {
-            return false;
-        }
-        if (entitlementId != null ? !entitlementId.equals(that.entitlementId) : that.entitlementId != null) {
-            return false;
-        }
-        if (eventType != that.eventType) {
-            return false;
-        }
-        if (id != null ? !id.equals(that.id) : that.id != null) {
-            return false;
-        }
-        if (nextBillingPeriod != that.nextBillingPeriod) {
-            return false;
-        }
-        if (nextPlan != null ? !nextPlan.equals(that.nextPlan) : that.nextPlan != null) {
-            return false;
-        }
-        if (nextPlanPhase != null ? !nextPlanPhase.equals(that.nextPlanPhase) : that.nextPlanPhase != null) {
-            return false;
-        }
-        if (nextPriceList != null ? !nextPriceList.equals(that.nextPriceList) : that.nextPriceList != null) {
-            return false;
-        }
-        if (nextProduct != null ? !nextProduct.equals(that.nextProduct) : that.nextProduct != null) {
-            return false;
-        }
-        if (prevBillingPeriod != that.prevBillingPeriod) {
-            return false;
-        }
-        if (prevPlan != null ? !prevPlan.equals(that.prevPlan) : that.prevPlan != null) {
-            return false;
-        }
-        if (prevPlanPhase != null ? !prevPlanPhase.equals(that.prevPlanPhase) : that.prevPlanPhase != null) {
-            return false;
-        }
-        if (prevPriceList != null ? !prevPriceList.equals(that.prevPriceList) : that.prevPriceList != null) {
-            return false;
-        }
-        if (prevProduct != null ? !prevProduct.equals(that.prevProduct) : that.prevProduct != null) {
-            return false;
-        }
-        if (requestedDate != null ? !requestedDate.equals(that.requestedDate) : that.requestedDate != null) {
-            return false;
-        }
-        if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) {
-            return false;
-        }
-        if (serviceStateName != null ? !serviceStateName.equals(that.serviceStateName) : that.serviceStateName != null) {
-            return false;
+        @Override
+        public int hashCode() {
+            int result = id != null ? id.hashCode() : 0;
+            result = 31 * result + (entitlementId != null ? entitlementId.hashCode() : 0);
+            result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+            result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
+            result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
+            result = 31 * result + (isBlockingEntitlement ? 1 : 0);
+            result = 31 * result + (isBlockingBilling ? 1 : 0);
+            result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
+            result = 31 * result + (serviceStateName != null ? serviceStateName.hashCode() : 0);
+            result = 31 * result + (prevProduct != null ? prevProduct.hashCode() : 0);
+            result = 31 * result + (prevPlan != null ? prevPlan.hashCode() : 0);
+            result = 31 * result + (prevPlanPhase != null ? prevPlanPhase.hashCode() : 0);
+            result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
+            result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
+            result = 31 * result + (nextProduct != null ? nextProduct.hashCode() : 0);
+            result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
+            result = 31 * result + (nextPlanPhase != null ? nextPlanPhase.hashCode() : 0);
+            result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
+            result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
+            result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
+            return result;
         }
-
-        return true;
     }
-
-    @Override
-    public int hashCode() {
-        int result = id != null ? id.hashCode() : 0;
-        result = 31 * result + (entitlementId != null ? entitlementId.hashCode() : 0);
-        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
-        result = 31 * result + (requestedDate != null ? requestedDate.hashCode() : 0);
-        result = 31 * result + (eventType != null ? eventType.hashCode() : 0);
-        result = 31 * result + (isBlockingEntitlement ? 1 : 0);
-        result = 31 * result + (isBlockingBilling ? 1 : 0);
-        result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
-        result = 31 * result + (serviceStateName != null ? serviceStateName.hashCode() : 0);
-        result = 31 * result + (prevProduct != null ? prevProduct.hashCode() : 0);
-        result = 31 * result + (prevPlan != null ? prevPlan.hashCode() : 0);
-        result = 31 * result + (prevPlanPhase != null ? prevPlanPhase.hashCode() : 0);
-        result = 31 * result + (prevPriceList != null ? prevPriceList.hashCode() : 0);
-        result = 31 * result + (prevBillingPeriod != null ? prevBillingPeriod.hashCode() : 0);
-        result = 31 * result + (nextProduct != null ? nextProduct.hashCode() : 0);
-        result = 31 * result + (nextPlan != null ? nextPlan.hashCode() : 0);
-        result = 31 * result + (nextPlanPhase != null ? nextPlanPhase.hashCode() : 0);
-        result = 31 * result + (nextPriceList != null ? nextPriceList.hashCode() : 0);
-        result = 31 * result + (nextBillingPeriod != null ? nextBillingPeriod.hashCode() : 0);
-        result = 31 * result + (createdDate != null ? createdDate.hashCode() : 0);
-        return result;
-    }
-}
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java
new file mode 100644
index 0000000..72898be
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEntitlements.java
@@ -0,0 +1,90 @@
+/*
+ * 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.entitlement.api.svcs;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.AccountEntitlements;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+public class DefaultAccountEntitlements implements AccountEntitlements {
+
+    private final AccountEventsStreams accountEventsStreams;
+    private final Map<UUID, Collection<Entitlement>> entitlements;
+
+    public DefaultAccountEntitlements(final AccountEventsStreams accountEventsStreams, final Map<UUID, Collection<Entitlement>> entitlements) {
+        this.accountEventsStreams = accountEventsStreams;
+        this.entitlements = entitlements;
+    }
+
+    @Override
+    public Account getAccount() {
+        return accountEventsStreams.getAccount();
+    }
+
+    @Override
+    public Map<UUID, SubscriptionBaseBundle> getBundles() {
+        return accountEventsStreams.getBundles();
+    }
+
+    @Override
+    public Map<UUID, Collection<Entitlement>> getEntitlements() {
+        return entitlements;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultAccountEntitlements{");
+        sb.append("accountEventsStreams=").append(accountEventsStreams);
+        sb.append(", entitlements=").append(entitlements);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultAccountEntitlements that = (DefaultAccountEntitlements) o;
+
+        if (accountEventsStreams != null ? !accountEventsStreams.equals(that.accountEventsStreams) : that.accountEventsStreams != null) {
+            return false;
+        }
+        if (entitlements != null ? !entitlements.equals(that.entitlements) : that.entitlements != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = accountEventsStreams != null ? accountEventsStreams.hashCode() : 0;
+        result = 31 * result + (entitlements != null ? entitlements.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java
new file mode 100644
index 0000000..d05df80
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultAccountEventsStreams.java
@@ -0,0 +1,108 @@
+/*
+ * 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.entitlement.api.svcs;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class DefaultAccountEventsStreams implements AccountEventsStreams {
+
+    private final Account account;
+    private final Map<UUID, Collection<EventsStream>> eventsStreams;
+    private final Map<UUID, SubscriptionBaseBundle> bundles = new HashMap<UUID, SubscriptionBaseBundle>();
+
+    public DefaultAccountEventsStreams(final Account account,
+                                       final Iterable<SubscriptionBaseBundle> bundles,
+                                       final Map<UUID, Collection<EventsStream>> eventsStreams) {
+        this.account = account;
+        this.eventsStreams = eventsStreams;
+        for (final SubscriptionBaseBundle baseBundle : bundles) {
+            this.bundles.put(baseBundle.getId(), baseBundle);
+        }
+    }
+
+    public DefaultAccountEventsStreams(final Account account) {
+        this(account, ImmutableList.<SubscriptionBaseBundle>of(), ImmutableMap.<UUID, Collection<EventsStream>>of());
+    }
+
+    @Override
+    public Account getAccount() {
+        return account;
+    }
+
+    @Override
+    public Map<UUID, SubscriptionBaseBundle> getBundles() {
+        return bundles;
+    }
+
+    @Override
+    public Map<UUID, Collection<EventsStream>> getEventsStreams() {
+        return eventsStreams;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultAccountEventsStreams{");
+        sb.append("account=").append(account);
+        sb.append(", eventsStreams=").append(eventsStreams);
+        sb.append(", bundles=").append(bundles);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultAccountEventsStreams that = (DefaultAccountEventsStreams) o;
+
+        if (account != null ? !account.equals(that.account) : that.account != null) {
+            return false;
+        }
+        if (bundles != null ? !bundles.equals(that.bundles) : that.bundles != null) {
+            return false;
+        }
+        if (eventsStreams != null ? !eventsStreams.equals(that.eventsStreams) : that.eventsStreams != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = account != null ? account.hashCode() : 0;
+        result = 31 * result + (eventsStreams != null ? eventsStreams.hashCode() : 0);
+        result = 31 * result + (bundles != null ? bundles.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
new file mode 100644
index 0000000..f941e9b
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/svcs/DefaultEntitlementInternalApi.java
@@ -0,0 +1,101 @@
+/*
+ * 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.entitlement.api.svcs;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import com.ning.billing.account.api.AccountInternalApi;
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.AccountEntitlements;
+import com.ning.billing.entitlement.AccountEventsStreams;
+import com.ning.billing.entitlement.EntitlementInternalApi;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.DefaultEntitlement;
+import com.ning.billing.entitlement.api.Entitlement;
+import com.ning.billing.entitlement.api.EntitlementApi;
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.api.EntitlementDateHelper;
+import com.ning.billing.entitlement.block.BlockingChecker;
+import com.ning.billing.entitlement.dao.BlockingStateDao;
+import com.ning.billing.entitlement.engine.core.EntitlementUtils;
+import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
+import com.ning.billing.notificationq.api.NotificationQueueService;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
+import com.ning.billing.util.callcontext.TenantContext;
+
+public class DefaultEntitlementInternalApi implements EntitlementInternalApi {
+
+    private final EntitlementApi entitlementApi;
+    private final SubscriptionBaseInternalApi subscriptionInternalApi;
+    private final Clock clock;
+    private final InternalCallContextFactory internalCallContextFactory;
+    private final BlockingChecker checker;
+    private final BlockingStateDao blockingStateDao;
+    private final EntitlementDateHelper dateHelper;
+    private final EventsStreamBuilder eventsStreamBuilder;
+    private final EntitlementUtils entitlementUtils;
+    private final NotificationQueueService notificationQueueService;
+
+    @Inject
+    public DefaultEntitlementInternalApi(final EntitlementApi entitlementApi, final InternalCallContextFactory internalCallContextFactory,
+                                         final SubscriptionBaseInternalApi subscriptionInternalApi,
+                                         final AccountInternalApi accountApi, final BlockingStateDao blockingStateDao, final Clock clock,
+                                         final BlockingChecker checker, final NotificationQueueService notificationQueueService,
+                                         final EventsStreamBuilder eventsStreamBuilder, final EntitlementUtils entitlementUtils) {
+        this.entitlementApi = entitlementApi;
+        this.internalCallContextFactory = internalCallContextFactory;
+        this.subscriptionInternalApi = subscriptionInternalApi;
+        this.clock = clock;
+        this.checker = checker;
+        this.blockingStateDao = blockingStateDao;
+        this.notificationQueueService = notificationQueueService;
+        this.eventsStreamBuilder = eventsStreamBuilder;
+        this.entitlementUtils = entitlementUtils;
+        this.dateHelper = new EntitlementDateHelper(accountApi, clock);
+    }
+
+    @Override
+    public AccountEntitlements getAllEntitlementsForAccountId(final UUID accountId, final TenantContext tenantContext) throws EntitlementApiException {
+        final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(accountId, tenantContext);
+
+        final AccountEventsStreams accountEventsStreams = eventsStreamBuilder.buildForAccount(context);
+
+        final Map<UUID, Collection<Entitlement>> entitlementsPerBundle = new HashMap<UUID, Collection<Entitlement>>();
+        for (final UUID bundleId : accountEventsStreams.getEventsStreams().keySet()) {
+            if (entitlementsPerBundle.get(bundleId) == null) {
+                entitlementsPerBundle.put(bundleId, new LinkedList<Entitlement>());
+            }
+
+            for (final EventsStream eventsStream : accountEventsStreams.getEventsStreams().get(bundleId)) {
+                final Entitlement entitlement = new DefaultEntitlement(eventsStream, eventsStreamBuilder, entitlementApi,
+                                                                       blockingStateDao, subscriptionInternalApi, checker, notificationQueueService,
+                                                                       entitlementUtils, dateHelper, clock, internalCallContextFactory);
+                entitlementsPerBundle.get(bundleId).add(entitlement);
+            }
+        }
+
+        return new DefaultAccountEntitlements(accountEventsStreams, entitlementsPerBundle);
+    }
+}
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/block/BlockingChecker.java b/entitlement/src/main/java/com/ning/billing/entitlement/block/BlockingChecker.java
index c466378..e91dc45 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/block/BlockingChecker.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/block/BlockingChecker.java
@@ -16,11 +16,13 @@
 
 package com.ning.billing.entitlement.block;
 
+import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.entitlement.api.Blockable;
 import com.ning.billing.entitlement.api.BlockingApiException;
+import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 
 public interface BlockingChecker {
@@ -33,15 +35,17 @@ public interface BlockingChecker {
     public static final Object ACTION_ENTITLEMENT = "Entitlement";
     public static final Object ACTION_BILLING = "Billing";
 
-
     public interface BlockingAggregator {
+
         public boolean isBlockChange();
+
         public boolean isBlockEntitlement();
+
         public boolean isBlockBilling();
     }
 
-    // Only throws if we can't find the blockable enties
-    public BlockingAggregator getBlockedStatus(Blockable blockable, InternalTenantContext context) throws BlockingApiException;
+    public BlockingAggregator getBlockedStatus(List<BlockingState> currentAccountEntitlementStatePerService, List<BlockingState> currentBundleEntitlementStatePerService,
+                                               List<BlockingState> currentSubscriptionEntitlementStatePerService, InternalTenantContext internalTenantContext);
 
     public BlockingAggregator getBlockedStatus(final UUID blockableId, final BlockingStateType type, final InternalTenantContext context) throws BlockingApiException;
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/block/DefaultBlockingChecker.java b/entitlement/src/main/java/com/ning/billing/entitlement/block/DefaultBlockingChecker.java
index 0cfe5ea..9d87e3c 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/block/DefaultBlockingChecker.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/block/DefaultBlockingChecker.java
@@ -19,6 +19,8 @@ package com.ning.billing.entitlement.block;
 import java.util.List;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
+
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.callcontext.InternalTenantContext;
@@ -32,6 +34,7 @@ import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
 
+import com.google.common.collect.ImmutableList;
 import com.google.inject.Inject;
 
 public class DefaultBlockingChecker implements BlockingChecker {
@@ -110,7 +113,6 @@ public class DefaultBlockingChecker implements BlockingChecker {
         return result;
     }
 
-
     private DefaultBlockingAggregator getBlockedStateBundleId(final UUID bundleId, final InternalTenantContext context) throws BlockingApiException {
 
         final SubscriptionBaseBundle bundle;
@@ -122,7 +124,6 @@ public class DefaultBlockingChecker implements BlockingChecker {
         }
     }
 
-
     private DefaultBlockingAggregator getBlockedStateBundle(final SubscriptionBaseBundle bundle, final InternalTenantContext context) {
         final DefaultBlockingAggregator result = getBlockedStateAccountId(bundle.getAccountId(), context);
         final DefaultBlockingAggregator bundleState = getBlockedStateForId(bundle.getId(), BlockingStateType.SUBSCRIPTION_BUNDLE, context);
@@ -143,14 +144,21 @@ public class DefaultBlockingChecker implements BlockingChecker {
         return getBlockedStateForId(accountId, BlockingStateType.ACCOUNT, context);
     }
 
-    private DefaultBlockingAggregator getBlockedStateForId(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        final DefaultBlockingAggregator result = new DefaultBlockingAggregator();
+    private DefaultBlockingAggregator getBlockedStateForId(@Nullable final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
+        // Last states across services
+        final List<BlockingState> blockableState;
         if (blockableId != null) {
-            // Last states across services
-            final List<BlockingState> blockableState = dao.getBlockingState(blockableId, blockingStateType, context);
-            for (BlockingState cur : blockableState) {
-                result.or(cur);
-            }
+            blockableState = dao.getBlockingState(blockableId, blockingStateType, context);
+        } else {
+            blockableState = ImmutableList.<BlockingState>of();
+        }
+        return getBlockedState(blockableState);
+    }
+
+    private DefaultBlockingAggregator getBlockedState(final Iterable<BlockingState> currentBlockableStatePerService) {
+        final DefaultBlockingAggregator result = new DefaultBlockingAggregator();
+        for (final BlockingState cur : currentBlockableStatePerService) {
+            result.or(cur);
         }
         return result;
     }
@@ -167,14 +175,11 @@ public class DefaultBlockingChecker implements BlockingChecker {
     }
 
     @Override
-    public BlockingAggregator getBlockedStatus(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
-            if (blockable instanceof SubscriptionBase) {
-                return getBlockedStateSubscription((SubscriptionBase) blockable, context);
-            } else if (blockable instanceof SubscriptionBaseBundle) {
-                return getBlockedStateBundle((SubscriptionBaseBundle) blockable, context);
-            } else { //(blockable instanceof Account) {
-                return getBlockedStateAccount((Account) blockable, context);
-            }
+    public BlockingAggregator getBlockedStatus(final List<BlockingState> accountEntitlementStates, final List<BlockingState> bundleEntitlementStates, final List<BlockingState> subscriptionEntitlementStates, final InternalTenantContext internalTenantContext) {
+        final DefaultBlockingAggregator result = getBlockedState(subscriptionEntitlementStates);
+        result.or(getBlockedState(bundleEntitlementStates));
+        result.or(getBlockedState(accountEntitlementStates));
+        return result;
     }
 
     @Override
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
new file mode 100644
index 0000000..604b488
--- /dev/null
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/dao/OptimizedProxyBlockingStateDao.java
@@ -0,0 +1,75 @@
+/*
+ * 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.entitlement.dao;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import org.skife.jdbi.v2.IDBI;
+
+import com.ning.billing.callcontext.InternalTenantContext;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.EventsStream;
+import com.ning.billing.entitlement.api.BlockingState;
+import com.ning.billing.entitlement.api.BlockingStateType;
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
+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.util.cache.CacheControllerDispatcher;
+import com.ning.billing.util.dao.NonEntityDao;
+
+import com.google.common.collect.ImmutableList;
+
+public class OptimizedProxyBlockingStateDao extends ProxyBlockingStateDao {
+
+    public OptimizedProxyBlockingStateDao(final EventsStreamBuilder eventsStreamBuilder, final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
+                                          final IDBI dbi, final Clock clock, final CacheControllerDispatcher cacheControllerDispatcher,
+                                          final NonEntityDao nonEntityDao) {
+        super(eventsStreamBuilder, subscriptionBaseInternalApi, dbi, clock, cacheControllerDispatcher, nonEntityDao);
+    }
+
+    // Special signature for EventsStreamBuilder to save some DAO calls
+    public List<BlockingState> getBlockingHistoryForService(final List<BlockingState> blockingStatesOnDisk,
+                                                            final SubscriptionBaseBundle bundle,
+                                                            @Nullable final SubscriptionBase baseSubscription,
+                                                            final SubscriptionBase subscription,
+                                                            final List<SubscriptionBase> allSubscriptionsForBundle,
+                                                            final InternalTenantContext context) throws EntitlementApiException {
+        // blockable id points to a subscription, but make sure it's an add-on
+        if (!ProductCategory.ADD_ON.equals(subscription.getCategory())) {
+            // blockable id points to a base or standalone subscription, there is nothing to do
+            return blockingStatesOnDisk;
+        }
+
+        // Find all base entitlements that we care about (for which we want to find future cancelled add-ons)
+        final Iterable<EventsStream> eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(bundle,
+                                                                                                                            baseSubscription,
+                                                                                                                            allSubscriptionsForBundle,
+                                                                                                                            context));
+
+        return addBlockingStatesNotOnDisk(subscription.getId(),
+                                          BlockingStateType.SUBSCRIPTION,
+                                          new LinkedList<BlockingState>(blockingStatesOnDisk),
+                                          ImmutableList.<SubscriptionBase>of(baseSubscription),
+                                          eventsStreams);
+    }
+}
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 8c96ed6..7662f22 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
@@ -20,6 +20,7 @@ import java.util.Collection;
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
@@ -36,12 +37,12 @@ import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.clock.Clock;
 import com.ning.billing.entitlement.EntitlementService;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.api.DefaultEntitlementApi;
 import com.ning.billing.entitlement.api.Entitlement.EntitlementState;
 import com.ning.billing.entitlement.api.EntitlementApiException;
-import com.ning.billing.entitlement.engine.core.EventsStream;
 import com.ning.billing.entitlement.engine.core.EventsStreamBuilder;
 import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
@@ -61,7 +62,7 @@ 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>() {
+    public static final Ordering<BlockingState> BLOCKING_STATE_ORDERING = Ordering.<BlockingState>from(new Comparator<BlockingState>() {
         @Override
         public int compare(final BlockingState o1, final BlockingState o2) {
             // effective_date column NOT NULL
@@ -96,10 +97,11 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
         }
     });
 
-    private final EventsStreamBuilder eventsStreamBuilder;
     private final SubscriptionBaseInternalApi subscriptionInternalApi;
     private final Clock clock;
-    private final DefaultBlockingStateDao delegate;
+
+    protected final EventsStreamBuilder eventsStreamBuilder;
+    protected final DefaultBlockingStateDao delegate;
 
     @Inject
     public ProxyBlockingStateDao(final EventsStreamBuilder eventsStreamBuilder, final SubscriptionBaseInternalApi subscriptionBaseInternalApi,
@@ -199,11 +201,12 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
 
         // Find all base entitlements that we care about (for which we want to find future cancelled add-ons)
         final Iterable<SubscriptionBase> baseSubscriptionsToConsider;
+        final Iterable<EventsStream> eventsStreams;
         try {
             if (blockingStateType == null) {
                 // We're coming from getBlockingAllForAccountRecordId
-                final Iterable<SubscriptionBase> subscriptions = Iterables.<SubscriptionBase>concat(subscriptionInternalApi.getSubscriptionsForAccount(context).values());
-                baseSubscriptionsToConsider = Iterables.<SubscriptionBase>filter(subscriptions,
+                final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionInternalApi.getSubscriptionsForAccount(context);
+                baseSubscriptionsToConsider = Iterables.<SubscriptionBase>filter(Iterables.<SubscriptionBase>concat(subscriptions.values()),
                                                                                  new Predicate<SubscriptionBase>() {
                                                                                      @Override
                                                                                      public boolean apply(final SubscriptionBase input) {
@@ -211,6 +214,7 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
                                                                                                 !EntitlementState.CANCELLED.equals(input.getState());
                                                                                      }
                                                                                  });
+                eventsStreams = Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(subscriptions, context).getEventsStreams().values());
             } else if (BlockingStateType.SUBSCRIPTION.equals(blockingStateType)) {
                 // We're coming from getBlockingHistoryForService / getBlockingAll
                 final SubscriptionBase subscription = subscriptionInternalApi.getSubscriptionFromId(blockableId, context);
@@ -219,6 +223,7 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
                 if (ProductCategory.ADD_ON.equals(subscription.getCategory())) {
                     final SubscriptionBase baseSubscription = subscriptionInternalApi.getBaseSubscription(subscription.getBundleId(), context);
                     baseSubscriptionsToConsider = ImmutableList.<SubscriptionBase>of(baseSubscription);
+                    eventsStreams = ImmutableList.<EventsStream>of(eventsStreamBuilder.buildForEntitlement(baseSubscription, context));
                 } else {
                     // blockable id points to a base or standalone subscription, there is nothing to do
                     // Simply return the sorted list
@@ -232,21 +237,32 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
         } catch (SubscriptionBaseApiException e) {
             log.error("Error retrieving subscriptions for account record id " + context.getAccountRecordId(), e);
             throw new RuntimeException(e);
+        } catch (EntitlementApiException e) {
+            log.error("Error computing blocking states for addons for account record id " + context.getAccountRecordId(), e);
+            throw new RuntimeException(e);
         }
 
+        return addBlockingStatesNotOnDisk(blockableId, blockingStateType, blockingStatesOnDiskCopy, baseSubscriptionsToConsider, eventsStreams);
+    }
+
+    protected List<BlockingState> addBlockingStatesNotOnDisk(@Nullable final UUID blockableId,
+                                                             @Nullable final BlockingStateType blockingStateType,
+                                                             final Collection<BlockingState> blockingStatesOnDiskCopy,
+                                                             final Iterable<SubscriptionBase> baseSubscriptionsToConsider,
+                                                             final Iterable<EventsStream> eventsStreams) {
         // Retrieve the cancellation blocking state on disk, if it exists (will be used later)
         final BlockingState cancellationBlockingStateOnDisk = findEntitlementCancellationBlockingState(blockableId, blockingStatesOnDiskCopy);
 
         // Compute the blocking states not on disk for all base subscriptions
         final DateTime now = clock.getUTCNow();
         for (final SubscriptionBase baseSubscription : baseSubscriptionsToConsider) {
-            final EventsStream eventsStream;
-            try {
-                eventsStream = eventsStreamBuilder.buildForEntitlement(baseSubscription.getId(), context);
-            } catch (EntitlementApiException e) {
-                log.error("Error computing blocking states for addons for account record id " + context.getAccountRecordId(), e);
-                throw new RuntimeException(e);
-            }
+            final EventsStream eventsStream = Iterables.<EventsStream>find(eventsStreams,
+                                                                           new Predicate<EventsStream>() {
+                                                                               @Override
+                                                                               public boolean apply(final EventsStream input) {
+                                                                                   return input.getSubscription().getId().equals(baseSubscription.getId());
+                                                                               }
+                                                                           });
 
             // First, check to see if the base entitlement is cancelled. If so, cancel the
             final Collection<BlockingState> blockingStatesNotOnDisk = eventsStream.computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
index 0c6e8eb..f9f9cfb 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventsStreamBuilder.java
@@ -16,12 +16,19 @@
 
 package com.ning.billing.entitlement.engine.core;
 
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
+import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import org.skife.jdbi.v2.IDBI;
+
 import com.ning.billing.ObjectType;
 import com.ning.billing.account.api.Account;
 import com.ning.billing.account.api.AccountApiException;
@@ -29,48 +36,61 @@ import com.ning.billing.account.api.AccountInternalApi;
 import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.clock.Clock;
+import com.ning.billing.entitlement.AccountEventsStreams;
 import com.ning.billing.entitlement.EntitlementService;
-import com.ning.billing.entitlement.api.BlockingApiException;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.api.svcs.DefaultAccountEventsStreams;
 import com.ning.billing.entitlement.block.BlockingChecker;
-import com.ning.billing.entitlement.block.BlockingChecker.BlockingAggregator;
-import com.ning.billing.entitlement.dao.BlockingStateDao;
+import com.ning.billing.entitlement.dao.DefaultBlockingStateDao;
+import com.ning.billing.entitlement.dao.OptimizedProxyBlockingStateDao;
 import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
+import com.ning.billing.util.cache.CacheControllerDispatcher;
 import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.callcontext.TenantContext;
+import com.ning.billing.util.dao.NonEntityDao;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
+import static com.ning.billing.entitlement.dao.ProxyBlockingStateDao.BLOCKING_STATE_ORDERING;
+
 @Singleton
 public class EventsStreamBuilder {
 
     private final AccountInternalApi accountInternalApi;
     private final SubscriptionBaseInternalApi subscriptionInternalApi;
     private final BlockingChecker checker;
-    private final BlockingStateDao blockingStateDao;
+    private final OptimizedProxyBlockingStateDao blockingStateDao;
+    private final DefaultBlockingStateDao defaultBlockingStateDao;
     private final Clock clock;
     private final InternalCallContextFactory internalCallContextFactory;
 
     @Inject
     public EventsStreamBuilder(final AccountInternalApi accountInternalApi, final SubscriptionBaseInternalApi subscriptionInternalApi,
-                               final BlockingChecker checker, final BlockingStateDao blockingStateDao,
-                               final Clock clock, final InternalCallContextFactory internalCallContextFactory) {
+                               final BlockingChecker checker, final IDBI dbi, final Clock clock,
+                               final CacheControllerDispatcher cacheControllerDispatcher,
+                               final NonEntityDao nonEntityDao,
+                               final InternalCallContextFactory internalCallContextFactory) {
         this.accountInternalApi = accountInternalApi;
         this.subscriptionInternalApi = subscriptionInternalApi;
         this.checker = checker;
-        this.blockingStateDao = blockingStateDao;
         this.clock = clock;
         this.internalCallContextFactory = internalCallContextFactory;
+
+        this.defaultBlockingStateDao = new DefaultBlockingStateDao(dbi, clock, cacheControllerDispatcher, nonEntityDao);
+        this.blockingStateDao = new OptimizedProxyBlockingStateDao(this, subscriptionInternalApi, dbi, clock, cacheControllerDispatcher, nonEntityDao);
     }
 
     public EventsStream refresh(final EventsStream eventsStream, final TenantContext tenantContext) throws EntitlementApiException {
-        return buildForEntitlement(eventsStream.getSubscription().getId(), tenantContext);
+        return buildForEntitlement(eventsStream.getEntitlementId(), tenantContext);
     }
 
     public EventsStream buildForBaseSubscription(final UUID bundleId, final TenantContext tenantContext) throws EntitlementApiException {
@@ -90,6 +110,107 @@ public class EventsStreamBuilder {
         return buildForEntitlement(entitlementId, internalTenantContext);
     }
 
+    public AccountEventsStreams buildForAccount(final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+        // Retrieve the subscriptions (map bundle id -> subscriptions)
+        final Map<UUID, List<SubscriptionBase>> subscriptions = subscriptionInternalApi.getSubscriptionsForAccount(internalTenantContext);
+        return buildForAccount(subscriptions, internalTenantContext);
+    }
+
+    // Special signature for ProxyBlockingStateDao to save a DAO call
+    public AccountEventsStreams buildForAccount(final Map<UUID, List<SubscriptionBase>> subscriptions, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+        // Retrieve the account
+        final Account account;
+        try {
+            account = accountInternalApi.getAccountByRecordId(internalTenantContext.getAccountRecordId(), internalTenantContext);
+        } catch (AccountApiException e) {
+            throw new EntitlementApiException(e);
+        }
+
+        return buildForAccount(account, subscriptions, internalTenantContext);
+    }
+
+    private AccountEventsStreams buildForAccount(final Account account, final Map<UUID, List<SubscriptionBase>> subscriptions, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+        if (subscriptions.isEmpty()) {
+            // Bail early
+            return new DefaultAccountEventsStreams(account);
+        }
+
+        // Retrieve the bundles
+        final List<SubscriptionBaseBundle> bundles = subscriptionInternalApi.getBundlesForAccount(account.getId(), internalTenantContext);
+        // Map bundle id -> bundles
+        final Map<UUID, SubscriptionBaseBundle> bundlesPerId = new HashMap<UUID, SubscriptionBaseBundle>();
+        for (final SubscriptionBaseBundle bundle : bundles) {
+            bundlesPerId.put(bundle.getId(), bundle);
+        }
+
+        // Retrieve the blocking states
+        final List<BlockingState> blockingStatesForAccount = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingAllForAccountRecordId(internalTenantContext));
+
+        // Optimization: build lookup tables for entitlement states
+        final List<BlockingState> accountEntitlementStates = new LinkedList<BlockingState>();
+        final Map<UUID, List<BlockingState>> entitlementStatesPerSubscription = new HashMap<UUID, List<BlockingState>>();
+        final Map<UUID, List<BlockingState>> entitlementStatesPerBundle = new HashMap<UUID, List<BlockingState>>();
+        for (final BlockingState blockingState : blockingStatesForAccount) {
+            if (!EntitlementService.ENTITLEMENT_SERVICE_NAME.equals(blockingState.getService())) {
+                continue;
+            } else if (BlockingStateType.SUBSCRIPTION.equals(blockingState.getType())) {
+                if (entitlementStatesPerSubscription.get(blockingState.getBlockedId()) == null) {
+                    entitlementStatesPerSubscription.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
+                }
+                entitlementStatesPerSubscription.get(blockingState.getBlockedId()).add(blockingState);
+            } else if (BlockingStateType.SUBSCRIPTION_BUNDLE.equals(blockingState.getType())) {
+                if (entitlementStatesPerBundle.get(blockingState.getBlockedId()) == null) {
+                    entitlementStatesPerBundle.put(blockingState.getBlockedId(), new LinkedList<BlockingState>());
+                }
+                entitlementStatesPerBundle.get(blockingState.getBlockedId()).add(blockingState);
+            } else if (BlockingStateType.ACCOUNT.equals(blockingState.getType()) &&
+                       account.getId().equals(blockingState.getBlockedId())) {
+                accountEntitlementStates.add(blockingState);
+            }
+        }
+
+        // Build the EventsStream objects
+        final Map<UUID, Collection<EventsStream>> entitlementsPerBundle = new HashMap<UUID, Collection<EventsStream>>();
+        for (final UUID bundleId : subscriptions.keySet()) {
+            final SubscriptionBaseBundle bundle = bundlesPerId.get(bundleId);
+            final List<SubscriptionBase> allSubscriptionsForBundle = subscriptions.get(bundleId);
+            final SubscriptionBase baseSubscription = Iterables.<SubscriptionBase>tryFind(allSubscriptionsForBundle,
+                                                                                          new Predicate<SubscriptionBase>() {
+                                                                                              @Override
+                                                                                              public boolean apply(final SubscriptionBase input) {
+                                                                                                  return ProductCategory.BASE.equals(input.getLastActiveProduct().getCategory());
+                                                                                              }
+                                                                                          }).orNull();
+            final List<BlockingState> bundleEntitlementStates = Objects.firstNonNull(entitlementStatesPerBundle.get(bundleId), ImmutableList.<BlockingState>of());
+
+            if (entitlementsPerBundle.get(bundleId) == null) {
+                entitlementsPerBundle.put(bundleId, new LinkedList<EventsStream>());
+            }
+
+            for (final SubscriptionBase subscription : allSubscriptionsForBundle) {
+                final List<BlockingState> subscriptionBlockingStatesOnDisk = Objects.firstNonNull(entitlementStatesPerSubscription.get(subscription.getId()), ImmutableList.<BlockingState>of());
+
+                // We cannot use blockingStatesForAccount here: we need subscriptionEntitlementStates to contain the events not on disk when building an EventsStream
+                // for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
+                // create EventsStream objects. To avoid an infinite recursion, bypass ProxyBlockingStateDao when it's not
+                // needed, i.e. if this EventStream is for a standalone or a base subscription
+                final List<BlockingState> subscriptionEntitlementStates = (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) ?
+                                                                          subscriptionBlockingStatesOnDisk :
+                                                                          blockingStateDao.getBlockingHistoryForService(subscriptionBlockingStatesOnDisk,
+                                                                                                                        bundle,
+                                                                                                                        baseSubscription,
+                                                                                                                        subscription,
+                                                                                                                        allSubscriptionsForBundle,
+                                                                                                                        internalTenantContext);
+
+                final EventsStream eventStream = buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, subscriptionEntitlementStates, bundleEntitlementStates, accountEntitlementStates, internalTenantContext);
+                entitlementsPerBundle.get(bundleId).add(eventStream);
+            }
+        }
+
+        return new DefaultAccountEventsStreams(account, bundles, entitlementsPerBundle);
+    }
+
     public EventsStream buildForEntitlement(final UUID entitlementId, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
         final SubscriptionBaseBundle bundle;
         final SubscriptionBase subscription;
@@ -113,8 +234,29 @@ public class EventsStreamBuilder {
         return buildForEntitlement(bundle, baseSubscription, subscription, allSubscriptionsForBundle, internalTenantContext);
     }
 
+    // Special signature for ProxyBlockingStateDao to save some DAO calls
+    public EventsStream buildForEntitlement(final SubscriptionBase subscription, final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+        final SubscriptionBaseBundle bundle;
+        try {
+            bundle = subscriptionInternalApi.getBundleFromId(subscription.getBundleId(), internalTenantContext);
+        } catch (SubscriptionBaseApiException e) {
+            throw new EntitlementApiException(e);
+        }
+
+        final List<SubscriptionBase> allSubscriptionsForBundle = subscriptionInternalApi.getSubscriptionsForBundle(subscription.getBundleId(), internalTenantContext);
+        return buildForEntitlement(bundle, subscription, subscription, allSubscriptionsForBundle, internalTenantContext);
+    }
+
+    // Special signature for OptimizedProxyBlockingStateDao to save some DAO calls
+    public EventsStream buildForEntitlement(final SubscriptionBaseBundle bundle,
+                                            final SubscriptionBase subscription,
+                                            final List<SubscriptionBase> allSubscriptionsForBundle,
+                                            final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+        return buildForEntitlement(bundle, subscription, subscription, allSubscriptionsForBundle, internalTenantContext);
+    }
+
     private EventsStream buildForEntitlement(final SubscriptionBaseBundle bundle,
-                                             final SubscriptionBase baseSubscription,
+                                             @Nullable final SubscriptionBase baseSubscription,
                                              final SubscriptionBase subscription,
                                              final List<SubscriptionBase> allSubscriptionsForBundle,
                                              final InternalTenantContext internalTenantContext) throws EntitlementApiException {
@@ -125,27 +267,45 @@ public class EventsStreamBuilder {
             throw new EntitlementApiException(e);
         }
 
-        final List<BlockingState> subscriptionEntitlementStates = blockingStateDao.getBlockingHistoryForService(subscription.getId(), BlockingStateType.SUBSCRIPTION, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext);
-        final List<BlockingState> bundleEntitlementStates = blockingStateDao.getBlockingHistoryForService(bundle.getId(), BlockingStateType.SUBSCRIPTION_BUNDLE, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext);
-        final List<BlockingState> accountEntitlementStates = blockingStateDao.getBlockingHistoryForService(account.getId(), BlockingStateType.ACCOUNT, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext);
+        final List<BlockingState> bundleEntitlementStates = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(bundle.getId(), BlockingStateType.SUBSCRIPTION_BUNDLE, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
+        final List<BlockingState> accountEntitlementStates = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(account.getId(), BlockingStateType.ACCOUNT, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
+        final ImmutableList<BlockingState> subscriptionEntitlementStatesOnDisk = BLOCKING_STATE_ORDERING.immutableSortedCopy(defaultBlockingStateDao.getBlockingHistoryForService(subscription.getId(), BlockingStateType.SUBSCRIPTION, EntitlementService.ENTITLEMENT_SERVICE_NAME, internalTenantContext));
 
-        final BlockingAggregator blockingAggregator;
-        try {
-            blockingAggregator = checker.getBlockedStatus(subscription, internalTenantContext);
-        } catch (BlockingApiException e) {
-            throw new EntitlementApiException(e);
-        }
+        // We need subscriptionEntitlementStates to contain the events not on disk when building an EventsStream
+        // for an add-on - which means going through the magic of ProxyBlockingStateDao, which will recursively
+        // create EventsStream objects. To avoid an infinite recursion, bypass ProxyBlockingStateDao when it's not
+        // needed, i.e. if this EventStream is for a standalone or a base subscription
+        final List<BlockingState> subscriptionEntitlementStates = (baseSubscription == null || subscription.getId().equals(baseSubscription.getId())) ?
+                                                                  subscriptionEntitlementStatesOnDisk :
+                                                                  blockingStateDao.getBlockingHistoryForService(subscriptionEntitlementStatesOnDisk,
+                                                                                                                bundle,
+                                                                                                                baseSubscription,
+                                                                                                                subscription,
+                                                                                                                allSubscriptionsForBundle,
+                                                                                                                internalTenantContext);
 
-        return new EventsStream(account,
-                                bundle,
-                                subscriptionEntitlementStates,
-                                bundleEntitlementStates,
-                                accountEntitlementStates,
-                                blockingAggregator,
-                                baseSubscription,
-                                subscription,
-                                allSubscriptionsForBundle,
-                                internalTenantContext,
-                                clock.getUTCNow());
+        return buildForEntitlement(account, bundle, baseSubscription, subscription, allSubscriptionsForBundle, subscriptionEntitlementStates, bundleEntitlementStates, accountEntitlementStates, internalTenantContext);
+    }
+
+    private EventsStream buildForEntitlement(final Account account,
+                                             final SubscriptionBaseBundle bundle,
+                                             @Nullable final SubscriptionBase baseSubscription,
+                                             final SubscriptionBase subscription,
+                                             final List<SubscriptionBase> allSubscriptionsForBundle,
+                                             final List<BlockingState> subscriptionEntitlementStates,
+                                             final List<BlockingState> bundleEntitlementStates,
+                                             final List<BlockingState> accountEntitlementStates,
+                                             final InternalTenantContext internalTenantContext) throws EntitlementApiException {
+        return new DefaultEventsStream(account,
+                                       bundle,
+                                       subscriptionEntitlementStates,
+                                       bundleEntitlementStates,
+                                       accountEntitlementStates,
+                                       checker,
+                                       baseSubscription,
+                                       subscription,
+                                       allSubscriptionsForBundle,
+                                       internalTenantContext,
+                                       clock.getUTCNow());
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
index d95248b..975419d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/DefaultEntitlementModule.java
@@ -19,11 +19,13 @@ package com.ning.billing.entitlement.glue;
 import org.skife.config.ConfigSource;
 
 import com.ning.billing.entitlement.DefaultEntitlementService;
+import com.ning.billing.entitlement.EntitlementInternalApi;
 import com.ning.billing.entitlement.EntitlementService;
 import com.ning.billing.entitlement.api.DefaultEntitlementApi;
 import com.ning.billing.entitlement.api.DefaultSubscriptionApi;
 import com.ning.billing.entitlement.api.EntitlementApi;
 import com.ning.billing.entitlement.api.SubscriptionApi;
+import com.ning.billing.entitlement.api.svcs.DefaultEntitlementInternalApi;
 import com.ning.billing.entitlement.api.svcs.DefaultInternalBlockingApi;
 import com.ning.billing.entitlement.block.BlockingChecker;
 import com.ning.billing.entitlement.block.DefaultBlockingChecker;
@@ -38,7 +40,6 @@ import com.google.inject.AbstractModule;
 
 public class DefaultEntitlementModule extends AbstractModule implements EntitlementModule {
 
-
     public DefaultEntitlementModule(final ConfigSource configSource) {
     }
 
@@ -47,6 +48,7 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
         installBlockingStateDao();
         installBlockingApi();
         installEntitlementApi();
+        installEntitlementInternalApi();
         installSubscriptionApi();
         installBlockingChecker();
         bind(EntitlementService.class).to(DefaultEntitlementService.class).asEagerSingleton();
@@ -70,6 +72,11 @@ public class DefaultEntitlementModule extends AbstractModule implements Entitlem
     }
 
     @Override
+    public void installEntitlementInternalApi() {
+        bind(EntitlementInternalApi.class).to(DefaultEntitlementInternalApi.class).asEagerSingleton();
+    }
+
+    @Override
     public void installSubscriptionApi() {
         bind(SubscriptionApi.class).to(DefaultSubscriptionApi.class).asEagerSingleton();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/block/MockBlockingChecker.java b/entitlement/src/test/java/com/ning/billing/entitlement/block/MockBlockingChecker.java
index 1a97df2..c5232db 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/block/MockBlockingChecker.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/block/MockBlockingChecker.java
@@ -16,17 +16,19 @@
 
 package com.ning.billing.entitlement.block;
 
+import java.util.List;
 import java.util.UUID;
 
 import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.entitlement.api.Blockable;
 import com.ning.billing.entitlement.api.BlockingApiException;
+import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 
 public class MockBlockingChecker implements BlockingChecker {
 
     @Override
-    public BlockingAggregator getBlockedStatus(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
+    public BlockingAggregator getBlockedStatus(final List<BlockingState> accountEntitlementStates, final List<BlockingState> bundleEntitlementStates, final List<BlockingState> subscriptionEntitlementStates, final InternalTenantContext internalTenantContext) {
         return null;
     }
 
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 b17e7d8..57073a4 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
@@ -23,11 +23,15 @@ import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
+import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.junction.DefaultBlockingState;
+import com.ning.billing.util.callcontext.CallOrigin;
+import com.ning.billing.util.callcontext.UserType;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
@@ -77,6 +81,9 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         final boolean blockEntitlement = false;
         final boolean blockBilling = false;
 
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), "TestBlockingApi", CallOrigin.TEST, UserType.SYSTEM, UUID.randomUUID());
+
         testListener.pushExpectedEvent(NextEvent.BLOCK);
         final BlockingState state1 = new DefaultBlockingState(uuid, BlockingStateType.ACCOUNT, overdueStateName, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
         blockingInternalApi.setBlockingState(state1, internalCallContext);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
index a0c387c..78ba189 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/core/TestEntitlementUtils.java
@@ -29,6 +29,7 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.BillingActionPolicy;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
@@ -36,6 +37,7 @@ 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.EntitlementTestSuiteWithEmbeddedDB;
+import com.ning.billing.entitlement.EventsStream;
 import com.ning.billing.entitlement.api.BlockingState;
 import com.ning.billing.entitlement.api.BlockingStateType;
 import com.ning.billing.entitlement.api.DefaultEntitlement;
@@ -46,6 +48,8 @@ import com.ning.billing.entitlement.api.EntitlementApiException;
 import com.ning.billing.entitlement.dao.BlockingStateSqlDao;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 
 public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
@@ -354,7 +358,14 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
     // Test the "read" path
     private void checkFutureBlockingStatesToCancel(final DefaultEntitlement baseEntitlement, @Nullable final DefaultEntitlement addOnEntitlement, @Nullable final DateTime effectiveCancellationDateTime) throws EntitlementApiException {
-        final Collection<BlockingState> blockingStatesForCancellation = computeFutureBlockingStatesForAssociatedAddons(baseEntitlement);
+        final Collection<BlockingState> blockingStatesForCancellationViaEntitlement = computeFutureBlockingStatesForAssociatedAddonsViaEntitlement(baseEntitlement);
+        doCheckFutureBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, blockingStatesForCancellationViaEntitlement);
+
+        final Collection<BlockingState> blockingStatesForCancellationViaAccount = computeFutureBlockingStatesForAssociatedAddonsViaAccount(baseEntitlement);
+        doCheckFutureBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, blockingStatesForCancellationViaAccount);
+    }
+
+    private void doCheckFutureBlockingStatesToCancel(final DefaultEntitlement addOnEntitlement, final DateTime effectiveCancellationDateTime, final Collection<BlockingState> blockingStatesForCancellation) {
         if (addOnEntitlement == null || effectiveCancellationDateTime == null) {
             Assert.assertEquals(blockingStatesForCancellation.size(), 0);
         } else {
@@ -370,7 +381,14 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
 
     // Test the "write" path
     private void checkActualBlockingStatesToCancel(final DefaultEntitlement baseEntitlement, final DefaultEntitlement addOnEntitlement, @Nullable final DateTime effectiveCancellationDateTime, final boolean approximateDateCheck) throws EntitlementApiException {
-        final Collection<BlockingState> blockingStatesForCancellation = computeBlockingStatesForAssociatedAddons(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+        final Collection<BlockingState> blockingStatesForCancellationViaEntitlement = computeBlockingStatesForAssociatedAddonsViaEntitlement(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+        doCheckActualBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, approximateDateCheck, blockingStatesForCancellationViaEntitlement);
+
+        final Collection<BlockingState> blockingStatesForCancellationViaAccount = computeBlockingStatesForAssociatedAddonsViaAccount(baseEntitlement, Objects.firstNonNull(effectiveCancellationDateTime, initialDate.toDateTimeAtStartOfDay()));
+        doCheckActualBlockingStatesToCancel(addOnEntitlement, effectiveCancellationDateTime, approximateDateCheck, blockingStatesForCancellationViaAccount);
+    }
+
+    private void doCheckActualBlockingStatesToCancel(final DefaultEntitlement addOnEntitlement, final DateTime effectiveCancellationDateTime, final boolean approximateDateCheck, final Collection<BlockingState> blockingStatesForCancellation) {
         if (effectiveCancellationDateTime == null) {
             Assert.assertEquals(blockingStatesForCancellation.size(), 0);
         } else {
@@ -415,13 +433,37 @@ public class TestEntitlementUtils extends EntitlementTestSuiteWithEmbeddedDB {
         Assert.assertEquals(blockingStatesForAddOn.get(0).getStateName(), DefaultEntitlementApi.ENT_STATE_CANCELLED);
     }
 
-    private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddons(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
+    private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaEntitlement(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
         final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(baseEntitlement.getId(), callContext);
         return eventsStream.computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
     }
 
-    private Collection<BlockingState> computeBlockingStatesForAssociatedAddons(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
+    private Collection<BlockingState> computeFutureBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement) throws EntitlementApiException {
+        final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(baseEntitlement.getAccountId(), callContext);
+        final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(context).getEventsStreams().values()),
+                                                                       new Predicate<EventsStream>() {
+                                                                           @Override
+                                                                           public boolean apply(final EventsStream input) {
+                                                                               return input.getSubscription().getId().equals(baseEntitlement.getId());
+                                                                           }
+                                                                       });
+        return eventsStream.computeAddonsBlockingStatesForFutureSubscriptionBaseEvents();
+    }
+
+    private Collection<BlockingState> computeBlockingStatesForAssociatedAddonsViaEntitlement(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
         final EventsStream eventsStream = eventsStreamBuilder.buildForEntitlement(baseEntitlement.getId(), callContext);
         return eventsStream.computeAddonsBlockingStatesForNextSubscriptionBaseEvent(effectiveDate);
     }
+
+    private Collection<BlockingState> computeBlockingStatesForAssociatedAddonsViaAccount(final DefaultEntitlement baseEntitlement, final DateTime effectiveDate) throws EntitlementApiException {
+        final InternalTenantContext context = internalCallContextFactory.createInternalTenantContext(baseEntitlement.getAccountId(), callContext);
+        final EventsStream eventsStream = Iterables.<EventsStream>find(Iterables.<EventsStream>concat(eventsStreamBuilder.buildForAccount(context).getEventsStreams().values()),
+                                                                       new Predicate<EventsStream>() {
+                                                                           @Override
+                                                                           public boolean apply(final EventsStream input) {
+                                                                               return input.getSubscription().getId().equals(baseEntitlement.getId());
+                                                                           }
+                                                                       });
+        return eventsStream.computeAddonsBlockingStatesForNextSubscriptionBaseEvent(effectiveDate);
+    }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
index 2227fd6..99711d9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/EntitlementTestSuiteWithEmbeddedDB.java
@@ -52,6 +52,7 @@ import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.SubscriptionBaseService;
 import com.ning.billing.subscription.engine.core.DefaultSubscriptionBaseService;
 import com.ning.billing.tag.TagInternalApi;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.svcsapi.bus.BusService;
 import com.ning.billing.util.tag.dao.TagDao;
 
@@ -106,6 +107,8 @@ public class EntitlementTestSuiteWithEmbeddedDB extends GuicyKillbillTestSuiteWi
     protected EntitlementUtils entitlementUtils;
     @Inject
     protected EventsStreamBuilder eventsStreamBuilder;
+    @Inject
+    protected InternalCallContextFactory internalCallContextFactory;
 
     protected Catalog catalog;
 

invoice/pom.xml 2(+1 -1)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index e26016f..9bc2960 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>

jaxrs/pom.xml 2(+1 -1)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index c4b1329..e9d7021 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/AccountApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/AccountApiExceptionMapper.java
index 26a1908..a9484fb 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/AccountApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/AccountApiExceptionMapper.java
@@ -54,8 +54,10 @@ public class AccountApiExceptionMapper extends ExceptionMapperBase implements Ex
             return buildBadRequestResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.ACCOUNT_UPDATE_FAILED.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
+        } else if (exception.getCode() == ErrorCode.ACCOUNT_DOES_NOT_EXIST_FOR_RECORD_ID.getCode()) {
+            return buildNotFoundResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/BlockingApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/BlockingApiExceptionMapper.java
index 7f23095..aed9c10 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/BlockingApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/BlockingApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class BlockingApiExceptionMapper extends ExceptionMapperBase implements E
 
     @Override
     public Response toResponse(final BlockingApiException exception) {
-        return buildBadRequestResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/CatalogApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/CatalogApiExceptionMapper.java
index d5a464d..fe85c6f 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/CatalogApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/CatalogApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class CatalogApiExceptionMapper extends ExceptionMapperBase implements Ex
 
     @Override
     public Response toResponse(final CatalogApiException exception) {
-        return buildBadRequestResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EmailApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EmailApiExceptionMapper.java
index e619556..ffdf1c9 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EmailApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EmailApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class EmailApiExceptionMapper extends ExceptionMapperBase implements Exce
 
     @Override
     public Response toResponse(final EmailApiException exception) {
-        return buildInternalErrorResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntitlementApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntitlementApiExceptionMapper.java
index 5dcfa3b..c4506cb 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntitlementApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntitlementApiExceptionMapper.java
@@ -45,7 +45,7 @@ public class EntitlementApiExceptionMapper extends ExceptionMapperBase implement
         } else if (exception.getCode() == ErrorCode.SUB_INVALID_SUBSCRIPTION_ID.getCode()) {
             return buildNotFoundResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntityPersistenceExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntityPersistenceExceptionMapper.java
index d7c6194..f9301b7 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntityPersistenceExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/EntityPersistenceExceptionMapper.java
@@ -37,6 +37,6 @@ public class EntityPersistenceExceptionMapper extends ExceptionMapperBase implem
 
     @Override
     public Response toResponse(final EntityPersistenceException exception) {
-        return buildInternalErrorResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/ExceptionMapperBase.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/ExceptionMapperBase.java
index 2c82fa6..c070c79 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/ExceptionMapperBase.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/ExceptionMapperBase.java
@@ -24,7 +24,22 @@ import javax.ws.rs.core.UriInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.ning.billing.BillingExceptionBase;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.entitlement.api.BlockingApiException;
+import com.ning.billing.entitlement.api.EntitlementApiException;
+import com.ning.billing.entitlement.api.SubscriptionApiException;
+import com.ning.billing.entity.EntityPersistenceException;
+import com.ning.billing.invoice.api.InvoiceApiException;
 import com.ning.billing.jaxrs.json.BillingExceptionJson;
+import com.ning.billing.overdue.OverdueApiException;
+import com.ning.billing.payment.api.PaymentApiException;
+import com.ning.billing.subscription.api.SubscriptionBillingApiException;
+import com.ning.billing.subscription.api.timeline.SubscriptionBaseRepairException;
+import com.ning.billing.util.api.TagApiException;
+import com.ning.billing.util.api.TagDefinitionApiException;
+import com.ning.billing.util.email.EmailApiException;
 import com.ning.billing.util.jackson.ObjectMapper;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -34,6 +49,67 @@ public abstract class ExceptionMapperBase {
     private static final Logger log = LoggerFactory.getLogger(ExceptionMapperBase.class);
     private static final ObjectMapper mapper = new ObjectMapper();
 
+    protected Response fallback(final Exception exception, final UriInfo uriInfo) {
+        if (exception.getCause() == null) {
+            return buildBadRequestResponse(exception, uriInfo);
+        } else {
+            return doFallback(exception, uriInfo);
+        }
+    }
+
+    private Response doFallback(final Exception exception, final UriInfo uriInfo) {
+        if (exception.getCause() == null || !(exception.getCause() instanceof BillingExceptionBase)) {
+            return buildBadRequestResponse(exception, uriInfo);
+        }
+
+        final BillingExceptionBase cause = (BillingExceptionBase) exception.getCause();
+        if (cause instanceof AccountApiException) {
+            final AccountApiExceptionMapper mapper = new AccountApiExceptionMapper(uriInfo);
+            return mapper.toResponse((AccountApiException) cause);
+        } else if (cause instanceof BlockingApiException) {
+            final BlockingApiExceptionMapper mapper = new BlockingApiExceptionMapper(uriInfo);
+            return mapper.toResponse((BlockingApiException) cause);
+        } else if (cause instanceof CatalogApiException) {
+            final CatalogApiExceptionMapper mapper = new CatalogApiExceptionMapper(uriInfo);
+            return mapper.toResponse((CatalogApiException) cause);
+        } else if (cause instanceof EmailApiException) {
+            final EmailApiExceptionMapper mapper = new EmailApiExceptionMapper(uriInfo);
+            return mapper.toResponse((EmailApiException) cause);
+        } else if (cause instanceof EntitlementApiException) {
+            final EntitlementApiExceptionMapper mapper = new EntitlementApiExceptionMapper(uriInfo);
+            return mapper.toResponse((EntitlementApiException) cause);
+        } else if (cause instanceof EntityPersistenceException) {
+            final EntityPersistenceExceptionMapper mapper = new EntityPersistenceExceptionMapper(uriInfo);
+            return mapper.toResponse((EntityPersistenceException) cause);
+        } else if (cause instanceof InvoiceApiException) {
+            final InvoiceApiExceptionMapper mapper = new InvoiceApiExceptionMapper(uriInfo);
+            return mapper.toResponse((InvoiceApiException) cause);
+        } else if (cause instanceof OverdueApiException) {
+            final OverdueApiExceptionMapper mapper = new OverdueApiExceptionMapper(uriInfo);
+            return mapper.toResponse((OverdueApiException) cause);
+        } else if (cause instanceof PaymentApiException) {
+            final PaymentApiExceptionMapper mapper = new PaymentApiExceptionMapper(uriInfo);
+            return mapper.toResponse((PaymentApiException) cause);
+        } else if (cause instanceof SubscriptionApiException) {
+            final SubscriptionApiExceptionMapper mapper = new SubscriptionApiExceptionMapper(uriInfo);
+            return mapper.toResponse((SubscriptionApiException) cause);
+        } else if (cause instanceof SubscriptionBillingApiException) {
+            final SubscriptionBillingApiExceptionMapper mapper = new SubscriptionBillingApiExceptionMapper(uriInfo);
+            return mapper.toResponse((SubscriptionBillingApiException) cause);
+        } else if (cause instanceof SubscriptionBaseRepairException) {
+            final SubscriptionRepairExceptionMapper mapper = new SubscriptionRepairExceptionMapper(uriInfo);
+            return mapper.toResponse((SubscriptionBaseRepairException) cause);
+        } else if (cause instanceof TagApiException) {
+            final TagApiExceptionMapper mapper = new TagApiExceptionMapper(uriInfo);
+            return mapper.toResponse((TagApiException) cause);
+        } else if (cause instanceof TagDefinitionApiException) {
+            final TagDefinitionApiExceptionMapper mapper = new TagDefinitionApiExceptionMapper(uriInfo);
+            return mapper.toResponse((TagDefinitionApiException) cause);
+        } else {
+            return buildBadRequestResponse(cause, uriInfo);
+        }
+    }
+
     protected Response buildConflictingRequestResponse(final Exception e, final UriInfo uriInfo) {
         // Log the full stacktrace
         log.warn("Conflicting request", e);
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java
index 6dc12e4..8f19f44 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/InvoiceApiExceptionMapper.java
@@ -69,7 +69,7 @@ public class InvoiceApiExceptionMapper extends ExceptionMapperBase implements Ex
         } else if (exception.getCode() == ErrorCode.EXTERNAL_CHARGE_AMOUNT_INVALID.getCode()) {
             return buildBadRequestResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueApiExceptionMapper.java
index 13b5e44..061f0f0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/OverdueApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class OverdueApiExceptionMapper extends ExceptionMapperBase implements Ex
 
     @Override
     public Response toResponse(final OverdueApiException exception) {
-        return buildBadRequestResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
index f4678e2..e1bafc0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/PaymentApiExceptionMapper.java
@@ -99,7 +99,7 @@ public class PaymentApiExceptionMapper extends ExceptionMapperBase implements Ex
         } else if (exception.getCode() == ErrorCode.PAYMENT_UPD_PAYMENT_PROVIDER_ACCOUNT.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionApiExceptionMapper.java
index cdcc153..36a9bd0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionApiExceptionMapper.java
@@ -25,7 +25,6 @@ import javax.ws.rs.ext.Provider;
 
 import com.ning.billing.ErrorCode;
 import com.ning.billing.entitlement.api.SubscriptionApiException;
-import com.ning.billing.subscription.api.user.SubscriptionBaseApiException;
 
 @Singleton
 @Provider
@@ -81,10 +80,10 @@ public class SubscriptionApiExceptionMapper extends ExceptionMapperBase implemen
             return buildNotFoundResponse(exception, uriInfo);
         } else if (exception.getCode() == ErrorCode.SUB_RECREATE_BAD_STATE.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
-        }  else if (exception.getCode() == ErrorCode.SUB_UNCANCEL_BAD_STATE.getCode()) {
+        } else if (exception.getCode() == ErrorCode.SUB_UNCANCEL_BAD_STATE.getCode()) {
             return buildInternalErrorResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionBillingApiExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionBillingApiExceptionMapper.java
index 2b38fe8..b48434e 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionBillingApiExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionBillingApiExceptionMapper.java
@@ -37,6 +37,6 @@ public class SubscriptionBillingApiExceptionMapper extends ExceptionMapperBase i
 
     @Override
     public Response toResponse(final SubscriptionBillingApiException exception) {
-        return buildBadRequestResponse(exception, uriInfo);
+        return fallback(exception, uriInfo);
     }
 }
diff --git a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionRepairExceptionMapper.java b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionRepairExceptionMapper.java
index 0947a82..f00f0c0 100644
--- a/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionRepairExceptionMapper.java
+++ b/jaxrs/src/main/java/com/ning/billing/jaxrs/mappers/SubscriptionRepairExceptionMapper.java
@@ -69,7 +69,7 @@ public class SubscriptionRepairExceptionMapper extends ExceptionMapperBase imple
         } else if (exception.getCode() == ErrorCode.SUB_REPAIR_VIEW_CHANGED.getCode()) {
             return buildBadRequestResponse(exception, uriInfo);
         } else {
-            return buildBadRequestResponse(exception, uriInfo);
+            return fallback(exception, uriInfo);
         }
     }
 }

junction/pom.xml 2(+1 -1)

diff --git a/junction/pom.xml b/junction/pom.xml
index 3a94722..75bfa39 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>
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 7f98ec3..20750d0 100644
--- a/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
+++ b/junction/src/test/java/com/ning/billing/junction/JunctionTestSuiteWithEmbeddedDB.java
@@ -47,6 +47,7 @@ import com.ning.billing.mock.MockAccountBuilder;
 import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.SubscriptionBaseService;
 import com.ning.billing.subscription.engine.core.DefaultSubscriptionBaseService;
+import com.ning.billing.util.callcontext.InternalCallContextFactory;
 import com.ning.billing.util.svcsapi.bus.BusService;
 
 import com.google.inject.Guice;
@@ -88,6 +89,8 @@ public abstract class JunctionTestSuiteWithEmbeddedDB extends GuicyKillbillTestS
     protected SubscriptionBaseService subscriptionBaseService;
     @Inject
     protected EntitlementService entitlementService;
+    @Inject
+    protected InternalCallContextFactory internalCallContextFactory;
 
     protected Catalog catalog;
 
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
index 99ed652..6aac83a 100644
--- 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
@@ -25,6 +25,7 @@ import org.testng.annotations.Test;
 
 import com.ning.billing.account.api.Account;
 import com.ning.billing.api.TestApiListener.NextEvent;
+import com.ning.billing.callcontext.InternalCallContext;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PriceListSet;
@@ -60,6 +61,8 @@ public class TestDefaultInternalBillingApi extends JunctionTestSuiteWithEmbedded
         clock.setDay(initialDate);
 
         final Account account = accountApi.createAccount(getAccountData(7), callContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), 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);

osgi/pom.xml 2(+1 -1)

diff --git a/osgi/pom.xml b/osgi/pom.xml
index 8dec480..b8f1e44 100644
--- a/osgi/pom.xml
+++ b/osgi/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi</artifactId>
diff --git a/osgi-bundles/bundles/jruby/pom.xml b/osgi-bundles/bundles/jruby/pom.xml
index 31b2830..4ea2473 100644
--- a/osgi-bundles/bundles/jruby/pom.xml
+++ b/osgi-bundles/bundles/jruby/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill-osgi-bundles</artifactId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-jruby</artifactId>
diff --git a/osgi-bundles/bundles/logger/pom.xml b/osgi-bundles/bundles/logger/pom.xml
index c2c06ce..5dbfd4d 100644
--- a/osgi-bundles/bundles/logger/pom.xml
+++ b/osgi-bundles/bundles/logger/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-logger</artifactId>
diff --git a/osgi-bundles/bundles/pom.xml b/osgi-bundles/bundles/pom.xml
index 4f8c8cc..d412316 100644
--- a/osgi-bundles/bundles/pom.xml
+++ b/osgi-bundles/bundles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-all-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles</artifactId>
diff --git a/osgi-bundles/bundles/webconsolebranding/pom.xml b/osgi-bundles/bundles/webconsolebranding/pom.xml
index b5133e7..f9b0f21 100644
--- a/osgi-bundles/bundles/webconsolebranding/pom.xml
+++ b/osgi-bundles/bundles/webconsolebranding/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-webconsolebranding</artifactId>
diff --git a/osgi-bundles/defaultbundles/pom.xml b/osgi-bundles/defaultbundles/pom.xml
index a72ee5f..1a093ba 100644
--- a/osgi-bundles/defaultbundles/pom.xml
+++ b/osgi-bundles/defaultbundles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-all-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-defaultbundles</artifactId>
diff --git a/osgi-bundles/libs/killbill/pom.xml b/osgi-bundles/libs/killbill/pom.xml
index 267a987..8f25117 100644
--- a/osgi-bundles/libs/killbill/pom.xml
+++ b/osgi-bundles/libs/killbill/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-lib-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-lib-killbill</artifactId>
diff --git a/osgi-bundles/libs/pom.xml b/osgi-bundles/libs/pom.xml
index 1bb6ae2..b086770 100644
--- a/osgi-bundles/libs/pom.xml
+++ b/osgi-bundles/libs/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-all-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-lib-bundles</artifactId>
diff --git a/osgi-bundles/libs/slf4j-osgi/pom.xml b/osgi-bundles/libs/slf4j-osgi/pom.xml
index 010af7f..936e2ad 100644
--- a/osgi-bundles/libs/slf4j-osgi/pom.xml
+++ b/osgi-bundles/libs/slf4j-osgi/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-lib-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-lib-slf4j-osgi</artifactId>
diff --git a/osgi-bundles/pom.xml b/osgi-bundles/pom.xml
index a3a5e6b..3143f43 100644
--- a/osgi-bundles/pom.xml
+++ b/osgi-bundles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-all-bundles</artifactId>
diff --git a/osgi-bundles/tests/beatrix/pom.xml b/osgi-bundles/tests/beatrix/pom.xml
index cf066e8..c1381ab 100644
--- a/osgi-bundles/tests/beatrix/pom.xml
+++ b/osgi-bundles/tests/beatrix/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-test-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-test-beatrix</artifactId>
diff --git a/osgi-bundles/tests/payment/pom.xml b/osgi-bundles/tests/payment/pom.xml
index 157f015..dae5a1b 100644
--- a/osgi-bundles/tests/payment/pom.xml
+++ b/osgi-bundles/tests/payment/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-test-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-bundles-test-payment</artifactId>
diff --git a/osgi-bundles/tests/pom.xml b/osgi-bundles/tests/pom.xml
index d629b01..e67b287 100644
--- a/osgi-bundles/tests/pom.xml
+++ b/osgi-bundles/tests/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill-osgi-all-bundles</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-osgi-test-bundles</artifactId>

overdue/pom.xml 2(+1 -1)

diff --git a/overdue/pom.xml b/overdue/pom.xml
index 00f74f3..91b133c 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-overdue</artifactId>

payment/pom.xml 2(+1 -1)

diff --git a/payment/pom.xml b/payment/pom.xml
index 3823ba6..d5ebace 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index d69541b..860b39e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
         <version>0.5.6</version>
     </parent>
     <artifactId>killbill</artifactId>
-    <version>0.8.3-SNAPSHOT</version>
+    <version>0.8.4-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>

server/pom.xml 2(+1 -1)

diff --git a/server/pom.xml b/server/pom.xml
index 4d079a1..65f3d74 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-server</artifactId>
diff --git a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
index 97201a2..b99874d 100644
--- a/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
+++ b/server/src/test/java/com/ning/billing/jaxrs/TestBundle.java
@@ -95,7 +95,7 @@ public class TestBundle extends TestJaxrsBase {
         queryParams.put(JaxrsResource.QUERY_EXTERNAL_KEY, "56566");
         uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
         response = doGet(uri, queryParams, DEFAULT_HTTP_TIMEOUT_SEC);
-        Assert.assertEquals(response.getStatusCode(), Status.NOT_FOUND.getStatusCode());
+        Assert.assertEquals(response.getStatusCode(), Status.OK.getStatusCode());
 
         uri = JaxrsResource.ACCOUNTS_PATH + "/" + accountJson.getAccountId() + "/" + JaxrsResource.BUNDLES;
         response = doGet(uri, DEFAULT_EMPTY_QUERY, DEFAULT_HTTP_TIMEOUT_SEC);
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 8b8f285..b81e255 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-subscription</artifactId>
diff --git a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 7aec7aa..21edeeb 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -39,10 +39,20 @@ import org.slf4j.LoggerFactory;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.bus.api.PersistentBus;
 import com.ning.billing.bus.api.PersistentBus.EventBusException;
+import com.ning.billing.callcontext.InternalCallContext;
+import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.catalog.api.CatalogService;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.clock.Clock;
+import com.ning.billing.entity.EntityPersistenceException;
+import com.ning.billing.events.EffectiveSubscriptionInternalEvent;
+import com.ning.billing.events.RepairSubscriptionInternalEvent;
+import com.ning.billing.notificationq.api.NotificationEvent;
+import com.ning.billing.notificationq.api.NotificationQueue;
+import com.ning.billing.notificationq.api.NotificationQueueService;
+import com.ning.billing.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
+import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.subscription.api.migration.AccountMigrationData;
 import com.ning.billing.subscription.api.migration.AccountMigrationData.BundleMigrationData;
 import com.ning.billing.subscription.api.migration.AccountMigrationData.SubscriptionMigrationData;
@@ -54,13 +64,13 @@ import com.ning.billing.subscription.api.user.DefaultRequestedSubscriptionEvent;
 import com.ning.billing.subscription.api.user.DefaultSubscriptionBase;
 import com.ning.billing.subscription.api.user.DefaultSubscriptionBaseBundle;
 import com.ning.billing.subscription.api.user.SubscriptionBaseBundle;
-import com.ning.billing.subscription.api.user.SubscriptionBuilder;
 import com.ning.billing.subscription.api.user.SubscriptionBaseTransitionData;
+import com.ning.billing.subscription.api.user.SubscriptionBuilder;
 import com.ning.billing.subscription.engine.addon.AddonUtils;
 import com.ning.billing.subscription.engine.core.DefaultSubscriptionBaseService;
 import com.ning.billing.subscription.engine.core.SubscriptionNotificationKey;
-import com.ning.billing.subscription.engine.dao.model.SubscriptionEventModelDao;
 import com.ning.billing.subscription.engine.dao.model.SubscriptionBundleModelDao;
+import com.ning.billing.subscription.engine.dao.model.SubscriptionEventModelDao;
 import com.ning.billing.subscription.engine.dao.model.SubscriptionModelDao;
 import com.ning.billing.subscription.events.SubscriptionBaseEvent;
 import com.ning.billing.subscription.events.SubscriptionBaseEvent.EventType;
@@ -72,27 +82,20 @@ import com.ning.billing.subscription.events.user.ApiEventChange;
 import com.ning.billing.subscription.events.user.ApiEventMigrateBilling;
 import com.ning.billing.subscription.events.user.ApiEventType;
 import com.ning.billing.subscription.exceptions.SubscriptionBaseError;
-import com.ning.billing.notificationq.api.NotificationEvent;
-import com.ning.billing.notificationq.api.NotificationQueue;
-import com.ning.billing.notificationq.api.NotificationQueueService;
-import com.ning.billing.notificationq.api.NotificationQueueService.NoSuchNotificationQueue;
-import com.ning.billing.subscription.api.SubscriptionBase;
 import com.ning.billing.util.cache.CacheControllerDispatcher;
-import com.ning.billing.callcontext.InternalCallContext;
-import com.ning.billing.callcontext.InternalTenantContext;
 import com.ning.billing.util.dao.NonEntityDao;
-import com.ning.billing.entity.EntityPersistenceException;
 import com.ning.billing.util.entity.dao.EntitySqlDao;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import com.ning.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
-import com.ning.billing.events.EffectiveSubscriptionInternalEvent;
-import com.ning.billing.events.RepairSubscriptionInternalEvent;
 
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
+import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Collections2;
-
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
 
 public class DefaultSubscriptionDao implements SubscriptionDao {
 
@@ -236,10 +239,9 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         return buildSubscription(shellSubscription, context);
     }
 
-
     @Override
     public List<SubscriptionBase> getSubscriptions(final UUID bundleId, final InternalTenantContext context) {
-        return buildBundleSubscriptions(bundleId, getSubscriptionFromBundleId(bundleId, context), context);
+        return buildBundleSubscriptions(getSubscriptionFromBundleId(bundleId, context), null, context);
     }
 
     private List<SubscriptionBase> getSubscriptionFromBundleId(final UUID bundleId, final InternalTenantContext context) {
@@ -257,13 +259,37 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         });
     }
 
+
     @Override
     public Map<UUID, List<SubscriptionBase>> getSubscriptionsForAccount(final InternalTenantContext context) {
         final Map<UUID, List<SubscriptionBase>> subscriptionsFromAccountId = getSubscriptionsFromAccountId(context);
 
+        final List<SubscriptionBaseEvent> eventsForAccount = getEventsForAccountId(context);
+
         final Map<UUID, List<SubscriptionBase>> result = new HashMap<UUID, List<SubscriptionBase>>();
         for (final UUID bundleId : subscriptionsFromAccountId.keySet()) {
-            result.put(bundleId, buildBundleSubscriptions(bundleId, subscriptionsFromAccountId.get(bundleId), context));
+
+            final List<SubscriptionBase> subscriptionsForBundle = subscriptionsFromAccountId.get(bundleId);
+            final Collection<UUID> subscriptionIdsForBundle = Collections2.transform(subscriptionsForBundle, new Function<SubscriptionBase, UUID>() {
+                @Override
+                public UUID apply(final SubscriptionBase input) {
+                    return input.getId();
+                }
+            });
+            final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscriptions = ArrayListMultimap.create();
+
+            for (final SubscriptionBase cur : subscriptionsForBundle) {
+                final Collection<SubscriptionBaseEvent> events= Collections2.filter(eventsForAccount, new Predicate<SubscriptionBaseEvent>() {
+                    @Override
+                    public boolean apply(final SubscriptionBaseEvent input) {
+                        return input.getSubscriptionId().equals(cur.getId());
+
+                    }
+                });
+                eventsForSubscriptions.putAll(cur.getId(), ImmutableList.copyOf(events));
+            }
+
+            result.put(bundleId, buildBundleSubscriptions(subscriptionsForBundle, eventsForSubscriptions,context));
         }
         return result;
     }
@@ -292,23 +318,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         return result;
     }
 
-    /*
-    @Override
-    public List<SubscriptionBase> getSubscriptionsForAccountAndKey(final UUID accountId,
-                                                               final String bundleKey, final InternalTenantContext callcontext) {
-        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<SubscriptionBase>>() {
-            @Override
-            public List<SubscriptionBase> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
-                final SubscriptionBundleModelDao bundleModel = entitySqlDaoWrapperFactory.become(BundleSqlDao.class).getBundlesFromAccountAndKey(accountId.toString(), bundleKey, callcontext);
-                if (bundleModel == null) {
-                    return Collections.emptyList();
-                }
-                return getSubscriptions(bundleModel.getId(), callcontext);
-            }
-        });
-    }
-    */
-
     @Override
     public void updateChargedThroughDate(final DefaultSubscriptionBase subscription, final InternalCallContext context) {
         final Date ctd = (subscription.getChargedThroughDate() != null) ? subscription.getChargedThroughDate().toDate() : null;
@@ -365,23 +374,12 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
             @Override
             public List<SubscriptionBaseEvent> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
                 final List<SubscriptionEventModelDao> models = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getEventsForSubscription(subscriptionId.toString(), context);
-                // Remove UNCANCEL events early on as they are not representative of a state transition but are just markers
-                final Collection<SubscriptionEventModelDao> filteredModels = Collections2.filter(models, new Predicate<SubscriptionEventModelDao>() {
-                    @Override
-                    public boolean apply(@Nullable final SubscriptionEventModelDao input) {
-                        return input.getUserType() != ApiEventType.UNCANCEL;
-                    }
-                });
-                return new ArrayList<SubscriptionBaseEvent>(Collections2.transform(filteredModels, new Function<SubscriptionEventModelDao, SubscriptionBaseEvent>() {
-                    @Override
-                    public SubscriptionBaseEvent apply(@Nullable final SubscriptionEventModelDao input) {
-                        return SubscriptionEventModelDao.toSubscriptionEvent(input);
-                    }
-                }));
+                return filterSubscriptionBaseEvents(models);
             }
         });
     }
 
+
     @Override
     public Map<UUID, List<SubscriptionBaseEvent>> getEventsForBundle(final UUID bundleId, final InternalTenantContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Map<UUID, List<SubscriptionBaseEvent>>>() {
@@ -455,7 +453,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         });
     }
 
-
     @Override
     public void recreateSubscription(final DefaultSubscriptionBase subscription, final List<SubscriptionBaseEvent> recreateEvents, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
@@ -494,7 +491,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         });
     }
 
-
     @Override
     public void cancelSubscription(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent cancelEvent, final InternalCallContext context, final int seqId) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
@@ -557,9 +553,9 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                 final UUID subscriptionId = subscription.getId();
 
                 final List<SubscriptionBaseEvent> changeEventsTweakedWithMigrateBilling = reinsertFutureMigrateBillingEventOnChangeFromTransaction(subscriptionId,
-                                                                                                                                              changeEvents,
-                                                                                                                                              entitySqlDaoWrapperFactory,
-                                                                                                                                              context);
+                                                                                                                                                   changeEvents,
+                                                                                                                                                   entitySqlDaoWrapperFactory,
+                                                                                                                                                   context);
 
                 cancelFutureEventsFromTransaction(subscriptionId, entitySqlDaoWrapperFactory, context);
 
@@ -669,6 +665,31 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         return changeEvents;
     }
 
+    private List<SubscriptionBaseEvent> filterSubscriptionBaseEvents(final List<SubscriptionEventModelDao> models) {
+        final Collection<SubscriptionEventModelDao> filteredModels = Collections2.filter(models, new Predicate<SubscriptionEventModelDao>() {
+            @Override
+            public boolean apply(@Nullable final SubscriptionEventModelDao input) {
+                return input.getUserType() != ApiEventType.UNCANCEL;
+            }
+        });
+        return new ArrayList<SubscriptionBaseEvent>(Collections2.transform(filteredModels, new Function<SubscriptionEventModelDao, SubscriptionBaseEvent>() {
+            @Override
+            public SubscriptionBaseEvent apply(@Nullable final SubscriptionEventModelDao input) {
+                return SubscriptionEventModelDao.toSubscriptionEvent(input);
+            }
+        }));
+    }
+
+    private List<SubscriptionBaseEvent> getEventsForAccountId(final InternalTenantContext context) {
+        return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<SubscriptionBaseEvent>>() {
+            @Override
+            public List<SubscriptionBaseEvent> inTransaction(final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory) throws Exception {
+                final List<SubscriptionEventModelDao> models = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getByAccountRecordId(context);
+                return filterSubscriptionBaseEvents(models);
+            }
+        });
+    }
+
     private void cancelSubscriptionFromTransaction(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent cancelEvent, final EntitySqlDaoWrapperFactory<EntitySqlDao> entitySqlDaoWrapperFactory, final InternalCallContext context, final int seqId)
             throws EntityPersistenceException {
         final UUID subscriptionId = subscription.getId();
@@ -701,7 +722,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
     }
 
     private SubscriptionEventModelDao findFutureEventFromTransaction(final UUID subscriptionId, final EntitySqlDaoWrapperFactory<EntitySqlDao> dao, final EventType type,
-                                                                    @Nullable final ApiEventType apiType, final InternalCallContext context) {
+                                                                     @Nullable final ApiEventType apiType, final InternalCallContext context) {
 
         SubscriptionEventModelDao futureEvent = null;
         final Date now = clock.getUTCNow().toDate();
@@ -711,7 +732,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                 (apiType == null || apiType == cur.getUserType())) {
                 if (futureEvent != null) {
                     throw new SubscriptionBaseError(String.format("Found multiple future events for type %s for subscriptions %s",
-                                                             type, subscriptionId.toString()));
+                                                                  type, subscriptionId.toString()));
                 }
                 futureEvent = cur;
                 // To check that there is only one such event
@@ -746,7 +767,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
             bundleInput.add(input);
         }
 
-        final List<SubscriptionBase> reloadedSubscriptions = buildBundleSubscriptions(input.getBundleId(), bundleInput, context);
+        final List<SubscriptionBase> reloadedSubscriptions = buildBundleSubscriptions(bundleInput, null, context);
         for (final SubscriptionBase cur : reloadedSubscriptions) {
             if (cur.getId().equals(input.getId())) {
                 return cur;
@@ -756,7 +777,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         throw new SubscriptionBaseError("Unexpected code path in buildSubscription");
     }
 
-    private List<SubscriptionBase> buildBundleSubscriptions(final UUID bundleId, final List<SubscriptionBase> input, final InternalTenantContext context) {
+    private List<SubscriptionBase> buildBundleSubscriptions(final List<SubscriptionBase> input, @Nullable final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscription, final InternalTenantContext context) {
         if (input == null || input.size() == 0) {
             return Collections.emptyList();
         }
@@ -778,7 +799,12 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         SubscriptionBaseEvent futureBaseEvent = null;
         final List<SubscriptionBase> result = new ArrayList<SubscriptionBase>(input.size());
         for (final SubscriptionBase cur : input) {
-            final List<SubscriptionBaseEvent> events = getEventsForSubscription(cur.getId(), context);
+
+
+            final List<SubscriptionBaseEvent> events = eventsForSubscription != null ?
+                                                       (List<SubscriptionBaseEvent>) eventsForSubscription.get(cur.getId()) :
+                                                       getEventsForSubscription(cur.getId(), context);
+
             SubscriptionBase reloaded = createSubscriptionForInternalUse(cur, events);
 
             switch (cur.getCategory()) {
@@ -806,15 +832,15 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                     if (createCancelEvent && reloaded.getFutureEndDate() == null) {
                         final DateTime now = clock.getUTCNow();
                         final SubscriptionBaseEvent addOnCancelEvent = new ApiEventCancel(new ApiEventBuilder()
-                                                                                             .setSubscriptionId(reloaded.getId())
-                                                                                             .setActiveVersion(((DefaultSubscriptionBase) reloaded).getActiveVersion())
-                                                                                             .setProcessedDate(now)
-                                                                                             .setEffectiveDate(futureBaseEvent.getEffectiveDate())
-                                                                                             .setRequestedDate(now)
-                                                                                             .setCreatedDate(futureBaseEvent.getCreatedDate())
-                                                                                                     // This event is only there to indicate the ADD_ON is future canceled, but it is not there
-                                                                                                     // on disk until the base plan cancellation becomes effective
-                                                                                             .setFromDisk(false));
+                                                                                                  .setSubscriptionId(reloaded.getId())
+                                                                                                  .setActiveVersion(((DefaultSubscriptionBase) reloaded).getActiveVersion())
+                                                                                                  .setProcessedDate(now)
+                                                                                                  .setEffectiveDate(futureBaseEvent.getEffectiveDate())
+                                                                                                  .setRequestedDate(now)
+                                                                                                  .setCreatedDate(futureBaseEvent.getCreatedDate())
+                                                                                                          // This event is only there to indicate the ADD_ON is future canceled, but it is not there
+                                                                                                          // on disk until the base plan cancellation becomes effective
+                                                                                                  .setFromDisk(false));
 
                         events.add(addOnCancelEvent);
                         // Finally reload subscription with full set of events
@@ -873,7 +899,7 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                 try {
                     // Note: we don't send a requested change event here, but a repair event
                     final RepairSubscriptionInternalEvent busEvent = new DefaultRepairSubscriptionEvent(accountId, bundleId, clock.getUTCNow(),
-                                                                                                      context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
+                                                                                                        context.getAccountRecordId(), context.getTenantRecordId(), context.getUserToken());
                     eventBus.postFromTransaction(busEvent, entitySqlDaoWrapperFactory.getSqlDao());
                 } catch (EventBusException e) {
                     log.warn("Failed to post repair subscription event for bundle " + bundleId, e);
@@ -922,7 +948,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
         return null;
     }
 
-
     //
     // Either records a notfication or sends a bus event is operation is immediate
     //
@@ -953,7 +978,6 @@ public class DefaultSubscriptionDao implements SubscriptionDao {
                                                                                                       context.getUserToken(),
                                                                                                       context.getAccountRecordId(), context.getTenantRecordId());
 
-
             eventBus.postFromTransaction(busEvent, entitySqlDaoWrapperFactory.getSqlDao());
         } catch (EventBusException e) {
             log.warn("Failed to post effective event for subscription " + subscription.getId(), e);
diff --git a/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java b/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java
index 0c35d1c..953a2ea 100644
--- a/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java
+++ b/subscription/src/main/java/com/ning/billing/subscription/glue/DefaultSubscriptionModule.java
@@ -23,6 +23,7 @@ import com.ning.billing.glue.SubscriptionModule;
 import com.ning.billing.subscription.alignment.MigrationPlanAligner;
 import com.ning.billing.subscription.alignment.PlanAligner;
 import com.ning.billing.subscription.api.SubscriptionBaseApiService;
+import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 import com.ning.billing.subscription.api.SubscriptionBaseService;
 import com.ning.billing.subscription.api.migration.DefaultSubscriptionBaseMigrationApi;
 import com.ning.billing.subscription.api.migration.SubscriptionBaseMigrationApi;
@@ -40,7 +41,6 @@ import com.ning.billing.subscription.engine.dao.DefaultSubscriptionDao;
 import com.ning.billing.subscription.engine.dao.RepairSubscriptionDao;
 import com.ning.billing.subscription.engine.dao.SubscriptionDao;
 import com.ning.billing.util.config.SubscriptionConfig;
-import com.ning.billing.subscription.api.SubscriptionBaseInternalApi;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.name.Names;
@@ -68,7 +68,6 @@ public class DefaultSubscriptionModule extends AbstractModule implements Subscri
     }
 
     protected void installSubscriptionCore() {
-
         bind(SubscriptionBaseApiService.class).annotatedWith(Names.named(REPAIR_NAMED)).to(RepairSubscriptionApiService.class).asEagerSingleton();
         bind(SubscriptionBaseApiService.class).to(DefaultSubscriptionBaseApiService.class).asEagerSingleton();
 
@@ -106,7 +105,6 @@ public class DefaultSubscriptionModule extends AbstractModule implements Subscri
         bind(SubscriptionBaseMigrationApi.class).to(DefaultSubscriptionBaseMigrationApi.class).asEagerSingleton();
     }
 
-
     @Override
     public void installSubscriptionInternalApi() {
         bind(SubscriptionBaseInternalApi.class).to(DefaultSubscriptionInternalApi.class).asEagerSingleton();

tenant/pom.xml 2(+1 -1)

diff --git a/tenant/pom.xml b/tenant/pom.xml
index afc66bf..5f070a3 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-tenant</artifactId>

usage/pom.xml 2(+1 -1)

diff --git a/usage/pom.xml b/usage/pom.xml
index f2d5cec..a6de309 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-usage</artifactId>

util/pom.xml 2(+1 -1)

diff --git a/util/pom.xml b/util/pom.xml
index 01c5fba..7ebfe6a 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -12,7 +12,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>com.ning.billing</groupId>
-        <version>0.8.3-SNAPSHOT</version>
+        <version>0.8.4-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
diff --git a/util/src/main/java/com/ning/billing/util/customfield/ShouldntHappenException.java b/util/src/main/java/com/ning/billing/util/customfield/ShouldntHappenException.java
new file mode 100644
index 0000000..5d4cc2d
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/customfield/ShouldntHappenException.java
@@ -0,0 +1,24 @@
+/*
+ * 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.util.customfield;
+
+public class ShouldntHappenException extends RuntimeException {
+
+    public ShouldntHappenException(final String message) {
+        super(message);
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
index 407e98f..60f9fe8 100644
--- a/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
+++ b/util/src/test/java/com/ning/billing/mock/glue/MockEntitlementModule.java
@@ -18,6 +18,7 @@ package com.ning.billing.mock.glue;
 
 import org.mockito.Mockito;
 
+import com.ning.billing.entitlement.EntitlementInternalApi;
 import com.ning.billing.entitlement.api.EntitlementApi;
 import com.ning.billing.entitlement.api.SubscriptionApi;
 import com.ning.billing.glue.EntitlementModule;
@@ -29,6 +30,7 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
 
     private final BlockingInternalApi blockingApi = Mockito.mock(BlockingInternalApi.class);
     private final EntitlementApi entitlementApi = Mockito.mock(EntitlementApi.class);
+    private final EntitlementInternalApi entitlementInternalApi = Mockito.mock(EntitlementInternalApi.class);
     private final SubscriptionApi subscriptionApi = Mockito.mock(SubscriptionApi.class);
 
     @Override
@@ -54,6 +56,11 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
     }
 
     @Override
+    public void installEntitlementInternalApi() {
+        bind(EntitlementInternalApi.class).toInstance(entitlementInternalApi);
+    }
+
+    @Override
     public void installSubscriptionApi() {
         bind(SubscriptionApi.class).toInstance(subscriptionApi);
     }
@@ -61,5 +68,4 @@ public class MockEntitlementModule extends AbstractModule implements Entitlement
     @Override
     public void installBlockingChecker() {
     }
-
 }