killbill-aplcache

Details

diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
index 6ec95b9..4518731 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
@@ -21,6 +21,8 @@ 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.clock.Clock;
+import com.ning.billing.util.clock.DefaultClock;
 import com.ning.billing.util.glue.EventBusModule;
 import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
@@ -42,6 +44,8 @@ public class AnalyticsTestModule extends AnalyticsModule
         install(new TagStoreModule());
         install(new NotificationQueueModule());
 
+        bind(Clock.class).to(DefaultClock.class).asEagerSingleton();
+
         // Install the Dao layer
         final MysqlTestingHelper helper = new MysqlTestingHelper();
         bind(MysqlTestingHelper.class).toInstance(helper);

beatrix/pom.xml 10(+10 -0)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index 32ceef9..e0e4352 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -28,6 +28,10 @@
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-entitlement</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-invoice</artifactId>
+        </dependency>
        <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-catalog</artifactId>
@@ -78,6 +82,12 @@
             <artifactId>commons-io</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-util</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/glue/BeatrixModule.java b/beatrix/src/main/java/com/ning/billing/beatrix/glue/BeatrixModule.java
index 7a672c1..02da6c9 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/glue/BeatrixModule.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/glue/BeatrixModule.java
@@ -17,13 +17,14 @@
 package com.ning.billing.beatrix.glue;
 
 import com.google.inject.AbstractModule;
+import com.ning.billing.beatrix.lifecycle.DefaultLifecycle;
 import com.ning.billing.beatrix.lifecycle.Lifecycle;
 
 public class BeatrixModule extends AbstractModule {
 
     @Override
     protected void configure() {
-        bind(Lifecycle.class).asEagerSingleton();
+        bind(Lifecycle.class).to(DefaultLifecycle.class).asEagerSingleton();
     }
 
 }
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/DefaultLifecycle.java b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/DefaultLifecycle.java
new file mode 100644
index 0000000..87075a9
--- /dev/null
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/DefaultLifecycle.java
@@ -0,0 +1,174 @@
+/*
+ * 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.beatrix.lifecycle;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.ning.billing.lifecycle.KillbillService;
+import com.ning.billing.lifecycle.LifecycleHandlerType;
+import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
+import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel.Sequence;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+
+public class DefaultLifecycle implements Lifecycle {
+
+    private final static Logger log = LoggerFactory.getLogger(DefaultLifecycle.class);
+    private final SetMultimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> handlersByLevel;
+
+    private final ServiceFinder serviceFinder;
+
+    protected final Injector injector;
+
+    @Inject
+    public DefaultLifecycle(Injector injector) {
+
+        this.serviceFinder = new ServiceFinder(DefaultLifecycle.class.getClassLoader());
+        this.handlersByLevel = Multimaps.newSetMultimap(new ConcurrentHashMap<LifecycleLevel, Collection<LifecycleHandler<? extends KillbillService>>>(),
+
+                new Supplier<Set<LifecycleHandler<? extends KillbillService>>>() {
+            @Override
+            public Set<LifecycleHandler<? extends KillbillService>> get() {
+                return new CopyOnWriteArraySet<LifecycleHandler<? extends KillbillService>>();
+            }
+        });
+        this.injector = injector;
+
+        init();
+    }
+
+
+    @Override
+    public void fireStartupSequencePriorEventRegistration() {
+        fireSequence(Sequence.STARTUP_PRE_EVENT_REGISTRATION);
+    }
+
+    @Override
+    public void fireStartupSequencePostEventRegistration() {
+        fireSequence(Sequence.STARTUP_POST_EVENT_REGISTRATION);
+    }
+
+    @Override
+    public void fireShutdownSequencePriorEventUnRegistration() {
+        fireSequence(Sequence.SHUTDOWN_PRE_EVENT_UNREGISTRATION);
+    }
+
+    @Override
+    public void fireShutdownSequencePostEventUnRegistration() {
+        fireSequence(Sequence.SHUTDOWN_POST_EVENT_UNREGISTRATION);
+    }
+
+    protected Set<? extends KillbillService> findServices() {
+
+        Set<KillbillService> result = new HashSet<KillbillService>();
+        Set<Class<? extends KillbillService>> services =  serviceFinder.getServices();
+        for (Class<? extends KillbillService> cur : services) {
+            log.debug("Found service {}", cur.getName());
+            try {
+                KillbillService instance = injector.getInstance(cur);
+                log.debug("got instance {}", instance.getName());
+                result.add(instance);
+            } catch (Exception e) {
+                logWarn("Failed to inject " + cur.getName(), e);
+            }
+
+        }
+        return result;
+    }
+
+    private void init() {
+        Set<? extends KillbillService> services = findServices();
+        Iterator<? extends KillbillService> it = services.iterator();
+        while (it.hasNext()) {
+            handlersByLevel.putAll(findAllHandlers(it.next()));
+        }
+    }
+
+    private void fireSequence(Sequence seq) {
+        List<LifecycleLevel> levels = LifecycleLevel.getLevelsForSequence(seq);
+        for (LifecycleLevel cur : levels) {
+            doFireStage(cur);
+        }
+    }
+
+    private void doFireStage(LifecycleLevel level) {
+        log.info("Killbill lifecycle firing stage {}", level);
+        Set<LifecycleHandler<? extends KillbillService>> handlers = handlersByLevel.get(level);
+        for (LifecycleHandler<? extends KillbillService> cur : handlers) {
+
+            try {
+                Method method = cur.getMethod();
+                KillbillService target = cur.getTarget();
+                log.info("Killbill lifecycle calling handler {} for service {}", cur.getMethod().getName(), target.getName());
+                method.invoke(target);
+            } catch (Exception e) {
+                logWarn("Killbill lifecycle failed to invoke lifecycle handler", e);
+            }
+        }
+
+    }
+
+
+    // Used to disable valid injection failure from unit tests
+    protected void logWarn(String msg, Exception e) {
+        log.warn(msg, e);
+    }
+
+    private Multimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> findAllHandlers(KillbillService service) {
+        Multimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> methodsInService = HashMultimap.create();
+        Class<? extends KillbillService> clazz = service.getClass();
+        for (Method method : clazz.getMethods()) {
+            LifecycleHandlerType annotation = method.getAnnotation(LifecycleHandlerType.class);
+            if (annotation != null) {
+                LifecycleLevel level = annotation.value();
+                LifecycleHandler<? extends KillbillService> handler = new  LifecycleHandler<KillbillService>(service, method);
+                methodsInService.put(level, handler);
+            }
+        }
+        return methodsInService;
+    }
+
+
+    private final class LifecycleHandler<T> {
+        private final T target;
+        private final Method method;
+
+        public LifecycleHandler(T target, Method method) {
+            this.target = target;
+            this.method = method;
+        }
+
+        public T getTarget() {
+            return target;
+        }
+
+        public Method getMethod() {
+            return method;
+        }
+    }
+}
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/Lifecycle.java b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/Lifecycle.java
index c0edf2c..8192a65 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/Lifecycle.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/Lifecycle.java
@@ -16,156 +16,14 @@
 
 package com.ning.billing.beatrix.lifecycle;
 
-import com.google.common.base.Supplier;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.SetMultimap;
-import com.google.inject.Inject;
-import com.google.inject.Injector;
-import com.ning.billing.lifecycle.KillbillService;
-import com.ning.billing.lifecycle.LifecycleHandlerType;
-import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
-import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel.Sequence;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import java.lang.reflect.Method;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArraySet;
+public interface Lifecycle {
 
+    public void fireStartupSequencePriorEventRegistration();
 
-public class Lifecycle {
+    public void fireStartupSequencePostEventRegistration();
 
-    private final static Logger log = LoggerFactory.getLogger(Lifecycle.class);
-    private final SetMultimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> handlersByLevel;
+    public void fireShutdownSequencePriorEventUnRegistration();
 
-    private final ServiceFinder serviceFinder;
-
-    private final Injector injector;
-
-    @Inject
-    public Lifecycle(Injector injector) {
-
-        this.serviceFinder = new ServiceFinder(Lifecycle.class.getClassLoader());
-        this.handlersByLevel = Multimaps.newSetMultimap(new ConcurrentHashMap<LifecycleLevel, Collection<LifecycleHandler<? extends KillbillService>>>(),
-
-                new Supplier<Set<LifecycleHandler<? extends KillbillService>>>() {
-            @Override
-            public Set<LifecycleHandler<? extends KillbillService>> get() {
-                return new CopyOnWriteArraySet<LifecycleHandler<? extends KillbillService>>();
-            }
-        });
-        this.injector = injector;
-
-        init();
-    }
-
-    public void init() {
-        Set<? extends KillbillService> services = findServices();
-        Iterator<? extends KillbillService> it = services.iterator();
-        while (it.hasNext()) {
-            handlersByLevel.putAll(findAllHandlers(it.next()));
-        }
-    }
-
-
-    public void fireStartupSequencePriorEventRegistration() {
-        fireSequence(Sequence.STARTUP_PRE_EVENT_REGISTRATION);
-    }
-
-    public void fireStartupSequencePostEventRegistration() {
-        fireSequence(Sequence.STARTUP_POST_EVENT_REGISTRATION);
-    }
-
-    public void fireShutdownSequencePriorEventUnRegistration() {
-        fireSequence(Sequence.SHUTDOWN_PRE_EVENT_UNREGISTRATION);
-    }
-
-    public void fireShutdownSequencePostEventUnRegistration() {
-        fireSequence(Sequence.SHUTDOWN_POST_EVENT_UNREGISTRATION);
-    }
-
-    private void fireSequence(Sequence seq) {
-        List<LifecycleLevel> levels = LifecycleLevel.getLevelsForSequence(seq);
-        for (LifecycleLevel cur : levels) {
-            doFireStage(cur);
-        }
-    }
-
-    private void doFireStage(LifecycleLevel level) {
-        log.info("Killbill lifecycle firing stage {}", level);
-        Set<LifecycleHandler<? extends KillbillService>> handlers = handlersByLevel.get(level);
-        for (LifecycleHandler<? extends KillbillService> cur : handlers) {
-
-            try {
-                Method method = cur.getMethod();
-                KillbillService target = cur.getTarget();
-                log.info("Killbill lifecycle calling handler {} for service {}", cur.getMethod().getName(), target.getName());
-                method.invoke(target);
-            } catch (Exception e) {
-                logWarn("Killbill lifecycle failed to invoke lifecycle handler", e);
-            }
-        }
-
-    }
-
-
-    private Set<? extends KillbillService> findServices() {
-
-        Set<KillbillService> result = new HashSet<KillbillService>();
-        Set<Class<? extends KillbillService>> services =  serviceFinder.getServices();
-        for (Class<? extends KillbillService> cur : services) {
-            log.debug("Found service {}", cur.getName());
-            try {
-                KillbillService instance = injector.getInstance(cur);
-                log.debug("got instance {}", instance.getName());
-                result.add(instance);
-            } catch (Exception e) {
-                logWarn("Failed to inject " + cur.getName(), e);
-            }
-
-        }
-        return result;
-    }
-
-
-    // Used to disable valid injection failure from unit tests
-    protected void logWarn(String msg, Exception e) {
-        log.warn(msg, e);
-    }
-
-    public Multimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> findAllHandlers(KillbillService service) {
-        Multimap<LifecycleLevel, LifecycleHandler<? extends KillbillService>> methodsInService = HashMultimap.create();
-        Class<? extends KillbillService> clazz = service.getClass();
-        for (Method method : clazz.getMethods()) {
-            LifecycleHandlerType annotation = method.getAnnotation(LifecycleHandlerType.class);
-            if (annotation != null) {
-                LifecycleLevel level = annotation.value();
-                LifecycleHandler<? extends KillbillService> handler = new  LifecycleHandler<KillbillService>(service, method);
-                methodsInService.put(level, handler);
-            }
-        }
-        return methodsInService;
-    }
-
-
-    private final class LifecycleHandler<T> {
-        private final T target;
-        private final Method method;
-
-        public LifecycleHandler(T target, Method method) {
-            this.target = target;
-            this.method = method;
-        }
-
-        public T getTarget() {
-            return target;
-        }
-
-        public Method getMethod() {
-            return method;
-        }
-    }
+    public void fireShutdownSequencePostEventUnRegistration();
 }
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/MockModule.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/MockModule.java
new file mode 100644
index 0000000..5fc81c6
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/MockModule.java
@@ -0,0 +1,104 @@
+/*
+ * 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.beatrix.integration.inv_ent;
+
+import static org.testng.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Set;
+
+import org.skife.config.ConfigurationObjectFactory;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.IDBI;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.ning.billing.account.glue.AccountModule;
+import com.ning.billing.beatrix.lifecycle.DefaultLifecycle;
+import com.ning.billing.beatrix.lifecycle.Lifecycle;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.glue.CatalogModule;
+import com.ning.billing.dbi.DBIProvider;
+import com.ning.billing.dbi.DbiConfig;
+import com.ning.billing.entitlement.api.EntitlementService;
+import com.ning.billing.entitlement.glue.EntitlementModule;
+import com.ning.billing.invoice.api.InvoiceService;
+import com.ning.billing.invoice.glue.InvoiceModule;
+import com.ning.billing.lifecycle.KillbillService;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.eventbus.BusService;
+import com.ning.billing.util.glue.EventBusModule;
+import com.ning.billing.util.glue.NotificationQueueModule;
+
+
+public class MockModule extends AbstractModule {
+
+    @Override
+    protected void configure() {
+
+        loadSystemPropertiesFromClasspath("/resource.properties");
+
+        bind(Clock.class).to(ClockMock.class).asEagerSingleton();
+        bind(ClockMock.class).asEagerSingleton();
+        bind(Lifecycle.class).to(SubsetDefaultLifecycle.class).asEagerSingleton();
+        bind(IDBI.class).to(DBI.class).asEagerSingleton();
+        bind(DBI.class).toProvider(DBIProvider.class).asEagerSingleton();
+        final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
+        bind(DbiConfig.class).toInstance(config);
+        install(new EventBusModule());
+        install(new NotificationQueueModule());
+        install(new AccountModule());
+        install(new CatalogModule());
+        install(new EntitlementModule());
+        install(new InvoiceModule());
+    }
+
+
+    private static void loadSystemPropertiesFromClasspath(final String resource) {
+        final URL url = TestBasic.class.getResource(resource);
+        assertNotNull(url);
+        try {
+            System.getProperties().load( url.openStream() );
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private final static class SubsetDefaultLifecycle extends DefaultLifecycle {
+
+
+        @Inject
+        public SubsetDefaultLifecycle(Injector injector) {
+            super(injector);
+        }
+
+        @Override
+        protected Set<? extends KillbillService> findServices() {
+            ImmutableSet<? extends KillbillService> services = new ImmutableSet.Builder<KillbillService>()
+                            .add(injector.getInstance(BusService.class))
+                            .add(injector.getInstance(CatalogService.class))
+                            .add(injector.getInstance(EntitlementService.class))
+                            .add(injector.getInstance(InvoiceService.class))
+                            .build();
+            return services;
+        }
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java
new file mode 100644
index 0000000..b79d2a9
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBasic.java
@@ -0,0 +1,286 @@
+/*
+ * 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.beatrix.integration.inv_ent;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.Interval;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.IDBI;
+import org.skife.jdbi.v2.TransactionCallback;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.AfterSuite;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+
+import com.google.inject.Inject;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.beatrix.integration.inv_ent.TestBusHandler.NextEvent;
+import com.ning.billing.beatrix.lifecycle.Lifecycle;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.entitlement.api.EntitlementService;
+import com.ning.billing.entitlement.api.user.EntitlementUserApi;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.invoice.api.InvoiceService;
+import com.ning.billing.invoice.api.InvoiceUserApi;
+
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.eventbus.BusService;
+
+@Guice(modules = {MockModule.class})
+public class TestBasic {
+
+    private static final Logger log = LoggerFactory.getLogger(TestBasic.class);
+    private static long AT_LEAST_ONE_MONTH_MS = (31 * 24 * 3600 * 1000);
+
+    @Inject IDBI dbi;
+
+    @Inject
+    private ClockMock clock;
+
+    @Inject
+    private Lifecycle lifecycle;
+
+    @Inject
+    private BusService busService;
+
+    @Inject
+    private EntitlementService entitlementService;
+
+    @Inject
+    private InvoiceService invoiceService;
+
+
+    private EntitlementUserApi entitlementUserApi;
+
+    private InvoiceUserApi invoiceUserApi;
+
+    private TestBusHandler busHandler;
+
+
+
+    @BeforeSuite(alwaysRun = true)
+    public void setup() throws Exception{
+
+        /**
+         * Initialize lifecyle for subset of services
+         */
+        busHandler = new TestBusHandler();
+        lifecycle.fireStartupSequencePriorEventRegistration();
+        busService.getBus().register(busHandler);
+        lifecycle.fireStartupSequencePostEventRegistration();
+
+        /**
+         * Retrieve APIs
+         */
+        entitlementUserApi = entitlementService.getUserApi();
+        invoiceUserApi = invoiceService.getUserApi();
+    }
+
+    @AfterSuite(alwaysRun = true)
+    public void tearDown() throws Exception {
+        lifecycle.fireShutdownSequencePriorEventUnRegistration();
+        busService.getBus().unregister(busHandler);
+        lifecycle.fireShutdownSequencePostEventUnRegistration();
+    }
+
+
+    @BeforeMethod(alwaysRun = true)
+    public void setupTest() {
+
+        log.warn("\n");
+        log.warn("RESET TEST FRAMEWORK\n\n");
+        busHandler.reset();
+        clock.resetDeltaFromReality();
+        cleanupData();
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void cleanupTest() {
+        log.warn("DONE WITH TEST\n");
+    }
+
+    private void cleanupData() {
+        dbi.inTransaction(new TransactionCallback<Void>() {
+            @Override
+            public Void inTransaction(Handle h, TransactionStatus status)
+                    throws Exception {
+                h.execute("truncate table accounts");
+                h.execute("truncate table events");
+                h.execute("truncate table subscriptions");
+                h.execute("truncate table bundles");
+                h.execute("truncate table notifications");
+                h.execute("truncate table claimed_notifications");
+                h.execute("truncate table invoices");
+                h.execute("truncate table invoice_items");
+                h.execute("truncate table tag_descriptions");
+                h.execute("truncate table tags");
+                return null;
+            }
+        });
+    }
+
+
+    private DateTime checkAndGetCTD(UUID subscriptionId) {
+        SubscriptionData subscription = (SubscriptionData) entitlementUserApi.getSubscriptionFromId(subscriptionId);
+        DateTime ctd = subscription.getChargedThroughDate();
+        assertNotNull(ctd);
+        assertTrue(clock.getUTCNow().isBefore(ctd));
+        return ctd;
+    }
+
+    @Test(groups = "fast", enabled = false)
+    public void testSimple() throws Exception {
+
+        UUID accountId = UUID.randomUUID();
+        SubscriptionBundle bundle = entitlementUserApi.createBundleForAccount(accountId, "whatever");
+
+        String productName = "Shotgun";
+        BillingPeriod term = BillingPeriod.MONTHLY;
+        String planSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+
+        //
+        // CREATE SUBSCRIPTION AND EXPECT BOTH EVENTS: NextEvent.CREATE NextEvent.INVOICE
+        //
+        busHandler.pushExpectedEvent(NextEvent.CREATE);
+        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+        SubscriptionData subscription = (SubscriptionData) entitlementUserApi.createSubscription(bundle.getId(),
+                new PlanPhaseSpecifier(productName, ProductCategory.BASE, term, planSetName, null), null);
+        assertNotNull(subscription);
+        assertTrue(busHandler.isCompleted(5000));
+
+        //
+        // VERIFY CTD HAS BEEN SET
+        //
+        checkAndGetCTD(subscription.getId());
+
+        //
+        // CHANGE PLAN IMMEDIATELY AND EXPECT BOTH EVENTS: NextEvent.CHANGE NextEvent.INVOICE
+        //
+        busHandler.pushExpectedEvent(NextEvent.CHANGE);
+        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+
+        BillingPeriod newTerm = BillingPeriod.MONTHLY;
+        String newPlanSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+        String newProductName = "Assault-Rifle";
+        subscription.changePlan(newProductName, newTerm, newPlanSetName, clock.getUTCNow());
+        assertTrue(busHandler.isCompleted(5000));
+
+
+        //
+        // VERIFY AGAIN CTD HAS BEEN SET
+        //
+        DateTime ctd = checkAndGetCTD(subscription.getId());
+
+        //
+        // CHANGE PAN EOT AND EXPECT NOTHING
+        //
+        newTerm = BillingPeriod.MONTHLY;
+        newPlanSetName = PriceListSet.DEFAULT_PRICELIST_NAME;
+        newProductName = "Pistol";
+        subscription.changePlan(newProductName, newTerm, newPlanSetName, clock.getUTCNow());
+
+        //
+        // MOVE TIME AFTER CTD AND EXPECT BOTH EVENTS : NextEvent.CHANGE NextEvent.INVOICE
+        //
+        busHandler.pushExpectedEvent(NextEvent.CHANGE);
+        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+        clock.setDeltaFromReality(ctd.getMillis() - clock.getUTCNow().getMillis());
+        //clock.setDeltaFromReality(AT_LEAST_ONE_MONTH_MS + 1000);
+        assertTrue(busHandler.isCompleted(5000));
+
+        //
+        // MOVE TIME AFTER NEXT BILL CYCLE DAY AND EXPECT EVENT : NextEvent.INVOICE
+        //
+        int maxCycles = 3;
+        DateTime lastCtd = null;
+        do {
+            clock.addDeltaFromReality(AT_LEAST_ONE_MONTH_MS + 1000);
+            busHandler.pushExpectedEvent(NextEvent.INVOICE);
+            assertTrue(busHandler.isCompleted(5000));
+            lastCtd = checkAndGetCTD(subscription.getId());
+        } while (maxCycles-- > 0);
+
+        //
+        // FINALY CANCEL SUBSCRIPTION EOT
+        //
+        subscription.cancel(clock.getUTCNow(), false);
+
+        // MOVE AFTER CANCEL DATE AND EXPECT EVENT : NextEvent.CANCEL, NextEvent.INVOICE
+        busHandler.pushExpectedEvent(NextEvent.CANCEL);
+        busHandler.pushExpectedEvent(NextEvent.INVOICE);
+        Interval it = new Interval(lastCtd, clock.getUTCNow());
+        clock.addDeltaFromReality(it.toDurationMillis());
+        assertTrue(busHandler.isCompleted(5000));
+    }
+
+
+    protected AccountData getAccountData() {
+        AccountData accountData = new AccountData() {
+            @Override
+            public String getName() {
+                return "firstName lastName";
+            }
+            @Override
+            public int getFirstNameLength() {
+                return "firstName".length();
+            }
+            @Override
+            public String getEmail() {
+                return "accountName@yahoo.com";
+            }
+            @Override
+            public String getPhone() {
+                return "4152876341";
+            }
+            @Override
+            public String getExternalKey() {
+                return "k123456";
+            }
+            @Override
+            public int getBillCycleDay() {
+                return 1;
+            }
+            @Override
+            public Currency getCurrency() {
+                return Currency.USD;
+            }
+            @Override
+            public String getPaymentProviderName() {
+                return "Paypal";
+            }
+        };
+        return accountData;
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBusHandler.java b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBusHandler.java
new file mode 100644
index 0000000..0c3e035
--- /dev/null
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/integration/inv_ent/TestBusHandler.java
@@ -0,0 +1,164 @@
+/*
+ * 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.beatrix.integration.inv_ent;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Joiner;
+import com.google.common.eventbus.Subscribe;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.invoice.api.InvoiceCreationNotification;
+
+public class TestBusHandler {
+
+    protected static final Logger log = LoggerFactory.getLogger(TestBusHandler.class);
+
+    private final List<NextEvent> nextExpectedEvent;
+
+    private volatile boolean completed;
+
+    public TestBusHandler() {
+        nextExpectedEvent = new Stack<NextEvent>();
+        this.completed = false;
+    }
+
+    public enum NextEvent {
+        MIGRATE_ENTITLEMENT,
+        CREATE,
+        CHANGE,
+        CANCEL,
+        UNCANCEL,
+        PAUSE,
+        RESUME,
+        PHASE,
+        INVOICE
+    }
+
+    @Subscribe
+    public void handleEntitlementEvents(SubscriptionTransition event) {
+        log.info(String.format("Got subscription event %s", event.toString()));
+        switch (event.getTransitionType()) {
+        case MIGRATE_ENTITLEMENT:
+            assertEqualsNicely(NextEvent.MIGRATE_ENTITLEMENT);
+            notifyIfStackEmpty();
+            break;
+        case CREATE:
+            assertEqualsNicely(NextEvent.CREATE);
+            notifyIfStackEmpty();
+
+            break;
+        case CANCEL:
+            assertEqualsNicely(NextEvent.CANCEL);
+            notifyIfStackEmpty();
+
+            break;
+        case CHANGE:
+            assertEqualsNicely(NextEvent.CHANGE);
+            notifyIfStackEmpty();
+
+            break;
+        case PAUSE:
+            assertEqualsNicely(NextEvent.PAUSE);
+            notifyIfStackEmpty();
+
+            break;
+        case RESUME:
+            assertEqualsNicely(NextEvent.RESUME);
+            notifyIfStackEmpty();
+
+            break;
+        case UNCANCEL:
+            assertEqualsNicely(NextEvent.UNCANCEL);
+            notifyIfStackEmpty();
+            break;
+        case PHASE:
+            assertEqualsNicely(NextEvent.PHASE);
+            notifyIfStackEmpty();
+            break;
+        default:
+            throw new RuntimeException("Unexpected event type " + event.getRequestedTransitionTime());
+        }
+    }
+
+    @Subscribe
+    public void handleInvoiceEvents(InvoiceCreationNotification event) {
+        log.info(String.format("Got Invoice event %s", event.toString()));
+        assertEqualsNicely(NextEvent.INVOICE);
+        notifyIfStackEmpty();
+
+    }
+
+    public void reset() {
+        nextExpectedEvent.clear();
+    }
+
+    public void pushExpectedEvent(NextEvent next) {
+        synchronized (this) {
+            nextExpectedEvent.add(next);
+            completed = false;
+        }
+    }
+
+    public boolean isCompleted(long timeout) {
+        synchronized (this) {
+            try {
+                wait(timeout);
+            } catch (Exception ignore) {
+            }
+        }
+        if (!completed) {
+            log.debug("TestBusHandler did not complete in " + timeout + " ms");
+        }
+        return completed;
+    }
+
+    private void notifyIfStackEmpty() {
+        log.debug("notifyIfStackEmpty ENTER");
+        synchronized (this) {
+            if (nextExpectedEvent.isEmpty()) {
+                log.debug("notifyIfStackEmpty EMPTY");
+                completed = true;
+                notify();
+            }
+        }
+        log.debug("notifyIfStackEmpty EXIT");
+    }
+
+    private void assertEqualsNicely(NextEvent expected) {
+
+        boolean foundIt = false;
+        Iterator<NextEvent> it = nextExpectedEvent.iterator();
+        while (it.hasNext()) {
+            NextEvent ev = it.next();
+            if (ev == expected) {
+                it.remove();
+                foundIt = true;
+                break;
+            }
+        }
+        if (!foundIt) {
+            Joiner joiner = Joiner.on(" ");
+            System.err.println("Expected event " + expected + " got " + joiner.join(nextExpectedEvent));
+            System.exit(1);
+        }
+    }
+}
diff --git a/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java b/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java
index 69e6a0d..595a01b 100644
--- a/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java
+++ b/beatrix/src/test/java/com/ning/billing/beatrix/lifecycle/TestLifecycle.java
@@ -34,7 +34,7 @@ public class TestLifecycle {
     private Service1 s1;
     private Service2 s2;
 
-    private Lifecycle lifecycle;
+    private DefaultLifecycle lifecycle;
 
     public static class ServiceBase {
 
@@ -122,7 +122,7 @@ public class TestLifecycle {
         final Injector g = Guice.createInjector(Stage.DEVELOPMENT, new TestLifecycleModule());
         s1 = g.getInstance(Service1.class);
         s2 = g.getInstance(Service2.class);
-        lifecycle = g.getInstance(Lifecycle.class);
+        lifecycle = g.getInstance(DefaultLifecycle.class);
     }
 
     @Test(enabled=true, groups={"fast"})
@@ -148,7 +148,7 @@ public class TestLifecycle {
         Assert.assertEquals(s1.getCount() + s2.getCount(), 1);
     }
 
-    public static class LifecycleNoWarn extends Lifecycle {
+    public static class LifecycleNoWarn extends DefaultLifecycle {
 
         @Inject
         public LifecycleNoWarn(Injector injector) {
@@ -163,7 +163,7 @@ public class TestLifecycle {
 
         @Override
         protected void configure() {
-            bind(Lifecycle.class).to(LifecycleNoWarn.class).asEagerSingleton();
+            bind(DefaultLifecycle.class).to(LifecycleNoWarn.class).asEagerSingleton();
             bind(Service1.class).asEagerSingleton();
             bind(Service2.class).asEagerSingleton();
         }
diff --git a/beatrix/src/test/resources/catalogSample.xml b/beatrix/src/test/resources/catalogSample.xml
new file mode 100644
index 0000000..ce3b250
--- /dev/null
+++ b/beatrix/src/test/resources/catalogSample.xml
@@ -0,0 +1,624 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+  ~ 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.
+  -->
+
+<!-- 
+Use cases covered so far:
+	Tiered Product (Pistol/Shotgun/Assault-Rifle)
+	Multiple changeEvent plan policies
+	Multiple PlanAlignment (see below, trial add-on alignments and rescue discount package)
+	Product transition rules
+	Add on (Scopes, Hoster)
+	Multi-pack addon (Extra-Ammo)
+	Addon Trial aligned to base plan (holster-monthly-regular)
+	Addon Trial aligned to creation (holster-monthly-special)
+	Rescue discount package (assault-rifle-annual-rescue)
+	Plan phase with a reccurring and a one off (refurbish-maintenance)
+	Phan with more than 2 phase (gunclub discount plans)
+		
+Use Cases to do:
+	Tiered Add On
+	Riskfree period
+	
+
+
+ -->
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+	<effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
+	<catalogName>Firearms</catalogName>
+
+	<currencies>
+		<currency>USD</currency>
+		<currency>EUR</currency>
+		<currency>GBP</currency>
+	</currencies>
+	
+	<products>
+		<product name="Pistol">
+			<category>BASE</category>
+			<available>
+				<addonProduct>Telescopic-Scope</addonProduct>
+				<addonProduct>Laser-Scope</addonProduct>
+			</available>
+		</product>
+		<product name="Shotgun">
+			<category>BASE</category>
+		</product>
+		<product name="Assault-Rifle">
+			<category>BASE</category>
+			<included> 
+				<addonProduct>Telescopic-Scope</addonProduct>
+			</included>
+			<available>
+				<addonProduct>Laser-Scope</addonProduct>
+			</available>
+		</product>
+		<product name="Telescopic-Scope">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Laser-Scope">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Holster">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Extra-Ammo">
+			<category>ADD_ON</category>
+		</product>
+		<product name="Refurbish-Maintenance">
+			<category>ADD_ON</category>
+		</product>
+	</products>
+	 
+	<rules>
+		<changePolicy>
+			<changePolicyCase> 
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<toProduct>Pistol</toProduct>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<toPriceList>rescue</toPriceList>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>		
+			<changePolicyCase> 
+				<fromProduct>Pistol</fromProduct>
+				<toProduct>Shotgun</toProduct>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<fromProduct>Assault-Rifle</fromProduct>
+				<toProduct>Shotgun</toProduct>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
+				<toProduct>Assault-Rifle</toProduct>
+				<toBillingPeriod>MONTHLY</toBillingPeriod>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<toProduct>Assault-Rifle</toProduct>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
+				<toBillingPeriod>ANNUAL</toBillingPeriod>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<fromBillingPeriod>ANNUAL</fromBillingPeriod>
+				<toBillingPeriod>MONTHLY</toBillingPeriod>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+		</changePolicy>
+		<changeAlignment>
+			<changeAlignmentCase>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</changeAlignmentCase>
+			<changeAlignmentCase>
+				<toPriceList>rescue</toPriceList>
+				<alignment>CHANGE_OF_PLAN</alignment>
+			</changeAlignmentCase>
+			<changeAlignmentCase>
+				<fromPriceList>rescue</fromPriceList>
+				<toPriceList>rescue</toPriceList>
+				<alignment>CHANGE_OF_PRICELIST</alignment>
+			</changeAlignmentCase>
+		</changeAlignment>
+		<cancelPolicy>
+			<cancelPolicyCase>
+				<policy>END_OF_TERM</policy>
+			</cancelPolicyCase>
+			<cancelPolicyCase>
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</cancelPolicyCase>
+		</cancelPolicy>
+		<createAlignment>
+			<createAlignmentCase>
+				<alignment>START_OF_BUNDLE</alignment>
+			</createAlignmentCase>
+		</createAlignment>
+		<billingAlignment>
+			<billingAlignmentCase>
+				<productCategory>ADD_ON</productCategory>
+				<alignment>BUNDLE</alignment>
+			</billingAlignmentCase>
+			<billingAlignmentCase>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<alignment>SUBSCRIPTION</alignment>
+			</billingAlignmentCase>
+			<billingAlignmentCase>
+				<alignment>ACCOUNT</alignment>
+			</billingAlignmentCase>
+		</billingAlignment>
+		<priceList>
+			<priceListCase>
+				<fromPriceList>rescue</fromPriceList>
+				<toPriceList>DEFAULT</toPriceList>
+			</priceListCase>
+		</priceList>
+	</rules>
+
+	<plans>
+		<plan name="pistol-monthly">
+			<product>Pistol</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice> <!-- empty price implies $0 -->
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price><currency>GBP</currency><value>29.95</value></price>
+					<price><currency>EUR</currency><value>29.95</value></price> 
+					<price><currency>USD</currency><value>29.95</value></price>								
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="shotgun-monthly">
+			<product>Shotgun</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice></fixedPrice>
+				    <!-- no price implies $0 -->
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+					<number>-1</number>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>249.95</value></price>								
+					<price><currency>EUR</currency><value>149.95</value></price>
+					<price><currency>GBP</currency><value>169.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-monthly">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>599.95</value></price>								
+					<price><currency>EUR</currency><value>349.95</value></price>
+					<price><currency>GBP</currency><value>399.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="pistol-annual">
+			<product>Pistol</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>199.95</value></price>								
+					<price><currency>EUR</currency><value>199.95</value></price>
+					<price><currency>GBP</currency><value>199.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="shotgun-annual">
+			<product>Shotgun</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>2399.95</value></price>								
+					<price><currency>EUR</currency><value>1499.95</value></price>
+					<price><currency>GBP</currency><value>1699.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-annual">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>5999.95</value></price>								
+					<price><currency>EUR</currency><value>3499.95</value></price>
+					<price><currency>GBP</currency><value>3999.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="pistol-annual-gunclub-discount">
+			<product>Pistol</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>6</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price><currency>USD</currency><value>9.95</value></price>								
+						<price><currency>EUR</currency><value>9.95</value></price>
+						<price><currency>GBP</currency><value>9.95</value></price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>199.95</value></price>								
+					<price><currency>EUR</currency><value>199.95</value></price>
+					<price><currency>GBP</currency><value>199.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="shotgun-annual-gunclub-discount">
+			<product>Shotgun</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>6</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price><currency>USD</currency><value>19.95</value></price>								
+						<price><currency>EUR</currency><value>49.95</value></price>
+						<price><currency>GBP</currency><value>69.95</value></price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>2399.95</value></price>								
+					<price><currency>EUR</currency><value>1499.95</value></price>
+					<price><currency>GBP</currency><value>1699.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-annual-gunclub-discount">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>MONTHS</unit>
+						<number>6</number>
+					</duration>
+					<billingPeriod>MONTHLY</billingPeriod>
+					<recurringPrice>
+						<price><currency>USD</currency><value>99.95</value></price>								
+						<price><currency>EUR</currency><value>99.95</value></price>
+						<price><currency>GBP</currency><value>99.95</value></price>
+						</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>5999.95</value></price>								
+					<price><currency>EUR</currency><value>3499.95</value></price>
+					<price><currency>GBP</currency><value>3999.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="laser-scope-monthly">
+		<product>Laser-Scope</product>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>1999.95</value></price>								
+					<price><currency>EUR</currency><value>1499.95</value></price>
+					<price><currency>GBP</currency><value>1999.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="telescopic-scope-monthly">
+			<product>Telescopic-Scope</product>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>999.95</value></price>								
+					<price><currency>EUR</currency><value>499.95</value></price>
+					<price><currency>GBP</currency><value>999.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="extra-ammo-monthly">
+			<product>Extra-Ammo</product>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>999.95</value></price>								
+					<price><currency>EUR</currency><value>499.95</value></price>
+					<price><currency>GBP</currency><value>999.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+			<plansAllowedInBundle>-1</plansAllowedInBundle> <!-- arbitrary number of these (multipack) -->
+		</plan>
+		<plan name="holster-monthly-regular">
+			<product>Holster</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>199.95</value></price>								
+					<price><currency>EUR</currency><value>199.95</value></price>
+					<price><currency>GBP</currency><value>199.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="holster-monthly-special">
+			<product>Holster</product>
+			<initialPhases>
+				<phase type="TRIAL">
+					<duration>
+						<unit>DAYS</unit>
+						<number>30</number>
+					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+					<fixedPrice>
+					</fixedPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>199.95</value></price>								
+					<price><currency>EUR</currency><value>199.95</value></price>
+					<price><currency>GBP</currency><value>199.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="assault-rifle-annual-rescue">
+			<product>Assault-Rifle</product>
+			<initialPhases>
+				<phase type="DISCOUNT">
+					<duration>
+						<unit>YEARS</unit>
+						<number>1</number>
+					</duration>
+					<billingPeriod>ANNUAL</billingPeriod>
+					<recurringPrice>
+						<price><currency>USD</currency><value>5999.95</value></price>								
+						<price><currency>EUR</currency><value>3499.95</value></price>
+						<price><currency>GBP</currency><value>3999.95</value></price>
+					</recurringPrice>
+				</phase>
+			</initialPhases>
+			<finalPhase type="EVERGREEN">
+				<duration>
+					<unit>UNLIMITED</unit>
+				</duration>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>5999.95</value></price>								
+					<price><currency>EUR</currency><value>3499.95</value></price>
+					<price><currency>GBP</currency><value>3999.95</value></price>
+				</recurringPrice>
+			</finalPhase>
+		</plan>
+		<plan name="refurbish-maintenance">
+			<product>Refurbish-Maintenance</product>
+			<finalPhase type="FIXEDTERM">
+				<duration>
+					<unit>MONTHS</unit>
+					<number>12</number>
+				</duration>
+				<billingPeriod>MONTHLY</billingPeriod>
+				<recurringPrice>
+					<price><currency>USD</currency><value>199.95</value></price>								
+					<price><currency>EUR</currency><value>199.95</value></price>
+					<price><currency>GBP</currency><value>199.95</value></price>
+				</recurringPrice>
+				<fixedPrice>
+					<price><currency>USD</currency><value>599.95</value></price>								
+					<price><currency>EUR</currency><value>599.95</value></price>
+					<price><currency>GBP</currency><value>599.95</value></price>
+				</fixedPrice>
+			</finalPhase>
+		</plan>
+	</plans>
+	<priceLists>
+		<defaultPriceList name="DEFAULT"> 
+			<plans>
+				<plan>pistol-monthly</plan>
+				<plan>shotgun-monthly</plan>
+				<plan>assault-rifle-monthly</plan>
+				<plan>pistol-annual</plan>
+				<plan>shotgun-annual</plan>
+				<plan>assault-rifle-annual</plan>
+				<plan>laser-scope-monthly</plan>
+				<plan>telescopic-scope-monthly</plan>
+				<plan>extra-ammo-monthly</plan>
+				<plan>holster-monthly-regular</plan>
+				<plan>refurbish-maintenance</plan>
+			</plans>
+		</defaultPriceList>
+		<childPriceList name="gunclubDiscount">
+			<plans>
+				<plan>pistol-monthly</plan>
+				<plan>shotgun-monthly</plan>
+				<plan>assault-rifle-monthly</plan>
+				<plan>pistol-annual-gunclub-discount</plan>
+				<plan>shotgun-annual-gunclub-discount</plan>
+				<plan>assault-rifle-annual-gunclub-discount</plan>
+				<plan>holster-monthly-special</plan>
+			</plans>
+		</childPriceList>
+		<childPriceList name="rescue">
+			<plans>
+				<plan>assault-rifle-annual-rescue</plan>
+			</plans>
+		</childPriceList>
+	</priceLists>
+
+</catalog>
diff --git a/beatrix/src/test/resources/resource.properties b/beatrix/src/test/resources/resource.properties
new file mode 100644
index 0000000..cc82754
--- /dev/null
+++ b/beatrix/src/test/resources/resource.properties
@@ -0,0 +1,6 @@
+killbill.catalog.uri=file:src/test/resources/catalogSample.xml
+killbill.entitlement.dao.claim.time=60000
+killbill.entitlement.dao.ready.max=1
+killbill.entitlement.engine.notifications.sleep=500
+
+
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
index a5ab3d8..7c5eee6 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/glue/EntitlementModule.java
@@ -43,10 +43,6 @@ import org.skife.config.ConfigurationObjectFactory;
 public class EntitlementModule extends AbstractModule {
 
 
-    protected void installClock() {
-        bind(Clock.class).to(DefaultClock.class).asEagerSingleton();
-    }
-
     protected void installConfig() {
         final EntitlementConfig config = new ConfigurationObjectFactory(System.getProperties()).build(EntitlementConfig.class);
         bind(EntitlementConfig.class).toInstance(config);
@@ -71,7 +67,6 @@ public class EntitlementModule extends AbstractModule {
     @Override
     protected void configure() {
         installConfig();
-        installClock();
         installEntitlementDao();
         installEntitlementCore();
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
index 1555bdb..b57d15b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
@@ -25,14 +25,11 @@ import com.ning.billing.util.glue.NotificationQueueModule;
 
 public class MockEngineModule extends EntitlementModule {
 
-    @Override
-    protected void installClock() {
-        bind(Clock.class).to(ClockMock.class).asEagerSingleton();
-    }
 
     @Override
     protected void configure() {
         super.configure();
+        bind(Clock.class).to(ClockMock.class).asEagerSingleton();
         install(new EventBusModule());
         install(new CatalogModule());
         install(new AccountModuleMock());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
index dbe2938..c2cc95c 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
@@ -35,10 +35,6 @@ public class MockEngineModuleSql extends MockEngineModule {
         bind(EntitlementDao.class).to(MockEntitlementDaoSql.class).asEagerSingleton();
     }
 
-    @Override
-    protected void installClock() {
-        bind(Clock.class).to(ClockMock.class).asEagerSingleton();
-    }
 
     protected void installDBI() {
         bind(DBI.class).toProvider(DBIProvider.class).asEagerSingleton();
diff --git a/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
index 54990c5..35c6ac6 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/glue/InvoiceModule.java
@@ -17,7 +17,9 @@
 package com.ning.billing.invoice.glue;
 
 import com.google.inject.AbstractModule;
+import com.ning.billing.invoice.api.DefaultInvoiceService;
 import com.ning.billing.invoice.api.InvoicePaymentApi;
+import com.ning.billing.invoice.api.InvoiceService;
 import com.ning.billing.invoice.api.invoice.DefaultInvoicePaymentApi;
 import com.ning.billing.invoice.api.user.DefaultInvoiceUserApi;
 import com.ning.billing.invoice.api.InvoiceUserApi;
@@ -25,6 +27,7 @@ import com.ning.billing.invoice.dao.DefaultInvoiceDao;
 import com.ning.billing.invoice.dao.InvoiceDao;
 
 public class InvoiceModule extends AbstractModule {
+
     private void installInvoiceDao() {
         bind(InvoiceDao.class).to(DefaultInvoiceDao.class).asEagerSingleton();
     }
@@ -39,6 +42,7 @@ public class InvoiceModule extends AbstractModule {
 
     @Override
     protected void configure() {
+        bind(InvoiceService.class).to(DefaultInvoiceService.class).asEagerSingleton();
         installInvoiceDao();
         installInvoiceUserApi();
         installInvoicePaymentApi();

pom.xml 6(+6 -0)

diff --git a/pom.xml b/pom.xml
index 28f48db..fb37e6b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -73,6 +73,11 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-invoice</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-catalog</artifactId>
                 <version>${project.version}</version>
             </dependency>
@@ -327,6 +332,7 @@
                                 <exclude>**/.project</exclude>
                                 <exclude>.git/**</exclude>
                                 <exclude>.gitignore</exclude>
+                                <exclude>ignore/**</exclude>
                                 <exclude>API.txt</exclude>
                                 <exclude>RELEASE.sh</exclude>
                                 <exclude>deploy.sh</exclude>