killbill-memoizeit

entitlement: refactor DefaultEntitlementApi Signed-off-by:

4/25/2018 3:39:11 PM

Details

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 475527c..db21945 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
@@ -72,12 +72,12 @@ import org.killbill.clock.Clock;
 import org.killbill.notificationq.api.NotificationQueueService;
 
 import com.google.common.base.Function;
+import com.google.common.base.Optional;
 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 static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logCreateEntitlement;
 import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logCreateEntitlementsWithAOs;
 import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logPauseResumeEntitlement;
 import static org.killbill.billing.entitlement.logging.EntitlementLoggingHelper.logTransferEntitlement;
@@ -145,19 +145,6 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
         return createdEntitlements.get(0);
     }
 
-    private BaseEntitlementWithAddOnsSpecifier getFirstBaseEntitlementWithAddOnsSpecifier(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiers) throws SubscriptionBaseApiException {
-        if (baseEntitlementWithAddOnsSpecifiers == null) {
-            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
-        }
-
-        final Iterator<BaseEntitlementWithAddOnsSpecifier> iterator = baseEntitlementWithAddOnsSpecifiers.iterator();
-        if (!iterator.hasNext()) {
-            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
-        }
-
-        return iterator.next();
-    }
-
     @Override
     public List<UUID> createBaseEntitlementsWithAddOns(final UUID accountId, final Iterable<BaseEntitlementWithAddOnsSpecifier> originalBaseEntitlementWithAddOnsSpecifiers, final boolean renameCancelledBundleIfExist, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
         logCreateEntitlementsWithAOs(log, originalBaseEntitlementWithAddOnsSpecifiers);
@@ -175,6 +162,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
             public List<UUID> doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
                 final InternalCallContext contextWithValidAccountRecordId = internalCallContextFactory.createInternalCallContext(accountId, callContext);
 
+                final Map<UUID, Optional<EventsStream>> eventsStreamForBaseSubscriptionPerBundle = new HashMap<UUID, Optional<EventsStream>>();
                 final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiersAfterPlugins = updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers();
                 final Collection<SubscriptionBaseWithAddOnsSpecifier> subscriptionBaseWithAddOnsSpecifiers = new LinkedList<SubscriptionBaseWithAddOnsSpecifier>();
                 DateTime upTo = null;
@@ -185,6 +173,9 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                                                                                                        contextWithValidAccountRecordId);
                     upTo = upTo == null || upTo.compareTo(entitlementRequestedDate) < 0 ? entitlementRequestedDate : upTo;
 
+                    // Verify if the operation is valid for that bundle
+                    preCheckAddEntitlement(baseEntitlementWithAddOnsSpecifier, entitlementRequestedDate, eventsStreamForBaseSubscriptionPerBundle, callContext, contextWithValidAccountRecordId);
+
                     final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(baseEntitlementWithAddOnsSpecifier.getBundleId(),
                                                                                                                                             baseEntitlementWithAddOnsSpecifier.getExternalKey(),
                                                                                                                                             baseEntitlementWithAddOnsSpecifier.getEntitlementSpecifier(),
@@ -193,38 +184,20 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                     subscriptionBaseWithAddOnsSpecifiers.add(subscriptionBaseWithAddOnsSpecifier);
                 }
 
+                // Verify if operation is allowed by looking for is_block_change on Account
+                // Note that to fully check for block_change we should also look for BlockingState at the BUNDLE/SUBSCRIPTION level in case some of the input contain a BP that already exists.
+                checkForAccountBlockingChange(accountId, upTo, contextWithValidAccountRecordId);
+
+                final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns;
                 try {
-                    // Verify if operation is allowed by looking for is_block_change on Account
-                    // Note that to fully check for block_change we should also look for BlockingState at the BUNDLE/SUBSCRIPTION level in case some of the input contain a BP that already exists.
-                    checkForAccountBlockingChange(accountId, upTo, contextWithValidAccountRecordId);
-
-                    final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(subscriptionBaseWithAddOnsSpecifiers,
-                                                                                                                            renameCancelledBundleIfExist,
-                                                                                                                            contextWithValidAccountRecordId);
-                    final List<UUID> createdSubscriptionIds = new LinkedList<UUID>();
-                    final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
-                    int i = 0;
-                    for (final Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiersAfterPlugins.iterator(); it.hasNext(); i++) {
-                        final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it.next();
-                        for (final SubscriptionBase subscriptionBase : subscriptionsWithAddOns.get(i).getSubscriptionBaseList()) {
-                            final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(),
-                                                                                         BlockingStateType.SUBSCRIPTION,
-                                                                                         DefaultEntitlementApi.ENT_STATE_START,
-                                                                                         EntitlementService.ENTITLEMENT_SERVICE_NAME,
-                                                                                         false,
-                                                                                         false,
-                                                                                         false,
-                                                                                         dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId));
-                            blockingStateMap.put(blockingState, subscriptionsWithAddOns.get(i).getBundle().getId());
-
-                            createdSubscriptionIds.add(subscriptionBase.getId());
-                        }
-                    }
-                    entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStateMap, contextWithValidAccountRecordId);
-                    return createdSubscriptionIds;
+                    subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(subscriptionBaseWithAddOnsSpecifiers,
+                                                                                                            renameCancelledBundleIfExist,
+                                                                                                            contextWithValidAccountRecordId);
                 } catch (final SubscriptionBaseApiException e) {
                     throw new EntitlementApiException(e);
                 }
+
+                return createEntitlementEvents(baseEntitlementWithAddOnsSpecifiersAfterPlugins, subscriptionsWithAddOns, updatedPluginContext, contextWithValidAccountRecordId);
             }
         };
         return pluginExecution.executeWithPlugin(createBaseEntitlementsWithAddOns, pluginContext);
@@ -232,94 +205,25 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
 
     @Override
     public UUID addEntitlement(final UUID bundleId, final PlanPhaseSpecifier planPhaseSpecifier, final List<PlanPhasePriceOverride> overrides, @Nullable final LocalDate entitlementEffectiveDate, @Nullable final LocalDate billingEffectiveDate,
-            final boolean isMigrated, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
-        logCreateEntitlement(log, bundleId, planPhaseSpecifier, overrides, entitlementEffectiveDate, billingEffectiveDate);
-
+                               final boolean isMigrated, final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
         final EntitlementSpecifier entitlementSpecifier = new DefaultEntitlementSpecifier(planPhaseSpecifier, overrides);
-        final List<EntitlementSpecifier> entitlementSpecifierList = new ArrayList<EntitlementSpecifier>();
-        entitlementSpecifierList.add(entitlementSpecifier);
-        final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(
-                bundleId,
-                null,
-                entitlementSpecifierList,
-                entitlementEffectiveDate,
-                billingEffectiveDate,
-                isMigrated);
-        final List<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifierList = new ArrayList<BaseEntitlementWithAddOnsSpecifier>();
-        baseEntitlementWithAddOnsSpecifierList.add(baseEntitlementWithAddOnsSpecifier);
-
-        final EntitlementContext pluginContext = new DefaultEntitlementContext(OperationType.CREATE_SUBSCRIPTION,
-                                                                               null,
-                                                                               null,
-                                                                               baseEntitlementWithAddOnsSpecifierList,
-                                                                               null,
-                                                                               properties,
-                                                                               callContext);
-
-        final WithEntitlementPlugin<UUID> addEntitlementWithPlugin = new WithEntitlementPlugin<UUID>() {
-            @Override
-            public UUID doCall(final EntitlementApi entitlementApi, final EntitlementContext updatedPluginContext) throws EntitlementApiException {
-                final InternalCallContext context = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, callContext);
-
-                final List<SubscriptionBase> subscriptionsByBundle;
-                try {
-                    subscriptionsByBundle = subscriptionBaseInternalApi.getSubscriptionsForBundle(bundleId, null, context);
-
-                    if (subscriptionsByBundle == null || subscriptionsByBundle.isEmpty()) {
-                        throw new EntitlementApiException(ErrorCode.SUB_NO_ACTIVE_SUBSCRIPTIONS, bundleId);
-                    }
-                } catch (final SubscriptionBaseApiException e) {
-                    throw new EntitlementApiException(e);
-                }
-                final boolean isStandalone = ProductCategory.STANDALONE.equals(subscriptionsByBundle.get(0).getCategory());
-
-                final EventsStream eventsStreamForBaseSubscription = isStandalone ? null : eventsStreamBuilder.buildForBaseSubscription(bundleId, callContext);
-
-                final DateTime entitlementRequestedDateRaw = dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), context);
-                DateTime entitlementRequestedDate = entitlementRequestedDateRaw;
-                if (!isStandalone) {
-                    final DateTime baseEntitlementStartDate = eventsStreamForBaseSubscription.getEntitlementEffectiveStartDateTime();
-                    entitlementRequestedDate = entitlementRequestedDateRaw.isBefore(baseEntitlementStartDate) ? baseEntitlementStartDate : entitlementRequestedDateRaw;
-                    preCheckAddEntitlement(bundleId, entitlementRequestedDate, baseEntitlementWithAddOnsSpecifier, eventsStreamForBaseSubscription, callContext);
-                }
-
-                try {
-                    final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifierAfterPlugins = getFirstBaseEntitlementWithAddOnsSpecifier(updatedPluginContext.getBaseEntitlementWithAddOnsSpecifiers());
-                    final SubscriptionBaseWithAddOnsSpecifier subscriptionBaseWithAddOnsSpecifier = new SubscriptionBaseWithAddOnsSpecifier(baseEntitlementWithAddOnsSpecifierAfterPlugins.getBundleId(),
-                                                                                                                                            baseEntitlementWithAddOnsSpecifierAfterPlugins.getExternalKey(),
-                                                                                                                                            baseEntitlementWithAddOnsSpecifierAfterPlugins.getEntitlementSpecifier(),
-                                                                                                                                            baseEntitlementWithAddOnsSpecifierAfterPlugins.getBillingEffectiveDate(),
-                                                                                                                                            baseEntitlementWithAddOnsSpecifierAfterPlugins.isMigrated());
-
-                    final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(ImmutableList.<SubscriptionBaseWithAddOnsSpecifier>of(subscriptionBaseWithAddOnsSpecifier),
-                                                                                                                                                   false,
-                                                                                                                                                   context);
-                    final SubscriptionBaseWithAddOns subscriptionBaseWithAddOns = subscriptionsWithAddOns.get(0);
-                    final UUID subscriptionId = subscriptionBaseWithAddOns.getSubscriptionBaseList().get(0).getId();
-
-                    final BlockingState newBlockingState = new DefaultBlockingState(subscriptionId, BlockingStateType.SUBSCRIPTION, DefaultEntitlementApi.ENT_STATE_START, EntitlementService.ENTITLEMENT_SERVICE_NAME, false, false, false, entitlementRequestedDate);
-                    entitlementUtils.setBlockingStatesAndPostBlockingTransitionEvent(ImmutableList.<BlockingState>of(newBlockingState), subscriptionBaseWithAddOns.getBundle().getId(), context);
-
-                    return subscriptionId;
-                } catch (final SubscriptionBaseApiException e) {
-                    throw new EntitlementApiException(e);
-                }
-            }
-        };
-        return pluginExecution.executeWithPlugin(addEntitlementWithPlugin, pluginContext);
-    }
-
-    private void preCheckAddEntitlement(final UUID bundleId, final DateTime entitlementRequestedDate, final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier, final EventsStream eventsStreamForBaseSubscription, final CallContext callContext) throws EntitlementApiException {
-        if (eventsStreamForBaseSubscription.isEntitlementCancelled() ||
-            (eventsStreamForBaseSubscription.isEntitlementPending() &&
-             (baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate() == null ||
-              baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate().compareTo(eventsStreamForBaseSubscription.getEntitlementEffectiveStartDate()) < 0))) {
-            throw new EntitlementApiException(ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION, bundleId);
-        }
-
-        // Check the base entitlement state is not blocked
-        if (eventsStreamForBaseSubscription.isBlockChange(entitlementRequestedDate)) {
-            throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
+        final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = new DefaultBaseEntitlementWithAddOnsSpecifier(bundleId,
+                                                                                                                                    null,
+                                                                                                                                    ImmutableList.<EntitlementSpecifier>of(entitlementSpecifier),
+                                                                                                                                    entitlementEffectiveDate,
+                                                                                                                                    billingEffectiveDate,
+                                                                                                                                    isMigrated);
+        final InternalCallContext context = internalCallContextFactory.createInternalCallContext(bundleId, ObjectType.BUNDLE, callContext);
+        try {
+            final UUID accountId = subscriptionBaseInternalApi.getAccountIdFromBundleId(bundleId, context);
+            final List<UUID> createdEntitlements = createBaseEntitlementsWithAddOns(accountId,
+                                                                                    ImmutableList.<BaseEntitlementWithAddOnsSpecifier>of(baseEntitlementWithAddOnsSpecifier),
+                                                                                    false,
+                                                                                    properties,
+                                                                                    callContext);
+            return createdEntitlements.get(0);
+        } catch (final SubscriptionBaseApiException e) {
+            throw new EntitlementApiException(e);
         }
     }
 
@@ -506,8 +410,78 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
         return pluginExecution.executeWithPlugin(transferWithPlugin, pluginContext);
     }
 
-    private void checkForAccountBlockingChange(final UUID accountId, @Nullable final DateTime upTo, final InternalCallContext context) throws EntitlementApiException {
+    private BaseEntitlementWithAddOnsSpecifier getFirstBaseEntitlementWithAddOnsSpecifier(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiers) throws SubscriptionBaseApiException {
+        if (baseEntitlementWithAddOnsSpecifiers == null) {
+            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
+        }
+
+        final Iterator<BaseEntitlementWithAddOnsSpecifier> iterator = baseEntitlementWithAddOnsSpecifiers.iterator();
+        if (!iterator.hasNext()) {
+            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
+        }
+
+        return iterator.next();
+    }
+
+    private void preCheckAddEntitlement(final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier,
+                                        final DateTime entitlementRequestedDate,
+                                        final Map<UUID, Optional<EventsStream>> eventsStreamForBaseSubscriptionPerBundle,
+                                        final TenantContext callContext,
+                                        final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
+        // TODO In the addEntitlement codepath, bundleId is always set. But technically an existing bundle could be specified by externalKey in
+        // the createBaseEntitlementsWithAddOns codepath. In that case, we should also check if that bundle is blocked (expensive though, especially
+        // since bundles are pulled again below in subscriptions)
+        if (baseEntitlementWithAddOnsSpecifier.getBundleId() != null) {
+            if (eventsStreamForBaseSubscriptionPerBundle.get(baseEntitlementWithAddOnsSpecifier.getBundleId()) == null) {
+                final List<SubscriptionBase> subscriptionsByBundle;
+                try {
+                    subscriptionsByBundle = subscriptionBaseInternalApi.getSubscriptionsForBundle(baseEntitlementWithAddOnsSpecifier.getBundleId(), null, contextWithValidAccountRecordId);
+
+                    if (subscriptionsByBundle == null || subscriptionsByBundle.isEmpty()) {
+                        throw new EntitlementApiException(ErrorCode.SUB_NO_ACTIVE_SUBSCRIPTIONS, baseEntitlementWithAddOnsSpecifier.getBundleId());
+                    }
+                } catch (final SubscriptionBaseApiException e) {
+                    throw new EntitlementApiException(e);
+                }
+
+                final boolean isStandalone = Iterables.any(subscriptionsByBundle,
+                                                           new Predicate<SubscriptionBase>() {
+                                                               @Override
+                                                               public boolean apply(final SubscriptionBase input) {
+                                                                   return ProductCategory.STANDALONE.equals(input.getCategory());
+                                                               }
+                                                           });
+
+                if (!isStandalone) {
+                    final EventsStream eventsStreamForBaseSubscription = eventsStreamBuilder.buildForBaseSubscription(baseEntitlementWithAddOnsSpecifier.getBundleId(), callContext);
+                    eventsStreamForBaseSubscriptionPerBundle.put(baseEntitlementWithAddOnsSpecifier.getBundleId(), Optional.<EventsStream>of(eventsStreamForBaseSubscription));
+                } else {
+                    eventsStreamForBaseSubscriptionPerBundle.put(baseEntitlementWithAddOnsSpecifier.getBundleId(), Optional.<EventsStream>absent());
+                }
+            }
+
+            final Optional<EventsStream> eventsStreamForBaseSubscription = eventsStreamForBaseSubscriptionPerBundle.get(baseEntitlementWithAddOnsSpecifier.getBundleId());
+            if (eventsStreamForBaseSubscription.isPresent()) {
+                preCheckAddEntitlement(baseEntitlementWithAddOnsSpecifier.getBundleId(), entitlementRequestedDate, baseEntitlementWithAddOnsSpecifier, eventsStreamForBaseSubscription.get());
+            }
+        }
+    }
+
+    private void preCheckAddEntitlement(final UUID bundleId, final DateTime entitlementRequestedDate, final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier, final EventsStream eventsStreamForBaseSubscription) throws EntitlementApiException {
+        if (eventsStreamForBaseSubscription.isEntitlementCancelled() ||
+            (eventsStreamForBaseSubscription.isEntitlementPending() &&
+             (baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate() == null ||
+              baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate().compareTo(eventsStreamForBaseSubscription.getEntitlementEffectiveStartDate()) < 0))) {
+            throw new EntitlementApiException(ErrorCode.SUB_GET_NO_SUCH_BASE_SUBSCRIPTION, bundleId);
+        }
 
+        // Check the base entitlement state is not blocked
+        if (eventsStreamForBaseSubscription.isBlockChange(entitlementRequestedDate)) {
+            throw new EntitlementApiException(new BlockingApiException(ErrorCode.BLOCK_BLOCKED_ACTION, BlockingChecker.ACTION_CHANGE, BlockingChecker.TYPE_SUBSCRIPTION, eventsStreamForBaseSubscription.getEntitlementId().toString()));
+        }
+    }
+
+    private void checkForAccountBlockingChange(final UUID accountId, @Nullable final DateTime upTo, final InternalCallContext context) throws EntitlementApiException {
         try {
             final BlockingAggregator blockingAggregator = checker.getBlockedStatus(accountId, BlockingStateType.ACCOUNT, upTo, context);
             if (blockingAggregator.isBlockChange()) {
@@ -517,4 +491,31 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
             throw new EntitlementApiException(e);
         }
     }
+
+    private List<UUID> createEntitlementEvents(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiersAfterPlugins,
+                                               final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns,
+                                               final CallContext updatedPluginContext,
+                                               final InternalCallContext contextWithValidAccountRecordId) throws EntitlementApiException {
+        final List<UUID> createdSubscriptionIds = new LinkedList<UUID>();
+        final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
+        int i = 0;
+        for (final Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiersAfterPlugins.iterator(); it.hasNext(); i++) {
+            final BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it.next();
+            for (final SubscriptionBase subscriptionBase : subscriptionsWithAddOns.get(i).getSubscriptionBaseList()) {
+                final BlockingState blockingState = new DefaultBlockingState(subscriptionBase.getId(),
+                                                                             BlockingStateType.SUBSCRIPTION,
+                                                                             DefaultEntitlementApi.ENT_STATE_START,
+                                                                             EntitlementService.ENTITLEMENT_SERVICE_NAME,
+                                                                             false,
+                                                                             false,
+                                                                             false,
+                                                                             dateHelper.fromLocalDateAndReferenceTime(baseEntitlementWithAddOnsSpecifier.getEntitlementEffectiveDate(), updatedPluginContext.getCreatedDate(), contextWithValidAccountRecordId));
+                blockingStateMap.put(blockingState, subscriptionsWithAddOns.get(i).getBundle().getId());
+
+                createdSubscriptionIds.add(subscriptionBase.getId());
+            }
+        }
+        entitlementUtils.setBlockingStateAndPostBlockingTransitionEvent(blockingStateMap, contextWithValidAccountRecordId);
+        return createdSubscriptionIds;
+    }
 }
diff --git a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementSpecifier.java b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementSpecifier.java
index dbf7876..0fc92b0 100644
--- a/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementSpecifier.java
+++ b/entitlement/src/main/java/org/killbill/billing/entitlement/api/DefaultEntitlementSpecifier.java
@@ -42,4 +42,16 @@ public class DefaultEntitlementSpecifier implements EntitlementSpecifier {
         return overrides;
     }
 
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("DefaultEntitlementSpecifier{");
+        sb.append("planName=").append(planPhaseSpecifier.getPlanName());
+        sb.append(", productName=").append(planPhaseSpecifier.getProductName());
+        sb.append(", billingPeriod=").append(planPhaseSpecifier.getBillingPeriod());
+        sb.append(", phaseType=").append(planPhaseSpecifier.getPhaseType());
+        sb.append(", priceListName=").append(planPhaseSpecifier.getPriceListName());
+        sb.append(", overrides=").append(overrides);
+        sb.append('}');
+        return sb.toString();
+    }
 }