killbill-uncached

Changes

analytics/pom.xml 20(+20 -0)

pom.xml 5(+5 -0)

Details

analytics/pom.xml 20(+20 -0)

diff --git a/analytics/pom.xml b/analytics/pom.xml
index 3755178..0ead039 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -74,6 +74,26 @@
         </dependency>
         <dependency>
             <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-catalog</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-entitlement</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
             <artifactId>killbill-util</artifactId>
             <type>test-jar</type>
             <scope>test</scope>
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 fbbe2b8..415a002 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -18,149 +18,51 @@ 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;
-import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
-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 org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-
 public class AnalyticsListener
 {
-    private static final Logger log = LoggerFactory.getLogger(AnalyticsListener.class);
-
-    private final BusinessSubscriptionTransitionDao dao;
-    private final IEntitlementUserApi entitlementApi;
-    private final IAccountUserApi accountApi;
+    private final BusinessSubscriptionTransitionRecorder bstRecorder;
+    private final BusinessAccountRecorder bacRecorder;
 
     @Inject
-    public AnalyticsListener(final BusinessSubscriptionTransitionDao dao, final IEntitlementUserApi entitlementApi, final IAccountUserApi accountApi)
+    public AnalyticsListener(final BusinessSubscriptionTransitionRecorder bstRecorder, final BusinessAccountRecorder bacRecorder)
     {
-        this.dao = dao;
-        this.entitlementApi = entitlementApi;
-        this.accountApi = accountApi;
+        this.bstRecorder = bstRecorder;
+        this.bacRecorder = bacRecorder;
     }
 
-    /*
-     * Disable until we fix IRS to allow for two instances (One for bilr proxy, or for killbill)
-     * @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)
+    @Subscribe
+    public void handleSubscriptionTransitionChange(final ISubscriptionTransition event)
     {
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
-        recordTransition(event, created);
-    }
-
-    public void subscriptionCancelled(final ISubscriptionTransition cancelled)
-    {
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
-        recordTransition(event, cancelled);
-    }
-
-    public void subscriptionChanged(final ISubscriptionTransition changed)
-    {
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
-        recordTransition(event, changed);
-    }
-
-    public void subscriptionPaused(final ISubscriptionTransition paused)
-    {
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
-        recordTransition(event, paused);
-    }
-
-    public void subscriptionResumed(final ISubscriptionTransition resumed)
-    {
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
-        recordTransition(event, resumed);
-    }
-
-    public void subscriptionPhaseChanged(final ISubscriptionTransition phaseChanged)
-    {
-        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
-        recordTransition(event, phaseChanged);
-    }
-
-    private void recordTransition(final BusinessSubscriptionEvent event, final ISubscriptionTransition transition)
-    {
-        Currency currency = null;
-        String transitionKey = null;
-
-        // Retrieve key and currency via the bundle
-        final ISubscriptionBundle bundle = entitlementApi.getBundleFromId(transition.getBundleId());
-        if (bundle != null) {
-            transitionKey = bundle.getKey();
-
-            final IAccount account = accountApi.getAccountFromId(bundle.getAccountId());
-            if (account != null) {
-                currency = account.getCurrency();
-            }
-        }
-
-        // The ISubscriptionTransition interface gives us all the prev/next information we need but the start date
-        // of the previous plan. We need to retrieve it from our own transitions table
-        DateTime previousEffectiveTransitionTime = null;
-        final List<BusinessSubscriptionTransition> transitions = dao.getTransitions(transitionKey);
-        if (transitions != null) {
-            final BusinessSubscriptionTransition lastTransition = transitions.get(transitions.size() - 1);
-            if (lastTransition != null && lastTransition.getNextSubscription() != null) {
-                previousEffectiveTransitionTime = lastTransition.getNextSubscription().getStartDate();
-            }
+        switch (event.getTransitionType()) {
+            case CREATE:
+                bstRecorder.subscriptionCreated(event);
+                break;
+            case CANCEL:
+                bstRecorder.subscriptionCancelled(event);
+                break;
+            case CHANGE:
+                bstRecorder.subscriptionChanged(event);
+                break;
+            case PAUSE:
+                bstRecorder.subscriptionPaused(event);
+                break;
+            case RESUME:
+                bstRecorder.subscriptionResumed(event);
+                break;
+            case UNCANCEL:
+                break;
+            case PHASE:
+                bstRecorder.subscriptionPhaseChanged(event);
+                break;
+            default:
+                throw new RuntimeException("Unexpected event type " + event.getTransitionType());
         }
-
-        // TODO Support currency changes
-        final BusinessSubscription prevSubscription = new BusinessSubscription(transition.getPreviousPlan(), transition.getPreviousPhase(), currency, previousEffectiveTransitionTime, transition.getPreviousState(), transition.getSubscriptionId(), transition.getBundleId());
-        final BusinessSubscription nextSubscription = new BusinessSubscription(transition.getNextPlan(), transition.getNextPhase(), currency, transition.getEffectiveTransitionTime(), transition.getNextState(), transition.getSubscriptionId(), transition.getBundleId());
-
-        recordTransition(transitionKey, transition.getRequestedTransitionTime(), event, prevSubscription, nextSubscription);
     }
 
-    // Public for now for internal reasons
-    public void recordTransition(final String key, final DateTime requestedDateTime, final BusinessSubscriptionEvent event, final BusinessSubscription prevSubscription, final BusinessSubscription nextSubscription)
+    @Subscribe
+    public void handleAccountChange(final Object event)
     {
-        final BusinessSubscriptionTransition transition = new BusinessSubscriptionTransition(
-            key,
-            requestedDateTime,
-            event,
-            prevSubscription,
-            nextSubscription
-        );
-
-        log.info(transition.getEvent() + " " + transition);
-        dao.createTransition(transition);
     }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/AnalyticsService.java b/analytics/src/main/java/com/ning/billing/analytics/api/AnalyticsService.java
new file mode 100644
index 0000000..216ff9b
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/AnalyticsService.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.api;
+
+import com.google.inject.Inject;
+import com.ning.billing.analytics.AnalyticsListener;
+import com.ning.billing.lifecycle.LyfecycleHandlerType;
+import com.ning.billing.util.eventbus.IEventBus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AnalyticsService implements IAnalyticsService
+{
+    private static final Logger log = LoggerFactory.getLogger(AnalyticsService.class);
+
+    private static final String ANALYTICS_SERVICE = "analytics-service";
+
+    private final AnalyticsListener listener;
+    private final IEventBus eventBus;
+
+    @Inject
+    public AnalyticsService(final AnalyticsListener listener, final IEventBus eventBus)
+    {
+        this.listener = listener;
+        this.eventBus = eventBus;
+    }
+
+    @Override
+    public String getName()
+    {
+        return ANALYTICS_SERVICE;
+    }
+
+    @LyfecycleHandlerType(LyfecycleHandlerType.LyfecycleLevel.REGISTER_EVENTS)
+    public void registerForNotifications()
+    {
+        try {
+            eventBus.register(listener);
+        }
+        catch (IEventBus.EventBusException e) {
+            log.error("Unable to register to the EventBus!", e);
+        }
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
new file mode 100644
index 0000000..7c5a6da
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessAccountRecorder.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.account.api.IAccountUserApi;
+import com.ning.billing.analytics.dao.BusinessAccountDao;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BusinessAccountRecorder
+{
+    private static final Logger log = LoggerFactory.getLogger(BusinessAccountRecorder.class);
+
+    private final BusinessAccountDao dao;
+    private final IAccountUserApi accountApi;
+
+    @Inject
+    public BusinessAccountRecorder(final BusinessAccountDao dao, final IAccountUserApi accountApi)
+    {
+        this.dao = dao;
+        this.accountApi = accountApi;
+    }
+
+    public void subscriptionCreated(final IAccount created)
+    {
+    }
+
+    public void subscriptionUpdated(final IAccount updated)
+    {
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
index dc33209..69c89cd 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
@@ -125,16 +125,16 @@ public class BusinessSubscription
                 mrr = getMrrFromISubscription(currentPhase.getDuration(), price);
             }
             else {
-                price = null;
-                mrr = null;
+                price = BigDecimal.ZERO;
+                mrr = BigDecimal.ZERO;
             }
         }
         else {
             slug = null;
             phase = null;
             billingPeriod = null;
-            price = null;
-            mrr = null;
+            price = BigDecimal.ZERO;
+            mrr = BigDecimal.ZERO;
         }
 
         if (currency != null) {
@@ -228,7 +228,7 @@ public class BusinessSubscription
     static BigDecimal getMrrFromISubscription(final IDuration duration, final BigDecimal price)
     {
         if (duration == null || duration.getUnit() == null || duration.getNumber() == 0) {
-            return null;
+            return BigDecimal.ZERO;
         }
 
         if (duration.getUnit().equals(TimeUnit.UNLIMITED)) {
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
new file mode 100644
index 0000000..c3ecc70
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.account.api.IAccountUserApi;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
+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 org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class BusinessSubscriptionTransitionRecorder
+{
+    private static final Logger log = LoggerFactory.getLogger(BusinessSubscriptionTransitionRecorder.class);
+
+    private final BusinessSubscriptionTransitionDao dao;
+    private final IEntitlementUserApi entitlementApi;
+    private final IAccountUserApi accountApi;
+
+    @Inject
+    public BusinessSubscriptionTransitionRecorder(final BusinessSubscriptionTransitionDao dao, final IEntitlementUserApi entitlementApi, final IAccountUserApi accountApi)
+    {
+        this.dao = dao;
+        this.entitlementApi = entitlementApi;
+        this.accountApi = accountApi;
+    }
+
+    public void subscriptionCreated(final ISubscriptionTransition created)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
+        recordTransition(event, created);
+    }
+
+    public void subscriptionCancelled(final ISubscriptionTransition cancelled)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
+        recordTransition(event, cancelled);
+    }
+
+    public void subscriptionChanged(final ISubscriptionTransition changed)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
+        recordTransition(event, changed);
+    }
+
+    public void subscriptionPaused(final ISubscriptionTransition paused)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
+        recordTransition(event, paused);
+    }
+
+    public void subscriptionResumed(final ISubscriptionTransition resumed)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
+        recordTransition(event, resumed);
+    }
+
+    public void subscriptionPhaseChanged(final ISubscriptionTransition phaseChanged)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
+        recordTransition(event, phaseChanged);
+    }
+
+    public void recordTransition(final BusinessSubscriptionEvent event, final ISubscriptionTransition transition)
+    {
+        Currency currency = null;
+        String transitionKey = null;
+
+        // Retrieve key and currency via the bundle
+        final ISubscriptionBundle bundle = entitlementApi.getBundleFromId(transition.getBundleId());
+        if (bundle != null) {
+            transitionKey = bundle.getKey();
+
+            final IAccount account = accountApi.getAccountFromId(bundle.getAccountId());
+            if (account != null) {
+                currency = account.getCurrency();
+            }
+        }
+
+        // The ISubscriptionTransition interface gives us all the prev/next information we need but the start date
+        // of the previous plan. We need to retrieve it from our own transitions table
+        DateTime previousEffectiveTransitionTime = null;
+        final List<BusinessSubscriptionTransition> transitions = dao.getTransitions(transitionKey);
+        if (transitions != null && transitions.size() > 0) {
+            final BusinessSubscriptionTransition lastTransition = transitions.get(transitions.size() - 1);
+            if (lastTransition != null && lastTransition.getNextSubscription() != null) {
+                previousEffectiveTransitionTime = lastTransition.getNextSubscription().getStartDate();
+            }
+        }
+
+        // TODO Support currency changes
+        final BusinessSubscription prevSubscription = new BusinessSubscription(transition.getPreviousPlan(), transition.getPreviousPhase(), currency, previousEffectiveTransitionTime, transition.getPreviousState(), transition.getSubscriptionId(), transition.getBundleId());
+        final BusinessSubscription nextSubscription = new BusinessSubscription(transition.getNextPlan(), transition.getNextPhase(), currency, transition.getEffectiveTransitionTime(), transition.getNextState(), transition.getSubscriptionId(), transition.getBundleId());
+
+        record(transitionKey, transition.getRequestedTransitionTime(), event, prevSubscription, nextSubscription);
+    }
+
+    // Public for internal reasons
+    public void record(final String key, final DateTime requestedDateTime, final BusinessSubscriptionEvent event, final BusinessSubscription prevSubscription, final BusinessSubscription nextSubscription)
+    {
+        final BusinessSubscriptionTransition transition = new BusinessSubscriptionTransition(
+            key,
+            requestedDateTime,
+            event,
+            prevSubscription,
+            nextSubscription
+        );
+
+        log.info(transition.getEvent() + " " + transition);
+        dao.createTransition(transition);
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java b/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
index 7c74808..59fc392 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/setup/AnalyticsModule.java
@@ -18,6 +18,11 @@ package com.ning.billing.analytics.setup;
 
 
 import com.google.inject.AbstractModule;
+import com.ning.billing.analytics.AnalyticsListener;
+import com.ning.billing.analytics.BusinessAccountRecorder;
+import com.ning.billing.analytics.BusinessSubscriptionTransitionRecorder;
+import com.ning.billing.analytics.api.AnalyticsService;
+import com.ning.billing.analytics.api.IAnalyticsService;
 import com.ning.billing.analytics.dao.BusinessAccountDao;
 import com.ning.billing.analytics.dao.BusinessAccountDaoProvider;
 import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
@@ -30,5 +35,11 @@ public class AnalyticsModule extends AbstractModule
     {
         bind(BusinessSubscriptionTransitionDao.class).toProvider(BusinessSubscriptionTransitionDaoProvider.class).asEagerSingleton();
         bind(BusinessAccountDao.class).toProvider(BusinessAccountDaoProvider.class).asEagerSingleton();
+
+        bind(BusinessSubscriptionTransitionRecorder.class).asEagerSingleton();
+        bind(BusinessAccountRecorder.class).asEagerSingleton();
+        bind(AnalyticsListener.class).asEagerSingleton();
+
+        bind(IAnalyticsService.class).to(AnalyticsService.class).asEagerSingleton();
     }
 }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java b/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java
index 0f0adef..ab5728d 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/utils/Rounder.java
@@ -22,6 +22,11 @@ public class Rounder
 {
     public static final int SCALE = 4;
 
+    // Static only
+    private Rounder()
+    {
+    }
+
     public static double round(final BigDecimal decimal)
     {
         if (decimal == null) {
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
new file mode 100644
index 0000000..03e102d
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import com.ning.billing.account.glue.AccountModule;
+import com.ning.billing.analytics.setup.AnalyticsModule;
+import com.ning.billing.catalog.glue.CatalogModule;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.util.glue.EventBusModule;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.IDBI;
+
+public class AnalyticsTestModule extends AnalyticsModule
+{
+    @Override
+    protected void configure()
+    {
+        super.configure();
+
+        // Need to configure a few more things for the EventBus
+        install(new AccountModule());
+        install(new CatalogModule());
+        install(new EventBusModule());
+        install(new EntitlementModule());
+
+        // Install the Dao layer
+        final MysqlTestingHelper helper = new MysqlTestingHelper();
+        bind(MysqlTestingHelper.class).toInstance(helper);
+        final DBI dbi = helper.getDBI();
+        bind(IDBI.class).toInstance(dbi);
+        bind(DBI.class).toInstance(dbi);
+    }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
new file mode 100644
index 0000000..aae04e7
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.api;
+
+import com.google.inject.Inject;
+import com.ning.billing.analytics.AnalyticsTestModule;
+import com.ning.billing.analytics.BusinessSubscription;
+import com.ning.billing.analytics.BusinessSubscriptionEvent;
+import com.ning.billing.analytics.BusinessSubscriptionTransition;
+import com.ning.billing.analytics.MockAccount;
+import com.ning.billing.analytics.MockDuration;
+import com.ning.billing.analytics.MockPhase;
+import com.ning.billing.analytics.MockPlan;
+import com.ning.billing.analytics.MockProduct;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
+import com.ning.billing.catalog.api.IPlan;
+import com.ning.billing.catalog.api.IPlanPhase;
+import com.ning.billing.catalog.api.IProduct;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
+import com.ning.billing.entitlement.api.user.ISubscription;
+import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
+import com.ning.billing.entitlement.api.user.ISubscriptionTransition;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.events.IEvent;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.util.eventbus.IEventBus;
+import org.apache.commons.io.IOUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.UUID;
+
+@Guice(modules = AnalyticsTestModule.class)
+public class TestAnalyticsService
+{
+    private static final String KEY = "1234";
+
+    @Inject
+    private IEntitlementUserApi entitlementApi;
+
+    @Inject
+    private AnalyticsService service;
+
+    @Inject
+    private IEventBus bus;
+
+    @Inject
+    private BusinessSubscriptionTransitionDao dao;
+
+    @Inject
+    private MysqlTestingHelper helper;
+
+    private ISubscriptionTransition transition;
+    private BusinessSubscriptionTransition expectedTransition;
+
+    @BeforeClass(alwaysRun = true)
+    public void startMysql() throws IOException, ClassNotFoundException, SQLException, EntitlementUserApiException
+    {
+        final String analyticsDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/analytics/ddl.sql"));
+        // For bundles
+        final String accountDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
+        final String entitlementDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+
+        helper.startMysql();
+        helper.initDb(analyticsDdl);
+        helper.initDb(accountDdl);
+        helper.initDb(entitlementDdl);
+
+        // We need a bundle to retrieve the event key
+        final ISubscriptionBundle bundle = entitlementApi.createBundleForAccount(new MockAccount(KEY), KEY);
+
+        // Verify we correctly initialized the account subsystem
+        Assert.assertNotNull(bundle);
+        Assert.assertEquals(bundle.getKey(), KEY);
+
+        // Create a subscription transition
+        final IProduct product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
+        final IPlan plan = new MockPlan("platinum-monthly", product);
+        final IPlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
+        final UUID subscriptionId = UUID.randomUUID();
+        final DateTime effectiveTransitionTime = new DateTime(DateTimeZone.UTC);
+        final DateTime requestedTransitionTime = new DateTime(DateTimeZone.UTC);
+        transition = new SubscriptionTransition(
+            subscriptionId,
+            bundle.getId(),
+            IEvent.EventType.API_USER,
+            ApiEventType.CREATE,
+            requestedTransitionTime,
+            effectiveTransitionTime,
+            null,
+            null,
+            null,
+            null,
+            ISubscription.SubscriptionState.ACTIVE,
+            plan,
+            phase,
+            "something"
+        );
+        expectedTransition = new BusinessSubscriptionTransition(
+            KEY,
+            requestedTransitionTime,
+            BusinessSubscriptionEvent.subscriptionCreated(plan),
+            null,
+            new BusinessSubscription(plan, phase, null, effectiveTransitionTime, ISubscription.SubscriptionState.ACTIVE, subscriptionId, bundle.getId())
+        );
+    }
+
+    @AfterClass(alwaysRun = true)
+    public void stopMysql()
+    {
+        helper.stopMysql();
+    }
+
+    @Test(groups = "slow")
+    public void testRegisterForNotifications() throws Exception
+    {
+        // Make sure the service has been instantiated
+        Assert.assertEquals(service.getName(), "analytics-service");
+
+        // Test the bus and make sure we can register our service
+        try {
+            bus.start();
+            service.registerForNotifications();
+        }
+        catch (Throwable t) {
+            Assert.fail("Unable to start the bus or service! " + t);
+        }
+
+        // Send an event to the bus and make sure our Dao got it
+        bus.post(transition);
+        Thread.sleep(1000);
+        Assert.assertEquals(dao.getTransitions(KEY).size(), 1);
+        Assert.assertEquals(dao.getTransitions(KEY).get(0), expectedTransition);
+
+        // Test the shutdown sequence
+        try {
+            bus.stop();
+        }
+        catch (Throwable t) {
+            Assert.fail("Unable to stop the bus!");
+        }
+    }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
index d575ee2..defdcef 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/dao/TestAnalyticsDao.java
@@ -40,6 +40,7 @@ import org.skife.jdbi.v2.IDBI;
 import org.testng.Assert;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import java.io.IOException;
@@ -55,6 +56,9 @@ public class TestAnalyticsDao
     private static final String ACCOUNT_KEY = "pierre-143343-vcc";
 
     private final MysqlTestingHelper helper = new MysqlTestingHelper();
+    private final IProduct product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
+    private final IPlan plan = new MockPlan("platinum-monthly", product);
+    private final IPlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
 
     private BusinessSubscriptionTransitionDao businessSubscriptionTransitionDao;
     private BusinessSubscriptionTransition transition;
@@ -75,10 +79,6 @@ public class TestAnalyticsDao
 
     private void setupBusinessSubscriptionTransition()
     {
-        final IProduct product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
-        final IPlan plan = new MockPlan("platinum-monthly", product);
-        final IPlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
-
         final BusinessSubscription prevSubscription = new BusinessSubscription(plan, phase, Currency.USD, new DateTime(DateTimeZone.UTC), ISubscription.SubscriptionState.ACTIVE, UUID.randomUUID(), UUID.randomUUID());
         final BusinessSubscription nextSubscription = new BusinessSubscription(plan, phase, Currency.USD, new DateTime(DateTimeZone.UTC), ISubscription.SubscriptionState.CANCELLED, UUID.randomUUID(), UUID.randomUUID());
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(plan);
@@ -123,6 +123,131 @@ public class TestAnalyticsDao
         helper.stopMysql();
     }
 
+    @BeforeMethod
+    public void cleanup() throws Exception
+    {
+        helper.cleanupTable("bst");
+    }
+
+    @Test(groups = "slow")
+    public void testTransitionsWithNullPrevSubscription()
+    {
+        final BusinessSubscriptionTransition transitionWithNullPrev = new BusinessSubscriptionTransition(
+            transition.getKey(),
+            transition.getRequestedTimestamp(),
+            transition.getEvent(),
+            null,
+            transition.getNextSubscription()
+        );
+        businessSubscriptionTransitionDao.createTransition(transitionWithNullPrev);
+
+        final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+        Assert.assertEquals(transitions.size(), 1);
+        Assert.assertEquals(transitions.get(0), transitionWithNullPrev);
+    }
+
+    @Test(groups = "slow")
+    public void testTransitionsWithNullNextSubscription()
+    {
+        final BusinessSubscriptionTransition transitionWithNullNext = new BusinessSubscriptionTransition(
+            transition.getKey(),
+            transition.getRequestedTimestamp(),
+            transition.getEvent(),
+            transition.getPreviousSubscription(),
+            null
+        );
+        businessSubscriptionTransitionDao.createTransition(transitionWithNullNext);
+
+        final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+        Assert.assertEquals(transitions.size(), 1);
+        Assert.assertEquals(transitions.get(0), transitionWithNullNext);
+    }
+
+    @Test(groups = "slow")
+    public void testTransitionsWithNullFieldsInSubscription()
+    {
+        final BusinessSubscription subscriptionWithNullFields = new BusinessSubscription(plan, phase, Currency.USD, null, null, null, null);
+        final BusinessSubscriptionTransition transitionWithNullFields = new BusinessSubscriptionTransition(
+            transition.getKey(),
+            transition.getRequestedTimestamp(),
+            transition.getEvent(),
+            subscriptionWithNullFields,
+            subscriptionWithNullFields
+        );
+        businessSubscriptionTransitionDao.createTransition(transitionWithNullFields);
+
+        final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+        Assert.assertEquals(transitions.size(), 1);
+        Assert.assertEquals(transitions.get(0), transitionWithNullFields);
+    }
+
+    @Test(groups = "slow")
+    public void testTransitionsWithNullPlanAndPhase() throws Exception
+    {
+        final BusinessSubscription subscriptionWithNullPlanAndPhase = new BusinessSubscription(null, null, Currency.USD, null, null, null, null);
+        final BusinessSubscriptionTransition transitionWithNullPlanAndPhase = new BusinessSubscriptionTransition(
+            transition.getKey(),
+            transition.getRequestedTimestamp(),
+            transition.getEvent(),
+            subscriptionWithNullPlanAndPhase,
+            subscriptionWithNullPlanAndPhase
+        );
+        businessSubscriptionTransitionDao.createTransition(transitionWithNullPlanAndPhase);
+
+        final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+        Assert.assertEquals(transitions.size(), 1);
+        Assert.assertEquals(transitions.get(0).getKey(), transition.getKey());
+        Assert.assertEquals(transitions.get(0).getRequestedTimestamp(), transition.getRequestedTimestamp());
+        Assert.assertEquals(transitions.get(0).getEvent(), transition.getEvent());
+        // Null Plan and Phase doesn't make sense so we turn the subscription into a null
+        Assert.assertNull(transitions.get(0).getPreviousSubscription());
+        Assert.assertNull(transitions.get(0).getNextSubscription());
+    }
+
+    @Test(groups = "slow")
+    public void testTransitionsWithNullPlan() throws Exception
+    {
+        final BusinessSubscription subscriptionWithNullPlan = new BusinessSubscription(null, phase, Currency.USD, null, null, null, null);
+        final BusinessSubscriptionTransition transitionWithNullPlan = new BusinessSubscriptionTransition(
+            transition.getKey(),
+            transition.getRequestedTimestamp(),
+            transition.getEvent(),
+            subscriptionWithNullPlan,
+            subscriptionWithNullPlan
+        );
+        businessSubscriptionTransitionDao.createTransition(transitionWithNullPlan);
+
+        final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+        Assert.assertEquals(transitions.size(), 1);
+        // Null Plan but Phase - we don't turn the subscription into a null
+        Assert.assertEquals(transitions.get(0), transitionWithNullPlan);
+    }
+
+    @Test(groups = "slow")
+    public void testTransitionsWithNullPhase() throws Exception
+    {
+        final BusinessSubscription subscriptionWithNullPhase = new BusinessSubscription(plan, null, Currency.USD, null, null, null, null);
+        final BusinessSubscriptionTransition transitionWithNullPhase = new BusinessSubscriptionTransition(
+            transition.getKey(),
+            transition.getRequestedTimestamp(),
+            transition.getEvent(),
+            subscriptionWithNullPhase,
+            subscriptionWithNullPhase
+        );
+        businessSubscriptionTransitionDao.createTransition(transitionWithNullPhase);
+
+        final List<BusinessSubscriptionTransition> transitions = businessSubscriptionTransitionDao.getTransitions(EVENT_KEY);
+        Assert.assertEquals(transitions.size(), 1);
+        Assert.assertEquals(transitions.get(0).getKey(), transition.getKey());
+        Assert.assertEquals(transitions.get(0).getRequestedTimestamp(), transition.getRequestedTimestamp());
+        Assert.assertEquals(transitions.get(0).getEvent(), transition.getEvent());
+
+        // Null Phase but Plan - we don't turn the subscription into a null, however price and mrr are both set to 0 (not null)
+        final BusinessSubscription blankSubscription = new BusinessSubscription(plan, new MockPhase(null, null, null, 0.0), Currency.USD, null, null, null, null);
+        Assert.assertEquals(transitions.get(0).getPreviousSubscription(), blankSubscription);
+        Assert.assertEquals(transitions.get(0).getNextSubscription(), blankSubscription);
+    }
+
     @Test(groups = "slow")
     public void testCreateAndRetrieveTransitions()
     {
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
new file mode 100644
index 0000000..c2ab5fd
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.catalog.api.Currency;
+
+import java.util.UUID;
+
+public class MockAccount implements IAccount
+{
+    private final String key;
+
+    public MockAccount(final String key)
+    {
+        this.key = key;
+    }
+
+    @Override
+    public String getName()
+    {
+        return "accountName";
+    }
+
+    @Override
+    public String getEmail()
+    {
+        return "accountName@yahoo.com";
+    }
+
+    @Override
+    public String getPhone()
+    {
+        return "4152876341";
+    }
+
+    @Override
+    public String getKey()
+    {
+        return key;
+    }
+
+    @Override
+    public int getBillCycleDay()
+    {
+        return 1;
+    }
+
+    @Override
+    public Currency getCurrency()
+    {
+        return Currency.USD;
+    }
+
+    @Override
+    public UUID getId()
+    {
+        return UUID.randomUUID();
+    }
+
+    @Override
+    public void load()
+    {
+    }
+
+    @Override
+    public void save()
+    {
+    }
+
+    @Override
+    public String getFieldValue(final String fieldName)
+    {
+        return null;
+    }
+
+    @Override
+    public void setFieldValue(final String fieldName, final String fieldValue)
+    {
+    }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionDao.java b/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionDao.java
new file mode 100644
index 0000000..d7e36b4
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockBusinessSubscriptionTransitionDao.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionBinder;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
+import org.skife.jdbi.v2.sqlobject.Bind;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class MockBusinessSubscriptionTransitionDao implements BusinessSubscriptionTransitionDao
+{
+    private final Map<String, List<BusinessSubscriptionTransition>> content = new HashMap<String, List<BusinessSubscriptionTransition>>();
+
+    @Override
+    public List<BusinessSubscriptionTransition> getTransitions(@Bind("event_key") final String key)
+    {
+        return content.get(key);
+    }
+
+    @Override
+    public int createTransition(@BusinessSubscriptionTransitionBinder final BusinessSubscriptionTransition transition)
+    {
+        if (content.get(transition.getKey()) == null) {
+            content.put(transition.getKey(), new ArrayList<BusinessSubscriptionTransition>());
+        }
+        content.get(transition.getKey()).add(transition);
+        return 1;
+    }
+
+    @Override
+    public void test()
+    {
+    }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
new file mode 100644
index 0000000..64fe29f
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.account.api.IAccountData;
+import com.ning.billing.account.api.IAccountUserApi;
+
+import java.util.List;
+import java.util.UUID;
+
+public class MockIAccountUserApi implements IAccountUserApi
+{
+    @Override
+    public IAccount createAccount(final IAccountData data)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IAccount getAccountByKey(final String key)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public IAccount getAccountFromId(final UUID uid)
+    {
+        return null;
+    }
+
+    @Override
+    public List<IAccount> getAccounts()
+    {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java
new file mode 100644
index 0000000..4ed2e8c
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIEntitlementUserApi.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import com.ning.billing.account.api.IAccount;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
+import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
+import com.ning.billing.entitlement.api.user.ISubscription;
+import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
+import org.joda.time.DateTime;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class MockIEntitlementUserApi implements IEntitlementUserApi
+{
+    private final Map<UUID, String> subscriptionBundles = new HashMap<UUID, String>();
+
+    public MockIEntitlementUserApi(final UUID bundleUUID, final String key)
+    {
+        subscriptionBundles.put(bundleUUID, key);
+    }
+
+    @Override
+    public ISubscriptionBundle getBundleFromId(final UUID id)
+    {
+        final String key = subscriptionBundles.get(id);
+        if (key == null) {
+            return null;
+        }
+
+        return new ISubscriptionBundle()
+        {
+            @Override
+            public UUID getAccountId()
+            {
+                return UUID.randomUUID();
+            }
+
+            @Override
+            public UUID getId()
+            {
+                return id;
+            }
+
+            @Override
+            public DateTime getStartDate()
+            {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public String getKey()
+            {
+                return key;
+            }
+
+            @Override
+            public void setPrivate(final String name, final String value)
+            {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public String getPrivate(final String name)
+            {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    @Override
+    public ISubscription getSubscriptionFromId(final UUID id)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<ISubscriptionBundle> getBundlesForAccount(final UUID accountId)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<ISubscription> getSubscriptionsForBundle(final UUID bundleId)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ISubscriptionBundle createBundleForAccount(final IAccount account, final String bundleKey) throws EntitlementUserApiException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ISubscription createSubscription(final UUID bundleId, final String productName, final BillingPeriod term, final String planSet, final DateTime requestedDate) throws EntitlementUserApiException
+    {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java b/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
index 4912957..2303e1d 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
@@ -16,9 +16,6 @@
 
 package com.ning.billing.analytics;
 
-import java.math.BigDecimal;
-import java.util.Date;
-
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.IDuration;
@@ -28,6 +25,9 @@ import com.ning.billing.catalog.api.IPlanPhase;
 import com.ning.billing.catalog.api.IPrice;
 import com.ning.billing.catalog.api.PhaseType;
 
+import java.math.BigDecimal;
+import java.util.Date;
+
 public class MockPhase implements IPlanPhase
 {
     private final PhaseType cohort;
@@ -60,10 +60,11 @@ public class MockPhase implements IPlanPhase
                 return BigDecimal.valueOf(price);
             }
 
-			@Override
-			public Date getEffectiveDateForExistingSubscriptons() {
-				return new Date();
-			}
+            @Override
+            public Date getEffectiveDateForExistingSubscriptons()
+            {
+                return new Date();
+            }
         };
     }
 
@@ -83,11 +84,12 @@ public class MockPhase implements IPlanPhase
             {
                 return BigDecimal.valueOf(price);
             }
-            
-			@Override
-			public Date getEffectiveDateForExistingSubscriptons() {
-				return new Date();
-			}
+
+            @Override
+            public Date getEffectiveDateForExistingSubscriptons()
+            {
+                return new Date();
+            }
         };
     }
 
@@ -100,7 +102,12 @@ public class MockPhase implements IPlanPhase
     @Override
     public String getName()
     {
-        return plan.getName() + "-" + cohort;
+        if (plan == null) {
+            return null;
+        }
+        else {
+            return plan.getName() + "-" + cohort;
+        }
     }
 
     @Override
diff --git a/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
new file mode 100644
index 0000000..9f13e2e
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/TestAnalyticsListener.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics;
+
+import com.ning.billing.catalog.api.IPlan;
+import com.ning.billing.catalog.api.IPlanPhase;
+import com.ning.billing.catalog.api.IProduct;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.user.ISubscription;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.events.IEvent;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.UUID;
+
+public class TestAnalyticsListener
+{
+    private static final String KEY = "1234";
+
+    private final MockBusinessSubscriptionTransitionDao dao = new MockBusinessSubscriptionTransitionDao();
+    private final UUID subscriptionId = UUID.randomUUID();
+    private final UUID bundleUUID = UUID.randomUUID();
+    private final IProduct product = new MockProduct("platinium", "subscription", ProductCategory.BASE);
+    private final IPlan plan = new MockPlan("platinum-monthly", product);
+    private final IPlanPhase phase = new MockPhase(PhaseType.EVERGREEN, plan, MockDuration.UNLIMITED(), 25.95);
+    private final String priceList = "something";
+
+    private AnalyticsListener listener;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() throws Exception
+    {
+        final BusinessSubscriptionTransitionRecorder recorder = new BusinessSubscriptionTransitionRecorder(dao, new MockIEntitlementUserApi(bundleUUID, KEY), new MockIAccountUserApi());
+        listener = new AnalyticsListener(recorder, null);
+    }
+
+    @Test(groups = "fast")
+    public void testSubscriptionLifecycle() throws Exception
+    {
+        // Create a subscription
+        final DateTime effectiveTransitionTime = new DateTime(DateTimeZone.UTC);
+        final DateTime requestedTransitionTime = new DateTime(DateTimeZone.UTC);
+        final SubscriptionTransition firstTransition = createFirstSubscriptionTransition(requestedTransitionTime, effectiveTransitionTime);
+        final BusinessSubscriptionTransition firstBST = createExpectedFirstBST(requestedTransitionTime, effectiveTransitionTime);
+        listener.handleSubscriptionTransitionChange(firstTransition);
+        Assert.assertEquals(dao.getTransitions(KEY).size(), 1);
+        Assert.assertEquals(dao.getTransitions(KEY).get(0), firstBST);
+
+        // Pause it
+        final DateTime effectivePauseTransitionTime = new DateTime(DateTimeZone.UTC);
+        final DateTime requestedPauseTransitionTime = new DateTime(DateTimeZone.UTC);
+        final SubscriptionTransition pausedSubscriptionTransition = createPauseSubscriptionTransition(effectivePauseTransitionTime, requestedPauseTransitionTime, firstTransition.getNextState());
+        final BusinessSubscriptionTransition pausedBST = createExpectedPausedBST(requestedPauseTransitionTime, effectivePauseTransitionTime, firstBST.getNextSubscription());
+        listener.handleSubscriptionTransitionChange(pausedSubscriptionTransition);
+        Assert.assertEquals(dao.getTransitions(KEY).size(), 2);
+        Assert.assertEquals(dao.getTransitions(KEY).get(1), pausedBST);
+
+        // Un-Pause it
+        final DateTime effectiveResumeTransitionTime = new DateTime(DateTimeZone.UTC);
+        final DateTime requestedResumeTransitionTime = new DateTime(DateTimeZone.UTC);
+        final SubscriptionTransition resumedSubscriptionTransition = createResumeSubscriptionTransition(requestedResumeTransitionTime, effectiveResumeTransitionTime, pausedSubscriptionTransition.getNextState());
+        final BusinessSubscriptionTransition resumedBST = createExpectedResumedBST(requestedResumeTransitionTime, effectiveResumeTransitionTime, pausedBST.getNextSubscription());
+        listener.handleSubscriptionTransitionChange(resumedSubscriptionTransition);
+        Assert.assertEquals(dao.getTransitions(KEY).size(), 3);
+        Assert.assertEquals(dao.getTransitions(KEY).get(2), resumedBST);
+
+        // Cancel it
+        final DateTime effectiveCancelTransitionTime = new DateTime(DateTimeZone.UTC);
+        final DateTime requestedCancelTransitionTime = new DateTime(DateTimeZone.UTC);
+        listener.handleSubscriptionTransitionChange(createCancelSubscriptionTransition(requestedCancelTransitionTime, effectiveCancelTransitionTime, resumedSubscriptionTransition.getNextState()));
+        final BusinessSubscriptionTransition cancelledBST = createExpectedCancelledBST(requestedCancelTransitionTime, effectiveCancelTransitionTime, resumedBST.getNextSubscription());
+        Assert.assertEquals(dao.getTransitions(KEY).size(), 4);
+        Assert.assertEquals(dao.getTransitions(KEY).get(3), cancelledBST);
+    }
+
+    private BusinessSubscriptionTransition createExpectedFirstBST(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(plan);
+        final ISubscription.SubscriptionState subscriptionState = ISubscription.SubscriptionState.ACTIVE;
+        final BusinessSubscription emptyBST = new BusinessSubscription(null, null, null, null, null, subscriptionId, bundleUUID);
+        return createExpectedBST(event, requestedTransitionTime, effectiveTransitionTime, emptyBST, subscriptionState);
+    }
+
+    private BusinessSubscriptionTransition createExpectedPausedBST(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final BusinessSubscription lastSubscription)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(plan);
+        final ISubscription.SubscriptionState subscriptionState = ISubscription.SubscriptionState.PAUSED;
+        return createExpectedBST(event, requestedTransitionTime, effectiveTransitionTime, lastSubscription, subscriptionState);
+    }
+
+    private BusinessSubscriptionTransition createExpectedResumedBST(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final BusinessSubscription lastSubscription)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(plan);
+        final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.ACTIVE;
+        return createExpectedBST(event, requestedTransitionTime, effectiveTransitionTime, lastSubscription, nextState);
+    }
+
+    private BusinessSubscriptionTransition createExpectedCancelledBST(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final BusinessSubscription lastSubscription)
+    {
+        final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(plan);
+        final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.CANCELLED;
+        return createExpectedBST(event, requestedTransitionTime, effectiveTransitionTime, lastSubscription, nextState);
+    }
+
+    private BusinessSubscriptionTransition createExpectedBST(
+        final BusinessSubscriptionEvent eventType,
+        final DateTime requestedTransitionTime,
+        final DateTime effectiveTransitionTime,
+        final BusinessSubscription previousSubscription,
+        final ISubscription.SubscriptionState nextState
+    )
+    {
+        return new BusinessSubscriptionTransition(
+            KEY,
+            requestedTransitionTime,
+            eventType,
+            previousSubscription,
+            new BusinessSubscription(
+                plan,
+                phase,
+                null,
+                effectiveTransitionTime,
+                nextState,
+                subscriptionId,
+                bundleUUID
+            )
+        );
+    }
+
+    private SubscriptionTransition createFirstSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime)
+    {
+        final ApiEventType eventType = ApiEventType.CREATE;
+        final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.ACTIVE;
+        return new SubscriptionTransition(
+            subscriptionId,
+            bundleUUID,
+            IEvent.EventType.API_USER,
+            eventType,
+            requestedTransitionTime,
+            effectiveTransitionTime,
+            null,
+            null,
+            null,
+            null,
+            nextState,
+            plan,
+            phase,
+            priceList
+        );
+    }
+
+    private SubscriptionTransition createPauseSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final ISubscription.SubscriptionState previousState)
+    {
+        final ApiEventType eventType = ApiEventType.PAUSE;
+        final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.PAUSED;
+        return createSubscriptionTransition(eventType, requestedTransitionTime, effectiveTransitionTime, previousState, nextState);
+    }
+
+    private SubscriptionTransition createResumeSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final ISubscription.SubscriptionState previousState)
+    {
+        final ApiEventType eventType = ApiEventType.RESUME;
+        final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.ACTIVE;
+        return createSubscriptionTransition(eventType, requestedTransitionTime, effectiveTransitionTime, previousState, nextState);
+    }
+
+    private SubscriptionTransition createCancelSubscriptionTransition(final DateTime requestedTransitionTime, final DateTime effectiveTransitionTime, final ISubscription.SubscriptionState previousState)
+    {
+        final ApiEventType eventType = ApiEventType.CANCEL;
+        final ISubscription.SubscriptionState nextState = ISubscription.SubscriptionState.CANCELLED;
+        return createSubscriptionTransition(eventType, requestedTransitionTime, effectiveTransitionTime, previousState, nextState);
+    }
+
+    private SubscriptionTransition createSubscriptionTransition(
+        final ApiEventType eventType,
+        final DateTime requestedTransitionTime,
+        final DateTime effectiveTransitionTime,
+        final ISubscription.SubscriptionState previousState,
+        final ISubscription.SubscriptionState nextState
+    )
+    {
+        return new SubscriptionTransition(
+            subscriptionId,
+            bundleUUID,
+            IEvent.EventType.API_USER,
+            eventType,
+            requestedTransitionTime,
+            effectiveTransitionTime,
+            previousState,
+            plan,
+            phase,
+            priceList,
+            nextState,
+            plan,
+            phase,
+            priceList
+        );
+    }
+}
\ No newline at end of file
diff --git a/analytics/src/test/java/com/ning/billing/analytics/utils/TestRounder.java b/analytics/src/test/java/com/ning/billing/analytics/utils/TestRounder.java
new file mode 100644
index 0000000..b3414d7
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/utils/TestRounder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.utils;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.math.BigDecimal;
+
+public class TestRounder
+{
+    @Test(groups = "fast")
+    public void testRound() throws Exception
+    {
+        Assert.assertEquals(Rounder.round(null), 0.0);
+        Assert.assertEquals(Rounder.round(BigDecimal.ZERO), 0.0);
+        Assert.assertEquals(Rounder.round(BigDecimal.ONE), 1.0);
+        Assert.assertEquals(Rounder.round(BigDecimal.TEN), 10.0);
+        Assert.assertEquals(Rounder.round(BigDecimal.valueOf(1.33333)), 1.3333);
+        Assert.assertEquals(Rounder.round(BigDecimal.valueOf(4444.33333)), 4444.3333);
+        Assert.assertEquals(Rounder.round(BigDecimal.valueOf(10.11111)), 10.1111);
+        Assert.assertEquals(Rounder.round(BigDecimal.valueOf(10.11115)), 10.1112);
+        Assert.assertEquals(Rounder.round(BigDecimal.valueOf(10.11116)), 10.1112);
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/analytics/api/IAnalyticsService.java b/api/src/main/java/com/ning/billing/analytics/api/IAnalyticsService.java
new file mode 100644
index 0000000..2a5d0c5
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/analytics/api/IAnalyticsService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.api;
+
+import com.ning.billing.lifecycle.IService;
+
+public interface IAnalyticsService extends IService {
+}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/Plan.java b/catalog/src/main/java/com/ning/billing/catalog/Plan.java
index 9a994f0..5b0a926 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/Plan.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/Plan.java
@@ -119,10 +119,12 @@ public class Plan extends ValidatingConfig<Catalog> implements IPlan {
 		super.initialize(catalog, sourceURI);
 		if(finalPhase != null) {
 			finalPhase.setPlan(this);
+			finalPhase.initialize(catalog, sourceURI);
 		}
 		if(initialPhases != null) {
 			for(PlanPhase p : initialPhases) {
 				p.setPlan(this);
+				p.initialize(catalog, sourceURI);
 			}
 		}
 	}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java b/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java
index 821d1f5..61dab1e 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java
@@ -138,15 +138,11 @@ public class PlanPhase extends ValidatingConfig<Catalog> implements IPlanPhase {
 
 	}
 	
-
 	@Override
 	public void initialize(Catalog root, URI uri) {
-		fixedPrice.initialize(root, uri);
-		recurringPrice.initialize(root, uri);
+		if (fixedPrice != null) { fixedPrice.initialize(root, uri);  }	
+		if (recurringPrice != null) { recurringPrice.initialize(root, uri); }
 	}
-
-	
-	
 	
 	protected PlanPhase setFixedPrice(InternationalPrice price) {
 		this.fixedPrice = price;
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 5280209..05f6e9d 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
@@ -276,6 +276,11 @@ public class Subscription extends PrivateFields  implements ISubscription {
         try {
 
             IProduct destProduct = catalog.getProductFromName(productName);
+            // STEPH really catalog exception
+            if (destProduct == null) {
+                throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BAD_CATALOG,
+                        productName, term.toString(), "");
+            }
 
             IPlan currentPlan = getCurrentPlan();
             PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),

pom.xml 5(+5 -0)

diff --git a/pom.xml b/pom.xml
index 8038652..4ed2b26 100644
--- a/pom.xml
+++ b/pom.xml
@@ -56,6 +56,11 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-account</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-entitlement</artifactId>
                 <version>${project.version}</version>
             </dependency>
diff --git a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
index d8aad63..e8a77f6 100644
--- a/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
+++ b/util/src/test/java/com/ning/billing/dbi/MysqlTestingHelper.java
@@ -25,6 +25,7 @@ import org.skife.jdbi.v2.IDBI;
 import org.skife.jdbi.v2.tweak.HandleCallback;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.testng.Assert;
 
 import java.io.File;
 import java.io.IOException;
@@ -47,13 +48,22 @@ public class MysqlTestingHelper
     private MysqldResource mysqldResource;
     private int port = 0;
 
-    public void startMysql() throws IOException
+    public MysqlTestingHelper()
     {
         // New socket on any free port
-        final ServerSocket socket = new ServerSocket(0);
-        port = socket.getLocalPort();
-        socket.close();
+        final ServerSocket socket;
+        try {
+            socket = new ServerSocket(0);
+            port = socket.getLocalPort();
+            socket.close();
+        }
+        catch (IOException e) {
+            Assert.fail();
+        }
+    }
 
+    public void startMysql() throws IOException
+    {
         dbDir = File.createTempFile("mysql", "");
         dbDir.delete();
         dbDir.mkdir();
@@ -76,7 +86,7 @@ public class MysqlTestingHelper
 
     public void cleanupTable(final String table)
     {
-        if (mysqldResource == null || mysqldResource.isRunning()) {
+        if (mysqldResource == null || !mysqldResource.isRunning()) {
             log.error("Asked to cleanup table " + table + " but MySQL is not running!");
             return;
         }
@@ -88,7 +98,7 @@ public class MysqlTestingHelper
             @Override
             public Void withHandle(final Handle handle) throws Exception
             {
-                handle.createQuery("delete * from " + table);
+                handle.execute("truncate " + table);
                 return null;
             }
         });
@@ -103,7 +113,7 @@ public class MysqlTestingHelper
         }
     }
 
-    public IDBI getDBI()
+    public DBI getDBI()
     {
         final String dbiString = "jdbc:mysql://localhost:" + port + "/" + DB_NAME + "?createDatabaseIfNotExist=true";
         return new DBI(dbiString, USERNAME, PASSWORD);