killbill-aplcache

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 18(+9 -9)

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

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

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

pom.xml 4(+2 -2)

profiles/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 9920ab7..796c5c0 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-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 b28c378..0495129 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-internal-api</artifactId>
diff --git a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
index ccf1b73..b2e6d4b 100644
--- a/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
+++ b/api/src/main/java/org/killbill/billing/entitlement/EventsStream.java
@@ -56,8 +56,6 @@ public interface EventsStream {
 
     boolean isSubscriptionCancelled();
 
-    Collection<BlockingState> getCurrentSubscriptionEntitlementBlockingStatesForServices();
-
     Collection<BlockingState> getPendingEntitlementCancellationEvents();
 
     BlockingState getEntitlementCancellationEvent();
diff --git a/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
index dedb858..dd450d4 100644
--- a/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
+++ b/api/src/main/java/org/killbill/billing/junction/DefaultBlockingState.java
@@ -33,7 +33,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
 
     private static BlockingState clearState = null;
 
-    private final UUID blockingId;
+    private final UUID blockedId;
     private final String stateName;
     private final String service;
     private final boolean blockChange;
@@ -52,7 +52,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
 
     // Used by the DAO
     public DefaultBlockingState(final UUID id,
-                                final UUID blockingId,
+                                final UUID blockedId,
                                 final BlockingStateType type,
                                 final String stateName,
                                 final String service,
@@ -64,7 +64,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
                                 final DateTime updatedDate,
                                 final Long totalOrdering) {
         super(id, createDate, updatedDate);
-        this.blockingId = blockingId;
+        this.blockedId = blockedId;
         this.type = type;
         this.stateName = stateName;
         this.service = service;
@@ -75,7 +75,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
         this.totalOrdering = totalOrdering;
     }
 
-    public DefaultBlockingState(final UUID blockingId,
+    public DefaultBlockingState(final UUID blockedId,
                                 final BlockingStateType type,
                                 final String stateName,
                                 final String service,
@@ -84,7 +84,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
                                 final boolean blockBilling,
                                 final DateTime effectiveDate) {
         this(UUIDs.randomUUID(),
-             blockingId,
+             blockedId,
              type,
              stateName,
              service,
@@ -99,7 +99,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
 
     @Override
     public UUID getBlockedId() {
-        return blockingId;
+        return blockedId;
     }
 
     @Override
@@ -192,7 +192,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
         if (blockEntitlement != that.blockEntitlement) {
             return false;
         }
-        if (blockingId != null ? !blockingId.equals(that.blockingId) : that.blockingId != null) {
+        if (blockedId != null ? !blockedId.equals(that.blockedId) : that.blockedId != null) {
             return false;
         }
         if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
@@ -217,7 +217,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
     @Override
     public int hashCode() {
         int result = super.hashCode();
-        result = 31 * result + (blockingId != null ? blockingId.hashCode() : 0);
+        result = 31 * result + (blockedId != null ? blockedId.hashCode() : 0);
         result = 31 * result + (stateName != null ? stateName.hashCode() : 0);
         result = 31 * result + (service != null ? service.hashCode() : 0);
         result = 31 * result + (blockChange ? 1 : 0);
@@ -248,7 +248,7 @@ public class DefaultBlockingState extends EntityBase implements BlockingState {
 
     @Override
     public String toString() {
-        return "BlockingState [blockingId=" + blockingId + ", stateName=" + stateName + ", service="
+        return "BlockingState [blockedId=" + blockedId + ", stateName=" + stateName + ", service="
                + service + ", blockChange=" + blockChange + ", blockEntitlement=" + blockEntitlement
                + ", blockBilling=" + blockBilling + ", effectiveDate=" + effectiveDate + "]";
     }

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

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index bb05e52..55be88f 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>
diff --git a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
index 7e914ce..33d9c68 100644
--- a/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
+++ b/beatrix/src/main/java/org/killbill/billing/beatrix/extbus/BeatrixListener.java
@@ -27,8 +27,10 @@ import javax.inject.Named;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.entitlement.EntitlementTransitionType;
+import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.events.AccountChangeInternalEvent;
 import org.killbill.billing.events.AccountCreationInternalEvent;
+import org.killbill.billing.events.BlockingTransitionInternalEvent;
 import org.killbill.billing.events.BusInternalEvent;
 import org.killbill.billing.events.BusInternalEvent.BusInternalEventType;
 import org.killbill.billing.events.ControlTagCreationInternalEvent;
@@ -142,6 +144,19 @@ public class BeatrixListener {
                 }
                 break;
 
+            case BLOCKING_STATE:
+                final BlockingTransitionInternalEvent realEventBS = (BlockingTransitionInternalEvent) event;
+                if (realEventBS.getBlockingType() == BlockingStateType.ACCOUNT) {
+                    objectType = ObjectType.ACCOUNT;
+                } else if (realEventBS.getBlockingType() == BlockingStateType.SUBSCRIPTION_BUNDLE) {
+                    objectType = ObjectType.BUNDLE;
+                } else if (realEventBS.getBlockingType() == BlockingStateType.SUBSCRIPTION) {
+                    objectType = ObjectType.SUBSCRIPTION;
+                }
+                objectId = realEventBS.getBlockableId();
+                // Probably we should serialize the isTransitionedTo* from BlockingTransitionInternalEvent into the metdata section
+                break;
+
             case ENTITLEMENT_TRANSITION:
                 final EntitlementInternalEvent realEventET = (EntitlementInternalEvent) event;
                 objectType = ObjectType.BUNDLE;

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

diff --git a/catalog/pom.xml b/catalog/pom.xml
index ec93d48..5d4943d 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-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 46ed013..5a32533 100644
--- a/currency/pom.xml
+++ b/currency/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-currency</artifactId>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 0508004..bfb53a7 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
index 44cfbc1..59d7909 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlement.java
@@ -453,12 +453,6 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                     throw new EntitlementApiException(ErrorCode.SUB_CHANGE_NON_ACTIVE, getId(), getState());
                 }
 
-                final InternalCallContext context = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-                try {
-                    checker.checkBlockedChange(getSubscriptionBase(), context);
-                } catch (BlockingApiException e) {
-                    throw new EntitlementApiException(e, e.getCode(), e.getMessage());
-                }
 
                 final DateTime effectiveChangeDate;
                 try {
@@ -467,6 +461,13 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                     throw new EntitlementApiException(e);
                 }
 
+                final InternalCallContext context = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
+                try {
+                    checker.checkBlockedChange(getSubscriptionBase(), effectiveChangeDate, context);
+                } catch (BlockingApiException e) {
+                    throw new EntitlementApiException(e, e.getCode(), e.getMessage());
+                }
+
                 blockAddOnsIfRequired(effectiveChangeDate, callContext, context);
 
                 return entitlementApi.getEntitlementForId(getId(), callContext);
@@ -502,12 +503,6 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                 }
 
                 final InternalCallContext context = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-                try {
-                    checker.checkBlockedChange(getSubscriptionBase(), context);
-                } catch (BlockingApiException e) {
-                    throw new EntitlementApiException(e, e.getCode(), e.getMessage());
-                }
-
                 final DateTime effectiveChangeDate = dateHelper.fromLocalDateAndReferenceTime(updatedPluginContext.getEffectiveDate(), getSubscriptionBase().getStartDate(), context);
                 try {
                     getSubscriptionBase().changePlanWithDate(productName, billingPeriod, priceList, overrides, effectiveChangeDate, callContext);
@@ -515,6 +510,13 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                     throw new EntitlementApiException(e);
                 }
 
+                try {
+                    checker.checkBlockedChange(getSubscriptionBase(), effectiveChangeDate, context);
+                } catch (BlockingApiException e) {
+                    throw new EntitlementApiException(e, e.getCode(), e.getMessage());
+                }
+
+
                 blockAddOnsIfRequired(effectiveChangeDate, callContext, context);
 
                 return entitlementApi.getEntitlementForId(getId(), callContext);
@@ -550,11 +552,6 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                 }
 
                 final InternalCallContext context = internalCallContextFactory.createInternalCallContext(getAccountId(), callContext);
-                try {
-                    checker.checkBlockedChange(getSubscriptionBase(), context);
-                } catch (BlockingApiException e) {
-                    throw new EntitlementApiException(e, e.getCode(), e.getMessage());
-                }
 
                 final DateTime effectiveChangeDate;
                 try {
@@ -563,6 +560,12 @@ public class DefaultEntitlement extends EntityBase implements Entitlement {
                     throw new EntitlementApiException(e);
                 }
 
+                try {
+                    checker.checkBlockedChange(getSubscriptionBase(), effectiveChangeDate, context);
+                } catch (BlockingApiException e) {
+                    throw new EntitlementApiException(e, e.getCode(), e.getMessage());
+                }
+
                 blockAddOnsIfRequired(effectiveChangeDate, callContext, context);
 
                 return entitlementApi.getEntitlementForId(getId(), callContext);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
index ca9ff4c..177f482 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementApi.java
@@ -343,6 +343,21 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
 
     }
 
+
+    @Override
+    public void setBlockingState(final UUID bundleId, final String stateName, final String serviceName, final LocalDate effectiveDate, boolean blockBilling, boolean blockEntitlement, boolean blockChange, final Iterable<PluginProperty> properties, final CallContext context)
+            throws EntitlementApiException {
+        final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, context);
+        super.setBlockingState(bundleId, stateName, serviceName, effectiveDate, blockBilling, blockEntitlement, blockChange, properties, contextWithValidAccountRecordId);
+    }
+
+
+    @Override
+    public Iterable<BlockingState> getBlockingStatesForServiceAndType(final UUID blockableId, final BlockingStateType blockingStateType, final String serviceName, final TenantContext tenantContext) {
+        // Not implemented see #431
+        return null;
+    }
+
     @Override
     public UUID transferEntitlements(final UUID sourceAccountId, final UUID destAccountId, final String externalKey, final LocalDate effectiveDate, final Iterable<PluginProperty> properties, final CallContext context) throws EntitlementApiException {
         return transferEntitlementsOverrideBillingPolicy(sourceAccountId, destAccountId, externalKey, effectiveDate, BillingActionPolicy.IMMEDIATE, properties, context);
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
index 57fc554..3667835 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultSubscription.java
@@ -18,22 +18,15 @@
 
 package org.killbill.billing.entitlement.api;
 
-import java.util.Collection;
 import java.util.List;
 
 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 Collection<BlockingState> currentSubscriptionBlockingStatesForServices;
-
     DefaultSubscription(final DefaultEntitlement entitlement) {
         super(entitlement);
-        this.currentSubscriptionBlockingStatesForServices = eventsStream.getCurrentSubscriptionEntitlementBlockingStatesForServices();
     }
 
     @Override
@@ -67,22 +60,6 @@ public class DefaultSubscription extends DefaultEntitlement implements Subscript
     }
 
     @Override
-    public String getCurrentStateForService(final String serviceName) {
-        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();
-        }
-    }
-
-    @Override
     public List<SubscriptionEvent> getSubscriptionEvents() {
         return SubscriptionEventOrdering.sortedCopy(this, getAccountTimeZone());
     }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
index 7d6272c..d802daa 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/EntitlementDateHelper.java
@@ -58,4 +58,5 @@ public class EntitlementDateHelper {
         final LocalDate targetDateInAccountTimezone = new LocalDate(inputDate, accountTimeZone);
         return targetDateInAccountTimezone.compareTo(localDateNowInAccountTimezone) <= 0;
     }
+
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
index 56ce01c..e092b73 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/svcs/DefaultEntitlementApiBase.java
@@ -25,12 +25,10 @@ import java.util.Map;
 import java.util.UUID;
 
 import javax.annotation.Nullable;
-import javax.inject.Inject;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
 import org.killbill.billing.ErrorCode;
-import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountInternalApi;
 import org.killbill.billing.account.api.ImmutableAccountData;
@@ -39,7 +37,6 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.AccountEntitlements;
 import org.killbill.billing.entitlement.AccountEventsStreams;
 import org.killbill.billing.entitlement.DefaultEntitlementService;
-import org.killbill.billing.entitlement.EntitlementInternalApi;
 import org.killbill.billing.entitlement.EntitlementService;
 import org.killbill.billing.entitlement.EntitlementTransitionType;
 import org.killbill.billing.entitlement.EventsStream;
@@ -71,7 +68,6 @@ import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
 import org.killbill.billing.subscription.api.user.SubscriptionBaseBundle;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.bus.api.PersistentBus;
 import org.killbill.bus.api.PersistentBus.EventBusException;
 import org.killbill.clock.Clock;
@@ -86,7 +82,6 @@ public class DefaultEntitlementApiBase {
 
     private static final Logger log = LoggerFactory.getLogger(DefaultEntitlementApiBase.class);
 
-
     protected final EntitlementApi entitlementApi;
     protected final AccountInternalApi accountApi;
 
@@ -104,16 +99,16 @@ public class DefaultEntitlementApiBase {
     protected final PersistentBus eventBus;
 
     protected DefaultEntitlementApiBase(final PersistentBus eventBus,
-                                     @Nullable final EntitlementApi entitlementApi, final EntitlementPluginExecution pluginExecution,
-                                     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, final SecurityApi securityApi) {
+                                        @Nullable final EntitlementApi entitlementApi, final EntitlementPluginExecution pluginExecution,
+                                        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, final SecurityApi securityApi) {
         this.eventBus = eventBus;
         this.entitlementApi = entitlementApi != null ? entitlementApi : (EntitlementApi) this;
         this.accountApi = accountApi;
-        this.pluginExecution= pluginExecution;
+        this.pluginExecution = pluginExecution;
         this.internalCallContextFactory = internalCallContextFactory;
         this.subscriptionInternalApi = subscriptionInternalApi;
         this.clock = clock;
@@ -187,12 +182,11 @@ public class DefaultEntitlementApiBase {
                         return null;
                     }
 
-                    final BlockingState state = new DefaultBlockingState(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_BLOCKED, EntitlementService.ENTITLEMENT_SERVICE_NAME, true, true, true, effectiveDate);
-                    entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(state, internalCallContext);
+                    final UUID blockingId = blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_BLOCKED, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, true, true, true, baseSubscription, internalCallContext);
 
                     // Should we send one event per entitlement in the bundle?
                     // Code below only sends one event for the bundle and use the base entitlementId
-                    final DefaultEffectiveEntitlementEvent event = new DefaultEffectiveEntitlementEvent(state.getId(), baseSubscription.getId(), bundleId, bundle.getAccountId(), EntitlementTransitionType.BLOCK_BUNDLE,
+                    final DefaultEffectiveEntitlementEvent event = new DefaultEffectiveEntitlementEvent(blockingId, baseSubscription.getId(), bundleId, bundle.getAccountId(), EntitlementTransitionType.BLOCK_BUNDLE,
                                                                                                         effectiveDate, clock.getUTCNow(),
                                                                                                         internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(),
                                                                                                         internalCallContext.getUserToken());
@@ -248,12 +242,11 @@ public class DefaultEntitlementApiBase {
                         return null;
                     }
 
-                    final BlockingState state = new DefaultBlockingState(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, DefaultEntitlementApi.ENT_STATE_CLEAR, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, effectiveDate);
-                    entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(state, internalCallContext);
+                    final UUID blockingId = blockUnblockBundle(bundleId, DefaultEntitlementApi.ENT_STATE_CLEAR, EntitlementService.ENTITLEMENT_SERVICE_NAME, localEffectiveDate, false, false, false, baseSubscription, internalCallContext);
 
                     // Should we send one event per entitlement in the bundle?
                     // Code below only sends one event for the bundle and use the base entitlementId
-                    final DefaultEffectiveEntitlementEvent event = new DefaultEffectiveEntitlementEvent(state.getId(), baseSubscription.getId(), bundleId, bundle.getAccountId(), EntitlementTransitionType.UNBLOCK_BUNDLE,
+                    final DefaultEffectiveEntitlementEvent event = new DefaultEffectiveEntitlementEvent(blockingId, baseSubscription.getId(), bundleId, bundle.getAccountId(), EntitlementTransitionType.UNBLOCK_BUNDLE,
                                                                                                         effectiveDate, clock.getUTCNow(),
                                                                                                         internalCallContext.getAccountRecordId(), internalCallContext.getTenantRecordId(),
                                                                                                         internalCallContext.getUserToken());
@@ -275,6 +268,23 @@ public class DefaultEntitlementApiBase {
         pluginExecution.executeWithPlugin(resumeWithPlugin, pluginContext);
     }
 
+    public void setBlockingState(final UUID bundleId, final String stateName, final String serviceName, final LocalDate localEffectiveDate, boolean blockBilling, boolean blockEntitlement, boolean blockChange, final Iterable<PluginProperty> properties, final InternalCallContext internalCallContext)
+            throws EntitlementApiException {
+        blockUnblockBundle(bundleId, stateName, serviceName, localEffectiveDate, blockBilling, blockEntitlement, blockChange, null, internalCallContext);
+    }
+
+    private UUID blockUnblockBundle(final UUID bundleId, final String stateName, final String serviceName, final LocalDate localEffectiveDate, boolean blockBilling, boolean blockEntitlement, boolean blockChange, @Nullable final SubscriptionBase inputBaseSubscription, final InternalCallContext internalCallContext)
+            throws EntitlementApiException {
+        try {
+            final SubscriptionBase baseSubscription = inputBaseSubscription == null ? subscriptionInternalApi.getBaseSubscription(bundleId, internalCallContext) : inputBaseSubscription;
+            final DateTime effectiveDate = dateHelper.fromLocalDateAndReferenceTime(localEffectiveDate, baseSubscription.getStartDate(), internalCallContext);
+            final BlockingState state = new DefaultBlockingState(bundleId, BlockingStateType.SUBSCRIPTION_BUNDLE, stateName, serviceName, blockChange, blockEntitlement, blockBilling, effectiveDate);
+            entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(state, internalCallContext);
+            return state.getId();
+        } catch (SubscriptionBaseApiException e) {
+            throw new EntitlementApiException(e);
+        }
+    }
 
     protected void recordPauseResumeNotificationEntry(final UUID entitlementId, final UUID bundleId, final DateTime effectiveDate, final boolean isPause, final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
         final NotificationEvent notificationEvent = new EntitlementNotificationKey(entitlementId,
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/block/BlockingChecker.java b/entitlement/src/main/java/org/killbill/billing/entitlement/block/BlockingChecker.java
index 903df5a..c3d416e 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/block/BlockingChecker.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/block/BlockingChecker.java
@@ -19,6 +19,7 @@ package org.killbill.billing.entitlement.block;
 import java.util.List;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.api.Blockable;
 import org.killbill.billing.entitlement.api.BlockingApiException;
@@ -47,11 +48,11 @@ public interface BlockingChecker {
     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;
+    public BlockingAggregator getBlockedStatus(final UUID blockableId, final BlockingStateType type, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException;
 
-    public void checkBlockedChange(Blockable blockable, InternalTenantContext context) throws BlockingApiException;
+    public void checkBlockedChange(Blockable blockable, final DateTime upToDate, InternalTenantContext context) throws BlockingApiException;
 
-    public void checkBlockedEntitlement(Blockable blockable, InternalTenantContext context) throws BlockingApiException;
+    public void checkBlockedEntitlement(Blockable blockable, final DateTime upToDate, InternalTenantContext context) throws BlockingApiException;
 
-    public void checkBlockedBilling(Blockable blockable, InternalTenantContext context) throws BlockingApiException;
+    public void checkBlockedBilling(Blockable blockable, final DateTime upToDate, InternalTenantContext context) throws BlockingApiException;
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/block/DefaultBlockingChecker.java b/entitlement/src/main/java/org/killbill/billing/entitlement/block/DefaultBlockingChecker.java
index 538bed0..d468dbd 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/block/DefaultBlockingChecker.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/block/DefaultBlockingChecker.java
@@ -21,6 +21,7 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.callcontext.InternalTenantContext;
@@ -120,67 +121,67 @@ public class DefaultBlockingChecker implements BlockingChecker {
         this.dao = dao;
     }
 
-    private DefaultBlockingAggregator getBlockedStateSubscriptionId(final UUID subscriptionId, final InternalTenantContext context) throws BlockingApiException {
+    private DefaultBlockingAggregator getBlockedStateSubscriptionId(final UUID subscriptionId, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
         final SubscriptionBase subscription;
         try {
             subscription = subscriptionApi.getSubscriptionFromId(subscriptionId, context);
-            return getBlockedStateSubscription(subscription, context);
+            return getBlockedStateSubscription(subscription, upToDate, context);
         } catch (SubscriptionBaseApiException e) {
             throw new BlockingApiException(e, ErrorCode.fromCode(e.getCode()));
         }
     }
 
-    private DefaultBlockingAggregator getBlockedStateSubscription(final SubscriptionBase subscription, final InternalTenantContext context) throws BlockingApiException {
+    private DefaultBlockingAggregator getBlockedStateSubscription(final SubscriptionBase subscription, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
         final DefaultBlockingAggregator result = new DefaultBlockingAggregator();
         if (subscription != null) {
-            final DefaultBlockingAggregator subscriptionState = getBlockedStateForId(subscription.getId(), BlockingStateType.SUBSCRIPTION, context);
+            final DefaultBlockingAggregator subscriptionState = getBlockedStateForId(subscription.getId(), BlockingStateType.SUBSCRIPTION, upToDate, context);
             if (subscriptionState != null) {
                 result.or(subscriptionState);
             }
             if (subscription.getBundleId() != null) {
                 // Recursive call to also fetch account state
-                result.or(getBlockedStateBundleId(subscription.getBundleId(), context));
+                result.or(getBlockedStateBundleId(subscription.getBundleId(), upToDate, context));
             }
         }
         return result;
     }
 
-    private DefaultBlockingAggregator getBlockedStateBundleId(final UUID bundleId, final InternalTenantContext context) throws BlockingApiException {
+    private DefaultBlockingAggregator getBlockedStateBundleId(final UUID bundleId, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
 
         final SubscriptionBaseBundle bundle;
         try {
             bundle = subscriptionApi.getBundleFromId(bundleId, context);
-            return getBlockedStateBundle(bundle, context);
+            return getBlockedStateBundle(bundle, upToDate, context);
         } catch (SubscriptionBaseApiException e) {
             throw new BlockingApiException(e, ErrorCode.fromCode(e.getCode()));
         }
     }
 
-    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);
+    private DefaultBlockingAggregator getBlockedStateBundle(final SubscriptionBaseBundle bundle, final DateTime upToDate, final InternalTenantContext context) {
+        final DefaultBlockingAggregator result = getBlockedStateAccountId(bundle.getAccountId(), upToDate, context);
+        final DefaultBlockingAggregator bundleState = getBlockedStateForId(bundle.getId(), BlockingStateType.SUBSCRIPTION_BUNDLE, upToDate, context);
         if (bundleState != null) {
             result.or(bundleState);
         }
         return result;
     }
 
-    private DefaultBlockingAggregator getBlockedStateAccount(final Account account, final InternalTenantContext context) {
+    private DefaultBlockingAggregator getBlockedStateAccount(final Account account, final DateTime upToDate, final InternalTenantContext context) {
         if (account != null) {
-            return getBlockedStateForId(account.getId(), BlockingStateType.ACCOUNT, context);
+            return getBlockedStateForId(account.getId(), BlockingStateType.ACCOUNT, upToDate, context);
         }
         return new DefaultBlockingAggregator();
     }
 
-    private DefaultBlockingAggregator getBlockedStateAccountId(final UUID accountId, final InternalTenantContext context) {
-        return getBlockedStateForId(accountId, BlockingStateType.ACCOUNT, context);
+    private DefaultBlockingAggregator getBlockedStateAccountId(final UUID accountId, final DateTime upToDate, final InternalTenantContext context) {
+        return getBlockedStateForId(accountId, BlockingStateType.ACCOUNT, upToDate, context);
     }
 
-    private DefaultBlockingAggregator getBlockedStateForId(@Nullable final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
+    private DefaultBlockingAggregator getBlockedStateForId(@Nullable final UUID blockableId, final BlockingStateType blockingStateType, final DateTime upToDate, final InternalTenantContext context) {
         // Last states across services
         final List<BlockingState> blockableState;
         if (blockableId != null) {
-            blockableState = dao.getBlockingState(blockableId, blockingStateType, context);
+            blockableState = dao.getBlockingState(blockableId, blockingStateType, upToDate, context);
         } else {
             blockableState = ImmutableList.<BlockingState>of();
         }
@@ -196,13 +197,13 @@ public class DefaultBlockingChecker implements BlockingChecker {
     }
 
     @Override
-    public BlockingAggregator getBlockedStatus(final UUID blockableId, final BlockingStateType type, final InternalTenantContext context) throws BlockingApiException {
+    public BlockingAggregator getBlockedStatus(final UUID blockableId, final BlockingStateType type, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
         if (type == BlockingStateType.SUBSCRIPTION) {
-            return getBlockedStateSubscriptionId(blockableId, context);
+            return getBlockedStateSubscriptionId(blockableId, upToDate, context);
         } else if (type == BlockingStateType.SUBSCRIPTION_BUNDLE) {
-            return getBlockedStateBundleId(blockableId, context);
+            return getBlockedStateBundleId(blockableId, upToDate, context);
         } else { // BlockingStateType.ACCOUNT {
-            return getBlockedStateAccountId(blockableId, context);
+            return getBlockedStateAccountId(blockableId, upToDate, context);
         }
     }
 
@@ -215,34 +216,34 @@ public class DefaultBlockingChecker implements BlockingChecker {
     }
 
     @Override
-    public void checkBlockedChange(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
-        if (blockable instanceof SubscriptionBase && getBlockedStateSubscription((SubscriptionBase) blockable, context).isBlockChange()) {
+    public void checkBlockedChange(final Blockable blockable, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
+        if (blockable instanceof SubscriptionBase && getBlockedStateSubscription((SubscriptionBase) blockable, upToDate, context).isBlockChange()) {
             throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_CHANGE, TYPE_SUBSCRIPTION, blockable.getId().toString());
-        } else if (blockable instanceof SubscriptionBaseBundle && getBlockedStateBundle((SubscriptionBaseBundle) blockable, context).isBlockChange()) {
+        } else if (blockable instanceof SubscriptionBaseBundle && getBlockedStateBundle((SubscriptionBaseBundle) blockable, upToDate, context).isBlockChange()) {
             throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_CHANGE, TYPE_BUNDLE, blockable.getId().toString());
-        } else if (blockable instanceof Account && getBlockedStateAccount((Account) blockable, context).isBlockChange()) {
+        } else if (blockable instanceof Account && getBlockedStateAccount((Account) blockable, upToDate, context).isBlockChange()) {
             throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_CHANGE, TYPE_ACCOUNT, blockable.getId().toString());
         }
     }
 
     @Override
-    public void checkBlockedEntitlement(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
-        if (blockable instanceof SubscriptionBase && getBlockedStateSubscription((SubscriptionBase) blockable, context).isBlockEntitlement()) {
+    public void checkBlockedEntitlement(final Blockable blockable, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
+        if (blockable instanceof SubscriptionBase && getBlockedStateSubscription((SubscriptionBase) blockable, upToDate, context).isBlockEntitlement()) {
             throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_ENTITLEMENT, TYPE_SUBSCRIPTION, blockable.getId().toString());
-        } else if (blockable instanceof SubscriptionBaseBundle && getBlockedStateBundle((SubscriptionBaseBundle) blockable, context).isBlockEntitlement()) {
+        } else if (blockable instanceof SubscriptionBaseBundle && getBlockedStateBundle((SubscriptionBaseBundle) blockable, upToDate, context).isBlockEntitlement()) {
             throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_ENTITLEMENT, TYPE_BUNDLE, blockable.getId().toString());
-        } else if (blockable instanceof Account && getBlockedStateAccount((Account) blockable, context).isBlockEntitlement()) {
+        } else if (blockable instanceof Account && getBlockedStateAccount((Account) blockable, upToDate, context).isBlockEntitlement()) {
             throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_ENTITLEMENT, TYPE_ACCOUNT, blockable.getId().toString());
         }
     }
 
     @Override
-    public void checkBlockedBilling(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
-        if (blockable instanceof SubscriptionBase && getBlockedStateSubscription((SubscriptionBase) blockable, context).isBlockBilling()) {
+    public void checkBlockedBilling(final Blockable blockable, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
+        if (blockable instanceof SubscriptionBase && getBlockedStateSubscription((SubscriptionBase) blockable, upToDate, context).isBlockBilling()) {
             throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_BILLING, TYPE_SUBSCRIPTION, blockable.getId().toString());
-        } else if (blockable instanceof SubscriptionBaseBundle && getBlockedStateBundle((SubscriptionBaseBundle) blockable, context).isBlockBilling()) {
+        } else if (blockable instanceof SubscriptionBaseBundle && getBlockedStateBundle((SubscriptionBaseBundle) blockable, upToDate, context).isBlockBilling()) {
             throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_BILLING, TYPE_BUNDLE, blockable.getId().toString());
-        } else if (blockable instanceof Account && getBlockedStateAccount((Account) blockable, context).isBlockBilling()) {
+        } else if (blockable instanceof Account && getBlockedStateAccount((Account) blockable, upToDate, context).isBlockBilling()) {
             throw new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, ACTION_BILLING, TYPE_ACCOUNT, blockable.getId().toString());
         }
     }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateDao.java
index c2d8c7f..493b519 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/BlockingStateDao.java
@@ -19,6 +19,7 @@ package org.killbill.billing.entitlement.dao;
 import java.util.List;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.clock.Clock;
@@ -48,7 +49,7 @@ public interface BlockingStateDao extends EntityDao<BlockingStateModelDao, Block
      * @param context           call context
      * @return list of current blocking states for that blockable object
      */
-    public List<BlockingState> getBlockingState(UUID blockableId, BlockingStateType blockingStateType, InternalTenantContext context);
+    public List<BlockingState> getBlockingState(UUID blockableId, BlockingStateType blockingStateType, DateTime upToDate, InternalTenantContext context);
 
     /**
      * Return all events (past and future) across all services) for a given callcontext (account_record_id)
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
index dcbce47..9b72f19 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/DefaultBlockingStateDao.java
@@ -26,6 +26,7 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
@@ -102,12 +103,12 @@ public class DefaultBlockingStateDao extends EntityDaoBase<BlockingStateModelDao
     }
 
     @Override
-    public List<BlockingState> getBlockingState(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
+    public List<BlockingState> getBlockingState(final UUID blockableId, final BlockingStateType blockingStateType, final DateTime upToDate, final InternalTenantContext context) {
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<List<BlockingState>>() {
             @Override
             public List<BlockingState> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 // Upper bound time limit is now
-                final Date upTo = clock.getUTCNow().toDate();
+                final Date upTo = upToDate.toDate();
                 final List<BlockingStateModelDao> models = entitySqlDaoWrapperFactory.become(BlockingStateSqlDao.class).getBlockingState(blockableId, upTo, context);
                 final Collection<BlockingStateModelDao> modelsFiltered = filterBlockingStates(models, blockingStateType);
                 return new ArrayList<BlockingState>(Collections2.transform(modelsFiltered, new Function<BlockingStateModelDao, BlockingState>() {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/ProxyBlockingStateDao.java b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/ProxyBlockingStateDao.java
index b404562..92f6fb0 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/dao/ProxyBlockingStateDao.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/dao/ProxyBlockingStateDao.java
@@ -218,8 +218,8 @@ public class ProxyBlockingStateDao implements BlockingStateDao {
     }
 
     @Override
-    public List<BlockingState> getBlockingState(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
-        return delegate.getBlockingState(blockableId, blockingStateType, context);
+    public List<BlockingState> getBlockingState(final UUID blockableId, final BlockingStateType blockingStateType, final DateTime upToDate, final InternalTenantContext context) {
+        return delegate.getBlockingState(blockableId, blockingStateType, upToDate, context);
     }
 
     @Override
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
index 23795b6..e6d701e 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/DefaultEventsStream.java
@@ -71,11 +71,6 @@ public class DefaultEventsStream implements EventsStream {
 
     private BlockingAggregator blockingAggregator;
     private List<BlockingState> subscriptionEntitlementStates;
-    private List<BlockingState> bundleEntitlementStates;
-    private List<BlockingState> accountEntitlementStates;
-    private List<BlockingState> currentSubscriptionEntitlementBlockingStatesForServices;
-    private List<BlockingState> currentBundleEntitlementBlockingStatesForServices;
-    private List<BlockingState> currentAccountEntitlementBlockingStatesForServices;
     private LocalDate entitlementEffectiveEndDate;
     private BlockingState entitlementCancelEvent;
     private EntitlementState entitlementState;
@@ -152,11 +147,6 @@ public class DefaultEventsStream implements EventsStream {
         return blockingAggregator.isBlockChange();
     }
 
-    @Override
-    public List<BlockingState> getCurrentSubscriptionEntitlementBlockingStatesForServices() {
-        return currentSubscriptionEntitlementBlockingStatesForServices;
-    }
-
     public boolean isEntitlementFutureCancelled() {
         return entitlementCancelEvent != null && entitlementCancelEvent.getEffectiveDate().isAfter(utcNow);
     }
@@ -187,34 +177,27 @@ public class DefaultEventsStream implements EventsStream {
 
     @Override
     public Collection<BlockingState> getPendingEntitlementCancellationEvents() {
-        return getPendingEntitlementEvents(DefaultEntitlementApi.ENT_STATE_CANCELLED);
-    }
-
-    @Override
-    public BlockingState getEntitlementCancellationEvent() {
-        return entitlementCancelEvent;
-    }
 
-    public Collection<BlockingState> getPendingEntitlementEvents(final String... types) {
-        final List<String> typeList = ImmutableList.<String>copyOf(types);
         return Collections2.<BlockingState>filter(subscriptionEntitlementStates,
                                                   new Predicate<BlockingState>() {
                                                       @Override
                                                       public boolean apply(final BlockingState input) {
                                                           return !input.getEffectiveDate().isBefore(utcNow) &&
-                                                                 typeList.contains(input.getStateName()) &&
+                                                                 DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(input.getStateName()) &&
                                                                  (
                                                                          // ... for that subscription
                                                                          BlockingStateType.SUBSCRIPTION.equals(input.getType()) && input.getBlockedId().equals(subscription.getId()) ||
                                                                          // ... for the associated base subscription
-                                                                         BlockingStateType.SUBSCRIPTION.equals(input.getType()) && input.getBlockedId().equals(baseSubscription.getId()) ||
-                                                                         // ... for that bundle
-                                                                         BlockingStateType.SUBSCRIPTION_BUNDLE.equals(input.getType()) && input.getBlockedId().equals(bundle.getId()) ||
-                                                                         // ... for that account
-                                                                         BlockingStateType.ACCOUNT.equals(input.getType()) && input.getBlockedId().equals(account.getId())
+                                                                         BlockingStateType.SUBSCRIPTION.equals(input.getType()) && input.getBlockedId().equals(baseSubscription.getId())
                                                                  );
                                                       }
                                                   });
+
+    }
+
+    @Override
+    public BlockingState getEntitlementCancellationEvent() {
+        return entitlementCancelEvent;
     }
 
     public BlockingState getEntitlementCancellationEvent(final UUID subscriptionId) {
@@ -370,30 +353,36 @@ public class DefaultEventsStream implements EventsStream {
     private void setup() {
         computeEntitlementBlockingStates();
         computeBlockingAggregator();
-        computeEntitlementEffectiveEndDate();
         computeEntitlementCancelEvent();
         computeStateForEntitlement();
     }
 
     private void computeBlockingAggregator() {
-        currentAccountEntitlementBlockingStatesForServices = filterCurrentBlockableStatePerService(accountEntitlementStates);
-        currentBundleEntitlementBlockingStatesForServices = filterCurrentBlockableStatePerService(bundleEntitlementStates);
-        currentSubscriptionEntitlementBlockingStatesForServices = filterCurrentBlockableStatePerService(subscriptionEntitlementStates);
-        blockingAggregator = blockingChecker.getBlockedStatus(currentAccountEntitlementBlockingStatesForServices,
-                                                              currentBundleEntitlementBlockingStatesForServices,
-                                                              currentSubscriptionEntitlementBlockingStatesForServices,
+
+        final List<BlockingState> currentSubscriptionBlockingStatesForServices = filterCurrentBlockableStatePerService(BlockingStateType.SUBSCRIPTION, subscription.getId());
+        final List<BlockingState> currentBundleBlockingStatesForServices = filterCurrentBlockableStatePerService(BlockingStateType.SUBSCRIPTION_BUNDLE, subscription.getBundleId());
+        final List<BlockingState> currentAccountBlockingStatesForServices = filterCurrentBlockableStatePerService(BlockingStateType.ACCOUNT, account.getId());
+        blockingAggregator = blockingChecker.getBlockedStatus(currentAccountBlockingStatesForServices,
+                                                              currentBundleBlockingStatesForServices,
+                                                              currentSubscriptionBlockingStatesForServices,
                                                               internalTenantContext);
     }
 
-    private List<BlockingState> filterCurrentBlockableStatePerService(final Iterable<BlockingState> allBlockingStates) {
+    private List<BlockingState> filterCurrentBlockableStatePerService(final BlockingStateType type, final UUID blockableId) {
         final Map<String, BlockingState> currentBlockingStatePerService = new HashMap<String, BlockingState>();
-        for (final BlockingState blockingState : allBlockingStates) {
+        for (final BlockingState blockingState : blockingStates) {
+            if (!blockingState.getBlockedId().equals(blockableId)) {
+                continue;
+            }
+            if (blockingState.getType() != type) {
+                continue;
+            }
             if (blockingState.getEffectiveDate().isAfter(utcNow)) {
                 continue;
             }
 
             if (currentBlockingStatePerService.get(blockingState.getService()) == null ||
-                currentBlockingStatePerService.get(blockingState.getService()).getEffectiveDate().isBefore(blockingState.getEffectiveDate())) {
+                !currentBlockingStatePerService.get(blockingState.getService()).getEffectiveDate().isAfter(blockingState.getEffectiveDate())) {
                 currentBlockingStatePerService.put(blockingState.getService(), blockingState);
             }
         }
@@ -401,29 +390,6 @@ public class DefaultEventsStream implements EventsStream {
         return ImmutableList.<BlockingState>copyOf(currentBlockingStatePerService.values());
     }
 
-    private void computeEntitlementEffectiveEndDate() {
-        LocalDate result = null;
-        BlockingState lastEntry;
-
-        lastEntry = (!subscriptionEntitlementStates.isEmpty()) ? subscriptionEntitlementStates.get(subscriptionEntitlementStates.size() - 1) : null;
-        if (lastEntry != null && DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(lastEntry.getStateName())) {
-            result = new LocalDate(lastEntry.getEffectiveDate(), account.getTimeZone());
-        }
-
-        lastEntry = (!bundleEntitlementStates.isEmpty()) ? bundleEntitlementStates.get(bundleEntitlementStates.size() - 1) : null;
-        if (lastEntry != null && DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(lastEntry.getStateName())) {
-            final LocalDate localDate = new LocalDate(lastEntry.getEffectiveDate(), account.getTimeZone());
-            result = ((result == null) || (localDate.compareTo(result) < 0)) ? localDate : result;
-        }
-
-        lastEntry = (!accountEntitlementStates.isEmpty()) ? accountEntitlementStates.get(accountEntitlementStates.size() - 1) : null;
-        if (lastEntry != null && DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(lastEntry.getStateName())) {
-            final LocalDate localDate = new LocalDate(lastEntry.getEffectiveDate(), account.getTimeZone());
-            result = ((result == null) || (localDate.compareTo(result) < 0)) ? localDate : result;
-        }
-
-        entitlementEffectiveEndDate = result;
-    }
 
     private void computeEntitlementCancelEvent() {
         entitlementCancelEvent = Iterables.<BlockingState>tryFind(subscriptionEntitlementStates,
@@ -433,6 +399,7 @@ public class DefaultEventsStream implements EventsStream {
                                                                           return DefaultEntitlementApi.ENT_STATE_CANCELLED.equals(input.getStateName());
                                                                       }
                                                                   }).orNull();
+        entitlementEffectiveEndDate = entitlementCancelEvent != null ?  new LocalDate(entitlementCancelEvent.getEffectiveDate(), account.getTimeZone()) : null;
     }
 
     private void computeStateForEntitlement() {
@@ -447,8 +414,6 @@ public class DefaultEventsStream implements EventsStream {
 
     private void computeEntitlementBlockingStates() {
         subscriptionEntitlementStates = filterBlockingStatesForEntitlementService(BlockingStateType.SUBSCRIPTION, subscription.getId());
-        bundleEntitlementStates = filterBlockingStatesForEntitlementService(BlockingStateType.SUBSCRIPTION_BUNDLE, subscription.getBundleId());
-        accountEntitlementStates = filterBlockingStatesForEntitlementService(BlockingStateType.ACCOUNT, account.getId());
     }
 
     private List<BlockingState> filterBlockingStatesForEntitlementService(final BlockingStateType blockingStateType, @Nullable final UUID blockableId) {
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EntitlementUtils.java b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EntitlementUtils.java
index da4ccaa..6be5e37 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EntitlementUtils.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/engine/core/EntitlementUtils.java
@@ -89,11 +89,11 @@ public class EntitlementUtils {
      * @param context call context
      */
     public void setBlockingStateAndPostBlockingTransitionEvent(final BlockingState state, final InternalCallContext context) {
-        final BlockingAggregator previousState = getBlockingStateFor(state.getBlockedId(), state.getType(), context);
+        final BlockingAggregator previousState = getBlockingStateFor(state.getBlockedId(), state.getType(), clock.getUTCNow(), context);
 
         dao.setBlockingState(state, clock, context);
 
-        final BlockingAggregator currentState = getBlockingStateFor(state.getBlockedId(), state.getType(), context);
+        final BlockingAggregator currentState = getBlockingStateFor(state.getBlockedId(), state.getType(), state.getEffectiveDate(), context);
         if (previousState != null && currentState != null) {
             postBlockingTransitionEvent(state.getId(), state.getEffectiveDate(), state.getBlockedId(), state.getType(), state.getService(), previousState, currentState, context);
         }
@@ -118,9 +118,9 @@ public class EntitlementUtils {
     }
 
 
-    private BlockingAggregator getBlockingStateFor(final UUID blockableId, final BlockingStateType type, final InternalCallContext context) {
+    private BlockingAggregator getBlockingStateFor(final UUID blockableId, final BlockingStateType type, final DateTime effectiveDate, final InternalCallContext context) {
         try {
-            return blockingChecker.getBlockedStatus(blockableId, type, context);
+            return blockingChecker.getBlockedStatus(blockableId, type, effectiveDate, context);
         } catch (BlockingApiException e) {
             log.warn("Failed to retrieve blocking state for {} {}", blockableId, type);
             return null;
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
index 8cc53e0..66723ef 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultEntitlementApi.java
@@ -22,11 +22,6 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.joda.time.LocalDate;
-import org.killbill.billing.payment.api.PluginProperty;
-import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
@@ -39,6 +34,10 @@ import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementSourceType;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.subscription.api.user.SubscriptionBaseApiException;
+import org.testng.Assert;
+import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
 
@@ -357,6 +356,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         }
     }
 
+
     @Test(groups = "slow", description = "Test pause / unpause in the future")
     public void testPauseUnpauseInTheFuture() throws AccountApiException, EntitlementApiException {
         final LocalDate initialDate = new LocalDate(2013, 8, 7);
@@ -386,7 +386,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         assertListenerStatus();
 
         testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
-        clock.setDay(pauseDate.plusDays(1));
+        clock.setDay(pauseDate);
         assertListenerStatus();
 
         // Verify blocking state
@@ -394,7 +394,7 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
         assertEquals(baseEntitlementPaused.getState(), EntitlementState.BLOCKED);
 
         testListener.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
-        clock.setDay(resumeDate.plusDays(1));
+        clock.setDay(resumeDate);
         assertListenerStatus();
 
         // Verify blocking state
@@ -444,6 +444,78 @@ public class TestDefaultEntitlementApi extends EntitlementTestSuiteWithEmbeddedD
     }
 
     @Test(groups = "slow")
+    public void testBlockBundle() throws AccountApiException, EntitlementApiException {
+        final LocalDate initialDate = new LocalDate(2013, 8, 7);
+        clock.setDay(initialDate);
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        // Create entitlement and check each field
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        final Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, initialDate, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        clock.addDays(5);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "BLOCK", "foo", new LocalDate(clock.getUTCNow()), true, true, true, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        List<Entitlement> bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
+
+        final BlockingState blockingState = blockingInternalApi.getBlockingStateForService(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "foo", internalCallContext);
+        assertTrue(blockingState.isBlockBilling());
+        assertTrue(blockingState.isBlockChange());
+        assertTrue(blockingState.isBlockEntitlement());
+
+
+        // Check unblocking on another service will not bring the sate back to ACTIVE
+        clock.addDays(1);
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "bar", new LocalDate(clock.getUTCNow()), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
+
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "foo", new LocalDate(clock.getUTCNow()), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.ACTIVE);
+
+        blockingInternalApi.getBlockingStateForService(baseEntitlement.getBundleId(), BlockingStateType.SUBSCRIPTION_BUNDLE, "foo", internalCallContext);
+        clock.addDays(1);
+
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "BLOCK", "foo", new LocalDate(clock.getUTCNow()), true, true, true, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.BLOCKED);
+
+        // Same day but happened after so should take precedence
+        testListener.pushExpectedEvents(NextEvent.BLOCK);
+        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), "UNBLOCK", "foo", new LocalDate(clock.getUTCNow()), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        bundleEntitlements = entitlementApi.getAllEntitlementsForBundle(baseEntitlement.getBundleId(), callContext);
+        assertEquals(bundleEntitlements.size(), 1);
+        assertEquals(bundleEntitlements.get(0).getState(), EntitlementState.ACTIVE);
+    }
+
+
+
+        @Test(groups = "slow")
     public void testCreateEntitlementInThePast() throws AccountApiException, EntitlementApiException, SubscriptionBaseApiException {
         final LocalDate initialDate = new LocalDate(2013, 8, 7);
         final LocalDate clockDate = new LocalDate(2013, 10, 7);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
index 9adb178..7a90cfc 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/api/TestDefaultSubscriptionApi.java
@@ -217,14 +217,22 @@ public class TestDefaultSubscriptionApi extends EntitlementTestSuiteWithEmbedded
         final LocalDate pauseDate = new LocalDate(2013, 9, 17);
         entitlementApi.pause(baseEntitlement.getBundleId(), pauseDate, ImmutableList.<PluginProperty>of(), callContext);
 
+        testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK);
+        clock.setDay(pauseDate);
+        assertListenerStatus();
+
         final LocalDate resumeDate = new LocalDate(2013, 12, 24);
         entitlementApi.resume(baseEntitlement.getBundleId(), resumeDate, ImmutableList.<PluginProperty>of(), callContext);
 
+        testListener.pushExpectedEvents(NextEvent.RESUME, NextEvent.BLOCK);
+        clock.setDay(resumeDate);
+        assertListenerStatus();
+
         final LocalDate cancelDate = new LocalDate(2013, 12, 27);
         baseEntitlement.cancelEntitlementWithDate(cancelDate, true, ImmutableList.<PluginProperty>of(), callContext);
 
-        testListener.pushExpectedEvents(NextEvent.PAUSE, NextEvent.BLOCK, NextEvent.RESUME, NextEvent.BLOCK, NextEvent.CANCEL, NextEvent.BLOCK);
-        clock.setDay(cancelDate.plusDays(1));
+        testListener.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK);
+        clock.setDay(cancelDate);
         assertListenerStatus();
 
         final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(baseEntitlement.getBundleId(), callContext);
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/block/MockBlockingChecker.java b/entitlement/src/test/java/org/killbill/billing/entitlement/block/MockBlockingChecker.java
index 0213916..9453136 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/block/MockBlockingChecker.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/block/MockBlockingChecker.java
@@ -19,6 +19,7 @@ package org.killbill.billing.entitlement.block;
 import java.util.List;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.entitlement.api.Blockable;
 import org.killbill.billing.entitlement.api.BlockingApiException;
@@ -33,19 +34,19 @@ public class MockBlockingChecker implements BlockingChecker {
     }
 
     @Override
-    public BlockingAggregator getBlockedStatus(final UUID blockableId, final BlockingStateType type, final InternalTenantContext context) throws BlockingApiException {
+    public BlockingAggregator getBlockedStatus(final UUID blockableId, final BlockingStateType type, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
         return null;
     }
 
     @Override
-    public void checkBlockedChange(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
+    public void checkBlockedChange(final Blockable blockable, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
     }
 
     @Override
-    public void checkBlockedEntitlement(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
+    public void checkBlockedEntitlement(final Blockable blockable, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
     }
 
     @Override
-    public void checkBlockedBilling(final Blockable blockable, final InternalTenantContext context) throws BlockingApiException {
+    public void checkBlockedBilling(final Blockable blockable, final DateTime upToDate, final InternalTenantContext context) throws BlockingApiException {
     }
 }
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
index 28e0e04..984955c 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingApi.java
@@ -24,10 +24,17 @@ import java.util.UUID;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.api.TestApiListener.NextEvent;
 import org.killbill.billing.callcontext.InternalCallContext;
+import org.killbill.billing.catalog.api.BillingPeriod;
+import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
+import org.killbill.billing.catalog.api.PriceListSet;
+import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.EntitlementTestSuiteWithEmbeddedDB;
 import org.killbill.billing.entitlement.api.BlockingState;
 import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.entitlement.api.Entitlement;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.killbill.billing.junction.DefaultBlockingState;
+import org.killbill.billing.payment.api.PluginProperty;
 import org.testng.Assert;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -36,6 +43,8 @@ import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
 import com.google.common.collect.ImmutableList;
 
+import static org.testng.Assert.assertEquals;
+
 public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
 
     @BeforeMethod(groups = "slow")
@@ -72,7 +81,6 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
 
     @Test(groups = "slow")
     public void testApiHistory() throws Exception {
-        final UUID uuid = UUID.randomUUID();
         final String overdueStateName = "WayPassedItMan";
         final String service = "TEST";
 
@@ -84,7 +92,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
 
         testListener.pushExpectedEvent(NextEvent.BLOCK);
-        final BlockingState state1 = new DefaultBlockingState(uuid, BlockingStateType.ACCOUNT, overdueStateName, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
+        final BlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, overdueStateName, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
         blockingInternalApi.setBlockingState(state1, internalCallContext);
         assertListenerStatus();
 
@@ -92,7 +100,7 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
 
         testListener.pushExpectedEvent(NextEvent.BLOCK);
         final String overdueStateName2 = "NoReallyThisCantGoOn";
-        final BlockingState state2 = new DefaultBlockingState(uuid, BlockingStateType.ACCOUNT, overdueStateName2, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
+        final BlockingState state2 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, overdueStateName2, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
         blockingInternalApi.setBlockingState(state2, internalCallContext);
         assertListenerStatus();
 
@@ -109,4 +117,74 @@ public class TestBlockingApi extends EntitlementTestSuiteWithEmbeddedDB {
         Assert.assertEquals(history.get(0).getStateName(), overdueStateName);
         Assert.assertEquals(history.get(1).getStateName(), overdueStateName2);
     }
+
+
+    @Test(groups = "slow")
+    public void testBlockingAcrossTypes() throws Exception {
+
+        final String stateNameBlock = "stateBlock";
+        final String stateNameUnBlock = "stateUnBlock";
+        final String service = "SVC_BLOC_TYPES";
+
+        final boolean blockChange = false;
+        final boolean blockEntitlement = true;
+        final boolean blockBilling = false;
+
+        final Account account = accountApi.createAccount(getAccountData(7), callContext);
+        final InternalCallContext internalCallContext = internalCallContextFactory.createInternalCallContext(account.getId(), callContext);
+
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        final BlockingState state1 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, stateNameBlock, service, blockChange, blockEntitlement, blockBilling, clock.getUTCNow());
+        blockingInternalApi.setBlockingState(state1, internalCallContext);
+        assertListenerStatus();
+
+        final PlanPhaseSpecifier spec = new PlanPhaseSpecifier("Shotgun", ProductCategory.BASE, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, null);
+
+        testListener.pushExpectedEvent(NextEvent.CREATE);
+        Entitlement baseEntitlement = entitlementApi.createBaseEntitlement(account.getId(), spec, account.getExternalKey(), null, clock.getUTCToday(), ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        assertEquals(baseEntitlement.getState(), EntitlementState.BLOCKED);
+
+        // Add blocking at bundle level.
+        clock.addDays(1);
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), stateNameBlock, service, clock.getUTCToday(), blockBilling, blockEntitlement, blockChange, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+
+        baseEntitlement = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
+        assertEquals(baseEntitlement.getState(), EntitlementState.BLOCKED);
+
+        // Remove blocking at account level
+        clock.addDays(1);
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        final BlockingState state2 = new DefaultBlockingState(account.getId(), BlockingStateType.ACCOUNT, stateNameUnBlock, service, false, false, false, clock.getUTCNow());
+        blockingInternalApi.setBlockingState(state2, internalCallContext);
+        assertListenerStatus();
+
+        baseEntitlement = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
+        assertEquals(baseEntitlement.getState(), EntitlementState.BLOCKED);
+
+
+        // Remove blocking at bundle level.
+        clock.addDays(1);
+        testListener.pushExpectedEvent(NextEvent.BLOCK);
+        entitlementApi.setBlockingState(baseEntitlement.getBundleId(), stateNameUnBlock, service, clock.getUTCToday(), false, false, false, ImmutableList.<PluginProperty>of(), callContext);
+        assertListenerStatus();
+
+        baseEntitlement = entitlementApi.getEntitlementForId(baseEntitlement.getId(), callContext);
+        assertEquals(baseEntitlement.getState(), EntitlementState.ACTIVE);
+
+        final List<BlockingState> blockingAll = blockingInternalApi.getBlockingAllForAccount(internalCallContext);
+        final List<BlockingState> history = ImmutableList.<BlockingState>copyOf(Collections2.<BlockingState>filter(blockingAll,
+                                                                                                                   new Predicate<BlockingState>() {
+                                                                                                                       @Override
+                                                                                                                       public boolean apply(final BlockingState input) {
+                                                                                                                           return input.getService().equals(service);
+                                                                                                                       }
+                                                                                                                   }));
+
+        Assert.assertEquals(history.size(), 4);
+    }
 }
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingChecker.java b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingChecker.java
index 01a857c..03fabcb 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingChecker.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/block/TestBlockingChecker.java
@@ -89,17 +89,17 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
         setStateAccount(false, false, false);
         setStateBundle(false, false, false);
         setStateSubscription(false, false, false);
-        blockingChecker.checkBlockedChange(subscription, internalCallContext);
-        blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
-        blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+        blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
 
         //BLOCKED SUBSCRIPTION
         clock.addDays(1);
         setStateSubscription(true, false, false);
-        blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
-        blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+        blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedChange(subscription, internalCallContext);
+            blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -107,10 +107,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateSubscription(false, true, false);
-        blockingChecker.checkBlockedChange(subscription, internalCallContext);
-        blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+        blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
+            blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -118,10 +118,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateSubscription(false, false, true);
-        blockingChecker.checkBlockedChange(subscription, internalCallContext);
-        blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
+        blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+            blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -131,10 +131,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
         clock.addDays(1);
         setStateSubscription(false, false, false);
         setStateBundle(true, false, false);
-        blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
-        blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+        blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedChange(subscription, internalCallContext);
+            blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -142,10 +142,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateBundle(false, true, false);
-        blockingChecker.checkBlockedChange(subscription, internalCallContext);
-        blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+        blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
+            blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -153,10 +153,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateBundle(false, false, true);
-        blockingChecker.checkBlockedChange(subscription, internalCallContext);
-        blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
+        blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+            blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -167,10 +167,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
         setStateSubscription(false, false, false);
         setStateBundle(false, false, false);
         setStateAccount(true, false, false);
-        blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
-        blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+        blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedChange(subscription, internalCallContext);
+            blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -178,10 +178,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateAccount(false, true, false);
-        blockingChecker.checkBlockedChange(subscription, internalCallContext);
-        blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+        blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
+            blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -189,10 +189,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateAccount(false, false, true);
-        blockingChecker.checkBlockedChange(subscription, internalCallContext);
-        blockingChecker.checkBlockedEntitlement(subscription, internalCallContext);
+        blockingChecker.checkBlockedChange(subscription, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedEntitlement(subscription, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedBilling(subscription, internalCallContext);
+            blockingChecker.checkBlockedBilling(subscription, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -204,18 +204,18 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
         setStateAccount(false, false, false);
         setStateBundle(false, false, false);
         setStateSubscription(false, false, false);
-        blockingChecker.checkBlockedChange(bundle, internalCallContext);
-        blockingChecker.checkBlockedEntitlement(bundle, internalCallContext);
-        blockingChecker.checkBlockedBilling(bundle, internalCallContext);
+        blockingChecker.checkBlockedChange(bundle, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedEntitlement(bundle, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(bundle, clock.getUTCNow(), internalCallContext);
 
         //BLOCKED BUNDLE
         clock.addDays(1);
         setStateSubscription(false, false, false);
         setStateBundle(true, false, false);
-        blockingChecker.checkBlockedEntitlement(bundle, internalCallContext);
-        blockingChecker.checkBlockedBilling(bundle, internalCallContext);
+        blockingChecker.checkBlockedEntitlement(bundle, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(bundle, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedChange(bundle, internalCallContext);
+            blockingChecker.checkBlockedChange(bundle, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -223,10 +223,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateBundle(false, true, false);
-        blockingChecker.checkBlockedChange(bundle, internalCallContext);
-        blockingChecker.checkBlockedBilling(bundle, internalCallContext);
+        blockingChecker.checkBlockedChange(bundle, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(bundle, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedEntitlement(bundle, internalCallContext);
+            blockingChecker.checkBlockedEntitlement(bundle, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -234,10 +234,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateBundle(false, false, true);
-        blockingChecker.checkBlockedChange(bundle, internalCallContext);
-        blockingChecker.checkBlockedEntitlement(bundle, internalCallContext);
+        blockingChecker.checkBlockedChange(bundle, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedEntitlement(bundle, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedBilling(bundle, internalCallContext);
+            blockingChecker.checkBlockedBilling(bundle, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -248,10 +248,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
         setStateSubscription(false, false, false);
         setStateBundle(false, false, false);
         setStateAccount(true, false, false);
-        blockingChecker.checkBlockedEntitlement(bundle, internalCallContext);
-        blockingChecker.checkBlockedBilling(bundle, internalCallContext);
+        blockingChecker.checkBlockedEntitlement(bundle, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(bundle, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedChange(bundle, internalCallContext);
+            blockingChecker.checkBlockedChange(bundle, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -259,10 +259,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateAccount(false, true, false);
-        blockingChecker.checkBlockedChange(bundle, internalCallContext);
-        blockingChecker.checkBlockedBilling(bundle, internalCallContext);
+        blockingChecker.checkBlockedChange(bundle, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(bundle, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedEntitlement(bundle, internalCallContext);
+            blockingChecker.checkBlockedEntitlement(bundle, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -270,10 +270,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateAccount(false, false, true);
-        blockingChecker.checkBlockedChange(bundle, internalCallContext);
-        blockingChecker.checkBlockedEntitlement(bundle, internalCallContext);
+        blockingChecker.checkBlockedChange(bundle, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedEntitlement(bundle, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedBilling(bundle, internalCallContext);
+            blockingChecker.checkBlockedBilling(bundle, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -286,19 +286,19 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
         setStateAccount(false, false, false);
         setStateBundle(false, false, false);
         setStateSubscription(false, false, false);
-        blockingChecker.checkBlockedChange(account, internalCallContext);
-        blockingChecker.checkBlockedEntitlement(account, internalCallContext);
-        blockingChecker.checkBlockedBilling(account, internalCallContext);
+        blockingChecker.checkBlockedChange(account, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedEntitlement(account, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(account, clock.getUTCNow(), internalCallContext);
 
         //BLOCKED ACCOUNT
         clock.addDays(1);
         setStateSubscription(false, false, false);
         setStateBundle(false, false, false);
         setStateAccount(true, false, false);
-        blockingChecker.checkBlockedEntitlement(account, internalCallContext);
-        blockingChecker.checkBlockedBilling(account, internalCallContext);
+        blockingChecker.checkBlockedEntitlement(account, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(account, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedChange(account, internalCallContext);
+            blockingChecker.checkBlockedChange(account, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -306,10 +306,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateAccount(false, true, false);
-        blockingChecker.checkBlockedChange(account, internalCallContext);
-        blockingChecker.checkBlockedBilling(account, internalCallContext);
+        blockingChecker.checkBlockedChange(account, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedBilling(account, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedEntitlement(account, internalCallContext);
+            blockingChecker.checkBlockedEntitlement(account, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
@@ -317,10 +317,10 @@ public class TestBlockingChecker extends EntitlementTestSuiteNoDB {
 
         clock.addDays(1);
         setStateAccount(false, false, true);
-        blockingChecker.checkBlockedChange(account, internalCallContext);
-        blockingChecker.checkBlockedEntitlement(account, internalCallContext);
+        blockingChecker.checkBlockedChange(account, clock.getUTCNow(), internalCallContext);
+        blockingChecker.checkBlockedEntitlement(account, clock.getUTCNow(), internalCallContext);
         try {
-            blockingChecker.checkBlockedBilling(account, internalCallContext);
+            blockingChecker.checkBlockedBilling(account, clock.getUTCNow(), internalCallContext);
             Assert.fail("The call should have been blocked!");
         } catch (BlockingApiException e) {
             //Expected behavior
diff --git a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/MockBlockingStateDao.java b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/MockBlockingStateDao.java
index 475f626..e36f469 100644
--- a/entitlement/src/test/java/org/killbill/billing/entitlement/dao/MockBlockingStateDao.java
+++ b/entitlement/src/test/java/org/killbill/billing/entitlement/dao/MockBlockingStateDao.java
@@ -24,6 +24,7 @@ import java.util.UUID;
 
 import javax.annotation.Nullable;
 
+import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.clock.Clock;
@@ -60,7 +61,7 @@ public class MockBlockingStateDao extends MockEntityDaoBase<BlockingStateModelDa
     }
 
     @Override
-    public List<BlockingState> getBlockingState(final UUID blockableId, final BlockingStateType blockingStateType, final InternalTenantContext context) {
+    public List<BlockingState> getBlockingState(final UUID blockableId, final BlockingStateType blockingStateType, final DateTime upToDate, final InternalTenantContext context) {
         final List<BlockingState> blockingStatesForId = blockingStates.get(blockableId);
         if (blockingStatesForId == null) {
             return new ArrayList<BlockingState>();

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

diff --git a/invoice/pom.xml b/invoice/pom.xml
index d617bf9..043c231 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>

jaxrs/pom.xml 18(+9 -9)

diff --git a/jaxrs/pom.xml b/jaxrs/pom.xml
index cadbda3..f42e1ad 100644
--- a/jaxrs/pom.xml
+++ b/jaxrs/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-jaxrs</artifactId>
@@ -55,16 +55,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>com.wordnik</groupId>
-            <artifactId>swagger-annotations</artifactId>
+            <groupId>com.sun.jersey</groupId>
+            <artifactId>jersey-core</artifactId>
         </dependency>
         <dependency>
-            <groupId>io.dropwizard.metrics</groupId>
-            <artifactId>metrics-annotation</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>io.dropwizard.metrics</groupId>
-            <artifactId>metrics-jersey</artifactId>
+            <groupId>com.wordnik</groupId>
+            <artifactId>swagger-annotations</artifactId>
         </dependency>
         <dependency>
             <groupId>javax.inject</groupId>
@@ -155,6 +151,10 @@
         </dependency>
         <dependency>
             <groupId>org.kill-bill.commons</groupId>
+            <artifactId>killbill-metrics</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.kill-bill.commons</groupId>
             <artifactId>killbill-queue</artifactId>
         </dependency>
         <dependency>
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BlockingStateJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BlockingStateJson.java
new file mode 100644
index 0000000..3d4cd25
--- /dev/null
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/BlockingStateJson.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.killbill.billing.jaxrs.json;
+
+import java.util.List;
+import java.util.UUID;
+
+import javax.annotation.Nullable;
+
+import org.joda.time.LocalDate;
+import org.killbill.billing.entitlement.api.BlockingStateType;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.wordnik.swagger.annotations.ApiModelProperty;
+
+public class BlockingStateJson extends JsonBase {
+
+    @ApiModelProperty(dataType = "java.util.UUID")
+    private final String blockedId;
+    private final String stateName;
+    private final String service;
+    private final Boolean blockChange;
+    private final Boolean blockEntitlement;
+    private final Boolean blockBilling;
+    private final LocalDate effectiveDate;
+    private final BlockingStateType type;
+
+    @JsonCreator
+    public BlockingStateJson(@JsonProperty("blockedId") final String blockedId,
+                             @JsonProperty("stateName") final String stateName,
+                             @JsonProperty("service") final String service,
+                             @JsonProperty("blockChange") final Boolean blockChange,
+                             @JsonProperty("blockEntitlement") final Boolean blockEntitlement,
+                             @JsonProperty("blockBilling") final Boolean blockBilling,
+                             @JsonProperty("effectiveDate") final LocalDate effectiveDate,
+                             @JsonProperty("type") final BlockingStateType type,
+                             @JsonProperty("auditLogs") @Nullable final List<AuditLogJson> auditLogs) {
+        super(auditLogs);
+        this.blockedId = blockedId;
+        this.stateName = stateName;
+        this.service = service;
+        this.blockChange = blockChange;
+        this.blockEntitlement = blockEntitlement;
+        this.blockBilling = blockBilling;
+        this.effectiveDate = effectiveDate;
+        this.type = type;
+    }
+
+    public String getBlockedId() {
+        return blockedId;
+    }
+
+    public String getStateName() {
+        return stateName;
+    }
+
+    public String getService() {
+        return service;
+    }
+
+    public Boolean isBlockChange() {
+        return blockChange;
+    }
+
+    public Boolean isBlockEntitlement() {
+        return blockEntitlement;
+    }
+
+    public Boolean isBlockBilling() {
+        return blockBilling;
+    }
+
+    public LocalDate getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    public BlockingStateType getType() {
+        return type;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof BlockingStateJson)) {
+            return false;
+        }
+
+        final BlockingStateJson that = (BlockingStateJson) o;
+
+        if (blockChange != that.blockChange) {
+            return false;
+        }
+        if (blockEntitlement != that.blockEntitlement) {
+            return false;
+        }
+        if (blockBilling != that.blockBilling) {
+            return false;
+        }
+        if (blockedId != null ? !blockedId.equals(that.blockedId) : that.blockedId != null) {
+            return false;
+        }
+        if (stateName != null ? !stateName.equals(that.stateName) : that.stateName != null) {
+            return false;
+        }
+        if (service != null ? !service.equals(that.service) : that.service != null) {
+            return false;
+        }
+        if (effectiveDate != null ? effectiveDate.compareTo(that.effectiveDate) != 0 : that.effectiveDate != null) {
+            return false;
+        }
+        return type == that.type;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = blockedId != null ? blockedId.hashCode() : 0;
+        result = 31 * result + (stateName != null ? stateName.hashCode() : 0);
+        result = 31 * result + (service != null ? service.hashCode() : 0);
+        result = 31 * result + (blockChange ? 1 : 0);
+        result = 31 * result + (blockEntitlement ? 1 : 0);
+        result = 31 * result + (blockBilling ? 1 : 0);
+        result = 31 * result + (effectiveDate != null ? effectiveDate.hashCode() : 0);
+        result = 31 * result + (type != null ? type.hashCode() : 0);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "BlockingStateJson{" +
+               "blockedId='" + blockedId + '\'' +
+               ", stateName='" + stateName + '\'' +
+               ", service='" + service + '\'' +
+               ", blockChange=" + blockChange +
+               ", blockEntitlement=" + blockEntitlement +
+               ", blockBilling=" + blockBilling +
+               ", effectiveDate=" + effectiveDate +
+               ", type=" + type +
+               '}';
+    }
+}
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/ComboPaymentTransactionJson.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/ComboPaymentTransactionJson.java
index 3ca1ead..cde1b73 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/ComboPaymentTransactionJson.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/json/ComboPaymentTransactionJson.java
@@ -45,6 +45,14 @@ public class ComboPaymentTransactionJson extends ComboPaymentJson {
         return transaction;
     }
 
+    public String getTransactionType() {
+        if (transaction != null) {
+            return transaction.getTransactionType();
+        }
+
+        return null;
+    }
+
     public Iterable<PluginPropertyJson> getTransactionPluginProperties() {
         return transactionPluginProperties;
     }
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
index 0bd1502..a26c775 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/AccountResource.java
@@ -49,7 +49,6 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
-import org.joda.time.DateTimeZone;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
@@ -112,8 +111,9 @@ import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.tag.ControlTagType;
 import org.killbill.billing.util.tag.Tag;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.MetricTag;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Collections2;
@@ -171,7 +171,7 @@ public class AccountResource extends JaxRsResourceBase {
         this.jaxrsConfig = jaxrsConfig;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -190,7 +190,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(accountJson).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -219,7 +219,7 @@ public class AccountResource extends JaxRsResourceBase {
                                                );
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -250,7 +250,7 @@ public class AccountResource extends JaxRsResourceBase {
                                                );
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + BUNDLES)
     @Produces(APPLICATION_JSON)
@@ -278,7 +278,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Retrieve an account by external key", response = AccountJson.class)
@@ -309,7 +309,7 @@ public class AccountResource extends JaxRsResourceBase {
         }
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -328,7 +328,7 @@ public class AccountResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, AccountResource.class, "getAccount", account.getId());
     }
 
-    @Timed
+    @TimedResource
     @PUT
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -350,7 +350,7 @@ public class AccountResource extends JaxRsResourceBase {
     }
 
     // Not supported
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{accountId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -371,7 +371,7 @@ public class AccountResource extends JaxRsResourceBase {
     }
 
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TIMELINE)
     @Produces(APPLICATION_JSON)
@@ -507,7 +507,7 @@ public class AccountResource extends JaxRsResourceBase {
     * ************************** EMAIL NOTIFICATIONS FOR INVOICES ********************************
     */
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAIL_NOTIFICATIONS)
     @Produces(APPLICATION_JSON)
@@ -522,7 +522,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(invoiceEmailJson).build();
     }
 
-    @Timed
+    @TimedResource
     @PUT
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAIL_NOTIFICATIONS)
     @Consumes(APPLICATION_JSON)
@@ -553,7 +553,7 @@ public class AccountResource extends JaxRsResourceBase {
     /*
      * ************************** INVOICE CBA REBALANCING ********************************
      */
-    @Timed
+    @TimedResource
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + CBA_REBALANCING)
     @Consumes(APPLICATION_JSON)
@@ -578,7 +578,7 @@ public class AccountResource extends JaxRsResourceBase {
      * ************************** INVOICES ********************************
      */
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + INVOICES)
     @Produces(APPLICATION_JSON)
@@ -615,7 +615,7 @@ public class AccountResource extends JaxRsResourceBase {
      */
 
     // STEPH should refactor code since very similar to @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENTS)
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + INVOICE_PAYMENTS)
     @Produces(APPLICATION_JSON)
@@ -642,7 +642,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
@@ -694,7 +694,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENT_METHODS)
     @Consumes(APPLICATION_JSON)
@@ -737,7 +737,7 @@ public class AccountResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(PaymentMethodResource.class, "getPaymentMethod", paymentMethodId, uriInfo.getBaseUri().toString());
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENT_METHODS)
     @Produces(APPLICATION_JSON)
@@ -765,7 +765,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @Timed
+    @TimedResource
     @PUT
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -799,7 +799,7 @@ public class AccountResource extends JaxRsResourceBase {
     /*
      * ************************* PAYMENTS *****************************
      */
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENTS)
     @Produces(APPLICATION_JSON)
@@ -824,7 +824,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource(name = "processPayment")
     @POST
     @Path("/" + PAYMENTS)
     @Consumes(APPLICATION_JSON)
@@ -832,7 +832,7 @@ public class AccountResource extends JaxRsResourceBase {
     @ApiOperation(value = "Trigger a payment using the account external key (authorization, purchase or credit)")
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account external key supplied"),
                            @ApiResponse(code = 404, message = "Account not found")})
-    public Response processPaymentByExternalKey(final PaymentTransactionJson json,
+    public Response processPaymentByExternalKey(@MetricTag(tag = "type", property = "transactionType") final PaymentTransactionJson json,
                                                 @QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
                                                 @QueryParam(QUERY_PAYMENT_METHOD_ID) final String paymentMethodIdStr,
                                                 @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@@ -848,7 +848,7 @@ public class AccountResource extends JaxRsResourceBase {
         return processPayment(json, account, paymentMethodIdStr, paymentControlPluginNames, pluginPropertiesString, uriInfo, callContext);
     }
 
-    @Timed
+    @TimedResource(name = "processPayment")
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + PAYMENTS)
     @Consumes(APPLICATION_JSON)
@@ -856,7 +856,7 @@ public class AccountResource extends JaxRsResourceBase {
     @ApiOperation(value = "Trigger a payment (authorization, purchase or credit)")
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid account id supplied"),
                            @ApiResponse(code = 404, message = "Account not found")})
-    public Response processPayment(final PaymentTransactionJson json,
+    public Response processPayment(@MetricTag(tag = "type", property = "transactionType") final PaymentTransactionJson json,
                                    @PathParam(QUERY_ACCOUNT_ID) final String accountIdStr,
                                    @QueryParam(QUERY_PAYMENT_METHOD_ID) final String paymentMethodIdStr,
                                    @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
@@ -923,7 +923,7 @@ public class AccountResource extends JaxRsResourceBase {
     /*
      * ************************** OVERDUE ********************************
      */
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + OVERDUE)
     @Produces(APPLICATION_JSON)
@@ -944,7 +944,7 @@ public class AccountResource extends JaxRsResourceBase {
      * *************************      CUSTOM FIELDS     *****************************
      */
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
@@ -956,7 +956,7 @@ public class AccountResource extends JaxRsResourceBase {
         return super.getCustomFields(UUID.fromString(id), auditMode, context.createContext(request));
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -974,7 +974,7 @@ public class AccountResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request), uriInfo);
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{accountId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -995,7 +995,7 @@ public class AccountResource extends JaxRsResourceBase {
      * *************************     TAGS     *****************************
      */
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -1010,7 +1010,7 @@ public class AccountResource extends JaxRsResourceBase {
         return super.getTags(accountId, accountId, auditMode, includedDeleted, context.createContext(request));
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + ALL_TAGS)
     @Produces(APPLICATION_JSON)
@@ -1031,7 +1031,7 @@ public class AccountResource extends JaxRsResourceBase {
     }
 
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -1048,7 +1048,7 @@ public class AccountResource extends JaxRsResourceBase {
                                 context.createContext(createdBy, reason, comment, request));
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{accountId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
@@ -1088,7 +1088,7 @@ public class AccountResource extends JaxRsResourceBase {
      * *************************     EMAILS     *****************************
      */
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAILS)
     @Produces(APPLICATION_JSON)
@@ -1106,7 +1106,7 @@ public class AccountResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(emailsJson).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAILS)
     @Consumes(APPLICATION_JSON)
@@ -1148,7 +1148,7 @@ public class AccountResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, AccountResource.class, "getEmails", json.getAccountId());
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{accountId:" + UUID_PATTERN + "}/" + EMAILS + "/{email}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
index 96c4678..7f3432a 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/BundleResource.java
@@ -49,6 +49,7 @@ import org.killbill.billing.entitlement.api.EntitlementApiException;
 import org.killbill.billing.entitlement.api.SubscriptionApi;
 import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.entitlement.api.SubscriptionBundle;
+import org.killbill.billing.jaxrs.json.BlockingStateJson;
 import org.killbill.billing.jaxrs.json.BundleJson;
 import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.TagJson;
@@ -67,8 +68,8 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
@@ -104,7 +105,7 @@ public class BundleResource extends JaxRsResourceBase {
         this.subscriptionApi = subscriptionApi;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -119,7 +120,7 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Retrieve a bundle by external key", response = BundleJson.class)
@@ -131,7 +132,7 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -159,7 +160,7 @@ public class BundleResource extends JaxRsResourceBase {
                                                 nextPageUri);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -189,7 +190,7 @@ public class BundleResource extends JaxRsResourceBase {
                                                 nextPageUri);
     }
 
-    @Timed
+    @TimedResource
     @PUT
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + PAUSE)
     @Consumes(APPLICATION_JSON)
@@ -214,7 +215,7 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    @Timed
+    @TimedResource
     @PUT
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + RESUME)
     @Consumes(APPLICATION_JSON)
@@ -239,7 +240,37 @@ public class BundleResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    @Timed
+    @TimedResource
+    @PUT
+    @Path("/{bundleId:" + UUID_PATTERN + "}/" + BLOCK)
+    @Consumes(APPLICATION_JSON)
+    @ApiOperation(value = "Block a bundle")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid bundle id supplied"),
+                           @ApiResponse(code = 404, message = "Bundle not found")})
+    public Response addBundleBlockingState(final BlockingStateJson json,
+                                           @PathParam(ID_PARAM_NAME) final String id,
+                                           @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
+                                           @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                           @HeaderParam(HDR_REASON) final String reason,
+                                           @HeaderParam(HDR_COMMENT) final String comment,
+                                           @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, EntitlementApiException, AccountApiException {
+
+        final Iterable<PluginProperty> pluginProperties = extractPluginProperties(pluginPropertiesString);
+        final CallContext callContext = context.createContext(createdBy, reason, comment, request);
+        final UUID bundleId = UUID.fromString(id);
+
+        final boolean isBlockBilling = (json.isBlockBilling() != null && json.isBlockBilling());
+        final boolean isBlockEntitlement = (json.isBlockEntitlement() != null && json.isBlockEntitlement());
+        final boolean isBlockChange = (json.isBlockChange() != null && json.isBlockChange());
+
+        entitlementApi.setBlockingState(bundleId, json.getStateName(), json.getService(), json.getEffectiveDate(), isBlockBilling, isBlockEntitlement, isBlockChange, pluginProperties, callContext);
+
+        return Response.status(Status.OK).build();
+    }
+
+
+
+    @TimedResource
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
@@ -251,7 +282,7 @@ public class BundleResource extends JaxRsResourceBase {
         return super.getCustomFields(UUID.fromString(id), auditMode, context.createContext(request));
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -269,7 +300,7 @@ public class BundleResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request), uriInfo);
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -286,7 +317,7 @@ public class BundleResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request));
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -303,7 +334,7 @@ public class BundleResource extends JaxRsResourceBase {
         return super.getTags(bundle.getAccountId(), bundleId, auditMode, includedDeleted, tenantContext);
     }
 
-    @Timed
+    @TimedResource
     @PUT
     @Path("/{bundleId:" + UUID_PATTERN + "}")
     @Consumes(APPLICATION_JSON)
@@ -337,7 +368,7 @@ public class BundleResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(BundleResource.class, "getBundle", newBundleId, uriInfo.getBaseUri().toString());
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
@@ -355,7 +386,7 @@ public class BundleResource extends JaxRsResourceBase {
                                 context.createContext(createdBy, reason, comment, request));
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{bundleId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
index 20b67db..bf5cc14 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
@@ -47,18 +47,16 @@ import org.killbill.billing.jaxrs.json.PlanDetailJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.payment.api.PaymentApi;
-import org.killbill.billing.tenant.api.TenantKV.TenantKey;
-import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 import org.killbill.xmlloader.XMLLoader;
 import org.killbill.xmlloader.XMLWriter;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.wordnik.swagger.annotations.Api;
@@ -92,7 +90,7 @@ public class CatalogResource extends JaxRsResourceBase {
         this.catalogUserApi = catalogUserApi;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Produces(APPLICATION_XML)
     @ApiOperation(value = "Retrieve the full catalog as XML", response = String.class, hidden = true)
@@ -102,7 +100,7 @@ public class CatalogResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(XMLWriter.writeXML((VersionedCatalog) catalogUserApi.getCatalog(catalogName, tenantContext), VersionedCatalog.class)).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Consumes(APPLICATION_XML)
     @ApiOperation(value = "Upload the full catalog as XML")
@@ -122,7 +120,7 @@ public class CatalogResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, CatalogResource.class, null, null);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Retrieve the full catalog as JSON", response = StaticCatalog.class)
@@ -147,7 +145,7 @@ public class CatalogResource extends JaxRsResourceBase {
     //        return result;
     //    }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/availableAddons")
     @Produces(APPLICATION_JSON)
@@ -166,7 +164,7 @@ public class CatalogResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(details).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/availableBasePlans")
     @Produces(APPLICATION_JSON)
@@ -183,7 +181,7 @@ public class CatalogResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(details).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/simpleCatalog")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
index 58f5bf9..488850c 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CustomFieldResource.java
@@ -43,8 +43,8 @@ import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.customfield.CustomField;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
@@ -72,7 +72,7 @@ public class CustomFieldResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -98,7 +98,7 @@ public class CustomFieldResource extends JaxRsResourceBase {
                                                 nextPageUri);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
index 50be28e..64d7145 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/ExportResource.java
@@ -40,8 +40,8 @@ import org.killbill.billing.util.api.ExportUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.inject.Singleton;
 import com.wordnik.swagger.annotations.Api;
 import com.wordnik.swagger.annotations.ApiOperation;
@@ -71,7 +71,7 @@ public class ExportResource extends JaxRsResourceBase {
         this.exportUserApi = exportUserApi;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{accountId:" + UUID_PATTERN + "}")
     @Produces(TEXT_PLAIN)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
index adff94f..9ee85d0 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoicePaymentResource.java
@@ -67,8 +67,8 @@ import org.killbill.billing.util.audit.AccountAuditLogs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -102,7 +102,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         this.invoicePaymentApi = invoicePaymentApi;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
@@ -134,7 +134,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + REFUNDS)
     @Consumes(APPLICATION_JSON)
@@ -181,7 +181,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(InvoicePaymentResource.class, "getInvoicePayment", result.getId(), uriInfo.getBaseUri().toString());
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CHARGEBACKS)
     @Consumes(APPLICATION_JSON)
@@ -210,7 +210,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId());
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
@@ -222,7 +222,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return super.getCustomFields(UUID.fromString(id), auditMode, context.createContext(request));
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -240,7 +240,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request), uriInfo);
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -257,7 +257,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request));
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -276,7 +276,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
         return super.getTags(payment.getAccountId(), paymentId, auditMode, includedDeleted, tenantContext);
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
@@ -294,7 +294,7 @@ public class InvoicePaymentResource extends JaxRsResourceBase {
                                 context.createContext(createdBy, reason, comment, request));
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
index 3994d2b..845bfd0 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/InvoiceResource.java
@@ -106,10 +106,10 @@ import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
 import org.killbill.clock.ClockUtil;
+import org.killbill.commons.metrics.TimedResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -167,7 +167,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         this.defaultLocale = Locale.getDefault();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
@@ -190,7 +190,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{invoiceNumber:" + NUMBER_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
@@ -212,7 +212,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/html")
     @Produces(TEXT_HTML)
@@ -223,7 +223,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(invoiceApi.getInvoiceAsHTML(UUID.fromString(invoiceId), context.createContext(request))).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -255,7 +255,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                );
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -288,7 +288,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                                );
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -316,7 +316,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/" + DRY_RUN)
     @Consumes(APPLICATION_JSON)
@@ -379,7 +379,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{invoiceId:" + UUID_PATTERN + "}" + "/{invoiceItemId:" + UUID_PATTERN + "}/cba")
     @Consumes(APPLICATION_JSON)
@@ -403,7 +403,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{invoiceId:" + UUID_PATTERN + "}")
     @Consumes(APPLICATION_JSON)
@@ -447,7 +447,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, InvoiceResource.class, "getInvoice", adjustmentItem.getInvoiceId());
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
@@ -540,7 +540,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         }
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + PAYMENTS)
     @Produces(APPLICATION_JSON)
@@ -581,7 +581,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
@@ -616,7 +616,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, InvoicePaymentResource.class, "getInvoicePayment", result.getId());
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + EMAIL_NOTIFICATIONS)
     @Consumes(APPLICATION_JSON)
@@ -644,7 +644,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + INVOICE_TRANSLATION + "/{locale:" + ANYTHING_PATTERN + "}/")
     @Produces(TEXT_PLAIN)
@@ -656,7 +656,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return getTemplateResource(localeStr, TenantKey.INVOICE_TRANSLATION_, request);
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Produces(TEXT_PLAIN)
     @Consumes(TEXT_PLAIN)
@@ -683,7 +683,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                       uriInfo);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + INVOICE_CATALOG_TRANSLATION + "/{locale:" + ANYTHING_PATTERN + "}/")
     @Produces(TEXT_PLAIN)
@@ -695,7 +695,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return getTemplateResource(localeStr, TenantKey.CATALOG_TRANSLATION_, request);
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Produces(TEXT_PLAIN)
     @Consumes(TEXT_PLAIN)
@@ -723,7 +723,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                       uriInfo);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + INVOICE_TEMPLATE)
     @Produces(TEXT_HTML)
@@ -733,7 +733,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return getTemplateResource(null, TenantKey.INVOICE_TEMPLATE, request);
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Produces(TEXT_HTML)
     @Consumes(TEXT_HTML)
@@ -759,7 +759,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                       uriInfo);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + INVOICE_MP_TEMPLATE)
     @Produces(TEXT_HTML)
@@ -770,7 +770,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return getTemplateResource(null, TenantKey.INVOICE_MP_TEMPLATE, request);
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Produces(TEXT_HTML)
     @Consumes(TEXT_HTML)
@@ -841,7 +841,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return result.isEmpty() ? Response.status(Status.NOT_FOUND).build() : Response.status(Status.OK).entity(result.get(0)).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Produces(APPLICATION_JSON)
@@ -853,7 +853,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return super.getCustomFields(UUID.fromString(id), auditMode, context.createContext(request));
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -871,7 +871,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request), uriInfo);
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
     @Consumes(APPLICATION_JSON)
@@ -888,7 +888,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                         context.createContext(createdBy, reason, comment, request));
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Produces(APPLICATION_JSON)
@@ -905,7 +905,7 @@ public class InvoiceResource extends JaxRsResourceBase {
         return super.getTags(invoice.getAccountId(), invoiceId, auditMode, includedDeleted, tenantContext);
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
@@ -923,7 +923,7 @@ public class InvoiceResource extends JaxRsResourceBase {
                                 context.createContext(createdBy, reason, comment, request));
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{invoiceId:" + UUID_PATTERN + "}/" + TAGS)
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
index abfe3c7..518b402 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/JaxrsResource.java
@@ -216,6 +216,7 @@ public interface JaxrsResource {
 
     public static final String PAUSE = "pause";
     public static final String RESUME = "resume";
+    public static final String BLOCK = "block";
 
     public static final String AUTHORIZATION = "authorization";
     public static final String CAPTURE = "capture";
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
index d97d1d6..bfe8b11 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/OverdueResource.java
@@ -33,26 +33,21 @@ import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.account.api.AccountUserApi;
-import org.killbill.billing.catalog.StandaloneCatalog;
-import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.overdue.api.OverdueApi;
-import org.killbill.billing.overdue.api.OverdueConfig;
 import org.killbill.billing.overdue.config.DefaultOverdueConfig;
 import org.killbill.billing.payment.api.PaymentApi;
-import org.killbill.billing.tenant.api.TenantKV.TenantKey;
-import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 import org.killbill.xmlloader.XMLLoader;
 import org.killbill.xmlloader.XMLWriter;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.wordnik.swagger.annotations.Api;
@@ -82,7 +77,7 @@ public class OverdueResource extends JaxRsResourceBase {
         this.overdueApi = overdueApi;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Produces(APPLICATION_XML)
     @ApiOperation(value = "Retrieve the full catalog as XML", response = String.class, hidden = true)
@@ -92,7 +87,7 @@ public class OverdueResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(XMLWriter.writeXML((DefaultOverdueConfig )overdueApi.getOverdueConfig(tenantContext), DefaultOverdueConfig.class)).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Consumes(APPLICATION_XML)
     @ApiOperation(value = "Upload the full overdue config as XML")
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
index 11a6221..aeb11d5 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentGatewayResource.java
@@ -53,8 +53,8 @@ import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Strings;
 import com.google.inject.Singleton;
 import com.wordnik.swagger.annotations.Api;
@@ -86,7 +86,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
         this.paymentGatewayApi = paymentGatewayApi;
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/" + HOSTED + "/" + FORM)
     @Consumes(APPLICATION_JSON)
@@ -121,7 +121,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/" + HOSTED + "/" + FORM + "/{" + QUERY_ACCOUNT_ID + ":" + UUID_PATTERN + "}")
     @Consumes(APPLICATION_JSON)
@@ -156,7 +156,7 @@ public class PaymentGatewayResource extends ComboPaymentResource {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/" + NOTIFICATION + "/{" + QUERY_PAYMENT_PLUGIN_NAME + ":" + ANYTHING_PATTERN + "}")
     @Consumes(WILDCARD)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
index 002622a..07fdd5e 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentMethodResource.java
@@ -26,21 +26,25 @@ import java.util.UUID;
 import java.util.concurrent.atomic.AtomicReference;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriInfo;
 
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.account.api.Account;
 import org.killbill.billing.account.api.AccountApiException;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.jaxrs.json.CustomFieldJson;
 import org.killbill.billing.jaxrs.json.PaymentMethodJson;
 import org.killbill.billing.jaxrs.util.Context;
 import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
@@ -49,6 +53,7 @@ import org.killbill.billing.payment.api.PaymentApiException;
 import org.killbill.billing.payment.api.PaymentMethod;
 import org.killbill.billing.payment.api.PluginProperty;
 import org.killbill.billing.util.api.AuditUserApi;
+import org.killbill.billing.util.api.CustomFieldApiException;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.audit.AccountAuditLogs;
@@ -56,8 +61,8 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Function;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
@@ -87,7 +92,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    @Timed
+    @TimedResource(name = "getPaymentMethod")
     @GET
     @Path("/{paymentMethodId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -110,7 +115,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @Timed
+    @TimedResource(name = "getPaymentMethod")
     @GET
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Retrieve a payment method by external key", response = PaymentMethodJson.class)
@@ -130,7 +135,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -186,7 +191,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                                );
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -245,7 +250,7 @@ public class PaymentMethodResource extends JaxRsResourceBase {
                                                );
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Produces(APPLICATION_JSON)
     @Path("/{paymentMethodId:" + UUID_PATTERN + "}")
@@ -270,6 +275,53 @@ public class PaymentMethodResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
+    @TimedResource
+    @GET
+    @Path("/{paymentMethodId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Retrieve payment method custom fields", response = CustomFieldJson.class, responseContainer = "List")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid payment method id supplied")})
+    public Response getCustomFields(@PathParam("paymentMethodId") final String paymentMethodId,
+                                    @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
+                                    @javax.ws.rs.core.Context final HttpServletRequest request) {
+        return super.getCustomFields(UUID.fromString(paymentMethodId), auditMode, context.createContext(request));
+    }
+
+    @TimedResource
+    @POST
+    @Path("/{paymentMethodId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Add custom fields to payment method")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid payment method id supplied")})
+    public Response createCustomFields(@PathParam("paymentMethodId") final String paymentMethodId,
+                                       final List<CustomFieldJson> customFields,
+                                       @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                       @HeaderParam(HDR_REASON) final String reason,
+                                       @HeaderParam(HDR_COMMENT) final String comment,
+                                       @javax.ws.rs.core.Context final HttpServletRequest request,
+                                       @javax.ws.rs.core.Context final UriInfo uriInfo) throws CustomFieldApiException {
+        return super.createCustomFields(UUID.fromString(paymentMethodId), customFields,
+                                        context.createContext(createdBy, reason, comment, request), uriInfo);
+    }
+
+    @TimedResource
+    @DELETE
+    @Path("/{paymentMethodId:" + UUID_PATTERN + "}/" + CUSTOM_FIELDS)
+    @Consumes(APPLICATION_JSON)
+    @Produces(APPLICATION_JSON)
+    @ApiOperation(value = "Remove custom fields from payment method")
+    @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid payment method id supplied")})
+    public Response deleteCustomFields(@PathParam("paymentMethodId") final String paymentMethodId,
+                                       @QueryParam(QUERY_CUSTOM_FIELDS) final String customFieldList,
+                                       @HeaderParam(HDR_CREATED_BY) final String createdBy,
+                                       @HeaderParam(HDR_REASON) final String reason,
+                                       @HeaderParam(HDR_COMMENT) final String comment,
+                                       @javax.ws.rs.core.Context final HttpServletRequest request) throws CustomFieldApiException {
+        return super.deleteCustomFields(UUID.fromString(paymentMethodId), customFieldList,
+                                        context.createContext(createdBy, reason, comment, request));
+    }
+
     @Override
     protected ObjectType getObjectType() {
         return ObjectType.PAYMENT_METHOD;
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
index 804063d..79968cc 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PaymentResource.java
@@ -69,8 +69,9 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.MetricTag;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
@@ -99,7 +100,7 @@ public class PaymentResource extends ComboPaymentResource {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    @Timed
+    @TimedResource(name = "getPayment")
     @GET
     @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Produces(APPLICATION_JSON)
@@ -120,7 +121,7 @@ public class PaymentResource extends ComboPaymentResource {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource(name = "getPayment")
     @GET
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Retrieve a payment by id", response = PaymentJson.class)
@@ -139,7 +140,7 @@ public class PaymentResource extends ComboPaymentResource {
         return Response.status(Response.Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -182,7 +183,7 @@ public class PaymentResource extends ComboPaymentResource {
                                                );
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -228,7 +229,7 @@ public class PaymentResource extends ComboPaymentResource {
                                                );
     }
 
-    @Timed
+    @TimedResource(name = "completeTransaction")
     @PUT
     @Path("/{paymentId:" + UUID_PATTERN + "}")
     @Consumes(APPLICATION_JSON)
@@ -236,7 +237,7 @@ public class PaymentResource extends ComboPaymentResource {
     @ApiOperation(value = "Complete an existing transaction")
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid paymentId supplied"),
                            @ApiResponse(code = 404, message = "Account or payment not found")})
-    public Response completeTransaction(final PaymentTransactionJson json,
+    public Response completeTransaction(@MetricTag(tag = "type", property = "transactionType") final PaymentTransactionJson json,
                                         @PathParam("paymentId") final String paymentIdStr,
                                         @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
                                         @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
@@ -248,13 +249,13 @@ public class PaymentResource extends ComboPaymentResource {
         return completeTransactionInternal(json, paymentIdStr, paymentControlPluginNames, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
     }
 
-    @Timed
+    @TimedResource(name = "completeTransaction")
     @PUT
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Complete an existing transaction")
     @ApiResponses(value = {@ApiResponse(code = 404, message = "Account or payment not found")})
-    public Response completeTransactionByExternalKey(final PaymentTransactionJson json,
+    public Response completeTransactionByExternalKey(@MetricTag(tag = "type", property = "transactionType") final PaymentTransactionJson json,
                                                      @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
                                                      @QueryParam(QUERY_PLUGIN_PROPERTY) final List<String> pluginPropertiesString,
                                                      @HeaderParam(HDR_CREATED_BY) final String createdBy,
@@ -372,7 +373,7 @@ public class PaymentResource extends ComboPaymentResource {
         return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", initialPayment.getId());
     }
 
-    @Timed
+    @TimedResource(name = "captureAuthorization")
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Consumes(APPLICATION_JSON)
@@ -391,7 +392,7 @@ public class PaymentResource extends ComboPaymentResource {
         return captureAuthorizationInternal(json, paymentIdStr, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
     }
 
-    @Timed
+    @TimedResource(name = "captureAuthorization")
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -431,7 +432,7 @@ public class PaymentResource extends ComboPaymentResource {
         return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
     }
 
-    @Timed
+    @TimedResource(name = "refundPayment")
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + REFUNDS)
     @Consumes(APPLICATION_JSON)
@@ -450,7 +451,7 @@ public class PaymentResource extends ComboPaymentResource {
         return refundPaymentInternal(json, paymentIdStr, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
     }
 
-    @Timed
+    @TimedResource(name = "refundPayment")
     @POST
     @Path("/" + REFUNDS)
     @Consumes(APPLICATION_JSON)
@@ -493,7 +494,7 @@ public class PaymentResource extends ComboPaymentResource {
 
     }
 
-    @Timed
+    @TimedResource(name = "voidPayment")
     @DELETE
     @Path("/{paymentId:" + UUID_PATTERN + "}/")
     @Consumes(APPLICATION_JSON)
@@ -512,7 +513,7 @@ public class PaymentResource extends ComboPaymentResource {
         return voidPaymentInternal(json, paymentIdStr, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
     }
 
-    @Timed
+    @TimedResource(name = "voidPayment")
     @DELETE
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -547,7 +548,7 @@ public class PaymentResource extends ComboPaymentResource {
         return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
     }
 
-    @Timed
+    @TimedResource(name = "chargebackPayment")
     @POST
     @Path("/{paymentId:" + UUID_PATTERN + "}/" + CHARGEBACKS)
     @Consumes(APPLICATION_JSON)
@@ -566,7 +567,7 @@ public class PaymentResource extends ComboPaymentResource {
         return chargebackPaymentInternal(json, paymentIdStr, pluginPropertiesString, createdBy, reason, comment, uriInfo, request);
     }
 
-    @Timed
+    @TimedResource(name = "chargebackPayment")
     @POST
     @Path("/" + CHARGEBACKS)
     @Consumes(APPLICATION_JSON)
@@ -606,14 +607,14 @@ public class PaymentResource extends ComboPaymentResource {
         return uriBuilder.buildResponse(uriInfo, PaymentResource.class, "getPayment", payment.getId());
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
     @Path("/" + COMBO)
     @ApiOperation(value = "Combo api to create a new payment transaction on a existing (or not) account ")
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid data for Account or PaymentMethod")})
-    public Response createComboPayment(final ComboPaymentTransactionJson json,
+    public Response createComboPayment(@MetricTag(tag = "type", property = "transactionType") final ComboPaymentTransactionJson json,
                                        @QueryParam(QUERY_PAYMENT_CONTROL_PLUGIN_NAME) final List<String> paymentControlPluginNames,
                                        @HeaderParam(HDR_CREATED_BY) final String createdBy,
                                        @HeaderParam(HDR_REASON) final String reason,
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java
index e328da6..62dd9cf 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginInfoResource.java
@@ -37,8 +37,8 @@ import org.killbill.billing.util.api.AuditUserApi;
 import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -67,7 +67,7 @@ public class PluginInfoResource extends JaxRsResourceBase {
         this.pluginsInfoApi = pluginsInfoApi;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Retrieve the list of registered plugins", response = PluginInfoJson.class, responseContainer = "List")
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
index 10ff827..6be2cdb 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/PluginResource.java
@@ -63,7 +63,6 @@ import org.killbill.clock.Clock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.LinkedHashMultimap;
 import com.google.common.io.ByteStreams;
@@ -98,7 +97,6 @@ public class PluginResource extends JaxRsResourceBase {
         this.osgiServlet = osgiServlet;
     }
 
-    @Timed
     @DELETE
     public Response doDELETE(@javax.ws.rs.core.Context final HttpServletRequest request,
                              @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -108,7 +106,6 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
-    @Timed
     @GET
     public Response doGET(@javax.ws.rs.core.Context final HttpServletRequest request,
                           @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -118,7 +115,6 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
-    @Timed
     @OPTIONS
     public Response doOPTIONS(@javax.ws.rs.core.Context final HttpServletRequest request,
                               @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -128,7 +124,6 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
-    @Timed
     @POST
     @Consumes("application/x-www-form-urlencoded")
     public Response doFormPOST(final MultivaluedMap<String, String> form,
@@ -140,7 +135,6 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(form, request, response, servletContext, servletConfig, uriInfo);
     }
 
-    @Timed
     @POST
     public Response doPOST(@javax.ws.rs.core.Context final HttpServletRequest request,
                            @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -150,7 +144,6 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
-    @Timed
     @PUT
     public Response doPUT(@javax.ws.rs.core.Context final HttpServletRequest request,
                           @javax.ws.rs.core.Context final HttpServletResponse response,
@@ -160,7 +153,6 @@ public class PluginResource extends JaxRsResourceBase {
         return serviceViaOSGIPlugin(request, response, servletContext, servletConfig, uriInfo);
     }
 
-    @Timed
     @HEAD
     public Response doHEAD(@javax.ws.rs.core.Context final HttpServletRequest request,
                            @javax.ws.rs.core.Context final HttpServletResponse response,
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
index 33f734f..ac8a283 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SecurityResource.java
@@ -51,8 +51,8 @@ import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Functions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -84,7 +84,7 @@ public class SecurityResource extends JaxRsResourceBase {
         this.securityApi = securityApi;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/permissions")
     @Produces(APPLICATION_JSON)
@@ -98,7 +98,7 @@ public class SecurityResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/subject")
     @Produces(APPLICATION_JSON)
@@ -110,7 +110,7 @@ public class SecurityResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(subjectJson).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/users")
     @Consumes(APPLICATION_JSON)
@@ -126,7 +126,7 @@ public class SecurityResource extends JaxRsResourceBase {
         return Response.status(Status.CREATED).build();
     }
 
-    @Timed
+    @TimedResource
     @PUT
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -144,7 +144,7 @@ public class SecurityResource extends JaxRsResourceBase {
     }
 
 
-    @Timed
+    @TimedResource
     @PUT
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -161,7 +161,7 @@ public class SecurityResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -179,7 +179,7 @@ public class SecurityResource extends JaxRsResourceBase {
 
 
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/roles")
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
index 3862996..2fcd5a9 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/SubscriptionResource.java
@@ -85,10 +85,10 @@ import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.userrequest.CompletionUserRequestBase;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterables;
@@ -129,7 +129,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         this.subscriptionApi = subscriptionApi;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{subscriptionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -144,7 +144,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -212,7 +212,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/createEntitlementWithAddOns")
     @Consumes(APPLICATION_JSON)
@@ -329,7 +329,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
     }
 
-    @Timed
+    @TimedResource
     @PUT
     @Path("/{subscriptionId:" + UUID_PATTERN + "}/uncancel")
     @Produces(APPLICATION_JSON)
@@ -349,7 +349,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return Response.status(Status.OK).build();
     }
 
-    @Timed
+    @TimedResource
     @PUT
     @Produces(APPLICATION_JSON)
     @Consumes(APPLICATION_JSON)
@@ -427,7 +427,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
         return callCompletionCreation.withSynchronization(callback, timeoutSec, callCompletion, callContext);
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{subscriptionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
index 0afb793..f5536f2 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagDefinitionResource.java
@@ -50,8 +50,8 @@ import org.killbill.billing.util.audit.AuditLog;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.tag.TagDefinition;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.wordnik.swagger.annotations.Api;
@@ -78,7 +78,7 @@ public class TagDefinitionResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "List tag definitions", response = TagDefinitionJson.class, responseContainer = "List")
@@ -97,7 +97,7 @@ public class TagDefinitionResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{tagDefinitionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -113,7 +113,7 @@ public class TagDefinitionResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(json).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -134,7 +134,7 @@ public class TagDefinitionResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, TagDefinitionResource.class, "getTagDefinition", createdTagDef.getId());
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/{tagDefinitionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
index 6370612..305aa45 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TagResource.java
@@ -47,15 +47,14 @@ import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.tag.Tag;
 import org.killbill.billing.util.tag.TagDefinition;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableMap;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.wordnik.swagger.annotations.Api;
 import com.wordnik.swagger.annotations.ApiOperation;
-import com.wordnik.swagger.annotations.ApiResponse;
 import com.wordnik.swagger.annotations.ApiResponses;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
@@ -77,7 +76,7 @@ public class TagResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + PAGINATION)
     @Produces(APPLICATION_JSON)
@@ -110,7 +109,7 @@ public class TagResource extends JaxRsResourceBase {
                                                 nextPageUri);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + SEARCH + "/{searchKey:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
index 3fb174e..8ff2b14 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TenantResource.java
@@ -52,8 +52,8 @@ import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 import com.wordnik.swagger.annotations.Api;
@@ -85,7 +85,7 @@ public class TenantResource extends JaxRsResourceBase {
         this.tenantApi = tenantApi;
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{tenantId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -97,7 +97,7 @@ public class TenantResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(new TenantJson(tenant)).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Produces(APPLICATION_JSON)
     @ApiOperation(value = "Retrieve a tenant by its API key", response = TenantJson.class)
@@ -107,7 +107,7 @@ public class TenantResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(new TenantJson(tenant)).build();
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -128,7 +128,7 @@ public class TenantResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, TenantResource.class, "getTenant", tenant.getId());
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/" + REGISTER_NOTIFICATION_CALLBACK)
     @Consumes(APPLICATION_JSON)
@@ -144,7 +144,7 @@ public class TenantResource extends JaxRsResourceBase {
         return insertTenantKey(TenantKey.PUSH_NOTIFICATION_CB,  null,  notificationCallback, uriInfo,"getPushNotificationCallbacks", createdBy, reason, comment, request);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + REGISTER_NOTIFICATION_CALLBACK)
     @Produces(APPLICATION_JSON)
@@ -154,7 +154,7 @@ public class TenantResource extends JaxRsResourceBase {
         return getTenantKey(TenantKey.PUSH_NOTIFICATION_CB, null, request);
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/" + REGISTER_NOTIFICATION_CALLBACK)
     @ApiOperation(value = "Delete a push notification")
@@ -166,7 +166,7 @@ public class TenantResource extends JaxRsResourceBase {
         return deleteTenantKey(TenantKey.PUSH_NOTIFICATION_CB, null, createdBy, reason, comment, request);
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/" + UPLOAD_PLUGIN_CONFIG + "/{pluginName:" + ANYTHING_PATTERN + "}")
     @Consumes(TEXT_PLAIN)
@@ -183,7 +183,7 @@ public class TenantResource extends JaxRsResourceBase {
         return insertTenantKey(TenantKey.PLUGIN_CONFIG_, pluginName, pluginConfig, uriInfo, "getPluginConfiguration", createdBy, reason, comment, request);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + UPLOAD_PLUGIN_CONFIG + "/{pluginName:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -194,7 +194,7 @@ public class TenantResource extends JaxRsResourceBase {
         return getTenantKey(TenantKey.PLUGIN_CONFIG_, pluginName, request);
     }
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/" + UPLOAD_PLUGIN_CONFIG + "/{pluginName:" + ANYTHING_PATTERN + "}")
     @ApiOperation(value = "Delete a per tenant configuration for a plugin")
@@ -208,7 +208,7 @@ public class TenantResource extends JaxRsResourceBase {
     }
 
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/" + USER_KEY_VALUE + "/{keyName:" + ANYTHING_PATTERN + "}")
     @Consumes(TEXT_PLAIN)
@@ -227,7 +227,7 @@ public class TenantResource extends JaxRsResourceBase {
         return uriBuilder.buildResponse(uriInfo, TenantResource.class, "getUserKeyValue", key);
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/" + USER_KEY_VALUE + "/{keyName:" + ANYTHING_PATTERN + "}")
     @Produces(APPLICATION_JSON)
@@ -242,7 +242,7 @@ public class TenantResource extends JaxRsResourceBase {
     }
 
 
-    @Timed
+    @TimedResource
     @DELETE
     @Path("/" + USER_KEY_VALUE + "/{keyName:" + ANYTHING_PATTERN + "}")
     @ApiOperation(value = "Delete  a per tenant user key/value")
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
index bf002c4..322731d 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/TransactionResource.java
@@ -47,8 +47,8 @@ import org.killbill.billing.util.api.CustomFieldUserApi;
 import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.collect.ImmutableList;
 import com.wordnik.swagger.annotations.Api;
 import com.wordnik.swagger.annotations.ApiOperation;
@@ -73,7 +73,7 @@ public class TransactionResource extends JaxRsResourceBase {
         super(uriBuilder, tagUserApi, customFieldUserApi, auditUserApi, accountUserApi, paymentApi, clock, context);
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Path("/{transactionId:" + UUID_PATTERN + "}/")
     @Consumes(APPLICATION_JSON)
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
index f961ac2..c11ff57 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/UsageResource.java
@@ -54,8 +54,8 @@ import org.killbill.billing.util.api.TagUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
+import org.killbill.commons.metrics.TimedResource;
 
-import com.codahale.metrics.annotation.Timed;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.Singleton;
@@ -90,7 +90,7 @@ public class UsageResource extends JaxRsResourceBase {
         this.entitlementApi = entitlementApi;
     }
 
-    @Timed
+    @TimedResource
     @POST
     @Consumes(APPLICATION_JSON)
     @Produces(APPLICATION_JSON)
@@ -119,7 +119,7 @@ public class UsageResource extends JaxRsResourceBase {
         return Response.status(Status.CREATED).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{subscriptionId:" + UUID_PATTERN + "}/{unitType}")
     @Produces(APPLICATION_JSON)
@@ -144,7 +144,7 @@ public class UsageResource extends JaxRsResourceBase {
         return Response.status(Status.OK).entity(result).build();
     }
 
-    @Timed
+    @TimedResource
     @GET
     @Path("/{subscriptionId:" + UUID_PATTERN + "}")
     @Produces(APPLICATION_JSON)

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

diff --git a/junction/pom.xml b/junction/pom.xml
index afc3a43..1076d6c 100644
--- a/junction/pom.xml
+++ b/junction/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-junction</artifactId>
diff --git a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
index c6ee181..6806e9c 100644
--- a/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
+++ b/junction/src/main/java/org/killbill/billing/junction/plumbing/billing/BlockingCalculator.java
@@ -18,8 +18,11 @@ package org.killbill.billing.junction.plumbing.billing;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.List;
+import java.util.Map;
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.UUID;
@@ -37,12 +40,17 @@ import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.entitlement.api.BlockingState;
+import org.killbill.billing.entitlement.api.BlockingStateType;
 import org.killbill.billing.junction.BillingEvent;
 import org.killbill.billing.junction.BlockingInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import com.google.inject.Inject;
 
 public class BlockingCalculator {
@@ -94,12 +102,29 @@ public class BlockingCalculator {
         final SortedSet<BillingEvent> billingEventsToAdd = new TreeSet<BillingEvent>();
         final SortedSet<BillingEvent> billingEventsToRemove = new TreeSet<BillingEvent>();
 
+
         final List<BlockingState> blockingEvents = blockingApi.getBlockingAllForAccount(context);
-        final List<DisabledDuration> blockingDurations = createBlockingDurations(blockingEvents);
+
+        final Iterable<BlockingState> accountBlockingEvents = Iterables.filter(blockingEvents, new Predicate<BlockingState>() {
+            @Override
+            public boolean apply(final BlockingState input) {
+                return BlockingStateType.ACCOUNT == input.getType();
+            }
+        });
+
+        final Map<UUID, List<BlockingState>> perBundleBlockingEvents = getPerTypeBlockingEvents(BlockingStateType.SUBSCRIPTION_BUNDLE, blockingEvents);
+        final Map<UUID, List<BlockingState>> perSubscriptionBlockingEvents = getPerTypeBlockingEvents(BlockingStateType.SUBSCRIPTION, blockingEvents);
+
         for (final UUID bundleId : bundleMap.keySet()) {
             for (final SubscriptionBase subscription : bundleMap.get(bundleId)) {
-                billingEventsToAdd.addAll(createNewEvents(blockingDurations, billingEvents, subscription));
-                billingEventsToRemove.addAll(eventsToRemove(blockingDurations, billingEvents, subscription));
+
+                final List<BlockingState> subscriptionBlockingEvents = perSubscriptionBlockingEvents.get(subscription.getId()) != null ? perSubscriptionBlockingEvents.get(subscription.getId()) : ImmutableList.<BlockingState>of();
+                final List<BlockingState> bundleBlockingEvents = perBundleBlockingEvents.get(bundleId)  != null ? perBundleBlockingEvents.get(bundleId) : ImmutableList.<BlockingState>of();
+                final List<BlockingState> aggregateSubscriptionBlockingEvents = getAggregateBlockingEventsPerSubscription(subscriptionBlockingEvents, bundleBlockingEvents, accountBlockingEvents);
+                final List<DisabledDuration> accountBlockingDurations = createBlockingDurations(aggregateSubscriptionBlockingEvents);
+
+                billingEventsToAdd.addAll(createNewEvents(accountBlockingDurations, billingEvents, subscription));
+                billingEventsToRemove.addAll(eventsToRemove(accountBlockingDurations, billingEvents, subscription));
             }
         }
 
@@ -112,6 +137,32 @@ public class BlockingCalculator {
         }
     }
 
+
+    final List<BlockingState> getAggregateBlockingEventsPerSubscription(final Iterable<BlockingState> subscriptionBlockingEvents, final Iterable<BlockingState> bundleBlockingEvents, final Iterable<BlockingState> accountBlockingEvents) {
+        final Iterable<BlockingState> tmp = Iterables.concat(subscriptionBlockingEvents, bundleBlockingEvents, accountBlockingEvents);
+        final List<BlockingState> result  = Lists.newArrayList(tmp);
+        Collections.sort(result);
+        return result;
+    }
+
+    final Map<UUID, List<BlockingState>> getPerTypeBlockingEvents(final BlockingStateType type, final List<BlockingState> blockingEvents) {
+        final Iterable<BlockingState> bundleBlockingEvents = Iterables.filter(blockingEvents, new Predicate<BlockingState>() {
+            @Override
+            public boolean apply(final BlockingState input) {
+                return type == input.getType();
+            }
+        });
+
+        final Map<UUID, List<BlockingState>> perTypeBlockingEvents = new HashMap<UUID, List<BlockingState>>();
+        for  (final BlockingState cur : bundleBlockingEvents) {
+            if (!perTypeBlockingEvents.containsKey(cur.getBlockedId())) {
+                perTypeBlockingEvents.put(cur.getBlockedId(), new ArrayList<BlockingState>());
+            }
+            perTypeBlockingEvents.get(cur.getBlockedId()).add(cur);
+        }
+        return perTypeBlockingEvents;
+    }
+
     protected SortedSet<BillingEvent> eventsToRemove(final List<DisabledDuration> disabledDuration,
                                                      final SortedSet<BillingEvent> billingEvents, final SubscriptionBase subscription) {
         final SortedSet<BillingEvent> result = new TreeSet<BillingEvent>();
diff --git a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
index e81a128..15b967f 100644
--- a/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
+++ b/junction/src/test/java/org/killbill/billing/junction/plumbing/billing/TestBlockingCalculator.java
@@ -733,7 +733,6 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
 
     @Test(groups = "fast")
     public void testSimpleWithClearBlockingDuration() throws Exception {
-        final UUID ovdId = UUID.randomUUID();
 
         final BillingEvent trial = createRealEvent(new LocalDate(2012, 5, 1).toDateTimeAtStartOfDay(DateTimeZone.UTC), subscription1, SubscriptionBaseTransitionType.CREATE);
         final BillingEvent phase = createRealEvent(new LocalDate(2012, 5, 31).toDateTimeAtStartOfDay(DateTimeZone.UTC), subscription1, SubscriptionBaseTransitionType.PHASE);
@@ -744,10 +743,10 @@ public class TestBlockingCalculator extends JunctionTestSuiteNoDB {
         billingEvents.add(upgrade);
 
         final List<BlockingState> blockingEvents = new ArrayList<BlockingState>();
-        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, false, false, new LocalDate(2012, 7, 5).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
-        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, new LocalDate(2012, 7, 15).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
-        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
-        blockingEvents.add(new DefaultBlockingState(ovdId, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
+        blockingEvents.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, false, false, new LocalDate(2012, 7, 5).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
+        blockingEvents.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, new LocalDate(2012, 7, 15).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
+        blockingEvents.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, DISABLED_BUNDLE, "test", true, true, true, new LocalDate(2012, 7, 24).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
+        blockingEvents.add(new DefaultBlockingState(bundleId1, BlockingStateType.SUBSCRIPTION_BUNDLE, CLEAR_BUNDLE, "test", false, false, false, new LocalDate(2012, 7, 25).toDateTimeAtStartOfDay(DateTimeZone.UTC)));
 
         setBlockingStates(blockingEvents);
 

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

diff --git a/overdue/pom.xml b/overdue/pom.xml
index b2e8c26..5b3639a 100644
--- a/overdue/pom.xml
+++ b/overdue/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-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 2d0a4b9..acad52a 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>
diff --git a/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java b/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
index bd4ca08..3b9350f 100644
--- a/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
+++ b/payment/src/main/java/org/killbill/billing/payment/core/PaymentExecutors.java
@@ -33,6 +33,8 @@ public class PaymentExecutors {
 
     private static final long TIMEOUT_EXECUTOR_SEC = 3L;
 
+    private static final int DEFAULT_MIN_PLUGIN_THREADS = 5;
+
     private static final String PLUGIN_THREAD_PREFIX = "Plugin-th-";
     private static final String PAYMENT_PLUGIN_TH_GROUP_NAME = "pay-plugin-grp";
 
@@ -76,7 +78,8 @@ public class PaymentExecutors {
     }
 
     private ExecutorService createPluginExecutorService() {
-        return new WithProfilingThreadPoolExecutor(paymentConfig.getPaymentPluginThreadNb(),
+        final int minThreadNb = DEFAULT_MIN_PLUGIN_THREADS < paymentConfig.getPaymentPluginThreadNb() ? DEFAULT_MIN_PLUGIN_THREADS : paymentConfig.getPaymentPluginThreadNb();
+        return new WithProfilingThreadPoolExecutor(minThreadNb,
                                                    paymentConfig.getPaymentPluginThreadNb(),
                                                    0L,
                                                    TimeUnit.MILLISECONDS,

pom.xml 4(+2 -2)

diff --git a/pom.xml b/pom.xml
index a37a070..cacc3a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,10 +21,10 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.58-SNAPSHOT</version>
+        <version>0.62-SNAPSHOT</version>
     </parent>
     <artifactId>killbill</artifactId>
-    <version>0.15.8-SNAPSHOT</version>
+    <version>0.15.9-SNAPSHOT</version>
     <packaging>pom</packaging>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
diff --git a/profiles/killbill/pom.xml b/profiles/killbill/pom.xml
index 8218988..e0ef4eb 100644
--- a/profiles/killbill/pom.xml
+++ b/profiles/killbill/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-profiles</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killbill</artifactId>
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
index 97e2bed..82f63d1 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/security/TenantFilter.java
@@ -134,8 +134,7 @@ public class TenantFilter implements Filter {
 
         if (request instanceof HttpServletRequest) {
             final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
-            // TODO Wrong - See https://github.com/killbill/killbill/issues/221
-            final String path = httpServletRequest.getRequestURI();
+            final String path = httpServletRequest.getPathInfo();
             final String httpMethod = httpServletRequest.getMethod();
             if (    // Chicken - egg problem
                     isTenantCreationRequest(path, httpMethod) ||
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
index 6a36f3c..e9bc2be 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestBundle.java
@@ -22,13 +22,17 @@ import java.util.List;
 import java.util.UUID;
 
 import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.client.KillBillClientException;
 import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.BlockingState;
 import org.killbill.billing.client.model.Bundle;
 import org.killbill.billing.client.model.Bundles;
 import org.killbill.billing.client.model.Subscription;
+import org.killbill.billing.entitlement.api.BlockingStateType;
+import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -116,6 +120,42 @@ public class TestBundle extends TestJaxrsBase {
         assertEquals(newBundle.getAccountId(), newAccount.getAccountId());
     }
 
+
+    @Test(groups = "slow", description = "Block a bundle")
+    public void testBlockBundle() throws Exception {
+        final DateTime initialDate = new DateTime(2012, 4, 25, 0, 3, 42, 0);
+        clock.setDeltaFromReality(initialDate.getMillis() - clock.getUTCNow().getMillis());
+
+        final Account accountJson = createAccountWithDefaultPaymentMethod();
+
+        final String productName = "Shotgun";
+        final BillingPeriod term = BillingPeriod.MONTHLY;
+        final String bundleExternalKey = "93199";
+
+        final Subscription entitlement = createEntitlement(accountJson.getAccountId(), bundleExternalKey, productName,
+                                                                       ProductCategory.BASE, term, true);
+
+        final Bundle bundle = killBillClient.getBundle(bundleExternalKey);
+        assertEquals(bundle.getAccountId(), accountJson.getAccountId());
+        assertEquals(bundle.getExternalKey(), bundleExternalKey);
+
+        final BlockingState blockingState = new BlockingState(bundle.getBundleId(), "block", "service", false, true, true, clock.getToday(DateTimeZone.forID(accountJson.getTimeZone())), BlockingStateType.SUBSCRIPTION_BUNDLE, null);
+        killBillClient.setBlockingState(bundle.getBundleId(), blockingState, createdBy, reason, comment);
+
+        final Subscription subscription = killBillClient.getSubscription(entitlement.getSubscriptionId());
+        assertEquals(subscription.getState(), EntitlementState.BLOCKED);
+
+        clock.addDays(1);
+
+        final BlockingState unblockingState = new BlockingState(bundle.getBundleId(), "unblock", "service", false, false, false, clock.getToday(DateTimeZone.forID(accountJson.getTimeZone())), BlockingStateType.SUBSCRIPTION_BUNDLE, null);
+        killBillClient.setBlockingState(bundle.getBundleId(), unblockingState, createdBy, reason, comment);
+
+        final Subscription subscription2 = killBillClient.getSubscription(entitlement.getSubscriptionId());
+        assertEquals(subscription2.getState(), EntitlementState.ACTIVE);
+    }
+
+
+
     @Test(groups = "slow", description = "Can paginate and search through all bundles")
     public void testBundlesPagination() throws Exception {
         final Account accountJson = createAccount();
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java
index 5d3893d..e44da34 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestPaymentMethod.java
@@ -1,7 +1,7 @@
 /*
  * Copyright 2010-2013 Ning, Inc.
- * Copyright 2014 Groupon, Inc
- * Copyright 2014 The Billing Project, LLC
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
  *
  * The Billing Project licenses this file to you under the Apache License, version 2.0
  * (the "License"); you may not use this file except in compliance with the
@@ -19,13 +19,18 @@
 package org.killbill.billing.jaxrs;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 
+import org.killbill.billing.ObjectType;
 import org.killbill.billing.client.model.Account;
+import org.killbill.billing.client.model.CustomField;
+import org.killbill.billing.client.model.CustomFields;
 import org.killbill.billing.client.model.PaymentMethod;
 import org.killbill.billing.client.model.PaymentMethods;
 import org.killbill.billing.client.model.PluginProperty;
+import org.killbill.billing.util.api.AuditLevel;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -97,6 +102,41 @@ public class TestPaymentMethod extends TestJaxrsBase {
         Assert.assertNull(page);
     }
 
+    @Test(groups = "slow", description = "Can create, retrieve and delete custom fields")
+    public void testPaymentMethodCustomFields() throws Exception {
+        final Account account = createAccountWithDefaultPaymentMethod();
+        final UUID paymentMethodId = account.getPaymentMethodId();
+
+        final CustomField customField = new CustomField();
+        customField.setObjectId(paymentMethodId);
+        customField.setObjectType(ObjectType.PAYMENT_METHOD);
+        customField.setName("testKey");
+        customField.setValue("testValue");
+
+        // Create custom field
+        final CustomFields createdCustomFields = killBillClient.createPaymentMethodCustomField(paymentMethodId, customField, createdBy, reason, comment);
+        Assert.assertEquals(createdCustomFields.size(), 1);
+        final CustomField createdCustomField = createdCustomFields.get(0);
+        Assert.assertEquals(createdCustomField.getName(), "testKey");
+        Assert.assertEquals(createdCustomField.getValue(), "testValue");
+        Assert.assertEquals(createdCustomField.getObjectId(), paymentMethodId);
+        Assert.assertEquals(createdCustomField.getObjectType(), ObjectType.PAYMENT_METHOD);
+
+        // Retrieve custom field
+        final CustomFields retrievedCustomFields = killBillClient.getPaymentMethodCustomFields(paymentMethodId, AuditLevel.NONE);
+        Assert.assertEquals(retrievedCustomFields.size(), 1);
+        final CustomField retrievedCustomField = retrievedCustomFields.get(0);
+        Assert.assertEquals(retrievedCustomField.getName(), "testKey");
+        Assert.assertEquals(retrievedCustomField.getValue(), "testValue");
+        Assert.assertEquals(retrievedCustomField.getObjectId(), paymentMethodId);
+        Assert.assertEquals(retrievedCustomField.getObjectType(), ObjectType.PAYMENT_METHOD);
+
+        // Delete custom field
+        killBillClient.deletePaymentMethodCustomFields(paymentMethodId, Collections.<UUID>singletonList(createdCustomField.getCustomFieldId()), createdBy, reason, comment);
+        final CustomFields deletedCustomFields = killBillClient.getPaymentMethodCustomFields(paymentMethodId, AuditLevel.NONE);
+        Assert.assertEquals(deletedCustomFields.size(), 0);
+    }
+
     private void doSearch(final String searchKey, final PaymentMethod paymentMethodJson) throws Exception {
         final List<PaymentMethod> results1 = killBillClient.searchPaymentMethodsByKey(searchKey, true);
         Assert.assertEquals(results1.size(), 1);
diff --git a/profiles/killpay/pom.xml b/profiles/killpay/pom.xml
index d1380da..ac70e48 100644
--- a/profiles/killpay/pom.xml
+++ b/profiles/killpay/pom.xml
@@ -20,7 +20,7 @@
     <parent>
         <artifactId>killbill-profiles</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles-killpay</artifactId>

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

diff --git a/profiles/pom.xml b/profiles/pom.xml
index ea3fb2c..fa1fd06 100644
--- a/profiles/pom.xml
+++ b/profiles/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-profiles</artifactId>
diff --git a/subscription/pom.xml b/subscription/pom.xml
index 71f6128..e3a8554 100644
--- a/subscription/pom.xml
+++ b/subscription/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-subscription</artifactId>

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

diff --git a/tenant/pom.xml b/tenant/pom.xml
index c90000b..6bfe74f 100644
--- a/tenant/pom.xml
+++ b/tenant/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-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 2c92952..641b9a5 100644
--- a/usage/pom.xml
+++ b/usage/pom.xml
@@ -19,7 +19,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-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 7ecef0e..180d50a 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.15.8-SNAPSHOT</version>
+        <version>0.15.9-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>