killbill-memoizeit

Merge branch 'work-for-release-0.17.x' of github.com:killbill/killbill

11/12/2016 12:43:38 AM

Changes

Details

diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 99aecde..dfffbd8 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -19,10 +19,10 @@
       <option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
       <option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
     </inspection_tool>
-    <inspection_tool class="MagicNumber" enabled="true" level="WARNING" enabled_by_default="true" />
     <inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true">
       <option name="m_requireAnnotationsFirst" value="true" />
     </inspection_tool>
+    <inspection_tool class="RedundantTypeArguments" enabled="false" level="WARNING" enabled_by_default="false" />
     <inspection_tool class="SizeReplaceableByIsEmpty" enabled="true" level="WARNING" enabled_by_default="true" />
     <inspection_tool class="TypeMayBeWeakened" enabled="true" level="WARNING" enabled_by_default="true">
       <option name="useRighthandTypeAsWeakestTypeInAssignments" value="true" />
diff --git a/account/src/main/resources/org/killbill/billing/account/ddl.sql b/account/src/main/resources/org/killbill/billing/account/ddl.sql
index 295a88d..031e560 100644
--- a/account/src/main/resources/org/killbill/billing/account/ddl.sql
+++ b/account/src/main/resources/org/killbill/billing/account/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS accounts;
 CREATE TABLE accounts (
diff --git a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
index 8599350..617049b 100644
--- a/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseInternalApi.java
@@ -44,7 +44,6 @@ import org.killbill.billing.util.entity.Pagination;
 
 public interface SubscriptionBaseInternalApi {
 
-
     public SubscriptionBase createSubscription(UUID bundleId, PlanPhaseSpecifier spec, List<PlanPhasePriceOverride> overrides, DateTime requestedDateWithMs,
                                                final boolean isMigrated, InternalCallContext context) throws SubscriptionBaseApiException;
 
@@ -103,6 +102,4 @@ public interface SubscriptionBaseInternalApi {
     public void updateBCD(final UUID subscriptionId, final int bcd, @Nullable final LocalDate effectiveFromDate, final InternalCallContext internalCallContext) throws SubscriptionBaseApiException;
 
     public int getDefaultBillCycleDayLocal(final Map<UUID, Integer> bcdCache, final SubscriptionBase subscription, final SubscriptionBase baseSubscription, final PlanPhaseSpecifier planPhaseSpecifier, final DateTimeZone accountTimeZone, final int accountBillCycleDayLocal, final DateTime effectiveDate, final InternalTenantContext context) throws SubscriptionBaseApiException;
-
-
 }
diff --git a/beatrix/src/main/resources/org/killbill/billing/beatrix/ddl.sql b/beatrix/src/main/resources/org/killbill/billing/beatrix/ddl.sql
index 6ce6e8e..96f32e8 100644
--- a/beatrix/src/main/resources/org/killbill/billing/beatrix/ddl.sql
+++ b/beatrix/src/main/resources/org/killbill/billing/beatrix/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS bus_ext_events;
 CREATE TABLE bus_ext_events (
diff --git a/catalog/src/main/resources/org/killbill/billing/catalog/ddl.sql b/catalog/src/main/resources/org/killbill/billing/catalog/ddl.sql
index cb443fb..fce5967 100644
--- a/catalog/src/main/resources/org/killbill/billing/catalog/ddl.sql
+++ b/catalog/src/main/resources/org/killbill/billing/catalog/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS catalog_override_plan_definition;
 CREATE TABLE catalog_override_plan_definition (
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 bd890dc..a84469f 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
@@ -92,7 +92,6 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
     private final BlockingChecker checker;
     private final BlockingStateDao blockingStateDao;
     private final EntitlementDateHelper dateHelper;
-    private final PersistentBus eventBus;
     private final EventsStreamBuilder eventsStreamBuilder;
     private final EntitlementUtils entitlementUtils;
     private final NotificationQueueService notificationQueueService;
@@ -108,7 +107,6 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
                                  final EntitlementPluginExecution pluginExecution,
                                  final SecurityApi securityApi) {
         super(eventBus, null, pluginExecution, internalCallContextFactory, subscriptionInternalApi, accountApi, blockingStateDao, clock, checker, notificationQueueService, eventsStreamBuilder, entitlementUtils, securityApi);
-        this.eventBus = eventBus;
         this.internalCallContextFactory = internalCallContextFactory;
         this.subscriptionBaseInternalApi = subscriptionInternalApi;
         this.subscriptionBaseTransferApi = subscriptionTransferApi;
@@ -125,7 +123,7 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
 
     @Override
     public Entitlement createBaseEntitlement(final UUID accountId, final PlanPhaseSpecifier planPhaseSpecifier, final String externalKey, final List<PlanPhasePriceOverride> overrides,
-                                             @Nullable final LocalDate entitlementEffectiveDate, @Nullable  LocalDate billingEffectiveDate, final boolean isMigrated,
+                                             @Nullable final LocalDate entitlementEffectiveDate, @Nullable final LocalDate billingEffectiveDate, final boolean isMigrated,
                                              final Iterable<PluginProperty> properties, final CallContext callContext) throws EntitlementApiException {
 
         logCreateEntitlement(log, null, planPhaseSpecifier, overrides, entitlementEffectiveDate, billingEffectiveDate);
@@ -183,12 +181,21 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
     }
 
     private EntitlementSpecifier getFirstEntitlementSpecifier(final Iterable<BaseEntitlementWithAddOnsSpecifier> baseEntitlementWithAddOnsSpecifiers) throws SubscriptionBaseApiException {
-        if ((baseEntitlementWithAddOnsSpecifiers == null) || baseEntitlementWithAddOnsSpecifiers.iterator().hasNext()) {
-            if ((baseEntitlementWithAddOnsSpecifiers.iterator().next().getEntitlementSpecifier() == null) || !baseEntitlementWithAddOnsSpecifiers.iterator().next().getEntitlementSpecifier().iterator().hasNext()) {
-                throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
-            }
+        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);
+        }
+
+        final BaseEntitlementWithAddOnsSpecifier entitlementWithAddOnsSpecifier = iterator.next();
+        if (entitlementWithAddOnsSpecifier.getEntitlementSpecifier() == null || !entitlementWithAddOnsSpecifier.getEntitlementSpecifier().iterator().hasNext()) {
+            throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_INVALID_ENTITLEMENT_SPECIFIER);
+        } else {
+            return entitlementWithAddOnsSpecifier.getEntitlementSpecifier().iterator().next();
         }
-        return baseEntitlementWithAddOnsSpecifiers.iterator().next().getEntitlementSpecifier().iterator().next();
     }
 
     @Override
@@ -211,11 +218,11 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
 
                 try {
                     final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns = subscriptionBaseInternalApi.createBaseSubscriptionsWithAddOns(accountId, baseEntitlementWithAddOnsSpecifiers, contextWithValidAccountRecordId);
-                    Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
+                    final Map<BlockingState, UUID> blockingStateMap = new HashMap<BlockingState, UUID>();
                     int i = 0;
-                    for (Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiers.iterator(); it.hasNext(); i++) {
-                        BaseEntitlementWithAddOnsSpecifier baseEntitlementWithAddOnsSpecifier = it.next();
-                        for (SubscriptionBase subscriptionBase : subscriptionsWithAddOns.get(i).getSubscriptionBaseList()) {
+                    for (final Iterator<BaseEntitlementWithAddOnsSpecifier> it = baseEntitlementWithAddOnsSpecifiers.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,
@@ -234,13 +241,13 @@ public class DefaultEntitlementApi extends DefaultEntitlementApiBase implements 
         return pluginExecution.executeWithPlugin(createBaseEntitlementsWithAddOns, pluginContext);
     }
 
-    private List<Entitlement> buildEntitlementList(final UUID accountId, final List<SubscriptionBaseWithAddOns> subscriptionsWithAddOns, final CallContext callContext) throws EntitlementApiException {
-        List<Entitlement> result = new ArrayList<Entitlement>();
-        for (SubscriptionBaseWithAddOns subscriptionWithAddOns : subscriptionsWithAddOns) {
-            for (SubscriptionBase subscriptionBase : subscriptionWithAddOns.getSubscriptionBaseList()) {
-                Entitlement entitlement = new DefaultEntitlement(accountId, subscriptionBase.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
-                                                                 blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
-                                                                 entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory, callContext);
+    private List<Entitlement> buildEntitlementList(final UUID accountId, final Iterable<SubscriptionBaseWithAddOns> subscriptionsWithAddOns, final CallContext callContext) throws EntitlementApiException {
+        final List<Entitlement> result = new ArrayList<Entitlement>();
+        for (final SubscriptionBaseWithAddOns subscriptionWithAddOns : subscriptionsWithAddOns) {
+            for (final SubscriptionBase subscriptionBase : subscriptionWithAddOns.getSubscriptionBaseList()) {
+                final Entitlement entitlement = new DefaultEntitlement(accountId, subscriptionBase.getId(), eventsStreamBuilder, entitlementApi, pluginExecution,
+                                                                       blockingStateDao, subscriptionBaseInternalApi, checker, notificationQueueService,
+                                                                       entitlementUtils, dateHelper, clock, securityApi, internalCallContextFactory, callContext);
                 result.add(entitlement);
             }
         }
diff --git a/entitlement/src/main/resources/org/killbill/billing/entitlement/ddl.sql b/entitlement/src/main/resources/org/killbill/billing/entitlement/ddl.sql
index a122b32..8d2d2be 100644
--- a/entitlement/src/main/resources/org/killbill/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/org/killbill/billing/entitlement/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS blocking_states;
 CREATE TABLE blocking_states (
diff --git a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
index 89602db..1487f3c 100644
--- a/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
+++ b/invoice/src/main/resources/org/killbill/billing/invoice/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS invoice_items;
 CREATE TABLE invoice_items (
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 97c3dad..04e39d5 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
@@ -118,13 +118,15 @@ public class BundleResource extends JaxRsResourceBase {
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid bundle id supplied"),
                            @ApiResponse(code = 404, message = "Bundle not found")})
     public Response getBundle(@PathParam("bundleId") final String bundleId,
+                              @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                               @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, AccountApiException, CatalogApiException {
         final UUID id = UUID.fromString(bundleId);
 
         final TenantContext tenantContext = this.context.createContext(request);
         final SubscriptionBundle bundle = subscriptionApi.getSubscriptionBundle(id, tenantContext);
         final Account account = accountUserApi.getAccountById(bundle.getAccountId(), tenantContext);
-        final BundleJson json = new BundleJson(bundle, account.getCurrency(), null);
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(bundle.getAccountId(), auditMode.getLevel(), tenantContext);
+        final BundleJson json = new BundleJson(bundle, account.getCurrency(), accountAuditLogs);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -135,6 +137,7 @@ public class BundleResource extends JaxRsResourceBase {
     @ApiResponses(value = {@ApiResponse(code = 404, message = "Bundle not found")})
     public Response getBundleByKey(@QueryParam(QUERY_EXTERNAL_KEY) final String externalKey,
                                    @QueryParam(QUERY_INCLUDED_DELETED) @DefaultValue("false") final Boolean includedDeleted,
+                                   @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, AccountApiException, CatalogApiException {
 
         final TenantContext tenantContext = this.context.createContext(request);
@@ -146,11 +149,11 @@ public class BundleResource extends JaxRsResourceBase {
             final SubscriptionBundle activeBundle = subscriptionApi.getActiveSubscriptionBundleForExternalKey(externalKey, tenantContext);
             bundles = ImmutableList.of(activeBundle);
         }
-
         final List<BundleJson> result = new ArrayList<BundleJson>(bundles.size());
         for (final SubscriptionBundle bundle : bundles) {
             final Account account = accountUserApi.getAccountById(bundle.getAccountId(), tenantContext);
-            final BundleJson json = new BundleJson(bundle, account.getCurrency(), null);
+            final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(bundle.getAccountId(), auditMode.getLevel(), tenantContext);
+            final BundleJson json = new BundleJson(bundle, account.getCurrency(), accountAuditLogs);
             result.add(json);
         }
         return Response.status(Status.OK).entity(result).build();
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 6532449..6cd4f7c 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
@@ -86,12 +86,14 @@ import org.killbill.billing.jaxrs.util.JaxrsUriBuilder;
 import org.killbill.billing.jaxrs.util.KillbillEventHandler;
 import org.killbill.billing.payment.api.PaymentApi;
 import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.api.AuditLevel;
 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.TagApiException;
 import org.killbill.billing.util.api.TagDefinitionApiException;
 import org.killbill.billing.util.api.TagUserApi;
+import org.killbill.billing.util.audit.AccountAuditLogs;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.billing.util.userrequest.CompletionUserRequestBase;
@@ -148,12 +150,14 @@ public class SubscriptionResource extends JaxRsResourceBase {
     @ApiResponses(value = {@ApiResponse(code = 400, message = "Invalid subscription id supplied"),
                            @ApiResponse(code = 404, message = "Subscription not found")})
     public Response getEntitlement(@PathParam("subscriptionId") final String subscriptionId,
+                                   @QueryParam(QUERY_AUDIT) @DefaultValue("NONE") final AuditMode auditMode,
                                    @javax.ws.rs.core.Context final HttpServletRequest request) throws SubscriptionApiException, AccountApiException, CatalogApiException {
         final UUID uuid = UUID.fromString(subscriptionId);
         final TenantContext context = this.context.createContext(request);
         final Subscription subscription = subscriptionApi.getSubscriptionForEntitlementId(uuid, context);
         final Account account = accountUserApi.getAccountById(subscription.getAccountId(), context);
-        final SubscriptionJson json = new SubscriptionJson(subscription, account.getCurrency(), null);
+        final AccountAuditLogs accountAuditLogs = auditUserApi.getAccountAuditLogs(subscription.getAccountId(), auditMode.getLevel(), context);
+        final SubscriptionJson json = new SubscriptionJson(subscription, account.getCurrency(), accountAuditLogs);
         return Response.status(Status.OK).entity(json).build();
     }
 
@@ -596,7 +600,7 @@ public class SubscriptionResource extends JaxRsResourceBase {
                 if (operationResponse.getStatus() != Status.OK.getStatusCode()) {
                     return operationResponse;
                 }
-                return getEntitlement(subscriptionId, request);
+                return getEntitlement(subscriptionId, new AuditMode(AuditLevel.NONE.toString()), request);
             }
         };
 
diff --git a/payment/src/main/resources/org/killbill/billing/payment/ddl.sql b/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
index 25af914..019e3cf 100644
--- a/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
+++ b/payment/src/main/resources/org/killbill/billing/payment/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS payment_attempts;
 CREATE TABLE payment_attempts (
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
index d1991cd..e3ad715 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/SubscriptionBaseApiService.java
@@ -47,7 +47,7 @@ public interface SubscriptionBaseApiService {
                                               CallContext context)
             throws SubscriptionBaseApiException;
 
-    public List<DefaultSubscriptionBaseWithAddOns> createPlansWithAddOns(UUID accountId, Iterable<SubscriptionAndAddOnsSpecifier> subscriptionsAndAddOns, CallContext context)
+    public List<SubscriptionBaseWithAddOns> createPlansWithAddOns(UUID accountId, Iterable<SubscriptionAndAddOnsSpecifier> subscriptionsAndAddOns, CallContext context)
             throws SubscriptionBaseApiException;
 
     public boolean cancel(DefaultSubscriptionBase subscription, CallContext context)
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
index 9ccf2a8..d8bcd5c 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/svcs/DefaultSubscriptionInternalApi.java
@@ -19,6 +19,7 @@
 package org.killbill.billing.subscription.api.svcs;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -57,6 +58,7 @@ import org.killbill.billing.events.EffectiveSubscriptionInternalEvent;
 import org.killbill.billing.invoice.api.DryRunArguments;
 import org.killbill.billing.subscription.api.SubscriptionApiBase;
 import org.killbill.billing.subscription.api.SubscriptionBase;
+import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
 import org.killbill.billing.subscription.api.SubscriptionBaseInternalApi;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
 import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
@@ -86,6 +88,7 @@ import org.killbill.billing.util.bcd.BillCycleDayCalculator;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationHelper.SourcePaginationBuilder;
 import org.killbill.clock.Clock;
@@ -133,7 +136,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     @Inject
     public DefaultSubscriptionInternalApi(final SubscriptionDao dao,
-                                          final DefaultSubscriptionBaseApiService apiService,
+                                          final SubscriptionBaseApiService apiService,
                                           final NotificationQueueService notificationQueueService,
                                           final Clock clock,
                                           final CatalogService catalogService,
@@ -204,7 +207,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         boolean first = true;
         final List<SubscriptionBase> subscriptionsForBundle = getSubscriptionsForBundle(bundleId, null, context);
 
-        for (EntitlementSpecifier entitlement : entitlements) {
+        for (final EntitlementSpecifier entitlement : entitlements) {
 
             final PlanPhaseSpecifier spec = entitlement.getPlanPhaseSpecifier();
 
@@ -227,8 +230,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             // verify the number of subscriptions (of the same kind) allowed per bundle and the existing ones
             if (ProductCategory.ADD_ON.toString().equalsIgnoreCase(plan.getProduct().getCategory().toString())) {
                 if (plan.getPlansAllowedInBundle() != -1 && plan.getPlansAllowedInBundle() > 0) {
-                    int existingAddOnsWithSamePlanName = addonUtils.countExistingAddOnsWithSamePlanName(subscriptionsForBundle, plan.getName());
-                    int currentAddOnsWithSamePlanName = countCurrentAddOnsWithSamePlanName(entitlements, catalog, plan.getName(), effectiveDate, callContext);
+                    final int existingAddOnsWithSamePlanName = addonUtils.countExistingAddOnsWithSamePlanName(subscriptionsForBundle, plan.getName());
+                    final int currentAddOnsWithSamePlanName = countCurrentAddOnsWithSamePlanName(entitlements, catalog, plan.getName(), effectiveDate, callContext);
                     if ((existingAddOnsWithSamePlanName + currentAddOnsWithSamePlanName) > plan.getPlansAllowedInBundle()) {
                         // a new ADD_ON subscription of the same plan can't be added because it has reached its limit by bundle
                         throw new SubscriptionBaseApiException(ErrorCode.SUB_CREATE_AO_MAX_PLAN_ALLOWED_BY_BUNDLE, plan.getName());
@@ -236,7 +239,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                 }
             }
 
-            SubscriptionSpecifier subscription = new SubscriptionSpecifier();
+            final SubscriptionSpecifier subscription = new SubscriptionSpecifier();
             subscription.setRealPriceList(plan.getPriceListName());
             subscription.setEffectiveDate(effectiveDate);
             subscription.setProcessedDate(now);
@@ -262,14 +265,14 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             final CallContext callContext = internalCallContextFactory.createCallContext(context);
             final DateTime now = clock.getUTCNow();
 
-            final List<SubscriptionAndAddOnsSpecifier> subscriptionAndAddOns = new ArrayList<SubscriptionAndAddOnsSpecifier>();
-            for (BaseEntitlementWithAddOnsSpecifier entitlementWithAddOnsSpecifier : baseEntitlementWithAddOnsSpecifier) {
+            final Collection<SubscriptionAndAddOnsSpecifier> subscriptionAndAddOns = new ArrayList<SubscriptionAndAddOnsSpecifier>();
+            for (final BaseEntitlementWithAddOnsSpecifier entitlementWithAddOnsSpecifier : baseEntitlementWithAddOnsSpecifier) {
                 final DateTime effectiveDate = (entitlementWithAddOnsSpecifier.getBillingEffectiveDate() != null) ?
                                                DefaultClock.truncateMs(entitlementWithAddOnsSpecifier.getBillingEffectiveDate().toDateTimeAtStartOfDay()) : now;
 
                 final SubscriptionBaseBundle bundle = createBundleForAccount(accountId, entitlementWithAddOnsSpecifier.getExternalKey(), context);
 
-                SubscriptionAndAddOnsSpecifier subscriptionAndAddOnsSpecifier = new SubscriptionAndAddOnsSpecifier(
+                final SubscriptionAndAddOnsSpecifier subscriptionAndAddOnsSpecifier = new SubscriptionAndAddOnsSpecifier(
                         bundle.getId(),
                         effectiveDate,
                         verifyAndBuildSubscriptionSpecifiers(bundle.getId(),
@@ -284,27 +287,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                 subscriptionAndAddOns.add(subscriptionAndAddOnsSpecifier);
             }
 
-            final List<DefaultSubscriptionBaseWithAddOns> defaultSubscriptionBaseWithAddOnsList = apiService.createPlansWithAddOns(accountId, subscriptionAndAddOns, callContext);
-            List<SubscriptionBaseWithAddOns> subscriptionBaseWithAddOnsList = new ArrayList<SubscriptionBaseWithAddOns>();
-            for (final DefaultSubscriptionBaseWithAddOns cur : defaultSubscriptionBaseWithAddOnsList) {
-                SubscriptionBaseWithAddOns subscriptionBaseWithAddOns = new SubscriptionBaseWithAddOns() {
-                    @Override
-                    public UUID getBundleId() {
-                        return cur.getBundleId();
-                    }
-                    @Override
-                    public List<SubscriptionBase> getSubscriptionBaseList() {
-                        return cur.getSubscriptionBaseList();
-                    }
-                    @Override
-                    public DateTime getEffectiveDate() {
-                        return cur.getEffectiveDate();
-                    }
-                };
-                subscriptionBaseWithAddOnsList.add(subscriptionBaseWithAddOns);
-            }
-           return subscriptionBaseWithAddOnsList;
-        } catch (CatalogApiException e) {
+            return apiService.createPlansWithAddOns(accountId, subscriptionAndAddOns, callContext);
+        } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
     }
@@ -313,7 +297,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                                                    final Catalog catalog, final String planName,
                                                    final DateTime effectiveDate, final CallContext callContext) throws CatalogApiException {
         int countCurrentAddOns = 0;
-        for (EntitlementSpecifier entitlement : entitlements) {
+        for (final EntitlementSpecifier entitlement : entitlements) {
             final PlanPhaseSpecifier spec = entitlement.getPlanPhaseSpecifier();
             final PlanPhasePriceOverridesWithCallContext overridesWithContext =
                     new DefaultPlanPhasePriceOverridesWithCallContext(entitlement.getOverrides(), callContext);
@@ -350,7 +334,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
         final List<SubscriptionBaseBundle> existingBundles = dao.getSubscriptionBundlesForKey(bundleKey, context);
         final DateTime now = clock.getUTCNow();
-        final DateTime originalCreatedDate = existingBundles.size() > 0 ? existingBundles.get(0).getCreatedDate() : now;
+        final DateTime originalCreatedDate = !existingBundles.isEmpty() ? existingBundles.get(0).getCreatedDate() : now;
         final DefaultSubscriptionBaseBundle bundle = new DefaultSubscriptionBaseBundle(bundleKey, accountId, now, originalCreatedDate, now, now);
 
         if (null != bundleKey && bundleKey.length() > 255) {
@@ -361,8 +345,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     @Override
     public List<SubscriptionBaseBundle> getBundlesForAccountAndKey(final UUID accountId, final String bundleKey, final InternalTenantContext context) throws SubscriptionBaseApiException {
-        final List<SubscriptionBaseBundle> bundlesForAccountAndKey = dao.getSubscriptionBundlesForAccountAndKey(accountId, bundleKey, context);
-        return bundlesForAccountAndKey;
+        return dao.getSubscriptionBundlesForAccountAndKey(accountId, bundleKey, context);
     }
 
     @Override
@@ -372,8 +355,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     @Override
     public List<SubscriptionBaseBundle> getBundlesForKey(final String bundleKey, final InternalTenantContext context) {
-        final List<SubscriptionBaseBundle> result = dao.getSubscriptionBundlesForKey(bundleKey, context);
-        return result;
+        return dao.getSubscriptionBundlesForKey(bundleKey, context);
     }
 
     @Override
@@ -418,7 +400,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
         return dao.getNonAOSubscriptionIdsForKey(bundleKey, context);
     }
 
-    public static SubscriptionBaseBundle getActiveBundleForKeyNotException(final List<SubscriptionBaseBundle> existingBundles, final SubscriptionDao dao, final Clock clock, final InternalTenantContext context) {
+    public static SubscriptionBaseBundle getActiveBundleForKeyNotException(final Iterable<SubscriptionBaseBundle> existingBundles, final SubscriptionDao dao, final Clock clock, final InternalTenantContext context) {
         for (final SubscriptionBaseBundle cur : existingBundles) {
             final List<SubscriptionBase> subscriptions;
             try {
@@ -531,7 +513,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     @Override
     public List<EffectiveSubscriptionInternalEvent> getAllTransitions(final SubscriptionBase subscription, final InternalTenantContext context) {
-        final List<SubscriptionBaseTransition> transitions = ((DefaultSubscriptionBase) subscription).getAllTransitions();
+        final List<SubscriptionBaseTransition> transitions = subscription.getAllTransitions();
         return convertEffectiveSubscriptionInternalEventFromSubscriptionTransitions(subscription, context, transitions);
     }
 
@@ -623,8 +605,8 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
 
     private void populateDryRunEvents(@Nullable final UUID bundleId,
                                       @Nullable final DryRunArguments dryRunArguments,
-                                      final List<SubscriptionBaseEvent> outputDryRunEvents,
-                                      final List<SubscriptionBase> outputSubscriptions,
+                                      final Collection<SubscriptionBaseEvent> outputDryRunEvents,
+                                      final Collection<SubscriptionBase> outputSubscriptions,
                                       final InternalTenantContext context) throws SubscriptionBaseApiException {
         if (dryRunArguments == null || dryRunArguments.getAction() == null) {
             return;
@@ -645,62 +627,59 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                               catalog.createOrFindPlan(inputSpec, overridesWithContext, utcNow);
             final TenantContext tenantContext = internalCallContextFactory.createTenantContext(context);
 
-
-            if (dryRunArguments != null) {
-                switch (dryRunArguments.getAction()) {
-                    case START_BILLING:
-
-                        final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
-                        final DateTime startEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : utcNow;
-                        final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, startEffectiveDate, context);
-                        final UUID subscriptionId = UUIDs.randomUUID();
-                        dryRunEvents = apiService.getEventsOnCreation(bundleId, subscriptionId, startEffectiveDate, bundleStartDate, plan, inputSpec.getPhaseType(), plan.getPriceListName(),
-                                                                      startEffectiveDate, utcNow, context);
-                        final SubscriptionBuilder builder = new SubscriptionBuilder()
-                                .setId(subscriptionId)
-                                .setBundleId(bundleId)
-                                .setCategory(plan.getProduct().getCategory())
-                                .setBundleStartDate(bundleStartDate)
-                                .setAlignStartDate(startEffectiveDate);
-                        final DefaultSubscriptionBase newSubscription = new DefaultSubscriptionBase(builder, apiService, clock);
-                        newSubscription.rebuildTransitions(dryRunEvents, catalog);
-                        outputSubscriptions.add(newSubscription);
-                        break;
-
-                    case CHANGE:
-                        final DefaultSubscriptionBase subscriptionForChange = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
-                        DateTime changeEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
-                        if (changeEffectiveDate == null) {
-                            BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
-                            if (policy == null) {
-                                final PlanChangeResult planChangeResult = apiService.getPlanChangeResult(subscriptionForChange, inputSpec, utcNow, tenantContext);
-                                policy = planChangeResult.getPolicy();
-                            }
-                            changeEffectiveDate = subscriptionForChange.getPlanChangeEffectiveDate(policy);
+            switch (dryRunArguments.getAction()) {
+                case START_BILLING:
+
+                    final DefaultSubscriptionBase baseSubscription = (DefaultSubscriptionBase) dao.getBaseSubscription(bundleId, context);
+                    final DateTime startEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : utcNow;
+                    final DateTime bundleStartDate = getBundleStartDateWithSanity(bundleId, baseSubscription, plan, startEffectiveDate, context);
+                    final UUID subscriptionId = UUIDs.randomUUID();
+                    dryRunEvents = apiService.getEventsOnCreation(bundleId, subscriptionId, startEffectiveDate, bundleStartDate, plan, inputSpec.getPhaseType(), plan.getPriceListName(),
+                                                                  startEffectiveDate, utcNow, context);
+                    final SubscriptionBuilder builder = new SubscriptionBuilder()
+                            .setId(subscriptionId)
+                            .setBundleId(bundleId)
+                            .setCategory(plan.getProduct().getCategory())
+                            .setBundleStartDate(bundleStartDate)
+                            .setAlignStartDate(startEffectiveDate);
+                    final DefaultSubscriptionBase newSubscription = new DefaultSubscriptionBase(builder, apiService, clock);
+                    newSubscription.rebuildTransitions(dryRunEvents, catalog);
+                    outputSubscriptions.add(newSubscription);
+                    break;
+
+                case CHANGE:
+                    final DefaultSubscriptionBase subscriptionForChange = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
+                    DateTime changeEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
+                    if (changeEffectiveDate == null) {
+                        BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
+                        if (policy == null) {
+                            final PlanChangeResult planChangeResult = apiService.getPlanChangeResult(subscriptionForChange, inputSpec, utcNow, tenantContext);
+                            policy = planChangeResult.getPolicy();
                         }
-                        dryRunEvents = apiService.getEventsOnChangePlan(subscriptionForChange, plan, plan.getPriceListName(), changeEffectiveDate, utcNow, true, context);
-                        break;
-
-                    case STOP_BILLING:
-                        final DefaultSubscriptionBase subscriptionForCancellation = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
-                        DateTime cancelEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
-                        if (dryRunArguments.getEffectiveDate() == null) {
-                            BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
-                            if (policy == null) {
-
-                                final Plan currentPlan = subscriptionForCancellation.getCurrentPlan();
-                                final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(currentPlan.getName(),
-                                                                                       subscriptionForCancellation.getCurrentPhase().getPhaseType());
-                                policy = catalogService.getFullCatalog(true, true, context).planCancelPolicy(spec, utcNow);
-                            }
-                            cancelEffectiveDate = subscriptionForCancellation.getPlanChangeEffectiveDate(policy);
+                        changeEffectiveDate = subscriptionForChange.getPlanChangeEffectiveDate(policy);
+                    }
+                    dryRunEvents = apiService.getEventsOnChangePlan(subscriptionForChange, plan, plan.getPriceListName(), changeEffectiveDate, utcNow, true, context);
+                    break;
+
+                case STOP_BILLING:
+                    final DefaultSubscriptionBase subscriptionForCancellation = (DefaultSubscriptionBase) dao.getSubscriptionFromId(dryRunArguments.getSubscriptionId(), context);
+                    DateTime cancelEffectiveDate = dryRunArguments.getEffectiveDate() != null ? context.toUTCDateTime(dryRunArguments.getEffectiveDate()) : null;
+                    if (dryRunArguments.getEffectiveDate() == null) {
+                        BillingActionPolicy policy = dryRunArguments.getBillingActionPolicy();
+                        if (policy == null) {
+
+                            final Plan currentPlan = subscriptionForCancellation.getCurrentPlan();
+                            final PlanPhaseSpecifier spec = new PlanPhaseSpecifier(currentPlan.getName(),
+                                                                                   subscriptionForCancellation.getCurrentPhase().getPhaseType());
+                            policy = catalogService.getFullCatalog(true, true, context).planCancelPolicy(spec, utcNow);
                         }
-                        dryRunEvents = apiService.getEventsOnCancelPlan(subscriptionForCancellation, cancelEffectiveDate, utcNow, true, context);
-                        break;
+                        cancelEffectiveDate = subscriptionForCancellation.getPlanChangeEffectiveDate(policy);
+                    }
+                    dryRunEvents = apiService.getEventsOnCancelPlan(subscriptionForCancellation, cancelEffectiveDate, utcNow, true, context);
+                    break;
 
-                    default:
-                        throw new IllegalArgumentException("Unexpected dryRunArguments action " + dryRunArguments.getAction());
-                }
+                default:
+                    throw new IllegalArgumentException("Unexpected dryRunArguments action " + dryRunArguments.getAction());
             }
         } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
@@ -723,7 +702,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
                     return input.getEffectiveDate();
                 }
             });
-        } catch (NoSuchNotificationQueue noSuchNotificationQueue) {
+        } catch (final NoSuchNotificationQueue noSuchNotificationQueue) {
             throw new IllegalStateException(noSuchNotificationQueue);
         }
     }
@@ -746,7 +725,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
             }
         });
         final Map<UUID, DateTime> result = filteredEvents.iterator().hasNext() ? new HashMap<UUID, DateTime>() : ImmutableMap.<UUID, DateTime>of();
-        for (SubscriptionBaseEvent cur : filteredEvents) {
+        for (final SubscriptionBaseEvent cur : filteredEvents) {
             final DateTime targetDate = result.get(cur.getSubscriptionId());
             if (targetDate == null || targetDate.compareTo(cur.getEffectiveDate()) > 0) {
                 result.put(cur.getSubscriptionId(), cur.getEffectiveDate());
@@ -840,7 +819,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
     }
 
     private List<EffectiveSubscriptionInternalEvent> convertEffectiveSubscriptionInternalEventFromSubscriptionTransitions(final SubscriptionBase subscription,
-                                                                                                                          final InternalTenantContext context, final List<SubscriptionBaseTransition> transitions) {
+                                                                                                                          final InternalTenantContext context, final Collection<SubscriptionBaseTransition> transitions) {
         return ImmutableList.<EffectiveSubscriptionInternalEvent>copyOf(Collections2.transform(transitions, new Function<SubscriptionBaseTransition, EffectiveSubscriptionInternalEvent>() {
             @Override
             @Nullable
@@ -851,7 +830,7 @@ public class DefaultSubscriptionInternalApi extends SubscriptionApiBase implemen
     }
 
     // For forward-compatibility
-    private DefaultSubscriptionBase getDefaultSubscriptionBase(final SubscriptionBase subscriptionBase, final InternalTenantContext context) throws CatalogApiException {
+    private DefaultSubscriptionBase getDefaultSubscriptionBase(final Entity subscriptionBase, final InternalTenantContext context) throws CatalogApiException {
         if (subscriptionBase instanceof DefaultSubscriptionBase) {
             return (DefaultSubscriptionBase) subscriptionBase;
         } else {
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
index 0cd9ce2..49efce2 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/api/user/DefaultSubscriptionBaseApiService.java
@@ -29,6 +29,7 @@ import java.util.UUID;
 import javax.annotation.Nullable;
 
 import org.joda.time.DateTime;
+import org.joda.time.ReadableInstant;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalCallContext;
@@ -36,6 +37,7 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
 import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
+import org.killbill.billing.catalog.api.CatalogEntity;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
@@ -44,7 +46,6 @@ import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
 import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
-import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.entitlement.api.Entitlement.EntitlementState;
@@ -52,13 +53,13 @@ import org.killbill.billing.subscription.alignment.PlanAligner;
 import org.killbill.billing.subscription.alignment.TimedPhase;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseApiService;
+import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
 import org.killbill.billing.subscription.api.svcs.DefaultPlanPhasePriceOverridesWithCallContext;
 import org.killbill.billing.subscription.engine.addon.AddonUtils;
 import org.killbill.billing.subscription.engine.dao.SubscriptionDao;
 import org.killbill.billing.subscription.events.SubscriptionBaseEvent;
 import org.killbill.billing.subscription.events.phase.PhaseEvent;
 import org.killbill.billing.subscription.events.phase.PhaseEventData;
-import org.killbill.billing.subscription.events.user.ApiEvent;
 import org.killbill.billing.subscription.events.user.ApiEventBuilder;
 import org.killbill.billing.subscription.events.user.ApiEventCancel;
 import org.killbill.billing.subscription.events.user.ApiEventChange;
@@ -70,7 +71,6 @@ import org.killbill.billing.util.callcontext.TenantContext;
 import org.killbill.clock.Clock;
 import org.killbill.clock.DefaultClock;
 
-import com.google.common.base.Function;
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -118,41 +118,34 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
     }
 
     @Override
-    public List<DefaultSubscriptionBaseWithAddOns> createPlansWithAddOns(final UUID accountId, final Iterable<SubscriptionAndAddOnsSpecifier> subscriptionsAndAddOns, final CallContext context) throws SubscriptionBaseApiException {
-        Map<UUID, List<SubscriptionBaseEvent>> eventsMap = new HashMap<UUID, List<SubscriptionBaseEvent>>();
-        List<List<DefaultSubscriptionBase>> subscriptionBaseAndAddOnsList = new ArrayList<List<DefaultSubscriptionBase>>();
-        List<DefaultSubscriptionBaseWithAddOns> allSubscriptions = new ArrayList<DefaultSubscriptionBaseWithAddOns>();
+    public List<SubscriptionBaseWithAddOns> createPlansWithAddOns(final UUID accountId, final Iterable<SubscriptionAndAddOnsSpecifier> subscriptionsAndAddOns, final CallContext context) throws SubscriptionBaseApiException {
+        final Map<UUID, List<SubscriptionBaseEvent>> eventsMap = new HashMap<UUID, List<SubscriptionBaseEvent>>();
+        final Collection<List<SubscriptionBase>> subscriptionBaseAndAddOnsList = new ArrayList<List<SubscriptionBase>>();
 
-        for (SubscriptionAndAddOnsSpecifier subscriptionAndAddOns : subscriptionsAndAddOns) {
-            List<DefaultSubscriptionBase> subscriptionBaseList = new ArrayList<DefaultSubscriptionBase>();
+        final List<SubscriptionBaseWithAddOns> allSubscriptions = new ArrayList<SubscriptionBaseWithAddOns>();
+        for (final SubscriptionAndAddOnsSpecifier subscriptionAndAddOns : subscriptionsAndAddOns) {
+            final List<SubscriptionBase> subscriptionBaseList = new ArrayList<SubscriptionBase>();
             createEvents(subscriptionAndAddOns.getSubscriptionSpecifiers(), context, eventsMap, subscriptionBaseList);
-
             subscriptionBaseAndAddOnsList.add(subscriptionBaseList);
 
-            DefaultSubscriptionBaseWithAddOns defaultSubscriptionBaseWithAddOns = new DefaultSubscriptionBaseWithAddOns(
-                    subscriptionAndAddOns.getBundleId(),
-                    ImmutableList.copyOf(Iterables.transform(subscriptionBaseList, new Function<DefaultSubscriptionBase, SubscriptionBase>() {
-                        @Override
-                        public SubscriptionBase apply(final DefaultSubscriptionBase input) {
-                            return input;
-                        }
-                    })),
-                    subscriptionAndAddOns.getEffectiveDate());
-            allSubscriptions.add(defaultSubscriptionBaseWithAddOns);
+            final SubscriptionBaseWithAddOns subscriptionBaseWithAddOns = new DefaultSubscriptionBaseWithAddOns(subscriptionAndAddOns.getBundleId(),
+                                                                                                                subscriptionBaseList,
+                                                                                                                subscriptionAndAddOns.getEffectiveDate());
+            allSubscriptions.add(subscriptionBaseWithAddOns);
         }
 
         final InternalCallContext internalCallContext = createCallContextFromAccountId(accountId, context);
         dao.createSubscriptionsWithAddOns(allSubscriptions, eventsMap, internalCallContext);
 
-        for (List<DefaultSubscriptionBase> subscriptions : subscriptionBaseAndAddOnsList) {
-            final DefaultSubscriptionBase baseSubscription = findBaseSubscription(subscriptions);
+        for (final List<SubscriptionBase> subscriptions : subscriptionBaseAndAddOnsList) {
+            final SubscriptionBase baseSubscription = findBaseSubscription(subscriptions);
             rebuildTransitions(internalCallContext, subscriptions, baseSubscription);
         }
         return allSubscriptions;
     }
 
-    private void createEvents(final Iterable<SubscriptionSpecifier> subscriptions, final CallContext context, final Map<UUID, List<SubscriptionBaseEvent>> eventsMap, final List<DefaultSubscriptionBase> subscriptionBaseList) throws SubscriptionBaseApiException {
-        for (SubscriptionSpecifier subscription : subscriptions) {
+    private void createEvents(final Iterable<SubscriptionSpecifier> subscriptions, final CallContext context, final Map<UUID, List<SubscriptionBaseEvent>> eventsMap, final Collection<SubscriptionBase> subscriptionBaseList) throws SubscriptionBaseApiException {
+        for (final SubscriptionSpecifier subscription : subscriptions) {
             try {
                 final DefaultSubscriptionBase subscriptionBase = new DefaultSubscriptionBase(subscription.getBuilder(), this, clock);
                 final InternalCallContext internalCallContext = createCallContextFromBundleId(subscriptionBase.getBundleId(), context);
@@ -162,32 +155,33 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                                                                                subscription.getEffectiveDate(), subscription.getProcessedDate(), internalCallContext);
                 eventsMap.put(subscriptionBase.getId(), events);
                 subscriptionBaseList.add(subscriptionBase);
-
             } catch (final CatalogApiException e) {
                 throw new SubscriptionBaseApiException(e);
             }
         }
     }
 
-    private void rebuildTransitions(final InternalCallContext internalCallContext, final List<DefaultSubscriptionBase> subscriptions, final DefaultSubscriptionBase baseSubscription) throws SubscriptionBaseApiException {
+    private void rebuildTransitions(final InternalCallContext internalCallContext, final Iterable<SubscriptionBase> subscriptions, final SubscriptionBase baseSubscription) throws SubscriptionBaseApiException {
         try {
-            baseSubscription.rebuildTransitions(dao.getEventsForSubscription(baseSubscription.getId(), internalCallContext),
-                                                catalogService.getFullCatalog(true, true, internalCallContext));
+            // Safe cast
+            ((DefaultSubscriptionBase) baseSubscription).rebuildTransitions(dao.getEventsForSubscription(baseSubscription.getId(), internalCallContext),
+                                                                            catalogService.getFullCatalog(true, true, internalCallContext));
 
-            for (final DefaultSubscriptionBase input : subscriptions) {
+            for (final SubscriptionBase input : subscriptions) {
                 if (input.getId().equals(baseSubscription.getId())) {
                     continue;
                 }
 
-                input.rebuildTransitions(dao.getEventsForSubscription(input.getId(), internalCallContext),
-                                         catalogService.getFullCatalog(true, true, internalCallContext));
+                // Safe cast
+                ((DefaultSubscriptionBase) input).rebuildTransitions(dao.getEventsForSubscription(input.getId(), internalCallContext),
+                                                                     catalogService.getFullCatalog(true, true, internalCallContext));
             }
-        } catch (CatalogApiException e) {
+        } catch (final CatalogApiException e) {
             throw new SubscriptionBaseApiException(e);
         }
     }
 
-    private DefaultSubscriptionBase findBaseSubscription(final List<DefaultSubscriptionBase> subscriptionBaseList) {
+    private SubscriptionBase findBaseSubscription(final Iterable<SubscriptionBase> subscriptionBaseList) {
         return Iterables.tryFind(subscriptionBaseList, new Predicate<SubscriptionBase>() {
             @Override
             public boolean apply(final SubscriptionBase subscription) {
@@ -400,7 +394,6 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
             final InternalTenantContext internalCallContext = createTenantContextFromBundleId(subscription.getBundleId(), context);
             final Plan currentPlan = subscription.getCurrentPlan();
 
-            final PriceList currentPriceList = subscription.getCurrentPriceList();
             final PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getName(),
                                                                             subscription.getCurrentPhase().getPhaseType());
             planChangeResult = catalogService.getFullCatalog(true, true, internalCallContext).planChange(fromPlanPhase, toPlanPhase, effectiveDate);
@@ -459,7 +452,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                 .setEventPriceList(realPriceList)
                 .setEffectiveDate(effectiveDate)
                 .setFromDisk(true);
-        final ApiEvent creationEvent = new ApiEventCreate(createBuilder);
+        final SubscriptionBaseEvent creationEvent = new ApiEventCreate(createBuilder);
 
         final TimedPhase nextTimedPhase = curAndNextPhases[1];
         final PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
@@ -478,7 +471,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                                                              final String newPriceList, final DateTime effectiveDate, final DateTime processedDate,
                                                              final boolean addCancellationAddOnForEventsIfRequired, final InternalTenantContext internalTenantContext) throws CatalogApiException, SubscriptionBaseApiException {
         final Collection<DefaultSubscriptionBase> addOnSubscriptionsToBeCancelled = new ArrayList<DefaultSubscriptionBase>();
-        final List<SubscriptionBaseEvent> addOnCancelEvents = new ArrayList<SubscriptionBaseEvent>();
+        final Collection<SubscriptionBaseEvent> addOnCancelEvents = new ArrayList<SubscriptionBaseEvent>();
 
         final List<SubscriptionBaseEvent> changeEvents = getEventsOnChangePlan(subscription, newPlan, newPriceList, effectiveDate, addCancellationAddOnForEventsIfRequired, addOnSubscriptionsToBeCancelled, addOnCancelEvents, internalTenantContext);
         changeEvents.addAll(addOnCancelEvents);
@@ -489,7 +482,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
                                                               final String newPriceList, final DateTime effectiveDate,
                                                               final boolean addCancellationAddOnForEventsIfRequired,
                                                               final Collection<DefaultSubscriptionBase> addOnSubscriptionsToBeCancelled,
-                                                              final List<SubscriptionBaseEvent> addOnCancelEvents,
+                                                              final Collection<SubscriptionBaseEvent> addOnCancelEvents,
                                                               final InternalTenantContext internalTenantContext) throws CatalogApiException, SubscriptionBaseApiException {
         final TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList, effectiveDate, internalTenantContext);
 
@@ -550,7 +543,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         return subscriptionsToBeCancelled.size();
     }
 
-    private List<DefaultSubscriptionBase> computeAddOnsToCancel(final List<SubscriptionBaseEvent> cancelEvents, final Product baseProduct, final UUID bundleId, final DateTime effectiveDate, final InternalCallContext internalCallContext) throws CatalogApiException {
+    private List<DefaultSubscriptionBase> computeAddOnsToCancel(final Collection<SubscriptionBaseEvent> cancelEvents, final CatalogEntity baseProduct, final UUID bundleId, final DateTime effectiveDate, final InternalCallContext internalCallContext) throws CatalogApiException {
         // If cancellation/change occur in the future, there is nothing to do
         final DateTime now = clock.getUTCNow();
         if (effectiveDate.compareTo(now) > 0) {
@@ -560,7 +553,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         }
     }
 
-    private List<DefaultSubscriptionBase> addCancellationAddOnForEventsIfRequired(final List<SubscriptionBaseEvent> events, final Product baseProduct, final UUID bundleId,
+    private List<DefaultSubscriptionBase> addCancellationAddOnForEventsIfRequired(final Collection<SubscriptionBaseEvent> events, final CatalogEntity baseProduct, final UUID bundleId,
                                                                                   final DateTime effectiveDate, final InternalTenantContext internalTenantContext) throws CatalogApiException {
 
         final List<DefaultSubscriptionBase> subscriptionsToBeCancelled = new ArrayList<DefaultSubscriptionBase>();
@@ -592,7 +585,7 @@ public class DefaultSubscriptionBaseApiService implements SubscriptionBaseApiSer
         return subscriptionsToBeCancelled;
     }
 
-    private void validateEffectiveDate(final DefaultSubscriptionBase subscription, final DateTime effectiveDate) throws SubscriptionBaseApiException {
+    private void validateEffectiveDate(final SubscriptionBase subscription, final ReadableInstant effectiveDate) throws SubscriptionBaseApiException {
 
         final SubscriptionBaseTransition previousTransition = subscription.getPreviousTransition();
 
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
index 81df1b3..9c842a1 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/DefaultSubscriptionDao.java
@@ -46,6 +46,7 @@ import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.entity.EntityPersistenceException;
 import org.killbill.billing.subscription.api.SubscriptionBase;
 import org.killbill.billing.subscription.api.SubscriptionBaseTransitionType;
+import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
 import org.killbill.billing.subscription.api.svcs.DefaultSubscriptionInternalApi;
 import org.killbill.billing.subscription.api.transfer.BundleTransferData;
 import org.killbill.billing.subscription.api.transfer.SubscriptionTransferData;
@@ -80,9 +81,11 @@ import org.killbill.billing.subscription.exceptions.SubscriptionBaseError;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.dao.NonEntityDao;
+import org.killbill.billing.util.entity.Entity;
 import org.killbill.billing.util.entity.Pagination;
 import org.killbill.billing.util.entity.dao.DefaultPaginationSqlDaoHelper.PaginationIteratorBuilder;
 import org.killbill.billing.util.entity.dao.EntityDaoBase;
+import org.killbill.billing.util.entity.dao.EntitySqlDao;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoTransactionalJdbiWrapper;
 import org.killbill.billing.util.entity.dao.EntitySqlDaoWrapperFactory;
@@ -339,12 +342,6 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         for (final UUID bundleId : subscriptionsFromAccountId.keySet()) {
 
             final List<SubscriptionBase> subscriptionsForBundle = subscriptionsFromAccountId.get(bundleId);
-            final Collection<UUID> subscriptionIdsForBundle = Collections2.transform(subscriptionsForBundle, new Function<SubscriptionBase, UUID>() {
-                @Override
-                public UUID apply(final SubscriptionBase input) {
-                    return input.getId();
-                }
-            });
             final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscriptions = ArrayListMultimap.create();
 
             for (final SubscriptionBase cur : subscriptionsForBundle) {
@@ -434,7 +431,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         return transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<SubscriptionBaseEvent>() {
             @Override
             public SubscriptionBaseEvent inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
-                SubscriptionEventModelDao model = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getById(eventId.toString(), context);
+                final SubscriptionEventModelDao model = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getById(eventId.toString(), context);
                 return SubscriptionEventModelDao.toSubscriptionEvent(model);
             }
         });
@@ -477,13 +474,12 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
             @Override
             public List<SubscriptionBaseEvent> inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 final List<SubscriptionEventModelDao> eventModels = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class).getFutureActiveEventForSubscription(subscriptionId.toString(), now, context);
-                final List<SubscriptionBaseEvent> events = new ArrayList<SubscriptionBaseEvent>(Collections2.transform(eventModels, new Function<SubscriptionEventModelDao, SubscriptionBaseEvent>() {
+                return new ArrayList<SubscriptionBaseEvent>(Collections2.transform(eventModels, new Function<SubscriptionEventModelDao, SubscriptionBaseEvent>() {
                     @Override
                     public SubscriptionBaseEvent apply(@Nullable final SubscriptionEventModelDao input) {
                         return SubscriptionEventModelDao.toSubscriptionEvent(input);
                     }
                 }));
-                return events;
             }
         });
     }
@@ -505,7 +501,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
 
                 }
                 // Notify the Bus of the latest requested change, if needed
-                if (initialEvents.size() > 0) {
+                if (!initialEvents.isEmpty()) {
                     notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscription, initialEvents.get(initialEvents.size() - 1), SubscriptionBaseTransitionType.CREATE, context);
                 }
                 return null;
@@ -514,33 +510,31 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     }
 
     @Override
-    public void createSubscriptionsWithAddOns(final List<DefaultSubscriptionBaseWithAddOns> subscriptions, final Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, final InternalCallContext context) {
+    public void createSubscriptionsWithAddOns(final List<SubscriptionBaseWithAddOns> subscriptions, final Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, final InternalCallContext context) {
         transactionalSqlDao.execute(new EntitySqlDaoTransactionWrapper<Void>() {
             @Override
             public Void inTransaction(final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory) throws Exception {
                 final SubscriptionSqlDao transactional = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
                 final SubscriptionEventSqlDao eventsDaoFromSameTransaction = entitySqlDaoWrapperFactory.become(SubscriptionEventSqlDao.class);
-                for (DefaultSubscriptionBaseWithAddOns subscription : subscriptions) {
-                    List<DefaultSubscriptionBase> currentSubscriptionBaseList = ImmutableList.copyOf(Iterables.transform(subscription.getSubscriptionBaseList(), new Function<SubscriptionBase, DefaultSubscriptionBase>() {
-                        @Override
-                        public DefaultSubscriptionBase apply(final SubscriptionBase input) {
-                            return (DefaultSubscriptionBase) input;
-                        }
-                    }));
-                    for (DefaultSubscriptionBase subscriptionBase : currentSubscriptionBaseList) {
-                        transactional.create(new SubscriptionModelDao(subscriptionBase), context);
 
-                        final List<SubscriptionBaseEvent> initialEvents = initialEventsMap.get(subscriptionBase.getId());
+                for (final SubscriptionBaseWithAddOns subscription : subscriptions) {
+                    for (final SubscriptionBase subscriptionBase : subscription.getSubscriptionBaseList()) {
+                        // Safe cast
+                        final DefaultSubscriptionBase defaultSubscriptionBase = (DefaultSubscriptionBase) subscriptionBase;
+
+                        transactional.create(new SubscriptionModelDao(defaultSubscriptionBase), context);
+
+                        final List<SubscriptionBaseEvent> initialEvents = initialEventsMap.get(defaultSubscriptionBase.getId());
                         for (final SubscriptionBaseEvent cur : initialEvents) {
                             eventsDaoFromSameTransaction.create(new SubscriptionEventModelDao(cur), context);
 
                             final boolean isBusEvent = cur.getEffectiveDate().compareTo(clock.getUTCNow()) <= 0 && (cur.getType() == EventType.API_USER);
-                            recordBusOrFutureNotificationFromTransaction(subscriptionBase, cur, entitySqlDaoWrapperFactory, isBusEvent, 0, context);
-
+                            recordBusOrFutureNotificationFromTransaction(defaultSubscriptionBase, cur, entitySqlDaoWrapperFactory, isBusEvent, 0, context);
                         }
+
                         // Notify the Bus of the latest requested change, if needed
-                        if (initialEvents.size() > 0) {
-                            notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, subscriptionBase, initialEvents.get(initialEvents.size() - 1), SubscriptionBaseTransitionType.CREATE, context);
+                        if (!initialEvents.isEmpty()) {
+                            notifyBusOfRequestedChange(entitySqlDaoWrapperFactory, defaultSubscriptionBase, initialEvents.get(initialEvents.size() - 1), SubscriptionBaseTransitionType.CREATE, context);
                         }
                     }
                 }
@@ -653,7 +647,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         });
     }
 
-    private List<SubscriptionBaseEvent> filterSubscriptionBaseEvents(final List<SubscriptionEventModelDao> models) {
+    private List<SubscriptionBaseEvent> filterSubscriptionBaseEvents(final Collection<SubscriptionEventModelDao> models) {
         final Collection<SubscriptionEventModelDao> filteredModels = Collections2.filter(models, new Predicate<SubscriptionEventModelDao>() {
             @Override
             public boolean apply(@Nullable final SubscriptionEventModelDao input) {
@@ -738,7 +732,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         return futureEvent;
     }
 
-    private void unactivateEventFromTransaction(final SubscriptionEventModelDao event, final EntitySqlDaoWrapperFactory dao, final InternalCallContext context) {
+    private void unactivateEventFromTransaction(final Entity event, final EntitySqlDaoWrapperFactory dao, final InternalCallContext context) {
         if (event != null) {
             final String eventId = event.getId().toString();
             dao.become(SubscriptionEventSqlDao.class).unactiveEvent(eventId, context);
@@ -774,15 +768,15 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     }
 
     private List<SubscriptionBase> buildBundleSubscriptions(final List<SubscriptionBase> input, @Nullable final Multimap<UUID, SubscriptionBaseEvent> eventsForSubscription,
-                                                            @Nullable List<SubscriptionBaseEvent> dryRunEvents, final InternalTenantContext context) throws CatalogApiException {
-        if (input == null || input.size() == 0) {
+                                                            @Nullable final Collection<SubscriptionBaseEvent> dryRunEvents, final InternalTenantContext context) throws CatalogApiException {
+        if (input == null || input.isEmpty()) {
             return Collections.emptyList();
         }
 
         // Make sure BasePlan -- if exists-- is first
         Collections.sort(input, DefaultSubscriptionInternalApi.SUBSCRIPTIONS_COMPARATOR);
 
-        final List<ApiEventChange> baseChangeEvents = new LinkedList<ApiEventChange>();
+        final Collection<ApiEventChange> baseChangeEvents = new LinkedList<ApiEventChange>();
         ApiEventCancel baseCancellationEvent = null;
         final List<SubscriptionBase> result = new ArrayList<SubscriptionBase>(input.size());
         for (final SubscriptionBase cur : input) {
@@ -856,11 +850,11 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         return result;
     }
 
-    private void mergeDryRunEvents(final UUID subscriptionId, final List<SubscriptionBaseEvent> events, @Nullable List<SubscriptionBaseEvent> dryRunEvents) {
+    private void mergeDryRunEvents(final UUID subscriptionId, final List<SubscriptionBaseEvent> events, @Nullable final Collection<SubscriptionBaseEvent> dryRunEvents) {
         if (dryRunEvents == null || dryRunEvents.isEmpty()) {
             return;
         }
-        for (SubscriptionBaseEvent curDryRun : dryRunEvents) {
+        for (final SubscriptionBaseEvent curDryRun : dryRunEvents) {
             if (curDryRun.getSubscriptionId() != null && curDryRun.getSubscriptionId().equals(subscriptionId)) {
 
                 //boolean inserted = false;
@@ -951,9 +945,9 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
 
     }
 
-    private DefaultSubscriptionBase createSubscriptionForInternalUse(final SubscriptionBase shellSubscription, final List<SubscriptionBaseEvent> events, final InternalTenantContext context) throws CatalogApiException {
+    private SubscriptionBase createSubscriptionForInternalUse(final SubscriptionBase shellSubscription, final List<SubscriptionBaseEvent> events, final InternalTenantContext context) throws CatalogApiException {
         final DefaultSubscriptionBase result = new DefaultSubscriptionBase(new SubscriptionBuilder(((DefaultSubscriptionBase) shellSubscription)), null, clock);
-        if (events.size() > 0) {
+        if (!events.isEmpty()) {
             final Catalog fullCatalog = catalogService.getFullCatalog(true, true, context);
             result.rebuildTransitions(events, fullCatalog);
         }
@@ -1029,14 +1023,14 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
             final NotificationQueue subscriptionEventQueue = notificationQueueService.getNotificationQueue(DefaultSubscriptionBaseService.SUBSCRIPTION_SERVICE_NAME,
                                                                                                            DefaultSubscriptionBaseService.NOTIFICATION_QUEUE_NAME);
             subscriptionEventQueue.recordFutureNotificationFromTransaction(entitySqlDaoWrapperFactory.getHandle().getConnection(), effectiveDate, notificationKey, context.getUserToken(), context.getAccountRecordId(), context.getTenantRecordId());
-        } catch (NoSuchNotificationQueue e) {
+        } catch (final NoSuchNotificationQueue e) {
             throw new RuntimeException(e);
-        } catch (IOException e) {
+        } catch (final IOException e) {
             throw new RuntimeException(e);
         }
     }
 
-    private void transferBundleDataFromTransaction(final BundleTransferData bundleTransferData, final SubscriptionEventSqlDao transactional,
+    private void transferBundleDataFromTransaction(final BundleTransferData bundleTransferData, final EntitySqlDao transactional,
                                                    final EntitySqlDaoWrapperFactory entitySqlDaoWrapperFactory, final InternalCallContext context) throws EntityPersistenceException {
 
         final SubscriptionSqlDao transSubDao = entitySqlDaoWrapperFactory.become(SubscriptionSqlDao.class);
@@ -1045,7 +1039,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
         final DefaultSubscriptionBaseBundle bundleData = bundleTransferData.getData();
 
         final List<SubscriptionBundleModelDao> existingBundleModels = transBundleDao.getBundlesFromAccountAndKey(bundleData.getAccountId().toString(), bundleData.getExternalKey(), context);
-        if (existingBundleModels.size() != 0) {
+        if (!existingBundleModels.isEmpty()) {
             log.warn("Bundle already exists for accountId='{}', bundleExternalKey='{}'", bundleData.getAccountId(), bundleData.getExternalKey());
             return;
         }
@@ -1072,7 +1066,7 @@ public class DefaultSubscriptionDao extends EntityDaoBase<SubscriptionBundleMode
     //
     // Creates a copy of the existing subscriptions whose 'transitions' will reflect the new event
     //
-    private DefaultSubscriptionBase createSubscriptionWithNewEvent(final DefaultSubscriptionBase subscription, SubscriptionBaseEvent newEvent, final InternalTenantContext context) throws CatalogApiException {
+    private DefaultSubscriptionBase createSubscriptionWithNewEvent(final DefaultSubscriptionBase subscription, final SubscriptionBaseEvent newEvent, final InternalTenantContext context) throws CatalogApiException {
 
         final DefaultSubscriptionBase subscriptionWithNewEvent = new DefaultSubscriptionBase(subscription, null, clock);
         final List<SubscriptionBaseEvent> allEvents = new LinkedList<SubscriptionBaseEvent>();
diff --git a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
index 7c5743d..3430d2e 100644
--- a/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
+++ b/subscription/src/main/java/org/killbill/billing/subscription/engine/dao/SubscriptionDao.java
@@ -25,6 +25,7 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.subscription.api.SubscriptionBase;
+import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
 import org.killbill.billing.subscription.api.transfer.BundleTransferData;
 import org.killbill.billing.subscription.api.transfer.TransferCancelData;
 import org.killbill.billing.subscription.api.user.DefaultSubscriptionBase;
@@ -82,7 +83,7 @@ public interface SubscriptionDao extends EntityDao<SubscriptionBundleModelDao, S
     // SubscriptionBase creation, cancellation, changePlanWithRequestedDate apis
     public void createSubscription(DefaultSubscriptionBase subscription, List<SubscriptionBaseEvent> initialEvents, InternalCallContext context);
 
-    public void createSubscriptionsWithAddOns(List<DefaultSubscriptionBaseWithAddOns> subscriptions, Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, InternalCallContext context);
+    public void createSubscriptionsWithAddOns(List<SubscriptionBaseWithAddOns> subscriptions, Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap, InternalCallContext context);
 
     public void cancelSubscriptionsOnBasePlanEvent(DefaultSubscriptionBase subscription, SubscriptionBaseEvent event, List<DefaultSubscriptionBase> subscriptions, List<SubscriptionBaseEvent> cancelEvents, InternalCallContext context);
 
diff --git a/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql b/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql
index 80abc65..968b006 100644
--- a/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql
+++ b/subscription/src/main/resources/org/killbill/billing/subscription/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS subscription_events;
 CREATE TABLE subscription_events (
diff --git a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
index d2306f1..8fc4b4b 100644
--- a/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
+++ b/subscription/src/test/java/org/killbill/billing/subscription/engine/dao/MockSubscriptionDaoMemory.java
@@ -38,6 +38,7 @@ import org.killbill.billing.catalog.api.TimeUnit;
 import org.killbill.billing.dao.MockNonEntityDao;
 import org.killbill.billing.entitlement.api.SubscriptionApiException;
 import org.killbill.billing.subscription.api.SubscriptionBase;
+import org.killbill.billing.subscription.api.SubscriptionBaseWithAddOns;
 import org.killbill.billing.subscription.api.transfer.BundleTransferData;
 import org.killbill.billing.subscription.api.transfer.TransferCancelData;
 import org.killbill.billing.subscription.api.user.DefaultEffectiveSubscriptionEvent;
@@ -224,24 +225,19 @@ public class MockSubscriptionDaoMemory extends MockEntityDaoBase<SubscriptionBun
         mockNonEntityDao.addTenantRecordIdMapping(updatedSubscription.getId(), context);
     }
 
-    public void createSubscriptionsWithAddOns(final List<DefaultSubscriptionBaseWithAddOns> subscriptions,
+    @Override
+    public void createSubscriptionsWithAddOns(final List<SubscriptionBaseWithAddOns> subscriptions,
                                               final Map<UUID, List<SubscriptionBaseEvent>> initialEventsMap,
                                               final InternalCallContext context) {
         synchronized (events) {
-            for (DefaultSubscriptionBaseWithAddOns subscription : subscriptions) {
-                List<DefaultSubscriptionBase> currentSubscriptionBaseList = ImmutableList.copyOf(Iterables.transform(subscription.getSubscriptionBaseList(), new Function<SubscriptionBase, DefaultSubscriptionBase>() {
-                    @Override
-                    public DefaultSubscriptionBase apply(final SubscriptionBase input) {
-                        return (DefaultSubscriptionBase) input;
-                    }
-                }));
-                for (DefaultSubscriptionBase subscriptionBase : currentSubscriptionBaseList) {
+            for (final SubscriptionBaseWithAddOns subscription : subscriptions) {
+                for (final SubscriptionBase subscriptionBase : subscription.getSubscriptionBaseList()) {
                     final List<SubscriptionBaseEvent> initialEvents = initialEventsMap.get(subscriptionBase.getId());
                     events.addAll(initialEvents);
                     for (final SubscriptionBaseEvent cur : initialEvents) {
                         recordFutureNotificationFromTransaction(null, cur.getEffectiveDate(), new SubscriptionNotificationKey(cur.getId()), context);
                     }
-                    final SubscriptionBase updatedSubscription = buildSubscription(subscriptionBase, context);
+                    final SubscriptionBase updatedSubscription = buildSubscription((DefaultSubscriptionBase) subscriptionBase, context);
                     this.subscriptions.add(updatedSubscription);
                     mockNonEntityDao.addTenantRecordIdMapping(updatedSubscription.getId(), context);
                 }
diff --git a/tenant/src/main/resources/org/killbill/billing/tenant/ddl.sql b/tenant/src/main/resources/org/killbill/billing/tenant/ddl.sql
index 988714f..152521c 100644
--- a/tenant/src/main/resources/org/killbill/billing/tenant/ddl.sql
+++ b/tenant/src/main/resources/org/killbill/billing/tenant/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS tenants;
 CREATE TABLE tenants (
diff --git a/usage/src/main/resources/org/killbill/billing/usage/ddl.sql b/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
index 12940eb..fbb27b1 100644
--- a/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
+++ b/usage/src/main/resources/org/killbill/billing/usage/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS rolled_up_usage;
 CREATE TABLE rolled_up_usage (
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
index ac29706..41a05eb 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionDao.java
@@ -20,14 +20,14 @@ package org.killbill.billing.util.security.shiro.dao;
 
 import java.io.IOException;
 import java.io.Serializable;
+import java.util.UUID;
 
 import javax.inject.Inject;
 
 import org.apache.shiro.session.Session;
 import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
+import org.killbill.billing.util.UUIDs;
 import org.skife.jdbi.v2.IDBI;
-import org.skife.jdbi.v2.Transaction;
-import org.skife.jdbi.v2.TransactionStatus;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,15 +54,10 @@ public class JDBCSessionDao extends CachingSessionDAO {
 
     @Override
     protected Serializable doCreate(final Session session) {
-        final Serializable sessionId = jdbcSessionSqlDao.inTransaction(new Transaction<Long, JDBCSessionSqlDao>() {
-            @Override
-            public Long inTransaction(final JDBCSessionSqlDao transactional, final TransactionStatus status) throws Exception {
-                transactional.create(new SessionModelDao(session));
-                return transactional.getLastInsertId();
-            }
-        });
+        final UUID sessionId = UUIDs.randomUUID();
         // See SessionModelDao#toSimpleSession for why we use toString()
         assignSessionId(session, sessionId.toString());
+        jdbcSessionSqlDao.create(new SessionModelDao(session));
         return sessionId;
     }
 
@@ -73,15 +68,9 @@ public class JDBCSessionDao extends CachingSessionDAO {
             return null;
         }
 
-        // Ignore unsupported JSESSIONID cookies
-        final Long recordId;
-        try {
-            recordId = Long.parseLong(sessionId.toString().trim());
-        } catch (final NumberFormatException e) {
-            return null;
-        }
+        final String sessionIdString = sessionId.toString();
+        final SessionModelDao sessionModelDao = jdbcSessionSqlDao.read(sessionIdString);
 
-        final SessionModelDao sessionModelDao = jdbcSessionSqlDao.read(recordId);
         if (sessionModelDao == null) {
             return null;
         }
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
index f8d9322..f277430 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.java
@@ -29,7 +29,7 @@ import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
 public interface JDBCSessionSqlDao extends Transactional<JDBCSessionSqlDao> {
 
     @SqlQuery
-    public SessionModelDao read(@Bind("recordId") final Long sessionId);
+    public SessionModelDao read(@Bind("id") final String sessionId);
 
     @SqlUpdate
     public void create(@SmartBindBean final SessionModelDao sessionModelDao);
@@ -40,6 +40,4 @@ public interface JDBCSessionSqlDao extends Transactional<JDBCSessionSqlDao> {
     @SqlUpdate
     public void delete(@SmartBindBean final SessionModelDao sessionModelDao);
 
-    @SqlQuery
-    public Long getLastInsertId();
 }
diff --git a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/SessionModelDao.java b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/SessionModelDao.java
index acfd28a..46341cb 100644
--- a/util/src/main/java/org/killbill/billing/util/security/shiro/dao/SessionModelDao.java
+++ b/util/src/main/java/org/killbill/billing/util/security/shiro/dao/SessionModelDao.java
@@ -33,6 +33,7 @@ public class SessionModelDao {
     private final Serializer<Map> serializer = new DefaultSerializer<Map>();
 
     private Long recordId;
+    private String id;
     private DateTime startTimestamp;
     private DateTime lastAccessTime;
     private long timeout;
@@ -42,7 +43,7 @@ public class SessionModelDao {
     public SessionModelDao() { /* For the DAO mapper */ }
 
     public SessionModelDao(final Session session) {
-        this.recordId = session.getId() == null ? null : Long.valueOf(session.getId().toString());
+        this.id = session.getId() == null ? null : session.getId().toString();
         this.startTimestamp = new DateTime(session.getStartTimestamp(), DateTimeZone.UTC);
         this.lastAccessTime = new DateTime(session.getLastAccessTime(), DateTimeZone.UTC);
         this.timeout = session.getTimeout();
@@ -56,11 +57,11 @@ public class SessionModelDao {
 
     public Session toSimpleSession() throws IOException {
         final SimpleSession simpleSession = new SimpleSession();
-        if (recordId != null) {
+        if (id != null) {
             // Make sure to use a String here! It will be used as-is as the key in Ehcache.
-            // When retrieving the session, the sessionId will be a String: if a Long is used,
-            // the lookup will trigger a miss. See https://github.com/killbill/killbill/issues/299
-            simpleSession.setId(recordId.toString());
+            // When retrieving the session, the sessionId will be a String
+            // See https://github.com/killbill/killbill/issues/299
+            simpleSession.setId(id);
         }
         simpleSession.setStartTimestamp(startTimestamp.toDate());
         simpleSession.setLastAccessTime(lastAccessTime.toDate());
@@ -78,6 +79,10 @@ public class SessionModelDao {
         return recordId;
     }
 
+    public String getId() {
+        return id;
+    }
+
     public DateTime getStartTimestamp() {
         return startTimestamp;
     }
@@ -102,6 +107,7 @@ public class SessionModelDao {
     public String toString() {
         final StringBuilder sb = new StringBuilder("SessionModelDao{");
         sb.append("recordId=").append(recordId);
+        sb.append(", id='").append(id).append('\'');
         sb.append(", startTimestamp=").append(startTimestamp);
         sb.append(", lastAccessTime=").append(lastAccessTime);
         sb.append(", timeout=").append(timeout);
@@ -125,33 +131,34 @@ public class SessionModelDao {
         if (timeout != that.timeout) {
             return false;
         }
-        if (host != null ? !host.equals(that.host) : that.host != null) {
+        if (recordId != null ? !recordId.equals(that.recordId) : that.recordId != null) {
             return false;
         }
-        if (lastAccessTime != null ? !lastAccessTime.equals(that.lastAccessTime) : that.lastAccessTime != null) {
+        if (id != null ? !id.equals(that.id) : that.id != null) {
             return false;
         }
-        if (recordId != null ? !recordId.equals(that.recordId) : that.recordId != null) {
+        if (startTimestamp != null ? !startTimestamp.equals(that.startTimestamp) : that.startTimestamp != null) {
             return false;
         }
-        if (!Arrays.equals(sessionData, that.sessionData)) {
+        if (lastAccessTime != null ? !lastAccessTime.equals(that.lastAccessTime) : that.lastAccessTime != null) {
             return false;
         }
-        if (startTimestamp != null ? !startTimestamp.equals(that.startTimestamp) : that.startTimestamp != null) {
+        if (host != null ? !host.equals(that.host) : that.host != null) {
             return false;
         }
+        return Arrays.equals(sessionData, that.sessionData);
 
-        return true;
     }
 
     @Override
     public int hashCode() {
         int result = recordId != null ? recordId.hashCode() : 0;
+        result = 31 * result + (id != null ? id.hashCode() : 0);
         result = 31 * result + (startTimestamp != null ? startTimestamp.hashCode() : 0);
         result = 31 * result + (lastAccessTime != null ? lastAccessTime.hashCode() : 0);
         result = 31 * result + (int) (timeout ^ (timeout >>> 32));
         result = 31 * result + (host != null ? host.hashCode() : 0);
-        result = 31 * result + (sessionData != null ? Arrays.hashCode(sessionData) : 0);
+        result = 31 * result + Arrays.hashCode(sessionData);
         return result;
     }
 
diff --git a/util/src/main/resources/org/killbill/billing/util/ddl.sql b/util/src/main/resources/org/killbill/billing/util/ddl.sql
index 7cd9a43..8e21ad6 100644
--- a/util/src/main/resources/org/killbill/billing/util/ddl.sql
+++ b/util/src/main/resources/org/killbill/billing/util/ddl.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS custom_fields;
 CREATE TABLE custom_fields (
@@ -235,6 +235,7 @@ CREATE TABLE bus_events_history (
 drop table if exists sessions;
 create table sessions (
   record_id serial unique
+, id varchar(36) NOT NULL
 , start_timestamp datetime not null
 , last_access_time datetime default null
 , timeout int
@@ -242,6 +243,7 @@ create table sessions (
 , session_data mediumblob default null
 , primary key(record_id)
 ) /*! CHARACTER SET utf8 COLLATE utf8_bin */;
+CREATE UNIQUE INDEX sessions_id ON sessions(id);
 
 
 DROP TABLE IF EXISTS users;
diff --git a/util/src/main/resources/org/killbill/billing/util/migration/V20161105000000__session_id.sql b/util/src/main/resources/org/killbill/billing/util/migration/V20161105000000__session_id.sql
new file mode 100644
index 0000000..cce5c63
--- /dev/null
+++ b/util/src/main/resources/org/killbill/billing/util/migration/V20161105000000__session_id.sql
@@ -0,0 +1,3 @@
+alter table sessions add column id varchar(36) not null after record_id;
+update sessions set id = record_id;
+create unique index sessions_id on sessions(id);
\ No newline at end of file
diff --git a/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg b/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
index fe228d2..b002312 100644
--- a/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
+++ b/util/src/main/resources/org/killbill/billing/util/security/shiro/dao/JDBCSessionSqlDao.sql.stg
@@ -3,25 +3,28 @@ group JDBCSessionSqlDao;
 read() ::= <<
 select
   record_id
+, id
 , start_timestamp
 , last_access_time
 , timeout
 , host
 , session_data
 from sessions
-where record_id = :recordId
+where id = :id
 ;
 >>
 
 create() ::= <<
 insert into sessions (
-  start_timestamp
+  id
+, start_timestamp
 , last_access_time
 , timeout
 , host
 , session_data
 ) values (
-  :startTimestamp
+  :id
+, :startTimestamp
 , :lastAccessTime
 , :timeout
 , :host
@@ -36,16 +39,12 @@ update sessions set
 , timeout = :timeout
 , host = :host
 , session_data = :sessionData
-where record_id = :recordId
+where id = :id
 ;
 >>
 
 delete() ::= <<
 delete from sessions
-where record_id = :recordId
+where id = :id
 ;
->>
-
-getLastInsertId() ::= <<
-select LAST_INSERT_ID();
->>
+>>
\ No newline at end of file
diff --git a/util/src/test/resources/org/killbill/billing/util/ddl_test.sql b/util/src/test/resources/org/killbill/billing/util/ddl_test.sql
index 6269d08..39e4b29 100644
--- a/util/src/test/resources/org/killbill/billing/util/ddl_test.sql
+++ b/util/src/test/resources/org/killbill/billing/util/ddl_test.sql
@@ -1,4 +1,4 @@
-/*! SET storage_engine=INNODB */;
+/*! SET default_storage_engine=INNODB */;
 
 DROP TABLE IF EXISTS dummy;
 CREATE TABLE dummy (