killbill-memoizeit

Changes

Details

diff --git a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
index 25cef98..95fbe01 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.analytics;
 
+import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
 import com.ning.billing.account.api.IAccount;
 import com.ning.billing.account.api.IAccountUserApi;
@@ -24,14 +25,15 @@ import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
 import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
 import com.ning.billing.entitlement.api.user.ISubscriptionTransition;
+import com.ning.billing.util.eventbus.IEventBus;
+
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.List;
 
-// STEPH fid with Pierre
-public class AnalyticsListener /* implements IApiListener */
+public class AnalyticsListener
 {
     private static final Logger log = LoggerFactory.getLogger(AnalyticsListener.class);
 
@@ -40,49 +42,72 @@ public class AnalyticsListener /* implements IApiListener */
     private final IAccountUserApi accountApi;
 
     @Inject
-    public AnalyticsListener(final BusinessSubscriptionTransitionDao dao, final IEntitlementUserApi entitlementApi, final IAccountUserApi accountApi)
+    public AnalyticsListener(final BusinessSubscriptionTransitionDao dao, final IEntitlementUserApi entitlementApi, final IAccountUserApi accountApi, final IEventBus eventBus)
     {
         this.dao = dao;
         this.entitlementApi = entitlementApi;
         this.accountApi = accountApi;
     }
 
-    @Override
+    @Subscribe
+    public void handleNotificationChange(ISubscriptionTransition event) {
+        switch (event.getTransitionType()) {
+        case CREATE:
+            subscriptionCreated(event);
+            break;
+        case CANCEL:
+            subscriptionCancelled(event);
+            break;
+        case CHANGE:
+            subscriptionChanged(event);
+            break;
+        case PAUSE:
+            subscriptionPaused(event);
+            break;
+        case RESUME:
+            subscriptionResumed(event);
+            break;
+        case UNCANCEL:
+            break;
+        case PHASE:
+            subscriptionPhaseChanged(event);
+            break;
+        default:
+            throw new RuntimeException("Unexpected event type " + event.getRequestedTransitionTime());
+        }
+    }
+
+
     public void subscriptionCreated(final ISubscriptionTransition created)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
         recordTransition(event, created);
     }
 
-    @Override
     public void subscriptionCancelled(final ISubscriptionTransition cancelled)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
         recordTransition(event, cancelled);
     }
 
-    @Override
     public void subscriptionChanged(final ISubscriptionTransition changed)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
         recordTransition(event, changed);
     }
 
-    @Override
     public void subscriptionPaused(final ISubscriptionTransition paused)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
         recordTransition(event, paused);
     }
 
-    @Override
     public void subscriptionResumed(final ISubscriptionTransition resumed)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
         recordTransition(event, resumed);
     }
 
-    @Override
     public void subscriptionPhaseChanged(final ISubscriptionTransition phaseChanged)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java b/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java
index 071936d..4547119 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java
@@ -18,7 +18,6 @@ package com.ning.billing.entitlement.api;
 
 import com.ning.billing.entitlement.api.billing.IEntitlementBillingApi;
 import com.ning.billing.entitlement.api.test.IEntitlementTestApi;
-import com.ning.billing.entitlement.api.user.IApiListener;
 import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
 import com.ning.billing.lifecycle.IService;
 
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java b/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java
index e100a5b..718c28a 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java
@@ -27,7 +27,7 @@ import com.ning.billing.util.eventbus.IEventBusType;
 
 public interface ISubscriptionTransition extends IEventBusType {
 
-    public enum SubscriptionTransitionTypeType {
+    public enum SubscriptionTransitionType {
         CREATE,
         CHANGE,
         PAUSE,
@@ -37,7 +37,7 @@ public interface ISubscriptionTransition extends IEventBusType {
         PHASE
     }
 
-    SubscriptionTransitionTypeType getTransitionType();
+    SubscriptionTransitionType getTransitionType();
 
     UUID getBundleId();
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
index 0d9ac8d..01ec2c0 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
@@ -139,13 +139,9 @@ public class PlanAligner implements IPlanAligner {
             planStartDate = subscription.getBundleStartDate();
             break;
         case CHANGE_OF_PLAN:
-            // STEPH
             throw new EntitlementError(String.format("Not implemented yet %s", alignment));
-            //break;
         case CHANGE_OF_PRICELIST:
-            // STEPH
             throw new EntitlementError(String.format("Not implemented yet %s", alignment));
-            //break;
         default:
             throw new EntitlementError(String.format("Unknwon PlanAlignmentChange %s", alignment));
         }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java
index 8ba459e..61cee03 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java
@@ -19,6 +19,7 @@ package com.ning.billing.entitlement.api.test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.inject.Inject;
 import com.ning.billing.config.IEntitlementConfig;
 import com.ning.billing.entitlement.engine.core.IApiEventProcessor;
 
@@ -29,6 +30,7 @@ public class EntitlementTestApi implements IEntitlementTestApi {
     private final IApiEventProcessor apiEventProcessor;
     private final IEntitlementConfig config;
 
+    @Inject
     public EntitlementTestApi(IApiEventProcessor apiEventProcessor, IEntitlementConfig config) {
         this.apiEventProcessor = apiEventProcessor;
         this.config = config;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
index d813be0..93fe561 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
@@ -35,7 +35,6 @@ import com.ning.billing.entitlement.alignment.IPlanAligner.TimedPhase;
 import com.ning.billing.entitlement.api.user.ISubscription;
 import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
 import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
-import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.engine.dao.IEntitlementDao;
 import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.phase.IPhaseEvent;
@@ -92,7 +91,6 @@ public class EntitlementUserApi implements IEntitlementUserApi {
     public ISubscription createSubscription(UUID bundleId, String productName,
             BillingPeriod term, String priceList, DateTime requestedDate) throws EntitlementUserApiException {
 
-        // STEPH Should really get 'standard' from catalog
         String realPriceList = (priceList == null) ? IPriceListSet.DEFAULT_PRICELIST_NAME : priceList;
 
         DateTime now = clock.getUTCNow();
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 2c04998..afcf053 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -47,7 +47,7 @@ import com.ning.billing.entitlement.events.user.ApiEventCancel;
 import com.ning.billing.entitlement.events.user.ApiEventChange;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.events.user.ApiEventUncancel;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.entitlement.glue.InjectorMagic;
 import com.ning.billing.util.clock.IClock;
@@ -295,8 +295,8 @@ public class Subscription extends PrivateFields  implements ISubscription {
         TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(this, newPlan, realPriceList, effectiveDate);
         IPhaseEvent nextPhaseEvent = PhaseEvent.getNextPhaseEvent(nextTimedPhase, this, now);
         List<IEvent> changeEvents = new ArrayList<IEvent>();
-        // Add phase event first so we expect to see PHASE event first-- mostly for test expectation
-        if (nextPhaseEvent != null) {
+        // Only add the PHASE if it does not coincide with the CHANGE, if not this is 'just' a CHANGE.
+        if (nextPhaseEvent != null && ! nextPhaseEvent.getEffectiveDate().equals(changeEvent.getEffectiveDate())) {
             changeEvents.add(nextPhaseEvent);
         }
         changeEvents.add(changeEvent);
@@ -479,7 +479,7 @@ public class Subscription extends PrivateFields  implements ISubscription {
                 break;
 
             case API_USER:
-                IUserEvent userEV = (IUserEvent) cur;
+                IApiEvent userEV = (IApiEvent) cur;
                 apiEventType = userEV.getEventType();
                 switch(apiEventType) {
                 case CREATE:
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
index 827d6a8..616e871 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
@@ -119,12 +119,12 @@ public class SubscriptionTransition implements ISubscriptionTransition {
     }
 
     @Override
-    public SubscriptionTransitionTypeType getTransitionType() {
+    public SubscriptionTransitionType getTransitionType() {
         switch(eventType) {
         case API_USER:
             return apiEventType.getSubscriptionTransitionType();
         case PHASE:
-            return SubscriptionTransitionTypeType.PHASE;
+            return SubscriptionTransitionType.PHASE;
         default:
             throw new EntitlementError("Unexpected event type " + eventType);
         }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java
index c5b9bd4..f3faf8e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java
@@ -141,8 +141,8 @@ public abstract class ApiEventProcessorBase implements IApiEventProcessor {
                             Thread.currentThread().getId()));
                 } catch (Throwable e) {
                     log.error(API_EVENT_THREAD_NAME + " got an exception exiting...", e);
-                    // STEPH let's review that later...
-                    System.exit(1);
+                    // Just to make it really obvious in the log
+                    e.printStackTrace();
                 }
             }
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index bf92822..622fa12 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -38,11 +38,13 @@ import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.IEvent.EventType;
 import com.ning.billing.entitlement.events.phase.IPhaseEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEvent;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.lifecycle.IService;
 import com.ning.billing.lifecycle.LyfecycleHandlerType;
 import com.ning.billing.lifecycle.LyfecycleHandlerType.LyfecycleLevel;
 import com.ning.billing.util.clock.IClock;
+import com.ning.billing.util.eventbus.IEventBus;
+import com.ning.billing.util.eventbus.IEventBus.EventBusException;
 
 public class Engine implements IEventListener, IEntitlementService {
 
@@ -57,21 +59,21 @@ public class Engine implements IEventListener, IEntitlementService {
     private final IEntitlementUserApi userApi;
     private final IEntitlementBillingApi billingApi;
     private final IEntitlementTestApi testApi;
-    private final IEntitlementConfig config;
-    private List<IApiListener> observers;
-
+    private final IEventBus eventBus;
 
     @Inject
     public Engine(IClock clock, IEntitlementDao dao, IApiEventProcessor apiEventProcessor,
             IPlanAligner planAligner, IEntitlementConfig config, EntitlementUserApi userApi,
-            EntitlementBillingApi billingApi) {
+            EntitlementBillingApi billingApi, EntitlementTestApi testApi, IEventBus eventBus) {
         super();
         this.clock = clock;
         this.dao = dao;
         this.apiEventProcessor = apiEventProcessor;
         this.planAligner = planAligner;
         this.userApi = userApi;
+        this.testApi = testApi;
         this.billingApi = billingApi;
+        this.eventBus = eventBus;
     }
 
     @Override
@@ -117,41 +119,15 @@ public class Engine implements IEventListener, IEntitlementService {
             log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
             return;
         }
-        if (event.getType() == EventType.API_USER) {
-            dispatchApiEvent((IUserEvent) event, subscription);
-        } else {
-            dispatchPhaseEvent((IPhaseEvent) event, subscription);
+        if (event.getType() == EventType.PHASE) {
             insertNextPhaseEvent(subscription);
         }
-    }
-
-    private void dispatchApiEvent(IUserEvent event, Subscription subscription) {
-/*
-        for (IApiListener listener : observers) {
-            switch(event.getEventType()) {
-            case CREATE:
-                listener.subscriptionCreated(subscription.getLatestTranstion());
-                break;
-            case CHANGE:
-                listener.subscriptionChanged(subscription.getLatestTranstion());
-                break;
-            case CANCEL:
-                listener.subscriptionCancelled(subscription.getLatestTranstion());
-                break;
-            default:
-                break;
-            }
+        try {
+            eventBus.post(subscription.getLatestTranstion());
+        } catch (EventBusException e) {
+            log.warn("Failed to post entitlement event " + event, e);
         }
-        */
-    }
-// STEPH observers
 
-    private void dispatchPhaseEvent(IPhaseEvent event, Subscription subscription) {
-/*
-        for (IApiListener listener : observers) {
-            listener.subscriptionPhaseChanged(subscription.getLatestTranstion());
-        }
-*/
     }
 
     private void insertNextPhaseEvent(Subscription subscription) {
@@ -161,7 +137,6 @@ public class Engine implements IEventListener, IEntitlementService {
         TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, subscription.getCurrentPlan(), now, subscription.getCurrentPlanStart());
         IPhaseEvent nextPhaseEvent = PhaseEvent.getNextPhaseEvent(nextTimedPhase, subscription, now);
         if (nextPhaseEvent != null) {
-            // STEPH Harden since event could be processed twice
             dao.createNextPhaseEvent(subscription.getId(), nextPhaseEvent);
         }
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index db4e375..197e4bd 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -41,7 +41,7 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.IEvent.EventType;
 import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.Hostname;
 import com.ning.billing.util.clock.IClock;
@@ -249,7 +249,7 @@ public class EntitlementDao implements IEntitlementDao {
                 List<IEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
 
                 for (IEvent cur : events) {
-                    if (cur.getType() == EventType.API_USER && ((IUserEvent) cur).getEventType() == ApiEventType.CANCEL) {
+                    if (cur.getType() == EventType.API_USER && ((IApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
                         if (existingCancelId != null) {
                             throw new EntitlementError(String.format("Found multiple cancel active events for subscriptions %s", subscriptionId.toString()));
                         }
@@ -299,7 +299,7 @@ public class EntitlementDao implements IEntitlementDao {
         List<IEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
         for (IEvent cur : events) {
             if (cur.getType() == type &&
-                    (apiType == null || apiType == ((IUserEvent) cur).getEventType() )) {
+                    (apiType == null || apiType == ((IApiEvent) cur).getEventType() )) {
                 if (futureEventId != null) {
                     throw new EntitlementError(
                             String.format("Found multiple future events for type %s for subscriptions %s",
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java
index 007d5b9..3c2180d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java
@@ -50,7 +50,7 @@ import com.ning.billing.entitlement.events.user.ApiEventPause;
 import com.ning.billing.entitlement.events.user.ApiEventResume;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.events.user.ApiEventUncancel;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 
 @ExternalizedSqlViaStringTemplate3()
@@ -99,15 +99,15 @@ public interface IEventSqlDao extends Transactional<IEventSqlDao>, CloseMe, Tran
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, IEvent evt) {
             stmt.bind("event_id", evt.getId().toString());
             stmt.bind("event_type", evt.getType().toString());
-            stmt.bind("user_type", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getEventType().toString() : null);
+            stmt.bind("user_type", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getEventType().toString() : null);
             stmt.bind("created_dt", getDate(evt.getProcessedDate()));
             stmt.bind("updated_dt", getDate(evt.getProcessedDate()));
             stmt.bind("requested_dt", getDate(evt.getRequestedDate()));
             stmt.bind("effective_dt", getDate(evt.getEffectiveDate()));
             stmt.bind("subscription_id", evt.getSubscriptionId().toString());
-            stmt.bind("plan_name", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getEventPlan() : null);
-            stmt.bind("phase_name", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getEventPlanPhase() : ((IPhaseEvent) evt).getPhase());
-            stmt.bind("plist_name", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getPriceList() : null);
+            stmt.bind("plan_name", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getEventPlan() : null);
+            stmt.bind("phase_name", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getEventPlanPhase() : ((IPhaseEvent) evt).getPhase());
+            stmt.bind("plist_name", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getPriceList() : null);
             stmt.bind("current_version", evt.getActiveVersion());
             stmt.bind("is_active", evt.isActive());
             stmt.bind("processing_available_dt", getDate(evt.getNextAvailableDate()));
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
index a0c526a..b1e9f02 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
@@ -22,7 +22,7 @@ import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.IPlan;
 import com.ning.billing.entitlement.events.IEventLyfecycle.IEventLyfecycleState;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 
 public abstract class EventBase implements IEvent {
@@ -213,7 +213,7 @@ public abstract class EventBase implements IEvent {
         } else if (getType() != other.getType()) {
             return (getType() == EventType.PHASE) ? -1 : 1;
         } else if (getType() == EventType.API_USER) {
-            return ((IUserEvent) this).getEventType().compareTo(((IUserEvent) other).getEventType());
+            return ((IApiEvent) this).getEventType().compareTo(((IApiEvent) other).getEventType());
         } else {
             return uuid.compareTo(other.getId());
         }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
index 4e2944e..e1b9177 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
@@ -27,7 +27,7 @@ import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.events.EventBase;
 import com.ning.billing.entitlement.events.IEventLyfecycle.IEventLyfecycleState;
 
-public class ApiEventBase extends EventBase implements IUserEvent {
+public class ApiEventBase extends EventBase implements IApiEvent {
 
     private final ApiEventType eventType;
     // Only valid for CREATE/CHANGE
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
index 94a446f..3d66a5b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
@@ -16,38 +16,35 @@
 
 package com.ning.billing.entitlement.events.user;
 
-import com.ning.billing.entitlement.api.user.ISubscriptionTransition.SubscriptionTransitionTypeType;
+import com.ning.billing.entitlement.api.user.ISubscriptionTransition.SubscriptionTransitionType;
 
 
 public enum ApiEventType {
-    /*
-     * Ordering is important for unit tests today.
-     */
     CREATE {
         @Override
-        public SubscriptionTransitionTypeType getSubscriptionTransitionType() { return SubscriptionTransitionTypeType.CREATE; }
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CREATE; }
     },
     CHANGE {
         @Override
-        public SubscriptionTransitionTypeType getSubscriptionTransitionType() { return SubscriptionTransitionTypeType.CHANGE; }
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CHANGE; }
     },
     PAUSE {
         @Override
-        public SubscriptionTransitionTypeType getSubscriptionTransitionType() { return SubscriptionTransitionTypeType.PAUSE; }
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.PAUSE; }
     },
     RESUME {
         @Override
-        public SubscriptionTransitionTypeType getSubscriptionTransitionType() { return SubscriptionTransitionTypeType.RESUME; }
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.RESUME; }
     },
     CANCEL {
         @Override
-        public SubscriptionTransitionTypeType getSubscriptionTransitionType() { return SubscriptionTransitionTypeType.CANCEL; }
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CANCEL; }
     },
     UNCANCEL {
         @Override
-        public SubscriptionTransitionTypeType getSubscriptionTransitionType() { return SubscriptionTransitionTypeType.UNCANCEL; }
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.UNCANCEL; }
     };
 
-    // STEPH Really, is that necessary?
-    public abstract SubscriptionTransitionTypeType getSubscriptionTransitionType();
+    // Used to map from internal events to User visible events (both user and phase)
+    public abstract SubscriptionTransitionType getSubscriptionTransitionType();
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
index f882943..5b5100b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
@@ -68,6 +68,9 @@ public class ApiTestListener {
             break;
         case UNCANCEL:
             break;
+        case PHASE:
+            subscriptionPhaseChanged(event);
+            break;
         default:
             throw new RuntimeException("Unexpected event type " + event.getRequestedTransitionTime());
         }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
index 5f11030..c087e39 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
@@ -63,7 +63,7 @@ import com.ning.billing.entitlement.engine.dao.IEntitlementDaoMock;
 import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.phase.IPhaseEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.glue.InjectorMagic;
 import com.ning.billing.lifecycle.IService.ServiceException;
 import com.ning.billing.util.clock.ClockMock;
@@ -105,8 +105,14 @@ public abstract class TestUserApiBase {
 
     @AfterClass(groups={"setup"})
     public void tearDown() {
-        InjectorMagic.instance = null;
-        ((EventBusService) busService).stopBus();
+        try {
+            InjectorMagic.instance = null;
+            busService.getEventBus().register(testListener);
+            ((EventBusService) busService).stopBus();
+        } catch (Exception e) {
+            log.warn("Failed to tearDown test properly ", e);
+        }
+
     }
 
     @BeforeClass(groups={"setup"})
@@ -157,11 +163,13 @@ public abstract class TestUserApiBase {
         log.warn("RESET TEST FRAMEWORK\n\n");
 
         testListener.reset();
+
         clock.resetDeltaFromReality();
         ((IEntitlementDaoMock) dao).reset();
         try {
+            busService.getEventBus().register(testListener);
             bundle = entitlementApi.createBundleForAccount(account, "myDefaultBundle");
-        } catch (EntitlementUserApiException e) {
+        } catch (Exception e) {
             Assert.fail(e.getMessage());
         }
         assertNotNull(bundle);
@@ -222,8 +230,8 @@ public abstract class TestUserApiBase {
                     assertEquals(foundPhase, false);
                     foundPhase = true;
                     assertEquals(cur.getEffectiveDate(), expPhaseChange);
-                } else if (cur instanceof IUserEvent) {
-                    IUserEvent uEvent = (IUserEvent) cur;
+                } else if (cur instanceof IApiEvent) {
+                    IApiEvent uEvent = (IApiEvent) cur;
                     assertEquals(ApiEventType.CHANGE, uEvent.getEventType());
                     assertEquals(foundChange, false);
                     foundChange = true;
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index 373db87..32f1dc9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -36,7 +36,7 @@ import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.events.IEvent;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.util.clock.Clock;
 
 public abstract class TestUserApiChangePlan extends TestUserApiBase {
@@ -145,7 +145,7 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
 
             // ALSO VERIFY PENDING CHANGE EVENT
             List<IEvent> events = dao.getPendingEventsForSubscription(subscription.getId());
-            assertTrue(events.get(0) instanceof IUserEvent);
+            assertTrue(events.get(0) instanceof IApiEvent);
 
 
             // MOVE TO EOT
@@ -341,7 +341,6 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
             IPlanPhase trialPhase = subscription.getCurrentPhase();
             assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
 
-            // MOVE TO NEXT PHASE
             testListener.pushExpectedEvent(NextEvent.PHASE);
             clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
             assertTrue(testListener.isCompleted(2000));
@@ -378,9 +377,25 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
 
+            // ACTIVATE CHNAGE BY MOVING AFTER CTD
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            clock.addDeltaFromReality(ctd);
+            assertTrue(testListener.isCompleted(3000));
+
+            currentPlan = subscription.getCurrentPlan();
+            assertNotNull(currentPlan);
+            assertEquals(currentPlan.getProduct().getName(), "Pistol");
+            assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+            assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.ANNUAL);
+
+            currentPhase = subscription.getCurrentPhase();
+            assertNotNull(currentPhase);
+            assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
+
+
+
             // MOVE TO NEXT PHASE
             testListener.pushExpectedEvent(NextEvent.PHASE);
-            testListener.pushExpectedEvent(NextEvent.CHANGE);
             clock.addDeltaFromReality(currentPhase.getDuration());
             assertTrue(testListener.isCompleted(3000));
             subscription = (Subscription) entitlementApi.getSubscriptionFromId(subscription.getId());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
index 4455436..8847612 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
@@ -57,7 +57,7 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
         invokeRealMethod(this);
     }
 
-    // STEPH set to false until we implement rescue example.
+    // Set to false until we implement rescue example.
     @Test(enabled=false, groups={"fast"})
     public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
         invokeRealMethod(this);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
index 174b85e..60d777d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
@@ -79,7 +79,7 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
         invokeRealMethod(this);
     }
 
-    // STEPH rescue not implemented
+    // rescue not implemented yet
     @Test(enabled=false, groups={"sql"})
     public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
         invokeRealMethod(this);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
index cc574ee..1e043eb 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
@@ -40,7 +40,7 @@ import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.IEvent.EventType;
 import com.ning.billing.entitlement.events.IEventLyfecycle.IEventLyfecycleState;
 import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.util.clock.IClock;
 
 public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDaoMock {
@@ -294,7 +294,7 @@ public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDa
                     continue;
                 }
                 if (cur.getType() == EventType.API_USER &&
-                        ApiEventType.CHANGE == ((IUserEvent) cur).getEventType() &&
+                        ApiEventType.CHANGE == ((IApiEvent) cur).getEventType() &&
                         cur.getProcessingState() == IEventLyfecycleState.AVAILABLE) {
                     cur.deactivate();
                     break;
@@ -315,7 +315,7 @@ public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDa
                     continue;
                 }
                 if (cur.getType() == EventType.API_USER &&
-                        ((IUserEvent) cur).getEventType() == ApiEventType.CANCEL) {
+                        ((IApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
                     cur.deactivate();
                     foundCancel = true;
                     break;
diff --git a/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java b/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java
index 0b4debb..5ee513a 100644
--- a/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java
+++ b/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java
@@ -30,7 +30,7 @@ import com.google.common.eventbus.AsyncEventBus;
 public class MemoryEventBus implements IEventBus {
 
     // STEPH config ?
-    private final static int MAX_EVENT_THREADS = 13;
+    private final static int MAX_EVENT_THREADS = 1;
 
     private final static String EVENT_BUS_IDENTIFIER = "eventbus-service";
     private final static String EVENT_BUS_GROUP_NAME = "eventbus-grp";
@@ -56,8 +56,13 @@ public class MemoryEventBus implements IEventBus {
             dispatchQueuedEvents();
         }
 
+        // No way to really 'stop' an executor; what we do is:
+        // i) disallow any new events into the queue
+        // ii) empty the queue
+        //
+        // That will only work if the event submitter handles EventBusException correctly when posting.
+        //
         public void stop() {
-            // STEPH hum..
         }
     }
 
@@ -113,7 +118,6 @@ public class MemoryEventBus implements IEventBus {
         if (isInitialized.compareAndSet(true, false)) {
             log.info("MemoryEventBus stopping...");
             delegate.completeDispatch();
-            log.info("MemoryEventBus completed dispatching events...");
             delegate.stop();
             log.info("MemoryEventBus stoped...");
         }