killbill-memoizeit

Changes

account/pom.xml 2(+1 -1)

api/pom.xml 6(+5 -1)

beatrix/pom.xml 2(+1 -1)

catalog/pom.xml 4(+2 -2)

catalog/src/main/java/com/ning/billing/catalog/rules/PlanPolicyChangeRule.java 98(+0 -98)

catalog/src/test/java/com/ning/billing/catalog/rules/TestPlanChangeRules.java 257(+0 -257)

invoice/pom.xml 2(+1 -1)

payment/pom.xml 2(+1 -1)

pom.xml 3(+2 -1)

util/pom.xml 11(+5 -6)

Details

account/pom.xml 2(+1 -1)

diff --git a/account/pom.xml b/account/pom.xml
index b3516c5..d388d5e 100644
--- a/account/pom.xml
+++ b/account/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.13-SNAPSHOT</version>
+        <version>0.0.14-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
diff --git a/analytics/pom.xml b/analytics/pom.xml
index cb22965..3755178 100644
--- a/analytics/pom.xml
+++ b/analytics/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.13-SNAPSHOT</version>
+        <version>0.0.14-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-analytics</artifactId>
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 1cb577f..564db0e 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -16,22 +16,23 @@
 
 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.IApiListener;
 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 implements IApiListener
+public class AnalyticsListener
 {
     private static final Logger log = LoggerFactory.getLogger(AnalyticsListener.class);
 
@@ -47,42 +48,67 @@ public class AnalyticsListener implements IApiListener
         this.accountApi = accountApi;
     }
 
-    @Override
+    /*
+     * 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)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
         recordTransition(event, created);
     }
 
-    @Override
     public void subscriptionCancelled(final ISubscriptionTransition cancelled)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
         recordTransition(event, cancelled);
     }
 
-    @Override
     public void subscriptionChanged(final ISubscriptionTransition changed)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
         recordTransition(event, changed);
     }
 
-    @Override
     public void subscriptionPaused(final ISubscriptionTransition paused)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
         recordTransition(event, paused);
     }
 
-    @Override
     public void subscriptionResumed(final ISubscriptionTransition resumed)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
         recordTransition(event, resumed);
     }
 
-    @Override
     public void subscriptionPhaseChanged(final ISubscriptionTransition phaseChanged)
     {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
index b85f6f3..dc33209 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscription.java
@@ -227,7 +227,7 @@ public class BusinessSubscription
 
     static BigDecimal getMrrFromISubscription(final IDuration duration, final BigDecimal price)
     {
-        if (duration == null || duration.getUnit() == null || duration.getLength() == 0) {
+        if (duration == null || duration.getUnit() == null || duration.getNumber() == 0) {
             return null;
         }
 
@@ -235,13 +235,13 @@ public class BusinessSubscription
             return BigDecimal.ZERO;
         }
         else if (duration.getUnit().equals(TimeUnit.DAYS)) {
-            return price.multiply(DAYS_IN_MONTH).multiply(BigDecimal.valueOf(duration.getLength()));
+            return price.multiply(DAYS_IN_MONTH).multiply(BigDecimal.valueOf(duration.getNumber()));
         }
         else if (duration.getUnit().equals(TimeUnit.MONTHS)) {
-            return price.divide(BigDecimal.valueOf(duration.getLength()), Rounder.SCALE, BigDecimal.ROUND_HALF_UP);
+            return price.divide(BigDecimal.valueOf(duration.getNumber()), Rounder.SCALE, BigDecimal.ROUND_HALF_UP);
         }
         else if (duration.getUnit().equals(TimeUnit.YEARS)) {
-            return price.divide(BigDecimal.valueOf(duration.getLength()), Rounder.SCALE, RoundingMode.HALF_UP).divide(MONTHS_IN_YEAR, Rounder.SCALE, RoundingMode.HALF_UP);
+            return price.divide(BigDecimal.valueOf(duration.getNumber()), Rounder.SCALE, RoundingMode.HALF_UP).divide(MONTHS_IN_YEAR, Rounder.SCALE, RoundingMode.HALF_UP);
         }
         else {
             log.error("Unknown duration [" + duration + "], can't compute mrr");
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockDuration.java b/analytics/src/test/java/com/ning/billing/analytics/MockDuration.java
index a56a3a5..7eb191e 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockDuration.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockDuration.java
@@ -32,7 +32,7 @@ public class MockDuration
             }
 
             @Override
-            public int getLength()
+            public int getNumber()
             {
                 return 1;
             }
@@ -50,7 +50,7 @@ public class MockDuration
             }
 
             @Override
-            public int getLength()
+            public int getNumber()
             {
                 return 1;
             }
@@ -68,7 +68,7 @@ public class MockDuration
             }
 
             @Override
-            public int getLength()
+            public int getNumber()
             {
                 return 1;
             }
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 5075a92..4912957 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
@@ -16,6 +16,9 @@
 
 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;
@@ -25,8 +28,6 @@ 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;
-
 public class MockPhase implements IPlanPhase
 {
     private final PhaseType cohort;
@@ -58,6 +59,11 @@ public class MockPhase implements IPlanPhase
             {
                 return BigDecimal.valueOf(price);
             }
+
+			@Override
+			public Date getEffectiveDateForExistingSubscriptons() {
+				return new Date();
+			}
         };
     }
 
@@ -77,6 +83,11 @@ public class MockPhase implements IPlanPhase
             {
                 return BigDecimal.valueOf(price);
             }
+            
+			@Override
+			public Date getEffectiveDateForExistingSubscriptons() {
+				return new Date();
+			}
         };
     }
 

api/pom.xml 6(+5 -1)

diff --git a/api/pom.xml b/api/pom.xml
index ad67258..5385aad 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.13-SNAPSHOT</version>
+        <version>0.0.14-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-api</artifactId>
@@ -26,6 +26,10 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.inject</groupId>
             <artifactId>guice</artifactId>
             <scope>provided</scope>
diff --git a/api/src/main/java/com/ning/billing/catalog/api/ICatalog.java b/api/src/main/java/com/ning/billing/catalog/api/ICatalog.java
index 5a5f872..e67c154 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/ICatalog.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/ICatalog.java
@@ -32,6 +32,9 @@ public interface ICatalog {
 	public abstract ActionPolicy getPlanChangePolicy(PlanPhaseSpecifier from,
 			PlanSpecifier to);
 
+	public abstract PlanChangeResult planChange(PlanPhaseSpecifier from,
+			PlanSpecifier to) throws IllegalPlanChange;
+	
     public abstract IPlan getPlanFromName(String name);
 
     public abstract IPlanPhase getPhaseFromName(String name);
diff --git a/api/src/main/java/com/ning/billing/catalog/api/IDuration.java b/api/src/main/java/com/ning/billing/catalog/api/IDuration.java
index 45973df..9a2205f 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/IDuration.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/IDuration.java
@@ -20,6 +20,6 @@ public interface IDuration {
 
 	public abstract TimeUnit getUnit();
 
-	public abstract int getLength();
+	public abstract int getNumber();
 
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/catalog/api/IInternationalPrice.java b/api/src/main/java/com/ning/billing/catalog/api/IInternationalPrice.java
index e20ed97..47b206c 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/IInternationalPrice.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/IInternationalPrice.java
@@ -17,6 +17,7 @@
 package com.ning.billing.catalog.api;
 
 import java.math.BigDecimal;
+import java.util.Date;
 
 
 public interface IInternationalPrice {
@@ -25,4 +26,6 @@ public interface IInternationalPrice {
 
 	public abstract BigDecimal getPrice(Currency currency);
 
+	public abstract Date getEffectiveDateForExistingSubscriptons();
+
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/catalog/api/IllegalPlanChange.java b/api/src/main/java/com/ning/billing/catalog/api/IllegalPlanChange.java
new file mode 100644
index 0000000..21bd513
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/catalog/api/IllegalPlanChange.java
@@ -0,0 +1,36 @@
+/*
+ * 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.catalog.api;
+
+import com.ning.billing.ErrorCode;
+
+public class IllegalPlanChange extends CatalogApiException {
+	private static final long serialVersionUID = 1L;
+
+	public IllegalPlanChange(Throwable cause, PlanPhaseSpecifier from,
+			PlanSpecifier to) {
+		super(cause, ErrorCode.CAT_ILLEGAL_CHANGE_REQUEST, from.getProductName(), from.getBillingPeriod(), from.getPriceListName(), to.getProductName(), to.getBillingPeriod(), to.getPriceListName());
+	}
+
+	public IllegalPlanChange(PlanPhaseSpecifier from,
+			PlanSpecifier to) {
+		super(ErrorCode.CAT_ILLEGAL_CHANGE_REQUEST, from.getProductName(), from.getBillingPeriod(), from.getPriceListName(), to.getProductName(), to.getBillingPeriod(), to.getPriceListName());
+	}
+
+
+}
+
diff --git a/api/src/main/java/com/ning/billing/catalog/api/IPriceList.java b/api/src/main/java/com/ning/billing/catalog/api/IPriceList.java
new file mode 100644
index 0000000..6fe3561
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/catalog/api/IPriceList.java
@@ -0,0 +1,26 @@
+/*
+ * 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.catalog.api;
+
+
+public interface IPriceList {
+
+	public abstract String getName();
+
+	public abstract IPlan findPlan(IProduct product, BillingPeriod period);
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/catalog/api/PlanChangeResult.java b/api/src/main/java/com/ning/billing/catalog/api/PlanChangeResult.java
new file mode 100644
index 0000000..c0be482
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/catalog/api/PlanChangeResult.java
@@ -0,0 +1,45 @@
+/*
+ * 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.catalog.api;
+
+public class PlanChangeResult {
+ 
+	private final IPriceList newPriceList;
+	private final ActionPolicy policy;
+	private final PlanAlignmentChange alignment;
+	
+	public PlanChangeResult(IPriceList newPriceList, ActionPolicy policy, PlanAlignmentChange alignment) {
+		super();
+		this.newPriceList = newPriceList;
+		this.policy = policy;
+		this.alignment = alignment;
+	}
+
+	public IPriceList getNewPriceList() {
+		return newPriceList;
+	}
+
+	public ActionPolicy getPolicy() {
+		return policy;
+	}
+
+	public PlanAlignmentChange getAlignment() {
+		return alignment;
+	}	
+	 
+	
+}
diff --git a/api/src/main/java/com/ning/billing/catalog/api/PlanPhaseSpecifier.java b/api/src/main/java/com/ning/billing/catalog/api/PlanPhaseSpecifier.java
index 7dadda0..18c864a 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/PlanPhaseSpecifier.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/PlanPhaseSpecifier.java
@@ -50,4 +50,7 @@ public class PlanPhaseSpecifier  {
 		return phaseType;
 	}
 
+	public PlanSpecifier toPlanSpecifier() {
+		return new PlanSpecifier(productName, productCategory, billingPeriod, priceListName);
+	}
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java b/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java
index ddcdf0c..4547119 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/IEntitlementService.java
@@ -16,11 +16,8 @@
 
 package com.ning.billing.entitlement.api;
 
-import java.util.List;
-
 import com.ning.billing.entitlement.api.billing.IEntitlementBillingApi;
 import com.ning.billing.entitlement.api.test.IEntitlementTestApi;
-import com.ning.billing.entitlement.api.user.IApiListener;
 import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
 import com.ning.billing.lifecycle.IService;
 
@@ -30,7 +27,7 @@ public interface IEntitlementService extends IService {
     @Override
     public String getName();
 
-    public IEntitlementUserApi getUserApi(List<IApiListener> listeners);
+    public IEntitlementUserApi getUserApi();
 
     public IEntitlementBillingApi getBillingApi();
 
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/IEntitlementUserApi.java b/api/src/main/java/com/ning/billing/entitlement/api/user/IEntitlementUserApi.java
index c3f79dc..f45f450 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/IEntitlementUserApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/IEntitlementUserApi.java
@@ -27,8 +27,6 @@ import com.ning.billing.catalog.api.BillingPeriod;
 
 public interface IEntitlementUserApi {
 
-    public void initialize(List<IApiListener> listeners);
-
     public ISubscriptionBundle getBundleFromId(UUID id);
 
     public ISubscription getSubscriptionFromId(UUID id);
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java b/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java
index d4e1a5a..718c28a 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/ISubscriptionTransition.java
@@ -23,9 +23,21 @@ import org.joda.time.DateTime;
 import com.ning.billing.catalog.api.IPlan;
 import com.ning.billing.catalog.api.IPlanPhase;
 import com.ning.billing.entitlement.api.user.ISubscription.SubscriptionState;
+import com.ning.billing.util.eventbus.IEventBusType;
 
-public interface ISubscriptionTransition {
+public interface ISubscriptionTransition extends IEventBusType {
 
+    public enum SubscriptionTransitionType {
+        CREATE,
+        CHANGE,
+        PAUSE,
+        RESUME,
+        CANCEL,
+        UNCANCEL,
+        PHASE
+    }
+
+    SubscriptionTransitionType getTransitionType();
 
     UUID getBundleId();
 
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index 24ee8c7..5cca8fa 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -42,7 +42,11 @@ public enum ErrorCode {
     /* Cancellation */
     ENT_CANCEL_BAD_STATE(1031, "Subscription %s is in state %s"),
     /* Un-cancellation */
-    ENT_UNCANCEL_BAD_STATE(1070, "Subscription %s was not in a cancelled state")
+    ENT_UNCANCEL_BAD_STATE(1070, "Subscription %s was not in a cancelled state"),
+    
+    CAT_ILLEGAL_CHANGE_REQUEST(2001, "Attempting to change plan from (product: '%s', billing period: '%s', " +
+    		"pricelist '%s') to (product: '%s', billing period: '%s', pricelist '%s'). This transition is not allowed by catalog rules")
+    
     ;
 
     private int code;
diff --git a/api/src/main/java/com/ning/billing/lifecycle/LyfecycleHandlerType.java b/api/src/main/java/com/ning/billing/lifecycle/LyfecycleHandlerType.java
index 520fca4..af15dea 100644
--- a/api/src/main/java/com/ning/billing/lifecycle/LyfecycleHandlerType.java
+++ b/api/src/main/java/com/ning/billing/lifecycle/LyfecycleHandlerType.java
@@ -20,6 +20,10 @@ import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.Collections;
+import java.util.List;
+
+import com.google.common.collect.Lists;
 
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
@@ -29,51 +33,55 @@ public @interface LyfecycleHandlerType {
     //
     // The level themselves are still work in progress depending on what we really need
     //
+    // Ordering is important in that enum
+    //
     public enum LyfecycleLevel {
 
         /**
          * Load and validate catalog (only for catalog subsytem)
          */
-        LOAD_CATALOG(Sequence.STARTUP),
+        LOAD_CATALOG(Sequence.STARTUP_PRE_EVENT_REGISTRATION),
         /**
          * Initialize event bus (only for the event bus)
          */
-        INIT_BUS(Sequence.STARTUP),
+        INIT_BUS(Sequence.STARTUP_PRE_EVENT_REGISTRATION),
         /**
-         * Service specific initalization
+         * Service specific initalization-- service does not start yet
          */
-        INIT_SERVICE(Sequence.STARTUP),
+        INIT_SERVICE(Sequence.STARTUP_PRE_EVENT_REGISTRATION),
         /**
          * Service register their interest in events
          */
-        REGISTER_EVENTS(Sequence.STARTUP),
+        REGISTER_EVENTS(Sequence.STARTUP_PRE_EVENT_REGISTRATION),
         /**
          * Service start
          * - API call should not work
          * - Events might be triggered
          * - Batch processing jobs started
          */
-        START_SERVICE(Sequence.STARTUP),
+        START_SERVICE(Sequence.STARTUP_POST_EVENT_REGISTRATION),
         /**
          * Stop service
          */
-        STOP_SERVICE(Sequence.SHUTOWN),
+        STOP_SERVICE(Sequence.SHUTOWN_PRE_EVENT_UNREGISTRATION),
         /**
          * Unregister interest in events
          */
-        UNREGISTER_EVENTS(Sequence.SHUTOWN),
+        UNREGISTER_EVENTS(Sequence.SHUTOWN_PRE_EVENT_UNREGISTRATION),
         /**
          * Stop bus
          */
-        STOP_BUS(Sequence.SHUTOWN),
+        STOP_BUS(Sequence.SHUTOWN_POST_EVENT_UNREGISTRATION),
         /**
          * Any service specific shutdown action before the end
          */
-        SHUTDOWN(Sequence.SHUTOWN);
+        SHUTDOWN(Sequence.SHUTOWN_POST_EVENT_UNREGISTRATION);
 
         public enum Sequence {
-            STARTUP,
-            SHUTOWN
+            STARTUP_PRE_EVENT_REGISTRATION,
+            STARTUP_POST_EVENT_REGISTRATION,
+            SHUTOWN_PRE_EVENT_UNREGISTRATION,
+            SHUTOWN_POST_EVENT_UNREGISTRATION
         };
 
         private Sequence seq;
@@ -85,6 +93,19 @@ public @interface LyfecycleHandlerType {
         public Sequence getSequence() {
             return seq;
         }
+
+        //
+        // Returns an ordered list of level for a particular sequence
+        //
+        public static List<LyfecycleLevel> getLevelsForSequence(Sequence seq) {
+            List<LyfecycleLevel> result = Lists.newLinkedList();
+            for (LyfecycleLevel level : LyfecycleLevel.values()) {
+                if (level.getSequence() == seq) {
+                    result.add(level);
+                }
+            }
+            return result;
+        }
     }
 
     public LyfecycleLevel value();
diff --git a/api/src/main/java/com/ning/billing/util/eventbus/IEventBus.java b/api/src/main/java/com/ning/billing/util/eventbus/IEventBus.java
new file mode 100644
index 0000000..90ab0bc
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/eventbus/IEventBus.java
@@ -0,0 +1,108 @@
+/*
+ * 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.util.eventbus;
+
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import com.google.common.eventbus.Subscribe;
+
+
+/**
+ *
+ * EventBus API based on the guava EventBus API
+ *
+ * The API also provides an API to send events from within a transaction
+ * with the guarantee that the event will be delivered if and only if
+ * the transaction completes. If the implementation is not based on a
+ * DB, this API is behaves the same as the regular post() call.
+ *
+ */
+public interface IEventBus {
+
+
+    public class EventBusException extends Exception {
+
+        private static final long serialVersionUID = 12355236L;
+
+        public EventBusException() {
+            super();
+        }
+        public EventBusException(String message, Throwable cause) {
+            super(message, cause);
+        }
+
+        public EventBusException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * Start accepting events and dispatching them
+     *
+     */
+    public void start();
+
+    /**
+     * Stop accepting events and flush event queue before it returns.
+     *
+     */
+    public void stop();
+
+    /**
+     *
+     * Registers all handler methods on {@code object} to receive events.
+     * Handler methods need to be Annotated with {@link Subscribe}
+     *
+     * @param handlerInstance
+     *
+     *  @throws EventBusException if bus not been started yet
+     */
+    public void register(Object handlerInstance) throws EventBusException;
+
+
+    /**
+     * Unregister the handler for a particular type of event
+     *
+     * @param handlerInstance
+     * @throws EventBusException
+     */
+    public void unregister(Object handlerInstance) throws EventBusException;
+
+
+    /**
+     * Post an event asynchronously
+     *
+     * @param event to be posted
+     *
+     *  @throws EventBusException if bus not been started yet
+     */
+    public void post(IEventBusType event) throws EventBusException;
+
+    /**
+     *
+     * Post an event from within a transaction.
+     * Guarantees that the event is persisted on disk from within the same transaction
+     *
+     *
+     * @param event to be posted
+     * @param dao a valid DAO object obtained through the DBI.onDeamand() API.
+     *
+     *  @throws EventBusException if bus not been started yet
+     */
+    public void postFromTransaction(IEventBusType event, Transmogrifier dao) throws EventBusException;
+
+
+}
diff --git a/api/src/main/java/com/ning/billing/util/eventbus/IEventBusService.java b/api/src/main/java/com/ning/billing/util/eventbus/IEventBusService.java
new file mode 100644
index 0000000..71fa8a3
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/util/eventbus/IEventBusService.java
@@ -0,0 +1,24 @@
+/*
+ * 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.util.eventbus;
+
+import com.ning.billing.lifecycle.IService;
+
+public interface IEventBusService extends IService {
+
+    public IEventBus getEventBus();
+}

beatrix/pom.xml 2(+1 -1)

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index a8c5515..3571e7d 100644
--- a/beatrix/pom.xml
+++ b/beatrix/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.13-SNAPSHOT</version>
+        <version>0.0.14-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>
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 6abdb75..f091a81 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
@@ -20,6 +20,7 @@ import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
@@ -74,21 +75,27 @@ public class Lifecycle {
         }
     }
 
-    public void fireStartupSequence() {
-        for (LyfecycleLevel level : LyfecycleLevel.values()) {
-            if (level.getSequence() == Sequence.SHUTOWN) {
-                break;
-            }
-            doFireStage(level);
-        }
+
+    public void fireStartupSequencePriorEventRegistration() {
+        fireSequence(Sequence.STARTUP_PRE_EVENT_REGISTRATION);
     }
 
-    public void fireShutdownSequence() {
-        for (LyfecycleLevel level : LyfecycleLevel.values()) {
-            if (level.getSequence() == Sequence.STARTUP) {
-                continue;
-            }
-            doFireStage(level);
+    public void fireStartupSequencePostEventRegistration() {
+        fireSequence(Sequence.STARTUP_POST_EVENT_REGISTRATION);
+    }
+
+    public void fireShutdownSequencePriorEventUnRegistration() {
+        fireSequence(Sequence.SHUTOWN_PRE_EVENT_UNREGISTRATION);
+    }
+
+    public void fireShutdownSequencePostEventUnRegistration() {
+        fireSequence(Sequence.SHUTOWN_POST_EVENT_UNREGISTRATION);
+    }
+
+    private void fireSequence(Sequence seq) {
+        List<LyfecycleLevel> levels = LyfecycleLevel.getLevelsForSequence(seq);
+        for (LyfecycleLevel cur : levels) {
+            doFireStage(cur);
         }
     }
 
@@ -103,12 +110,13 @@ public class Lifecycle {
                 log.info("Killbill lifecycle calling handler {} for service {}", cur.getMethod().getName(), target.getName());
                 method.invoke(target);
             } catch (Exception e) {
-                log.warn("Killbill lifecycle failed to invoke lifecycle handler", e);
+                logWarn("Killbill lifecycle failed to invoke lifecycle handler", e);
             }
         }
 
     }
 
+
     private Set<? extends IService> findServices() {
 
         Set<IService> result = new HashSet<IService>();
@@ -120,7 +128,7 @@ public class Lifecycle {
                 log.debug("got instance {}", instance.getName());
                 result.add(instance);
             } catch (Exception e) {
-                log.warn("Failed to inject " + cur.getName(), e);
+                logWarn("Failed to inject " + cur.getName(), e);
             }
 
         }
@@ -128,9 +136,13 @@ public class Lifecycle {
     }
 
 
+    // Used to disable valid injection failure from unit tests
+    protected void logWarn(String msg, Exception e) {
+        log.warn(msg, e);
+    }
+
     public Multimap<LyfecycleLevel, LifecycleHandler<? extends IService>> findAllHandlers(IService service) {
-        Multimap<LyfecycleLevel, LifecycleHandler<? extends IService>> methodsInService =
-            HashMultimap.create();
+        Multimap<LyfecycleLevel, LifecycleHandler<? extends IService>> methodsInService = HashMultimap.create();
         Class<? extends IService> clazz = service.getClass();
         for (Method method : clazz.getMethods()) {
             LyfecycleHandlerType annotation = method.getAnnotation(LyfecycleHandlerType.class);
diff --git a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java
index fe3f0c5..6916cf1 100644
--- a/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java
+++ b/beatrix/src/main/java/com/ning/billing/beatrix/lifecycle/ServiceFinder.java
@@ -99,10 +99,16 @@ public class ServiceFinder {
 	        File classPath = new File( (URL.class).isInstance(classPaths[h]) ?
 	                ((URL)classPaths[h]).getFile() : classPaths[h].toString());
 	        if (classPath.isDirectory()) {
+
+                log.debug("DIR : " + classPath);
+
 	            List<String> dirListing = new ArrayList<String>();
 	            recursivelyListDir(dirListing, classPath, new StringBuffer() );
 	            files = Collections.enumeration( dirListing );
 	        } else if (classPath.getName().endsWith(".jar")) {
+
+	            log.debug("JAR : " + classPath);
+
 	            String [] jarParts = classPath.getName().split("/");
 	            String jarName = jarParts[jarParts.length - 1];
 	            if (jarFilter != null && jarName != null && ! jarName.startsWith(jarFilter)) {
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 f85232f..791b2fe 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
@@ -18,15 +18,16 @@ package com.ning.billing.beatrix.lifecycle;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.testng.Assert;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
+import com.google.inject.Inject;
 import com.google.inject.Injector;
 import com.google.inject.Stage;
 import com.ning.billing.lifecycle.IService;
-import com.ning.billing.lifecycle.Lifecycled;
 import com.ning.billing.lifecycle.LyfecycleHandlerType;
 import com.ning.billing.lifecycle.LyfecycleHandlerType.LyfecycleLevel;
 
@@ -40,16 +41,45 @@ public class TestLifecycle {
 
     private Lifecycle lifecycle;
 
-    public static class Service1 implements IService {
+    public static class ServiceBase {
+
+        private int count = 0;
+
+        public ServiceBase() {
+            reset();
+        }
+
+        public synchronized void reset() {
+            this.count = 0;
+        }
+
+        public synchronized int getCount() {
+            return count;
+        }
+
+        public synchronized void incrementCount() {
+            count++;
+        }
+    }
+
+    public static class Service1 extends ServiceBase implements IService  {
 
         @LyfecycleHandlerType(LyfecycleLevel.INIT_BUS)
         public void initBus() {
             log.info("Service1 : got INIT_BUS");
+            incrementCount();
         }
 
         @LyfecycleHandlerType(LyfecycleLevel.START_SERVICE)
         public void startService() {
             log.info("Service1 : got START_SERVICE");
+            incrementCount();
+        }
+
+        @LyfecycleHandlerType(LyfecycleLevel.SHUTDOWN)
+        public void shutdownService() {
+            log.info("Service1 : got SHUTDOWN");
+            incrementCount();
         }
 
         @Override
@@ -58,24 +88,41 @@ public class TestLifecycle {
         }
     }
 
-    @Lifecycled
-    public static class Service2 implements IService {
+    public static class Service2 extends ServiceBase implements IService {
 
         @LyfecycleHandlerType(LyfecycleLevel.LOAD_CATALOG)
         public void loadCatalog() {
-            log.info("Service1 : got LOAD_CATALOG");
+            log.info("Service2 : got LOAD_CATALOG");
+            incrementCount();
+        }
+
+        @LyfecycleHandlerType(LyfecycleLevel.REGISTER_EVENTS)
+        public void registerEvents() {
+            log.info("Service2 : got REGISTER_EVENTS");
+            incrementCount();
+        }
+
+        @LyfecycleHandlerType(LyfecycleLevel.UNREGISTER_EVENTS)
+        public void unregisterEvents() {
+            log.info("Service2 : got UNREGISTER_EVENTS");
+            incrementCount();
+        }
+
+        @LyfecycleHandlerType(LyfecycleLevel.START_SERVICE)
+        public void startService() {
+            log.info("Service2 : got START_SERVICE");
+            incrementCount();
         }
 
         @Override
         public String getName() {
             return null;
         }
-
     }
 
 
 
-    @BeforeClass(groups={"fast"})
+    @BeforeClass
     public void setup() {
         final Injector g = Guice.createInjector(Stage.DEVELOPMENT, new TestLifecycleModule());
         s1 = g.getInstance(Service1.class);
@@ -83,17 +130,45 @@ public class TestLifecycle {
         lifecycle = g.getInstance(Lifecycle.class);
     }
 
-    @Test
+    @Test(enabled=true, groups={"fast"})
     public void testLifecycle() {
-        lifecycle.fireStartupSequence();
+        s1.reset();
+        s2.reset();
+        lifecycle.fireStartupSequencePriorEventRegistration();
+        Assert.assertEquals(s1.getCount() + s2.getCount(), 3);
+
+        s1.reset();
+        s2.reset();
+        lifecycle.fireStartupSequencePostEventRegistration();
+        Assert.assertEquals(s1.getCount() + s2.getCount(), 2);
+
+        s1.reset();
+        s2.reset();
+        lifecycle.fireShutdownSequencePriorEventUnRegistration();
+        Assert.assertEquals(s1.getCount() + s2.getCount(), 1);
+
+        s1.reset();
+        s2.reset();
+        lifecycle.fireShutdownSequencePostEventUnRegistration();
+        Assert.assertEquals(s1.getCount() + s2.getCount(), 1);
     }
 
+    public static class LifecycleNoWarn extends Lifecycle {
+
+        @Inject
+        public LifecycleNoWarn(Injector injector) {
+            super(injector);
+        }
+        @Override
+        protected void logWarn(String msg, Exception e) {
+        }
+    }
 
     public static class TestLifecycleModule extends AbstractModule {
 
         @Override
         protected void configure() {
-            bind(Lifecycle.class).asEagerSingleton();
+            bind(Lifecycle.class).to(LifecycleNoWarn.class).asEagerSingleton();
             bind(Service1.class).asEagerSingleton();
             bind(Service2.class).asEagerSingleton();
         }

catalog/pom.xml 4(+2 -2)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 0b6b6a3..5643272 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.13-SNAPSHOT</version>
+        <version>0.0.14-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
@@ -27,7 +27,7 @@
         <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-api</artifactId>
-        </dependency>
+        </dependency> 
         <dependency>
             <groupId>com.ning.billing</groupId>
             <artifactId>killbill-util</artifactId>
diff --git a/catalog/src/main/java/com/ning/billing/catalog/Catalog.java b/catalog/src/main/java/com/ning/billing/catalog/Catalog.java
index bc6482d..fa24752 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/Catalog.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/Catalog.java
@@ -32,6 +32,7 @@ import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.ICatalog;
 import com.ning.billing.catalog.api.IProduct;
+import com.ning.billing.catalog.api.IllegalPlanChange;
 import com.ning.billing.catalog.api.PlanAlignmentChange;
 import com.ning.billing.catalog.api.PlanAlignmentCreate;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
@@ -40,6 +41,7 @@ import com.ning.billing.catalog.rules.PlanRules;
 import com.ning.billing.util.config.ValidatingConfig;
 import com.ning.billing.util.config.ValidationError;
 import com.ning.billing.util.config.ValidationErrors;
+import com.ning.billing.catalog.api.PlanChangeResult;
 
 @XmlRootElement
 @XmlAccessorType(XmlAccessType.NONE)
@@ -278,6 +280,12 @@ public class Catalog extends ValidatingConfig<Catalog> implements ICatalog {
 	public PriceList getPriceListFromName(String priceListName) {
 		return priceLists.findPriceListFrom(priceListName);
 	}
+
+	@Override
+	public PlanChangeResult planChange(PlanPhaseSpecifier from, PlanSpecifier to)
+			throws IllegalPlanChange {
+		return planRules.planChange(from, to, this);
+	}
 	
 	//TODO: MDW validation - only allow one default pricelist
 
diff --git a/catalog/src/main/java/com/ning/billing/catalog/Duration.java b/catalog/src/main/java/com/ning/billing/catalog/Duration.java
index ac34cf6..af55806 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/Duration.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/Duration.java
@@ -32,7 +32,7 @@ public class Duration extends ValidatingConfig<Catalog> implements IDuration {
 
 	@XmlElement(required=false)
     private Integer number = -1;
-
+	
     /* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IDuration#getUnit()
 	 */
@@ -45,23 +45,25 @@ public class Duration extends ValidatingConfig<Catalog> implements IDuration {
 	 * @see com.ning.billing.catalog.IDuration#getLength()
 	 */
     @Override
-	public int getLength() {
+	public int getNumber() {
         return number;
     }
 
-    public void setUnit(TimeUnit unit) {
-		this.unit = unit;
-	}
-
-	public void setLength(int length) {
-		this.number = length;
-	}
-
-
-
 	@Override
 	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
+		//TODO MDW - Validation TimeUnit UNLIMITED iff number == -1
 		return errors;
+	}
+
+	protected Duration setUnit(TimeUnit unit) {
+		this.unit = unit;
+		return this;
+	}
 
+	protected Duration setNumber(Integer number) {
+		this.number = number;
+		return this;
 	}
+	
+	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/InternationalPrice.java b/catalog/src/main/java/com/ning/billing/catalog/InternationalPrice.java
index c81c9b7..63cf388 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/InternationalPrice.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/InternationalPrice.java
@@ -49,8 +49,12 @@ public class InternationalPrice extends ValidatingConfig<Catalog> implements IIn
 		return prices;
 	}
 
-	public void setPrices(Price[] prices) {
-		this.prices = prices;
+	/* (non-Javadoc)
+	 * @see com.ning.billing.catalog.IInternationalPrice#getEffectiveDateForExistingSubscriptons()
+	 */
+	@Override
+	public Date getEffectiveDateForExistingSubscriptons() {
+		return effectiveDateForExistingSubscriptons;
 	}
 
 	/* (non-Javadoc)
@@ -88,13 +92,15 @@ public class InternationalPrice extends ValidatingConfig<Catalog> implements IIn
 		}
 		return false;
 	}
-
-	public Date getEffectiveDateForExistingSubscriptons() {
-		return effectiveDateForExistingSubscriptons;
-	}
-
-	public void setEffectiveDateForExistingSubscriptons(
+	
+	protected void setEffectiveDateForExistingSubscriptons(
 			Date effectiveDateForExistingSubscriptons) {
 		this.effectiveDateForExistingSubscriptons = effectiveDateForExistingSubscriptons;
 	}
+
+	protected InternationalPrice setPrices(Price[] prices) {
+		this.prices = prices;
+		return this;
+	}
+
 }
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 92a2e06..9a994f0 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/Plan.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/Plan.java
@@ -60,27 +60,6 @@ public class Plan extends ValidatingConfig<Catalog> implements IPlan {
 	//A value of -1 means unlimited
 	@XmlElement(required=false)
 	private Integer plansAllowedInBundle = 1;
-	
-	public Plan(){}
-	
-	public Plan(String name, Product product, PlanPhase finalPhase) {
-		this.name = name;
-		this.product = product;
-		this.finalPhase = finalPhase;
-	}
-
-	@Override
-	public void initialize(Catalog catalog, URI sourceURI) {
-		super.initialize(catalog, sourceURI);
-		if(finalPhase != null) {
-			finalPhase.setPlan(this);
-		}
-		if(initialPhases != null) {
-			for(PlanPhase p : initialPhases) {
-				p.setPlan(this);
-			}
-		}
-	}
 
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlan#getPhases()
@@ -98,15 +77,6 @@ public class Plan extends ValidatingConfig<Catalog> implements IPlan {
         return product;
     }
 
-	public void setInitialPhases(PlanPhase[] phases) {
-		this.initialPhases = phases;
-		
-	}
-
-	public void setProduct(Product product) {
-		this.product = product;
-	}
-
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlan#getName()
 	 */
@@ -114,12 +84,25 @@ public class Plan extends ValidatingConfig<Catalog> implements IPlan {
 	public String getName() {
 		return name;
 	}
-
-	public void setName(String name) {
-		this.name = name;
+	@Override
+	public PlanPhase getFinalPhase() {
+		return finalPhase;
+	}
+	
+	@Override
+	public BillingPeriod getBillingPeriod(){
+		return finalPhase.getBillingPeriod();
 	}
 
 	/* (non-Javadoc)
+	 * @see com.ning.billing.catalog.IPlan#getPlansAllowedInBundle()
+	 */
+	@Override
+	public int getPlansAllowedInBundle() {
+		return plansAllowedInBundle;
+	}
+	
+	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlan#getPhaseIterator()
 	 */
 	@Override
@@ -132,33 +115,48 @@ public class Plan extends ValidatingConfig<Catalog> implements IPlan {
 	}
 	
 	@Override
+	public void initialize(Catalog catalog, URI sourceURI) {
+		super.initialize(catalog, sourceURI);
+		if(finalPhase != null) {
+			finalPhase.setPlan(this);
+		}
+		if(initialPhases != null) {
+			for(PlanPhase p : initialPhases) {
+				p.setPlan(this);
+			}
+		}
+	}
+
+	@Override
 	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
 		return errors;
 	}
 	
-	@Override
-	public PlanPhase getFinalPhase() {
-		return finalPhase;
+
+	protected Plan setName(String name) {
+		this.name = name;
+		return this;
+	}
+	
+	protected Plan setPlansAllowedInBundle(int plansAllowedInBundle) {
+		this.plansAllowedInBundle = plansAllowedInBundle;
+		return this;		
 	}
 
-	public void setFinalPhase(PlanPhase finalPhase) {
+	protected Plan setFinalPhase(PlanPhase finalPhase) {
 		this.finalPhase = finalPhase;
+		return this;		
 	}
 	
-	@Override
-	public BillingPeriod getBillingPeriod(){
-		return finalPhase.getBillingPeriod();
+	protected Plan setProduct(Product product) {
+		this.product = product;
+		return this;		
 	}
 
-	/* (non-Javadoc)
-	 * @see com.ning.billing.catalog.IPlan#getPlansAllowedInBundle()
-	 */
-	@Override
-	public int getPlansAllowedInBundle() {
-		return plansAllowedInBundle;
+	protected Plan setInitialPhases(PlanPhase[] phases) {
+		this.initialPhases = phases;
+		return this;		
 	}
 
-	public void setPlansAllowedInBundle(int plansAllowedInBundle) {
-		this.plansAllowedInBundle = plansAllowedInBundle;
-	}
+
 }
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 6d9630b..851ef12 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/PlanPhase.java
@@ -28,6 +28,7 @@ import com.ning.billing.catalog.api.IPlan;
 import com.ning.billing.catalog.api.IPlanPhase;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.util.config.ValidatingConfig;
+import com.ning.billing.util.config.ValidationError;
 import com.ning.billing.util.config.ValidationErrors;
 
 @XmlAccessorType(XmlAccessType.NONE)
@@ -39,7 +40,7 @@ public class PlanPhase extends ValidatingConfig<Catalog> implements IPlanPhase {
     @XmlElement(required=true)
     private Duration duration;
     
-    @XmlElement(required=false)
+    @XmlElement(required=true)
     private BillingPeriod billingPeriod = BillingPeriod.NO_BILLING_PERIOD;
 
 	@XmlElement(required=false)
@@ -48,18 +49,12 @@ public class PlanPhase extends ValidatingConfig<Catalog> implements IPlanPhase {
 	@XmlElement(required=false)
 	private InternationalPrice fixedPrice;
 
+//  Not supported: variable pricing
 //	@XmlElement(required=false)
 //	private InternationalPrice unitPrice;
 
 	//Not exposed in XML
 	private IPlan plan;
-	
-	public PlanPhase(){}
-
-    public PlanPhase(BillingPeriod period, PhaseType type) {
-		this.billingPeriod = period;
-		this.type = type;
-	}
 
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlanPhase#getRecurringPrice()
@@ -69,10 +64,6 @@ public class PlanPhase extends ValidatingConfig<Catalog> implements IPlanPhase {
         return recurringPrice;
     }
 
-	public void setReccuringPrice(InternationalPrice price) {
-		this.recurringPrice = price;
-	}
-
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlanPhase#getInternationalPrice()
 	 */
@@ -81,11 +72,15 @@ public class PlanPhase extends ValidatingConfig<Catalog> implements IPlanPhase {
         return fixedPrice;
     }
 
-	public void setFixedPrice(InternationalPrice price) {
-		this.fixedPrice = price;
+	/* (non-Javadoc)
+	 * @see com.ning.billing.catalog.IPlanPhase#getCohort()
+	 */
+	@Override
+	public PhaseType getPhaseType() {
+		return type;
 	}
 
-    /* (non-Javadoc)
+	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlanPhase#getBillCycleDuration()
 	 */
     @Override
@@ -93,10 +88,6 @@ public class PlanPhase extends ValidatingConfig<Catalog> implements IPlanPhase {
     	return billingPeriod;
     }
 
-	public void setBillCycleDuration(BillingPeriod billingPeriod) {
-		this.billingPeriod = billingPeriod;
-	}
-
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlanPhase#getName()
 	 */
@@ -113,10 +104,6 @@ public class PlanPhase extends ValidatingConfig<Catalog> implements IPlanPhase {
 		return plan;
 	}
 
-	public void setPlan(IPlan plan) {
-		this.plan = plan;
-	}
-
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlanPhase#getDuration()
 	 */
@@ -125,34 +112,66 @@ public class PlanPhase extends ValidatingConfig<Catalog> implements IPlanPhase {
  		return duration;
 	}
 
-	public void setDuration(Duration duration) {
-		this.duration = duration;
-	}
-
 	@Override
 	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
+		//Validation: check for nulls
+		if(billingPeriod == null) {
+			errors.add(new ValidationError(String.format("Phase %s of plan %s has a reccurring price but no billing period", type.toString(), plan.getName()), 
+					catalog.getCatalogURI(), PlanPhase.class, type.toString()));
+		}
+		
+		
+		
+		
+		//Validation: if there is a recurring price there must be a billing period
+		if(recurringPrice != null && (billingPeriod == null || billingPeriod ==BillingPeriod.NO_BILLING_PERIOD)) {
+			errors.add(new ValidationError(String.format("Phase %s of plan %s has a reccurring price but no billing period", type.toString(), plan.getName()), 
+					catalog.getCatalogURI(), PlanPhase.class, type.toString()));
+		}
+		//Validation: if there is no reccuring price there should be no billing period
+		if(recurringPrice == null && billingPeriod != BillingPeriod.NO_BILLING_PERIOD) {
+			errors.add(new ValidationError(String.format("Phase %s of plan %s has no reccurring price but does have a billing period. The billing period shoudl be set to '%s'", 
+					type.toString(), plan.getName(), BillingPeriod.NO_BILLING_PERIOD), 
+					catalog.getCatalogURI(), PlanPhase.class, type.toString()));
+		}
+		
 		return errors;
 
 	}
 
-	/* (non-Javadoc)
-	 * @see com.ning.billing.catalog.IPlanPhase#getCohort()
-	 */
-	@Override
-	public PhaseType getPhaseType() {
-		return type;
+	protected PlanPhase setFixedPrice(InternationalPrice price) {
+		this.fixedPrice = price;
+		return this;
 	}
 
-	public void setCohort(PhaseType cohort) {
+	protected PlanPhase setReccuringPrice(InternationalPrice price) {
+		this.recurringPrice = price;
+		return this;
+	}
+
+	protected PlanPhase setPhaseType(PhaseType cohort) {
 		this.type = cohort;
+		return this;
 	}
 
-	public void setBillingPeriod(BillingPeriod billingPeriod) {
+	protected PlanPhase setBillingPeriod(BillingPeriod billingPeriod) {
 		this.billingPeriod = billingPeriod;
+		return this;
+	}
+
+	protected PlanPhase setDuration(Duration duration) {
+		this.duration = duration;
+		return this;
+	}
+
+	protected PlanPhase setPlan(IPlan plan) {
+		this.plan = plan;
+		return this;
 	}
 	
-	//TODO MDW - validation: if there is a recurring price there must be a billing period
-	//TODO MDW - validation: if there is no reccuring price there should be no billing period
-	
+	protected PlanPhase setBillCycleDuration(BillingPeriod billingPeriod) {
+		this.billingPeriod = billingPeriod;
+		return this;
+	}
 
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/Price.java b/catalog/src/main/java/com/ning/billing/catalog/Price.java
index 885191e..369b9ac 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/Price.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/Price.java
@@ -42,9 +42,7 @@ public class Price extends ValidatingConfig<Catalog> implements IPrice {
 	public Currency getCurrency() {
 		return currency;
 	}
-	public void setCurrency(Currency currency) {
-		this.currency = currency;
-	}
+
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPrice#getValue()
 	 */
@@ -52,9 +50,17 @@ public class Price extends ValidatingConfig<Catalog> implements IPrice {
 	public BigDecimal getValue() {
 		return value;
 	}
-	public void setValue(BigDecimal value) {
+	
+	public Price setCurrency(Currency currency) {
+		this.currency = currency;
+		return this;
+	}
+
+	protected Price setValue(BigDecimal value) {
 		this.value = value;
+		return this;
 	}
+	
 	@Override
 	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
 		return errors;
diff --git a/catalog/src/main/java/com/ning/billing/catalog/PriceList.java b/catalog/src/main/java/com/ning/billing/catalog/PriceList.java
index c4842f9..2ce3ef9 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/PriceList.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/PriceList.java
@@ -25,12 +25,13 @@ import javax.xml.bind.annotation.XmlID;
 import javax.xml.bind.annotation.XmlIDREF;
 
 import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.IPriceList;
 import com.ning.billing.catalog.api.IProduct;
 import com.ning.billing.util.config.ValidatingConfig;
 import com.ning.billing.util.config.ValidationErrors;
 
 @XmlAccessorType(XmlAccessType.NONE)
-public class PriceList extends ValidatingConfig<Catalog>  {
+public class PriceList extends ValidatingConfig<Catalog> implements IPriceList  {
 
 	@XmlAttribute(required=true)
 	@XmlID
@@ -52,10 +53,18 @@ public class PriceList extends ValidatingConfig<Catalog>  {
 		return plans;
 	}
 	
+	/* (non-Javadoc)
+	 * @see com.ning.billing.catalog.IPriceList#getName()
+	 */
+	@Override
 	public String getName() {
         return name;
     }
 
+	/* (non-Javadoc)
+	 * @see com.ning.billing.catalog.IPriceList#findPlan(com.ning.billing.catalog.api.IProduct, com.ning.billing.catalog.api.BillingPeriod)
+	 */
+	@Override
 	public Plan findPlan(IProduct product, BillingPeriod period) {
         for (Plan cur : getPlans()) {
             if (cur.getProduct().equals(product) && 
diff --git a/catalog/src/main/java/com/ning/billing/catalog/Product.java b/catalog/src/main/java/com/ning/billing/catalog/Product.java
index 371c4be..fb2ffb5 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/Product.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/Product.java
@@ -86,19 +86,6 @@ public class Product extends ValidatingConfig<Catalog> implements IProduct {
         return name;
     }
 
-	public void setName(String name) {
-		this.name = name;
-	}
-
-	public void setCatagory(ProductCategory category) {
-		this.category = category;
-	}
-
-	@Override
-	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
-		return errors;
-	}
-
 	public boolean isIncluded(Product addon) {
 		for(Product p : included) {
 			if (addon == p) {
@@ -122,7 +109,41 @@ public class Product extends ValidatingConfig<Catalog> implements IProduct {
 		catalogName = catalog.getCalalogName();
 	}
 
+	@Override
+	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
+		//TODO: MDW validation: inclusion and exclusion lists can only contain addon products
+		//TODO: MDW validation: a given product can only be in, at most, one of inclusion and exclusion lists
+		return errors;
+	}
+
+	protected Product setName(String name) {
+		this.name = name;
+		return this;
+	}
+
+	protected Product setCatagory(ProductCategory category) {
+		this.category = category;
+		return this;
+	}
+
+	protected Product setCategory(ProductCategory category) {
+		this.category = category;
+		return this;
+	}
+
+	protected Product setIncluded(Product[] included) {
+		this.included = included;
+		return this;
+	}
+
+	protected Product setAvailable(Product[] available) {
+		this.available = available;
+		return this;
+	}
+
+	protected Product setCatalogName(String catalogName) {
+		this.catalogName = catalogName;
+		return this;
+	}
 
-	//TODO: MDW validation: inclusion and exclusion lists can only contain addon products
-	//TODO: MDW validation: a given product can only be in, at most, one of inclusion and exclusion lists
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/Case.java b/catalog/src/main/java/com/ning/billing/catalog/rules/Case.java
index d63cc2e..67f7bae 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/Case.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/Case.java
@@ -16,9 +16,6 @@
 
 package com.ning.billing.catalog.rules;
 
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlIDREF;
-
 import com.ning.billing.catalog.Catalog;
 import com.ning.billing.catalog.PriceList;
 import com.ning.billing.catalog.Product;
@@ -36,22 +33,6 @@ public abstract class Case<T> extends ValidatingConfig<Catalog> {
 	protected PriceList priceList;
 
 	protected abstract T getResult();
-	
-	public Case() {}
-	
-	protected Case (
-			Product product, 
-			ProductCategory productCategory, 
-			BillingPeriod billingPeriod, 
-			PriceList priceList, 
-			T result
-			) {
-		this.product = product;
-		this.productCategory = productCategory;
-		this.billingPeriod = billingPeriod;
-		this.priceList = priceList;
-	}
-
 
 	public T getResult(PlanSpecifier planPhase, Catalog c) {
 		if (satisfiesCase(planPhase, c)	) {
@@ -69,8 +50,8 @@ public abstract class Case<T> extends ValidatingConfig<Catalog> {
 
 	public static <K> K getResult(Case<K>[] cases, PlanSpecifier planSpec, Catalog catalog) {
     	if(cases != null) {
-    		for(int i = cases.length - 1; i >=0; i --) {
-    			K result = cases[i].getResult(planSpec, catalog);
+    		for(Case<K> c : cases) {
+    			K result = c.getResult(planSpec, catalog);
     			if(result != null) { 
     				return result; 
     			}        					
@@ -82,8 +63,29 @@ public abstract class Case<T> extends ValidatingConfig<Catalog> {
 	
 	@Override
 	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
-		// TODO Auto-generated method stub
-		return null;
+		return errors;
+	}
+
+	protected Case<T> setProduct(Product product) {
+		this.product = product;
+		return this;
+	}
+
+	protected Case<T> setProductCategory(ProductCategory productCategory) {
+		this.productCategory = productCategory;
+		return this;
+	}
+
+	protected Case<T> setBillingPeriod(BillingPeriod billingPeriod) {
+		this.billingPeriod = billingPeriod;
+		return this;
+	}
+
+	protected Case<T> setPriceList(PriceList priceList) {
+		this.priceList = priceList;
+		return this;
 	}
 
+	
+	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseBillingAlignment.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseBillingAlignment.java
index 2744431..8b8c9ab 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseBillingAlignment.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseBillingAlignment.java
@@ -18,29 +18,22 @@ package com.ning.billing.catalog.rules;
 
 import javax.xml.bind.annotation.XmlElement;
 
-import com.ning.billing.catalog.PriceList;
-import com.ning.billing.catalog.Product;
 import com.ning.billing.catalog.api.BillingAlignment;
-import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.PhaseType;
-import com.ning.billing.catalog.api.ProductCategory;
 
 public class CaseBillingAlignment extends CasePhase<BillingAlignment> {
 
-
 	@XmlElement(required=true)
 	private BillingAlignment alignment;
 
-	public CaseBillingAlignment() {}
-
-	public CaseBillingAlignment(Product product, ProductCategory productCategory, BillingPeriod billingPeriod,
-			PriceList priceList, PhaseType phaseType, BillingAlignment alignment) {
-		super(product, productCategory, billingPeriod, priceList, phaseType, alignment);
-		this.alignment = alignment;
-	}
-
 	@Override
 	protected BillingAlignment getResult() {
 		return alignment;
 	}
+
+	protected CaseBillingAlignment setAlignment(BillingAlignment alignment) {
+		this.alignment = alignment;
+		return this;
+	}
+	
+	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseCancelPolicy.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseCancelPolicy.java
index 0405077..eb49803 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseCancelPolicy.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseCancelPolicy.java
@@ -18,31 +18,21 @@ package com.ning.billing.catalog.rules;
 
 import javax.xml.bind.annotation.XmlElement;
 
-import com.ning.billing.catalog.PriceList;
-import com.ning.billing.catalog.Product;
 import com.ning.billing.catalog.api.ActionPolicy;
-import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.PhaseType;
-import com.ning.billing.catalog.api.ProductCategory;
 
 public class CaseCancelPolicy extends CasePhase<ActionPolicy>{
 
 	@XmlElement(required=true)
 	private ActionPolicy policy;
 
-	public CaseCancelPolicy() {}
-
-	public CaseCancelPolicy(Product product, ProductCategory productCategory, BillingPeriod billingPeriod, PriceList priceList,
-			PhaseType phaseType, ActionPolicy policy) {
-		super(product, productCategory, billingPeriod, priceList, phaseType, policy);
-		this.policy = policy;
-	}
-
-
-
 	@Override
 	protected ActionPolicy getResult() {
 		return policy;
 	}
 
+	protected CaseCancelPolicy setPolicy(ActionPolicy policy) {
+		this.policy = policy;
+		return this;
+	}
+
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChange.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChange.java
index 1251487..e0095f8 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChange.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChange.java
@@ -65,26 +65,6 @@ public abstract class CaseChange<T>  extends ValidatingConfig<Catalog> {
 	@XmlElement(required=false)
 	@XmlIDREF
 	private PriceList toPriceList;
-	
-	public CaseChange(){}
-	
-	protected CaseChange (
-			Product from, Product to, 
-			ProductCategory fromProductCategory, ProductCategory toProductCategory, 
-			BillingPeriod fromBP, BillingPeriod toBP, 
-			PriceList fromPriceList, PriceList toPriceList,
-			PhaseType fromType,
-			T result) {
-		this.fromProduct = from;
-		this.toProduct = to;
-		this.fromProductCategory = fromProductCategory;
-		this.toProductCategory = toProductCategory;
-		this.fromBillingPeriod = fromBP;
-		this.toBillingPeriod = toBP;
-		this.phaseType = fromType;
-		this.fromPriceList = fromPriceList;
-		this.toPriceList = toPriceList;
-	}
 
 	protected abstract T getResult();
 	
@@ -109,8 +89,8 @@ public abstract class CaseChange<T>  extends ValidatingConfig<Catalog> {
 	static public <K> K getResult(CaseChange<K>[] cases, PlanPhaseSpecifier from,
 			PlanSpecifier to, Catalog catalog) {
     	if(cases != null) {
-    		for(int i = cases.length - 1; i >=0; i --) {
-    			K result = cases[i].getResult(from, to, catalog);
+    		for(CaseChange<K> cc : cases) {
+    			K result = cc.getResult(from, to, catalog);
     			if(result != null) { 
     				return result; 
     			}        					
@@ -119,12 +99,55 @@ public abstract class CaseChange<T>  extends ValidatingConfig<Catalog> {
         return null;
         
     }
+	
 	@Override
 	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
-		// TODO Auto-generated method stub
 		return errors;
 	}
 
+	protected CaseChange<T> setPhaseType(PhaseType phaseType) {
+		this.phaseType = phaseType;
+		return this;
+	}
+
+	protected CaseChange<T> setFromProduct(Product fromProduct) {
+		this.fromProduct = fromProduct;
+		return this;
+	}
+
+	protected CaseChange<T> setFromProductCategory(ProductCategory fromProductCategory) {
+		this.fromProductCategory = fromProductCategory;
+		return this;
+	}
+
+	protected CaseChange<T> setFromBillingPeriod(BillingPeriod fromBillingPeriod) {
+		this.fromBillingPeriod = fromBillingPeriod;
+		return this;
+	}
+
+	protected CaseChange<T> setFromPriceList(PriceList fromPriceList) {
+		this.fromPriceList = fromPriceList;
+		return this;
+	}
+
+	protected CaseChange<T> setToProduct(Product toProduct) {
+		this.toProduct = toProduct;
+		return this;
+	}
+
+	protected CaseChange<T> setToProductCategory(ProductCategory toProductCategory) {
+		this.toProductCategory = toProductCategory;
+		return this;
+	}
 
+	protected CaseChange<T> setToBillingPeriod(BillingPeriod toBillingPeriod) {
+		this.toBillingPeriod = toBillingPeriod;
+		return this;
+	}
+
+	protected CaseChange<T> setToPriceList(PriceList toPriceList) {
+		this.toPriceList = toPriceList;
+		return this;
+	}
 
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChangePlanAlignment.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChangePlanAlignment.java
index 8b8276d..3186288 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChangePlanAlignment.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChangePlanAlignment.java
@@ -18,39 +18,21 @@ package com.ning.billing.catalog.rules;
 
 import javax.xml.bind.annotation.XmlElement;
 
-import com.ning.billing.catalog.PriceList;
-import com.ning.billing.catalog.Product;
-import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.PlanAlignmentChange;
-import com.ning.billing.catalog.api.ProductCategory;
 
 public class CaseChangePlanAlignment extends CaseChange<PlanAlignmentChange> {
 
 	@XmlElement(required=true)
 	private PlanAlignmentChange alignment;
-	
-	public CaseChangePlanAlignment() {}
-
-	protected CaseChangePlanAlignment(
-			Product from, Product to, 
-			ProductCategory fromProductCategory, ProductCategory toProductCategory, 
-			BillingPeriod fromBP,BillingPeriod toBP, 
-			PriceList fromPriceList, PriceList toPriceList,
-			PhaseType fromType, 
-			PlanAlignmentChange result) {
-		super(from, to, 
-				fromProductCategory, toProductCategory, 
-				fromBP, toBP, 
-				fromPriceList, toPriceList,  
-				fromType,
-				result);
-		alignment = result;
-	}
 
 	@Override
 	protected PlanAlignmentChange getResult() {
 		return alignment;
 	}
 
+	protected CaseChangePlanAlignment setAlignment(PlanAlignmentChange alignment) {
+		this.alignment = alignment;
+		return this;
+	}
+	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChangePlanPolicy.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChangePlanPolicy.java
index 0a894ce..f6862f9 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChangePlanPolicy.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseChangePlanPolicy.java
@@ -19,42 +19,22 @@ package com.ning.billing.catalog.rules;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlSeeAlso;
 
-import com.ning.billing.catalog.PriceList;
-import com.ning.billing.catalog.Product;
 import com.ning.billing.catalog.api.ActionPolicy;
-import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.PhaseType;
-import com.ning.billing.catalog.api.ProductCategory;
 
 @XmlSeeAlso(CaseChange.class)
 public class CaseChangePlanPolicy extends CaseChange<ActionPolicy> {
 	
 	@XmlElement(required=true)
 	private ActionPolicy policy;
-	
-	public CaseChangePlanPolicy() {}
-
-	protected CaseChangePlanPolicy(
-			Product from, Product to, 
-			ProductCategory fromProductCategory, ProductCategory toProductCategory, 
-			BillingPeriod fromBP,BillingPeriod toBP, 
-			PriceList fromPriceList, PriceList toPriceList,
-			PhaseType fromType,
-			ActionPolicy result) {
-		super(from, to, 
-				fromProductCategory, toProductCategory,
-				fromBP, toBP, 
-				fromPriceList, toPriceList, 
-				fromType, 
-				result);
-		policy = result;
-	}
-
-
 
 	@Override
 	protected ActionPolicy getResult() {
 		return policy;
 	}
 
+	protected CaseChangePlanPolicy setPolicy(ActionPolicy policy) {
+		this.policy = policy;
+		return this;
+	}
+	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseCreateAlignment.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseCreateAlignment.java
index 1f25773..39f6607 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseCreateAlignment.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseCreateAlignment.java
@@ -18,28 +18,21 @@ package com.ning.billing.catalog.rules;
 
 import javax.xml.bind.annotation.XmlElement;
 
-import com.ning.billing.catalog.PriceList;
-import com.ning.billing.catalog.Product;
-import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.PlanAlignmentCreate;
-import com.ning.billing.catalog.api.ProductCategory;
 
 public class CaseCreateAlignment extends CaseStandardNaming<PlanAlignmentCreate>{
 
 	@XmlElement(required=true)
 	private PlanAlignmentCreate alignment;
 
-	public CaseCreateAlignment() {}
-
-	public CaseCreateAlignment(Product product, ProductCategory productCategory,  BillingPeriod billingPeriod,
-			PriceList priceList, PlanAlignmentCreate alignment) {
-		super(product, productCategory, billingPeriod, priceList, alignment);
-		this.alignment = alignment;
-	}
-
 	@Override
 	protected PlanAlignmentCreate getResult() {
 		return alignment;
 	}
 
+	protected CaseCreateAlignment setAlignment(PlanAlignmentCreate alignment) {
+		this.alignment = alignment;
+		return this;
+	}
+	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CasePhase.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CasePhase.java
index 122ed7a..afd6d4b 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CasePhase.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CasePhase.java
@@ -31,17 +31,7 @@ import com.ning.billing.util.config.ValidationErrors;
 public abstract class CasePhase<T> extends CaseStandardNaming<T> {
 
 	@XmlElement(required=false)
-	private PhaseType phaseType;
-
-	public CasePhase() {}
-
-	public CasePhase(Product product, ProductCategory productCategory,
-			BillingPeriod billingPeriod, PriceList priceList,
-			PhaseType phaseType, T result) {
-		super(product, productCategory, billingPeriod, priceList, result);
-		this.phaseType = phaseType;
-	}
-	
+	private PhaseType phaseType;	
 	
 	public T getResult(PlanPhaseSpecifier specifier, Catalog c) {
 		if (	
@@ -55,8 +45,8 @@ public abstract class CasePhase<T> extends CaseStandardNaming<T> {
 	
 	public static <K> K getResult(CasePhase<K>[] cases, PlanPhaseSpecifier planSpec, Catalog catalog) {
     	if(cases != null) {
-    		for(int i = cases.length - 1; i >=0; i --) {
-    			K result = cases[i].getResult(planSpec, catalog);
+    		for(CasePhase<K> cp : cases) {
+    			K result = cp.getResult(planSpec, catalog);
     			if(result != null) { 
     				return result; 
     			}        					
@@ -68,7 +58,13 @@ public abstract class CasePhase<T> extends CaseStandardNaming<T> {
 
 	@Override
 	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
-		// TODO Auto-generated method stub
-		return null;
+		return errors;
 	}
+
+	protected CasePhase<T> setPhaseType(PhaseType phaseType) {
+		this.phaseType = phaseType;
+		return this;
+	}
+	
+	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CasePriceList.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CasePriceList.java
index ccd0f87..2e155f8 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CasePriceList.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CasePriceList.java
@@ -56,4 +56,11 @@ public class CasePriceList extends Case<PriceList> {
 	protected PriceList getResult() {
 		return toPriceList;
 	}
+
+	protected CasePriceList setToPriceList(PriceList toPriceList) {
+		this.toPriceList = toPriceList;
+		return this;
+	}
+	
+	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseStandardNaming.java b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseStandardNaming.java
index 3b20634..f74a48d 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/CaseStandardNaming.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/CaseStandardNaming.java
@@ -26,13 +26,6 @@ import com.ning.billing.catalog.api.ProductCategory;
 
 public abstract class CaseStandardNaming<T> extends Case<T> {
 
-	public CaseStandardNaming() {}
-	
-	public CaseStandardNaming(Product product, ProductCategory productCategory,
-			BillingPeriod billingPeriod, PriceList priceList, T result) {
-		super(product, productCategory, billingPeriod, priceList, result);
-	}
-	
 	@XmlElement(required=false, name="product")
 	@XmlIDREF
 	public Product getProduct(){
diff --git a/catalog/src/main/java/com/ning/billing/catalog/rules/PlanRules.java b/catalog/src/main/java/com/ning/billing/catalog/rules/PlanRules.java
index 6665390..787dbaf 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/rules/PlanRules.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/rules/PlanRules.java
@@ -22,134 +22,145 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 
 import com.ning.billing.catalog.Catalog;
+import com.ning.billing.catalog.PriceList;
 import com.ning.billing.catalog.api.ActionPolicy;
 import com.ning.billing.catalog.api.BillingAlignment;
-import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.IProduct;
+import com.ning.billing.catalog.api.IllegalPlanChange;
 import com.ning.billing.catalog.api.PlanAlignmentChange;
 import com.ning.billing.catalog.api.PlanAlignmentCreate;
+import com.ning.billing.catalog.api.PlanChangeResult;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PlanSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.util.config.ValidatingConfig;
 import com.ning.billing.util.config.ValidationErrors;
 
 @XmlAccessorType(XmlAccessType.NONE)
 public class PlanRules extends ValidatingConfig<Catalog>  {
 
-	@XmlElementWrapper(name="tiers", required=true)
-	@XmlElement(name="tier", required=false) // may not have tiers in some catalogs
-	private ProductTier[] productTiers;
-
-	@XmlElement(name="changePolicyRule", required=true)
-	private PlanPolicyChangeRule[] rules;
-
+	@XmlElementWrapper(name="changePolicy")
 	@XmlElement(name="changePolicyCase", required=false)
 	private CaseChangePlanPolicy[] changeCase;
 	
+	@XmlElementWrapper(name="changeAlignment")
 	@XmlElement(name="changeAlignmentCase", required=false)
 	private CaseChangePlanAlignment[] changeAlignmentCase;
 
+	@XmlElementWrapper(name="cancelPolicy")
 	@XmlElement(name="cancelPolicyCase", required=false)
 	private CaseCancelPolicy[] cancelCase;
 
+	@XmlElementWrapper(name="createAlignment")
 	@XmlElement(name="createAlignmentCase", required=false)
 	private CaseCreateAlignment[] createAlignmentCase;
 	
+	@XmlElementWrapper(name="billingAlignment")
 	@XmlElement(name="billingAlignmentCase", required=false)
 	private CaseBillingAlignment[] billingAlignmentCase;
 
+	@XmlElementWrapper(name="priceList")
 	@XmlElement(name="priceListCase", required=false)
 	private CasePriceList[] priceListCase;
 
-	@Override
-	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
-		return errors;
-
-	}
+	public PlanAlignmentCreate getPlanCreateAlignment(PlanSpecifier specifier, Catalog catalog) {
+		return Case.getResult(createAlignmentCase, specifier, catalog);      
+    }
 	
-	public PlanRules(){}
-
-	//For test
-	protected PlanRules(ProductTier[] productTiers, PlanPolicyChangeRule[] rules,
-			CaseChangePlanPolicy[] changeCase, CaseCancelPolicy[] cancelCase,
-			CaseChangePlanAlignment[] changeAlignmentCase,
-			CaseCreateAlignment[] createAlignmentCase) {
-		super();
-		this.productTiers = productTiers;
-		this.rules = rules;
-		this.changeCase = changeCase;
-		this.cancelCase = cancelCase;
-		this.changeAlignmentCase = changeAlignmentCase;
-		this.createAlignmentCase = createAlignmentCase;
+	public ActionPolicy getPlanCancelPolicy(PlanPhaseSpecifier planPhase, Catalog catalog) {
+		return CasePhase.getResult(cancelCase, planPhase, catalog);      
 	}
 
-	public ActionPolicy getPlanChangePolicy(PlanPhaseSpecifier from,
-			PlanSpecifier to, Catalog catalog) {
-		
-		ActionPolicy policy = CaseChange.getResult(changeCase, from, to, catalog); 
-		if (policy != null) {
-			return policy;
-		}
+	public BillingAlignment getBillingAlignment(PlanPhaseSpecifier planPhase, Catalog catalog) {
+		return CasePhase.getResult(billingAlignmentCase, planPhase, catalog);      
+	}
 
-    	
-        for(int i = rules.length - 1; i >=0; i --) {
-        	int fromProductIndex       = getProductIndex(catalog.getProductFromName(from.getProductName()));
-        	int fromBillingPeriodIndex = getBillingPeriodIndex(from.getBillingPeriod());
-			int toProductIndex         = getProductIndex(catalog.getProductFromName(to.getProductName()));
-			int toBillingPeriodIndex   = getBillingPeriodIndex(to.getBillingPeriod());
-			
-			policy = rules[i].getPlanChangePolicy(
-        		fromProductIndex, fromBillingPeriodIndex,
-        		toProductIndex, toBillingPeriodIndex,
-        		from.getPhaseType());
-        	if (policy != null) { return policy; }        
-        }
-        return null;
-        
-    }
-	
-	private int getProductIndex(IProduct src) {
-		for(ProductTier tier : productTiers) {
-			for(int i = 0; i < tier.getProducts().length; i++ ){
-				if (src.equals(tier.getProducts()[i])) {
-					return i;
-				}
-			}
+	public PlanChangeResult planChange(PlanPhaseSpecifier from, PlanSpecifier to, Catalog catalog) throws IllegalPlanChange {
+		PriceList priceList = catalog.getPriceListFromName(to.getPriceListName());
+		if( priceList== null ) {
+			priceList = findPriceList(from.toPlanSpecifier(), catalog);
+			to = new PlanSpecifier(to.getProductName(), to.getProductCategory(), to.getBillingPeriod(), priceList.getName());
+		} 
+		
+		ActionPolicy policy = getPlanChangePolicy(from, to, catalog);
+		if(policy == ActionPolicy.ILLEGAL) {
+			throw new IllegalPlanChange(from, to);
 		}
-		return 0;
+		
+		PlanAlignmentChange alignment = getPlanChangeAlignment(from, to, catalog);
+		
+		return new PlanChangeResult(priceList, policy, alignment);
 	}
+	
 	public PlanAlignmentChange getPlanChangeAlignment(PlanPhaseSpecifier from,
 			PlanSpecifier to, Catalog catalog) {
 		return CaseChange.getResult(changeAlignmentCase, from, to, catalog);      
     }
 
-	public PlanAlignmentCreate getPlanCreateAlignment(PlanSpecifier specifier, Catalog catalog) {
-		return Case.getResult(createAlignmentCase, specifier, catalog);      
-    }
+	public ActionPolicy getPlanChangePolicy(PlanPhaseSpecifier from,
+			PlanSpecifier to, Catalog catalog) {
+		if(from.getProductName().equals(to.getProductName()) &&
+				from.getBillingPeriod() == to.getBillingPeriod() &&
+				from.getPriceListName().equals(to.getPriceListName())) {
+			return ActionPolicy.ILLEGAL;
+		}
+		
+		return CaseChange.getResult(changeCase, from, to, catalog); 
+	}
 	
-	public ActionPolicy getPlanCancelPolicy(PlanPhaseSpecifier planPhase, Catalog catalog) {
-		return CasePhase.getResult(cancelCase, planPhase, catalog);      
+	private PriceList findPriceList(PlanSpecifier specifier, Catalog catalog) {
+		PriceList result = Case.getResult(priceListCase, specifier, catalog);
+		if (result == null) {
+			result = catalog.getPriceListFromName(specifier.getPriceListName());
+		}
+		return result;
 	}
+	
+	
+	@Override
+	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
+	    //TODO: MDW - Validation: check that the plan change special case pairs are unique!
+	    //TODO: MDW - Validation: check that the each product appears in at most one tier.
+		//TODO: MDW - Unit tests for rules
+		//TODO: MDW - validate that there is a default policy for change AND cancel
 
-	public BillingAlignment getBillingAlignment(PlanPhaseSpecifier planPhase, Catalog catalog) {
-		return CasePhase.getResult(billingAlignmentCase, planPhase, catalog);      
+		return errors;
 	}
 
-	private int getBillingPeriodIndex(BillingPeriod src) {
-		return src.ordinal();
+	
+	/////////////////////////////////////////////////////////////////////////////////////
+	// Setters for testing
+	/////////////////////////////////////////////////////////////////////////////////////
+	
+	protected PlanRules setChangeCase(CaseChangePlanPolicy[] changeCase) {
+		this.changeCase = changeCase;
+		return this;
 	}
 
+	protected PlanRules setChangeAlignmentCase(
+			CaseChangePlanAlignment[] changeAlignmentCase) {
+		this.changeAlignmentCase = changeAlignmentCase;
+		return this;
+	}
 
-	protected void setProductTiers(ProductTier[] productTiers) {
-		this.productTiers = productTiers;
+	protected PlanRules setCancelCase(CaseCancelPolicy[] cancelCase) {
+		this.cancelCase = cancelCase;
+		return this;
 	}
 
+	protected PlanRules setCreateAlignmentCase(CaseCreateAlignment[] createAlignmentCase) {
+		this.createAlignmentCase = createAlignmentCase;
+		return this;
+	}
 
+	protected PlanRules setBillingAlignmentCase(
+			CaseBillingAlignment[] billingAlignmentCase) {
+		this.billingAlignmentCase = billingAlignmentCase;
+		return this;
+	}
 
-	
-    //TODO: MDW - Validation: check that the plan change special case pairs are unique!
-    //TODO: MDW - Validation: check that the each product appears in at most one tier.
-	//TODO: MDW - Unit tests for rules
-	//TODO: MDW - validate that there is a default policy for change AND cancel
+	protected PlanRules setPriceListCase(CasePriceList[] priceListCase) {
+		this.priceListCase = priceListCase;
+		return this;
+	}
 
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
index 288c3df..cb447ea 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
@@ -31,8 +31,10 @@ import com.ning.billing.catalog.api.ICatalog;
 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.IllegalPlanChange;
 import com.ning.billing.catalog.api.PlanAlignmentChange;
 import com.ning.billing.catalog.api.PlanAlignmentCreate;
+import com.ning.billing.catalog.api.PlanChangeResult;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PlanSpecifier;
 import com.ning.billing.util.config.ValidatingConfig;
@@ -181,6 +183,12 @@ public class VersionedCatalog extends ValidatingConfig<Catalog> implements ICata
 	public BillingAlignment getBillingAlignment(PlanPhaseSpecifier planPhase) {
 		return currentCatalog.getBillingAlignment(planPhase);
 	}
+
+	@Override
+	public PlanChangeResult planChange(PlanPhaseSpecifier from, PlanSpecifier to)
+			throws IllegalPlanChange {
+		return currentCatalog.planChange(from, to);
+	}
 	
 	//TODO MDW validation - ensure all catalog versions have a single name
 	//TODO MDW validation - ensure effective dates are different (actually do we want this?)
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java b/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java
new file mode 100644
index 0000000..cd9afbe
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java
@@ -0,0 +1,43 @@
+/*
+ * 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.catalog;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import com.ning.billing.catalog.api.Currency;
+
+public class MockInternationalPrice extends InternationalPrice {
+	
+	MockInternationalPrice() {
+		setEffectiveDateForExistingSubscriptons(new Date());
+		setPrices(new Price[] {
+			new Price().setCurrency(Currency.USD).setValue(new BigDecimal(1))	
+		});
+	}
+	
+	MockInternationalPrice(Date effectiveDateForExistingSubscriptions, Price[] price) {
+		setEffectiveDateForExistingSubscriptons(effectiveDateForExistingSubscriptions);
+		setPrices(price);
+	}
+
+	MockInternationalPrice(Price... price) {
+		setEffectiveDateForExistingSubscriptons(new Date());
+		setPrices(price);
+	}
+
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockPlan.java b/catalog/src/test/java/com/ning/billing/catalog/MockPlan.java
new file mode 100644
index 0000000..cf5324d
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockPlan.java
@@ -0,0 +1,46 @@
+/*
+ * 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.catalog;
+
+public class MockPlan extends Plan {
+
+	public MockPlan(String name, Product product, PlanPhase[] planPhases, PlanPhase finalPhase, int plansAllowedInBundle) {
+		setName(name);
+		setProduct(product);
+		setFinalPhase(finalPhase);
+		setInitialPhases(planPhases);
+		setPlansAllowedInBundle(plansAllowedInBundle);
+	}
+	
+	public MockPlan() {
+		setName("test-plan");
+		setProduct(new MockProduct());
+		setFinalPhase(new MockPlanPhase(this));
+		setInitialPhases(null);
+		setPlansAllowedInBundle(1);
+	}
+
+	public MockPlan(MockPlanPhase mockPlanPhase) {
+		setName("test-plan");
+		setProduct(new MockProduct());
+		setFinalPhase(mockPlanPhase);
+		setInitialPhases(null);
+		setPlansAllowedInBundle(1);
+	}
+
+
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockPlanPhase.java b/catalog/src/test/java/com/ning/billing/catalog/MockPlanPhase.java
new file mode 100644
index 0000000..ede4319
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockPlanPhase.java
@@ -0,0 +1,57 @@
+/*
+ * 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.catalog;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.TimeUnit;
+
+public class MockPlanPhase extends PlanPhase {
+
+    public MockPlanPhase(
+    		BillingPeriod billingPeriod, 
+    		PhaseType type, 
+    		Duration duration, 
+    		InternationalPrice recurringPrice, 
+    		InternationalPrice fixedPrice) {
+		setBillingPeriod(billingPeriod);
+		setPhaseType(type);
+		setDuration(duration);
+		setReccuringPrice(recurringPrice);
+		setFixedPrice(fixedPrice);
+	}
+    
+    public MockPlanPhase() {
+		setBillingPeriod(BillingPeriod.MONTHLY);
+		setPhaseType(PhaseType.EVERGREEN);
+		setDuration(new Duration().setNumber(-1).setUnit(TimeUnit.UNLIMITED));
+		setReccuringPrice(new MockInternationalPrice());
+		setFixedPrice(null);
+		setPlan(new MockPlan(this));
+	}
+
+	public MockPlanPhase(MockPlan mockPlan) {
+		setBillingPeriod(BillingPeriod.MONTHLY);
+		setPhaseType(PhaseType.EVERGREEN);
+		setDuration(new Duration().setNumber(-1).setUnit(TimeUnit.UNLIMITED));
+		setReccuringPrice(new MockInternationalPrice());
+		setFixedPrice(null);
+		setPlan(mockPlan);
+	}
+
+	
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockProduct.java b/catalog/src/test/java/com/ning/billing/catalog/MockProduct.java
new file mode 100644
index 0000000..11620a0
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockProduct.java
@@ -0,0 +1,28 @@
+/*
+ * 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.catalog;
+
+import com.ning.billing.catalog.api.ProductCategory;
+
+public class MockProduct extends Product {
+
+	public MockProduct() {
+		setName("TestProduct");
+		setCatagory(ProductCategory.BASE);
+		setCatalogName("Ning");
+	}
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/rules/MockPlanRules.java b/catalog/src/test/java/com/ning/billing/catalog/rules/MockPlanRules.java
new file mode 100644
index 0000000..1c2eb1c
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/rules/MockPlanRules.java
@@ -0,0 +1,33 @@
+/*
+ * 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.catalog.rules;
+
+public class MockPlanRules extends PlanRules {
+
+	//For test
+	public MockPlanRules(
+			CaseChangePlanPolicy[] changeCase, CaseCancelPolicy[] cancelCase,
+			CaseChangePlanAlignment[] changeAlignmentCase,
+			CaseCreateAlignment[] createAlignmentCase) {
+		super();
+		setChangeCase(changeCase);
+		setCancelCase(cancelCase);
+		setChangeAlignmentCase(changeAlignmentCase);
+		setCreateAlignmentCase(createAlignmentCase);
+	}
+
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/rules/TestCase.java b/catalog/src/test/java/com/ning/billing/catalog/rules/TestCase.java
index 40ed1fe..3cd031b 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/rules/TestCase.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/rules/TestCase.java
@@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlElement;
 import org.testng.annotations.Test;
 
 import com.ning.billing.catalog.Catalog;
+import com.ning.billing.catalog.MockCatalog;
 import com.ning.billing.catalog.PriceList;
 import com.ning.billing.catalog.Product;
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -33,9 +34,7 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.catalog.rules.Case;
 
 public class TestCase {
-	protected enum Result {
-		FOO, BAR, WIBBLE
-	}
+
 	protected class CaseResult extends Case<Result>  {
 
 		@XmlElement(required=true)
@@ -43,7 +42,10 @@ public class TestCase {
 
 		public CaseResult(Product product, ProductCategory productCategory, BillingPeriod billingPeriod, PriceList priceList,
 				 Result policy) {
-			super(product, productCategory, billingPeriod, priceList,  policy);
+			setProduct(product);
+			setProductCategory(productCategory);
+			setBillingPeriod(billingPeriod);
+			setPriceList(priceList);
 			this.policy = policy;
 		}
 
@@ -166,6 +168,54 @@ public class TestCase {
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), cat);
 		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", cat);
 	}
+	
+	@Test
+	public void testCaseOrder() {
+		MockCatalog cat = new MockCatalog();
+
+		Product product = cat.getProducts()[0];
+		PriceList priceList = cat.getPriceListFromName(IPriceListSet.DEFAULT_PRICELIST_NAME);
+
+
+		CaseResult cr0 = new CaseResult(
+				product, 
+				ProductCategory.BASE,
+				BillingPeriod.MONTHLY, 
+				priceList,
+				Result.FOO);
+
+		CaseResult cr1 = new CaseResult(
+				product, 
+				ProductCategory.BASE,
+				BillingPeriod.MONTHLY, 
+				priceList,
+				Result.BAR);
+
+		CaseResult cr2 = new CaseResult(
+				product, 
+				ProductCategory.BASE,
+				BillingPeriod.ANNUAL, 
+				priceList,
+				Result.DIPSY);
+
+		CaseResult cr3 = new CaseResult(
+				product, 
+				ProductCategory.BASE,
+				BillingPeriod.ANNUAL, 
+				priceList,
+				Result.LALA);
+
+		Result r1 = Case.getResult(new CaseResult[]{cr0, cr1, cr2,cr3}, 
+				new PlanSpecifier(product.getName(), product.getCategory(), BillingPeriod.MONTHLY, priceList.getName()), cat);
+		assertEquals(Result.FOO, r1);
+		
+		Result r2 = Case.getResult(new CaseResult[]{cr0, cr1, cr2}, 
+				new PlanSpecifier(product.getName(), product.getCategory(), BillingPeriod.ANNUAL, priceList.getName()), cat);
+		assertEquals(Result.DIPSY, r2);
+	}
+
+	
+
 
 	protected void assertionNull(CaseResult cr, String productName, ProductCategory productCategory, BillingPeriod bp, String priceListName, Catalog cat){
 		assertNull(cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat));
diff --git a/catalog/src/test/java/com/ning/billing/catalog/rules/TestCaseChange.java b/catalog/src/test/java/com/ning/billing/catalog/rules/TestCaseChange.java
index beefcfd..34f055d 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/rules/TestCaseChange.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/rules/TestCaseChange.java
@@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlElement;
 import org.testng.annotations.Test;
 
 import com.ning.billing.catalog.Catalog;
+import com.ning.billing.catalog.MockCatalog;
 import com.ning.billing.catalog.PriceList;
 import com.ning.billing.catalog.Product;
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -34,11 +35,7 @@ import com.ning.billing.catalog.api.PlanSpecifier;
 import com.ning.billing.catalog.api.ProductCategory;
 
 public class TestCaseChange {
-	protected enum Result {
-		FOO, BAR, WIBBLE
-	}
-	
-	protected class CaseChangeResult extends CaseChange<Result>  {
+	protected static class CaseChangeResult extends CaseChange<Result>  {
 
 		@XmlElement(required=true)
 		private Result result;
@@ -49,12 +46,16 @@ public class TestCaseChange {
 				PriceList fromPriceList, PriceList toPriceList,
 				PhaseType fromType, 
 				Result result) {
-			super(from, to, 
-					fromProductCategory, toProductCategory,
-					fromBP, toBP, 
-					fromPriceList, toPriceList,
-					fromType, 
-					result);
+			setFromProduct(from);
+			setToProduct(to);
+			setFromProductCategory(fromProductCategory);
+			setToProductCategory(toProductCategory);
+			setFromPriceList(fromPriceList);
+			setToPriceList(toPriceList);
+			setFromBillingPeriod(fromBP);
+			setToBillingPeriod(toBP);
+			setPhaseType(fromType);
+			
 			this.result = result;
 		}
 
@@ -958,6 +959,72 @@ public class TestCaseChange {
 	}
 	
 	
+	@Test(enabled=true)
+	public void testOrder(){
+		MockCatalog cat = new MockCatalog();
+
+		Product product1 = cat.getProducts()[0];
+		PriceList priceList1 = cat.getPriceListFromName(IPriceListSet.DEFAULT_PRICELIST_NAME);
+
+		Product product2 = cat.getProducts()[2];
+		PriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
+
+
+		CaseChangeResult cr0 = new CaseChangeResult(
+				product1, product2,
+				ProductCategory.BASE, ProductCategory.BASE,
+				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
+				priceList1,priceList2,
+				PhaseType.EVERGREEN,
+				Result.FOO);
+
+		CaseChangeResult cr1 = new CaseChangeResult(
+				product1, product2,
+				ProductCategory.BASE, ProductCategory.BASE,
+				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
+				priceList1,priceList2,
+				PhaseType.EVERGREEN,
+				Result.BAR);
+
+		CaseChangeResult cr2 = new CaseChangeResult(
+				product1, product2,
+				ProductCategory.BASE, ProductCategory.BASE,
+				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
+				priceList1,priceList2,
+				PhaseType.EVERGREEN,
+				Result.TINKYWINKY);
+
+		CaseChangeResult cr3 = new CaseChangeResult(
+				product1, product2,
+				ProductCategory.BASE, ProductCategory.BASE,
+				BillingPeriod.MONTHLY, BillingPeriod.ANNUAL, 
+				priceList1,priceList2,
+				PhaseType.EVERGREEN,
+				Result.DIPSY);
+
+		CaseChangeResult cr4 = new CaseChangeResult(
+				product1, product2,
+				ProductCategory.BASE, ProductCategory.BASE,
+				BillingPeriod.MONTHLY, BillingPeriod.ANNUAL, 
+				priceList1,priceList2,
+				PhaseType.EVERGREEN,
+				Result.LALA);
+		
+		Result r1 = CaseChange.getResult(new CaseChangeResult[]{ cr0, cr1, cr2, cr3, cr4 }, 
+				new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN), 
+				new PlanSpecifier(product2.getName(), product2.getCategory(), BillingPeriod.MONTHLY, priceList2.getName()), cat);
+
+		assertEquals(Result.FOO,r1);
+		
+		Result r2 = CaseChange.getResult(new CaseChangeResult[]{ cr0, cr1, cr2, cr3, cr4 }, 
+				new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN), 
+				new PlanSpecifier(product2.getName(), product2.getCategory(), BillingPeriod.ANNUAL, priceList2.getName()), cat);
+
+		assertEquals(Result.DIPSY,r2);
+		
+	}
+	
+	
 	protected void assertionNull(CaseChangeResult cr, 
 			String fromProductName, String toProductName,
 			ProductCategory fromProductCategory, ProductCategory toProductCategory, 
diff --git a/catalog/src/test/java/com/ning/billing/catalog/rules/TestCasePhase.java b/catalog/src/test/java/com/ning/billing/catalog/rules/TestCasePhase.java
index 2fe2f2f..32c8354 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/rules/TestCasePhase.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/rules/TestCasePhase.java
@@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlElement;
 import org.testng.annotations.Test;
 
 import com.ning.billing.catalog.Catalog;
+import com.ning.billing.catalog.MockCatalog;
 import com.ning.billing.catalog.PriceList;
 import com.ning.billing.catalog.Product;
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -33,9 +34,6 @@ import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.catalog.rules.CasePhase;
 
 public class TestCasePhase {
-	protected enum Result {
-		FOO, BAR, WIBBLE
-	}
 	protected class CaseResult extends CasePhase<Result>  {
 
 		@XmlElement(required=true)
@@ -43,7 +41,12 @@ public class TestCasePhase {
 
 		public CaseResult(Product product, ProductCategory productCategory, BillingPeriod billingPeriod, PriceList priceList,
 				PhaseType phaseType, Result policy) {
-			super(product, productCategory, billingPeriod, priceList, phaseType, policy);
+			setProduct(product);
+			setProductCategory(productCategory);
+			setBillingPeriod(billingPeriod);
+			setPriceList(priceList);
+			setPhaseType(phaseType);
+			
 			this.policy = policy;
 		}
 
@@ -196,6 +199,67 @@ public class TestCasePhase {
 		assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
 		assertion(Result.FOO,cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
 	}
+	
+	@Test(enabled=true)
+	public void testOrder(){
+		MockCatalog cat = new MockCatalog();
+
+		Product product = cat.getProducts()[0];
+		PriceList priceList = cat.getPriceLists().getDefaultPricelist();
+
+
+		CaseResult cr0 = new CaseResult(
+				product, 
+				ProductCategory.BASE,
+				BillingPeriod.MONTHLY, 
+				priceList,
+				PhaseType.EVERGREEN, 
+				Result.FOO);
+
+		CaseResult cr1 = new CaseResult(
+				product, 
+				ProductCategory.BASE,
+				BillingPeriod.MONTHLY, 
+				priceList,
+				PhaseType.EVERGREEN, 
+				Result.BAR);
+
+		CaseResult cr2 = new CaseResult(
+				product, 
+				ProductCategory.BASE,
+				BillingPeriod.MONTHLY, 
+				priceList,
+				PhaseType.EVERGREEN, 
+				Result.TINKYWINKY);
+
+		CaseResult cr3 = new CaseResult(
+				product, 
+				ProductCategory.BASE,
+				BillingPeriod.ANNUAL, 
+				priceList,
+				PhaseType.EVERGREEN, 
+				Result.DIPSY);
+
+		CaseResult cr4 = new CaseResult(
+				product, 
+				ProductCategory.BASE,
+				BillingPeriod.ANNUAL, 
+				priceList,
+				PhaseType.EVERGREEN, 
+				Result.LALA);
+		
+		Result r1 = CasePhase.getResult(new CaseResult[]{cr0, cr1, cr2,cr3,cr4}, 
+				new PlanPhaseSpecifier(product.getName(), product.getCategory(), BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN), cat);
+		
+		assertEquals(Result.FOO, r1);
+
+		Result r2 = CasePhase.getResult(new CaseResult[]{cr0, cr1, cr2,cr3,cr4}, 
+				new PlanPhaseSpecifier(product.getName(), product.getCategory(), BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN), cat);
+		
+		assertEquals(Result.DIPSY, r2);
+
+	}
+
 
 	protected void assertionNull(CaseResult cr, String productName, ProductCategory productCategory, BillingPeriod bp, String priceListName, PhaseType phaseType, Catalog cat){
 		assertNull(cr.getResult(new PlanPhaseSpecifier(productName, productCategory, bp, priceListName, phaseType), cat));
diff --git a/catalog/src/test/java/com/ning/billing/catalog/rules/TestPlanRules.java b/catalog/src/test/java/com/ning/billing/catalog/rules/TestPlanRules.java
index 6fb2fbc..fac41a2 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/rules/TestPlanRules.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/rules/TestPlanRules.java
@@ -13,67 +13,114 @@
  * License for the specific language governing permissions and limitations
  * under the License.
  */
+
 package com.ning.billing.catalog.rules;
 
-import com.ning.billing.catalog.Catalog;
-import com.ning.billing.catalog.Plan;
+import junit.framework.Assert;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.MockCatalog;
 import com.ning.billing.catalog.PriceList;
 import com.ning.billing.catalog.Product;
-import com.ning.billing.catalog.api.ProductCategory;
-
+import com.ning.billing.catalog.api.ActionPolicy;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.IPriceListSet;
+import com.ning.billing.catalog.api.IllegalPlanChange;
+import com.ning.billing.catalog.api.PhaseType;
+import com.ning.billing.catalog.api.PlanAlignmentChange;
+import com.ning.billing.catalog.api.PlanChangeResult;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PlanSpecifier;
 
 public class TestPlanRules {
+	Logger log = LoggerFactory.getLogger(TestPlanRules.class);
 
-	public Catalog createCatalog(PlanPolicyChangeRule[] generalRules, 
-			CaseChangePlanPolicy[] specialCaseRules,
-			CaseCancelPolicy[] cancelCaseRules,
-					Product fromP,
-					Product toP) {
-		Catalog c = new Catalog();
-		PlanRules pcr = new PlanRules(
-				new ProductTier[] {new ProductTier(new Product[]{ fromP, toP })},
-				generalRules,
-				specialCaseRules,
-				cancelCaseRules,
-				null,
-				null
-		);
-		c.setPlanChangeRules(pcr);
-		c.setProducts(new Product[] { fromP, toP });
-		return c;
-	}
-	
-	public Catalog createCatalog(PlanPolicyChangeRule[] generalRules, 
-			CaseChangePlanPolicy[] specialCaseRules,
-			CaseCancelPolicy[] cancelCaseRules,
-			CaseChangePlanAlignment[] alignmentChangeCases,
-					Product fromP,
-					Product toP) {
-		Catalog c = new Catalog();
-		PlanRules pcr = new PlanRules(
-				new ProductTier[] {new ProductTier(new Product[]{ fromP, toP })},
-				generalRules,
-				specialCaseRules,
-				cancelCaseRules,
-				alignmentChangeCases,
-				null
-		);
-
-		c.setPlanChangeRules(pcr);
-		c.setProducts(new Product[] { fromP, toP });
-		return c;
+	private MockCatalog cat = null;
+
+	@BeforeTest
+	public void setup() {
+		cat = new MockCatalog();
+
+		PriceList priceList2 = cat.getPriceLists().getChildPriceLists()[0];
+
+		CaseChangePlanPolicy casePolicy = new CaseChangePlanPolicy().setPolicy(ActionPolicy.END_OF_TERM);
+		CaseChangePlanAlignment caseAlignment = new CaseChangePlanAlignment().setAlignment(PlanAlignmentChange.START_OF_SUBSCRIPTION);
+		CasePriceList casePriceList = new CasePriceList().setToPriceList(priceList2);
+
+		cat.getPlanRules().
+		setChangeCase(new CaseChangePlanPolicy[]{casePolicy}).
+		setChangeAlignmentCase(new CaseChangePlanAlignment[]{caseAlignment}).
+		setPriceListCase(new CasePriceList[]{casePriceList});
 	}
 
-	
-	public Product createProduct(String name) {
-		return new Product(name, ProductCategory.BASE);
+	@Test
+	public void testCannotChangeToSamePlan() {
+		Product product1 = cat.getProducts()[0];
+		PriceList priceList1 = cat.getPriceListFromName(IPriceListSet.DEFAULT_PRICELIST_NAME);
+		
+		PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
+		PlanSpecifier to = new PlanSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName());
+
+		try {
+			cat.getPlanRules().planChange(from, to, cat);
+			Assert.fail("We did not see an exception when  trying to change plan to the same plan");
+		} catch (IllegalPlanChange e) {
+			log.info("Correct - cannot change to the same plan:", e);
+		}
+
 	}
 	
+	@Test
+	public void testExistingPriceListIsKept() {
+		Product product1 = cat.getProducts()[0];
+		PriceList priceList1 = cat.getPriceListFromName(IPriceListSet.DEFAULT_PRICELIST_NAME);
+		
+		PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
+		PlanSpecifier to = new PlanSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.ANNUAL, priceList1.getName());
+
+		PlanChangeResult result = null;
+		try {
+			result = cat.getPlanRules().planChange(from, to, cat);		
+		} catch (IllegalPlanChange e) {
+			log.info("Correct - cannot change to the same plan:", e);
+			Assert.fail("We should not have triggered this error");
+		}
+		
+		Assert.assertEquals(result.getPolicy(), ActionPolicy.END_OF_TERM);
+		Assert.assertEquals(result.getAlignment(), PlanAlignmentChange.START_OF_SUBSCRIPTION);
+		Assert.assertEquals(result.getNewPriceList(), priceList1);
 
-	protected PriceList createPriceList(String name) {
-		PriceList result = new PriceList(new Plan[]{},name);
-		return result;
 	}
+	
+	
+	@Test
+	public void testBaseCase() {
+		Product product1 = cat.getProducts()[0];
+		Product product2 = cat.getProducts()[1];
+		PriceList priceList1 = cat.getPriceListFromName(IPriceListSet.DEFAULT_PRICELIST_NAME);
+		PriceList priceList2 = cat.getPriceLists().getChildPriceLists()[0];
+		
+		PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
+		PlanSpecifier to = new PlanSpecifier(product2.getName(), product2.getCategory(), BillingPeriod.MONTHLY, null);
 
+		PlanChangeResult result = null;
+		try {
+			result = cat.getPlanRules().planChange(from, to, cat);		
+		} catch (IllegalPlanChange e) {
+			log.info("Correct - cannot change to the same plan:", e);
+			Assert.fail("We should not have triggered this error");
+		}
+		
+		Assert.assertEquals(result.getPolicy(), ActionPolicy.END_OF_TERM);
+		Assert.assertEquals(result.getAlignment(), PlanAlignmentChange.START_OF_SUBSCRIPTION);
+		Assert.assertEquals(result.getNewPriceList(), priceList2);
 
+	}
+	
+	
+	
 }
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java b/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java
new file mode 100644
index 0000000..b38a823
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java
@@ -0,0 +1,44 @@
+/*
+ * 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.catalog;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.util.config.ValidationErrors;
+
+public class TestPlanPhase {
+	Logger log = LoggerFactory.getLogger(TestPlanPhase.class);
+	
+	@Test(enabled=true)
+	public void testValidation() {
+		log.info("Testing Plan Phase Validation");
+		
+		PlanPhase pp = new MockPlanPhase().setBillCycleDuration(BillingPeriod.MONTHLY).setReccuringPrice(null);
+		ValidationErrors errors = pp.validate(new MockCatalog(), new ValidationErrors());
+		errors.log(log);
+		Assert.assertEquals(errors.size(), 1);
+
+		pp = new MockPlanPhase().setBillCycleDuration(BillingPeriod.NO_BILLING_PERIOD).setReccuringPrice(new MockInternationalPrice());
+		errors = pp.validate(new MockCatalog(), new ValidationErrors());
+		errors.log(log);
+		Assert.assertEquals(errors.size(), 1);
+	}
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestPriceListSet.java b/catalog/src/test/java/com/ning/billing/catalog/TestPriceListSet.java
index 0825eb9..4aedb25 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestPriceListSet.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestPriceListSet.java
@@ -16,6 +16,9 @@
 
 package com.ning.billing.catalog;
 
+import static com.ning.billing.catalog.api.BillingPeriod.*;
+import static com.ning.billing.catalog.api.PhaseType.*;
+
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -30,14 +33,14 @@ public class TestPriceListSet {
 		Product foo = new Product("Foo", ProductCategory.BASE);
 		Product bar = new Product("Bar", ProductCategory.BASE);
 		Plan[] defaultPlans = new Plan[]{ 
-				new Plan("plan-foo-monthly", foo, new PlanPhase(BillingPeriod.MONTHLY, PhaseType.EVERGREEN)),
-				new Plan("plan-bar-monthly", bar, new PlanPhase(BillingPeriod.MONTHLY, PhaseType.EVERGREEN)),
-				new Plan("plan-foo-annual", foo, new PlanPhase(BillingPeriod.ANNUAL, PhaseType.EVERGREEN)),
-				new Plan("plan-bar-annual", bar, new PlanPhase(BillingPeriod.ANNUAL, PhaseType.EVERGREEN))
+				new MockPlan().setName("plan-foo-monthly").setProduct(foo).setFinalPhase(new MockPlanPhase().setBillCycleDuration(MONTHLY).setPhaseType(EVERGREEN)),
+				new MockPlan().setName("plan-bar-monthly").setProduct(bar).setFinalPhase(new MockPlanPhase().setBillCycleDuration(MONTHLY).setPhaseType(EVERGREEN)),
+				new MockPlan().setName("plan-foo-annual").setProduct(foo).setFinalPhase(new MockPlanPhase().setBillCycleDuration(ANNUAL).setPhaseType(EVERGREEN)),
+				new MockPlan().setName("plan-bar-annual").setProduct(bar).setFinalPhase(new MockPlanPhase().setBillCycleDuration(ANNUAL).setPhaseType(EVERGREEN))
 				};
 		Plan[] childPlans = new Plan[]{ 
-				new Plan("plan-foo", foo, new PlanPhase(BillingPeriod.ANNUAL, PhaseType.DISCOUNT)),
-				new Plan("plan-bar", bar, new PlanPhase(BillingPeriod.ANNUAL, PhaseType.DISCOUNT))
+				new MockPlan().setName("plan-foo").setProduct(foo).setFinalPhase(new MockPlanPhase().setBillCycleDuration(ANNUAL).setPhaseType(DISCOUNT)),
+				new MockPlan().setName("plan-bar").setProduct(bar).setFinalPhase(new MockPlanPhase().setBillCycleDuration(ANNUAL).setPhaseType(DISCOUNT))
 				};
 		PriceListDefault defaultPriceList = new PriceListDefault(defaultPlans);
 		PriceList[] childPriceLists = new PriceList[] {
@@ -55,15 +58,16 @@ public class TestPriceListSet {
 		Product foo = new Product("Foo", ProductCategory.BASE);
 		Product bar = new Product("Bar", ProductCategory.BASE);
 		Plan[] defaultPlans = new Plan[]{ 
-				new Plan("plan-foo-monthly", foo, new PlanPhase(BillingPeriod.MONTHLY, PhaseType.EVERGREEN)),
-				new Plan("plan-bar-monthly", bar, new PlanPhase(BillingPeriod.MONTHLY, PhaseType.EVERGREEN)),
-				new Plan("plan-foo-annual", foo, new PlanPhase(null, PhaseType.EVERGREEN)),
-				new Plan("plan-bar-annual", bar, new PlanPhase(null, PhaseType.EVERGREEN))
+				new MockPlan().setName("plan-foo-monthly").setProduct(foo).setFinalPhase(new MockPlanPhase().setBillCycleDuration(MONTHLY).setPhaseType(EVERGREEN)),
+				new MockPlan().setName("plan-bar-monthly").setProduct(bar).setFinalPhase(new MockPlanPhase().setBillCycleDuration(MONTHLY).setPhaseType(EVERGREEN)),
+				new MockPlan().setName("plan-foo-annual").setProduct(foo).setFinalPhase(new MockPlanPhase().setBillCycleDuration(null).setPhaseType(EVERGREEN)),
+				new MockPlan().setName("plan-bar-annual").setProduct(bar).setFinalPhase(new MockPlanPhase().setBillCycleDuration(null).setPhaseType(EVERGREEN))
 				};
 		Plan[] childPlans = new Plan[]{ 
-				new Plan("plan-foo", foo, new PlanPhase(BillingPeriod.ANNUAL, PhaseType.DISCOUNT)),
-				new Plan("plan-bar", bar, new PlanPhase(BillingPeriod.ANNUAL, PhaseType.DISCOUNT))
+				new MockPlan().setName("plan-foo").setProduct(foo).setFinalPhase(new MockPlanPhase().setBillCycleDuration(ANNUAL).setPhaseType(DISCOUNT)),
+				new MockPlan().setName("plan-bar").setProduct(bar).setFinalPhase(new MockPlanPhase().setBillCycleDuration(ANNUAL).setPhaseType(DISCOUNT))
 				};
+
 		PriceListDefault defaultPriceList = new PriceListDefault(defaultPlans);
 		PriceList[] childPriceLists = new PriceList[] {
 				new PriceList(childPlans, "child")
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
index 2214b04..a29e929 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
@@ -40,43 +40,26 @@
 	</products>
 	 
 	<rules>
-		<tiers>
-			<tier>
-				<product>Pistol</product>
-				<product>Shotgun</product>
-			</tier>
-		</tiers>
-		<changePolicyRule>
-			<qualifier>DEFAULT</qualifier>
-			<policy>END_OF_TERM</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>TERM_FROM_SHORT_TO_LONG</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyCase> 
-			<fromBillingPeriod>MONTHLY</fromBillingPeriod>
-			<toProduct>Shotgun</toProduct>
-			<toBillingPeriod>MONTHLY</toBillingPeriod>
-			<policy>END_OF_TERM</policy>
-		</changePolicyCase>
-		<changePolicyCase> 
-			<phaseType>TRIAL</phaseType>
-			<policy>IMMEDIATE</policy>
-		</changePolicyCase>	
-		<changeAlignmentCase>
-			<alignment>START_OF_SUBSCRIPTION</alignment>
-		</changeAlignmentCase>
+		<changePolicy>
+			<changePolicyCase> 
+				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
+				<toProduct>Shotgun</toProduct>
+				<toBillingPeriod>MONTHLY</toBillingPeriod>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>	
+		</changePolicy>
+		<changeAlignment>
+			<changeAlignmentCase>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</changeAlignmentCase>
+		</changeAlignment>
 	</rules>
 
+
 	<plans>
 		<plan name="pistol-monthly">
 			<product>Pistol</product>
@@ -86,6 +69,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice> <!-- empty price implies $0 -->
 					</fixedPrice>
 				</phase>
@@ -110,6 +94,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice></fixedPrice>
 				    <!-- no price implies $0 -->
 				</phase>
@@ -135,6 +120,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
index 7d0e1b8..135a89e 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
@@ -40,41 +40,23 @@
 	</products>
 	 
 	<rules>
-		<tiers>
-			<tier>
-				<product>Pistol</product>
-				<product>Shotgun</product>
-			</tier>
-		</tiers>
-		<changePolicyRule>
-			<qualifier>DEFAULT</qualifier>
-			<policy>END_OF_TERM</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>TERM_FROM_SHORT_TO_LONG</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyCase> 
-			<fromBillingPeriod>MONTHLY</fromBillingPeriod>
-			<toProduct>Shotgun</toProduct>
-			<toBillingPeriod>MONTHLY</toBillingPeriod>
-			<policy>END_OF_TERM</policy>
-		</changePolicyCase>
-		<changePolicyCase> 
-			<phaseType>TRIAL</phaseType>
-			<policy>IMMEDIATE</policy>
-		</changePolicyCase>	
-		<changeAlignmentCase>
-			<alignment>START_OF_SUBSCRIPTION</alignment>
-		</changeAlignmentCase>
+		<changePolicy>
+			<changePolicyCase> 
+				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
+				<toProduct>Shotgun</toProduct>
+				<toBillingPeriod>MONTHLY</toBillingPeriod>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>	
+		</changePolicy>
+		<changeAlignment>
+			<changeAlignmentCase>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</changeAlignmentCase>
+		</changeAlignment>
 	</rules>
 
 	<plans>
@@ -86,6 +68,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice> <!-- empty price implies $0 -->
 					</fixedPrice>
 				</phase>
@@ -110,6 +93,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice></fixedPrice>
 				    <!-- no price implies $0 -->
 				</phase>
@@ -135,6 +119,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
index 070adbe..747f54b 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
@@ -40,43 +40,26 @@
 	</products>
 	 
 	<rules>
-		<tiers>
-			<tier>
-				<product>Pistol</product>
-				<product>Shotgun</product>
-			</tier>
-		</tiers>
-		<changePolicyRule>
-			<qualifier>DEFAULT</qualifier>
-			<policy>END_OF_TERM</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>TERM_FROM_SHORT_TO_LONG</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyCase> 
-			<fromBillingPeriod>MONTHLY</fromBillingPeriod>
-			<toProduct>Shotgun</toProduct>
-			<toBillingPeriod>MONTHLY</toBillingPeriod>
-			<policy>END_OF_TERM</policy>
-		</changePolicyCase>
-		<changePolicyCase> 
-			<phaseType>TRIAL</phaseType>
-			<policy>IMMEDIATE</policy>
-		</changePolicyCase>	
-		<changeAlignmentCase>
-			<alignment>START_OF_SUBSCRIPTION</alignment>
-		</changeAlignmentCase>
+		<changePolicy>
+			<changePolicyCase> 
+				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
+				<toProduct>Shotgun</toProduct>
+				<toBillingPeriod>MONTHLY</toBillingPeriod>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>	
+		</changePolicy>
+		<changeAlignment>
+			<changeAlignmentCase>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</changeAlignmentCase>
+		</changeAlignment>
 	</rules>
 
+
 	<plans>
 		<plan name="pistol-monthly">
 			<product>Pistol</product>
@@ -86,6 +69,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice> <!-- empty price implies $0 -->
 					</fixedPrice>
 				</phase>
@@ -110,6 +94,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice></fixedPrice>
 				    <!-- no price implies $0 -->
 				</phase>
@@ -135,6 +120,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
diff --git a/catalog/src/test/resources/WeaponsHire.xml b/catalog/src/test/resources/WeaponsHire.xml
index cfd445b..740a903 100644
--- a/catalog/src/test/resources/WeaponsHire.xml
+++ b/catalog/src/test/resources/WeaponsHire.xml
@@ -86,76 +86,100 @@ Use Cases to do:
 	</products>
 	 
 	<rules>
-		<tiers>
-			<tier>
-				<product>Pistol</product>
-				<product>Shotgun</product>
-				<product>Assault-Rifle</product>
-			</tier>
-		</tiers>
-		<changePolicyRule>
-			<qualifier>DEFAULT</qualifier>
-			<policy>END_OF_TERM</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>TERM_FROM_SHORT_TO_LONG</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyCase> 
-			<fromBillingPeriod>MONTHLY</fromBillingPeriod>
-			<toProduct>Assault-Rifle</toProduct>
-			<toBillingPeriod>MONTHLY</toBillingPeriod>
-			<policy>END_OF_TERM</policy>
-		</changePolicyCase>
-		<changePolicyCase> 
-			<toPriceList>rescue</toPriceList>
-			<policy>END_OF_TERM</policy>
-		</changePolicyCase>		
-		<changePolicyCase> 
-			<phaseType>TRIAL</phaseType>
-			<policy>IMMEDIATE</policy>
-		</changePolicyCase>
-		<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>
-		<cancelPolicyCase>
-			<policy>END_OF_TERM</policy>
-		</cancelPolicyCase>
-		<cancelPolicyCase>
-			<phaseType>TRIAL</phaseType>
-			<policy>IMMEDIATE</policy>
-		</cancelPolicyCase>
-		<createAlignmentCase>
-			<alignment>START_OF_BUNDLE</alignment>
-		</createAlignmentCase>
+		<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>
 			<alignment>ACCOUNT</alignment>
 		</billingAlignmentCase>
-		<billingAlignmentCase>
-			<billingPeriod>ANNUAL</billingPeriod>
-			<alignment>SUBSCRIPTION</alignment>
-		</billingAlignmentCase>
-		<billingAlignmentCase>
-			<productCategory>ADD_ON</productCategory>
-			<alignment>BUNDLE</alignment>
-		</billingAlignmentCase>
-		<priceListCase>
-			<fromPriceList>rescue</fromPriceList>
-			<toPriceList>DEFAULT</toPriceList>
-		</priceListCase>
+			<billingAlignmentCase>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<alignment>SUBSCRIPTION</alignment>
+			</billingAlignmentCase>
+			<billingAlignmentCase>
+				<productCategory>ADD_ON</productCategory>
+				<alignment>BUNDLE</alignment>
+			</billingAlignmentCase>
+		</billingAlignment>
+		<priceList>
+			<priceListCase>
+				<fromPriceList>rescue</fromPriceList>
+				<toPriceList>DEFAULT</toPriceList>
+			</priceListCase>
+		</priceList>
 	</rules>
 
 	<plans>
@@ -167,6 +191,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice> <!-- empty price implies $0 -->
 					</fixedPrice>
 				</phase>
@@ -191,6 +216,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice></fixedPrice>
 				    <!-- no price implies $0 -->
 				</phase>
@@ -216,6 +242,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -240,6 +267,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -264,6 +292,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -288,6 +317,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -312,6 +342,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -348,6 +379,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -384,6 +416,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -463,6 +496,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -487,6 +521,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
diff --git a/catalog/src/test/resources/WeaponsHireSmall.xml b/catalog/src/test/resources/WeaponsHireSmall.xml
index c4a098d..0a3ed52 100644
--- a/catalog/src/test/resources/WeaponsHireSmall.xml
+++ b/catalog/src/test/resources/WeaponsHireSmall.xml
@@ -40,41 +40,23 @@
 	</products>
 	 
 	<rules>
-		<tiers>
-			<tier>
-				<product>Pistol</product>
-				<product>Shotgun</product>
-			</tier>
-		</tiers>
-		<changePolicyRule>
-			<qualifier>DEFAULT</qualifier>
-			<policy>END_OF_TERM</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>TERM_FROM_SHORT_TO_LONG</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyCase> 
-			<fromBillingPeriod>MONTHLY</fromBillingPeriod>
-			<toProduct>Shotgun</toProduct>
-			<toBillingPeriod>MONTHLY</toBillingPeriod>
-			<policy>END_OF_TERM</policy>
-		</changePolicyCase>
-		<changePolicyCase> 
-			<phaseType>TRIAL</phaseType>
-			<policy>IMMEDIATE</policy>
-		</changePolicyCase>	
-		<changeAlignmentCase>
-			<alignment>START_OF_SUBSCRIPTION</alignment>
-		</changeAlignmentCase>
+		<changePolicy>
+			<changePolicyCase> 
+				<fromBillingPeriod>MONTHLY</fromBillingPeriod>
+				<toProduct>Shotgun</toProduct>
+				<toBillingPeriod>MONTHLY</toBillingPeriod>
+				<policy>END_OF_TERM</policy>
+			</changePolicyCase>
+			<changePolicyCase> 
+				<phaseType>TRIAL</phaseType>
+				<policy>IMMEDIATE</policy>
+			</changePolicyCase>	
+		</changePolicy>
+		<changeAlignment>
+			<changeAlignmentCase>
+				<alignment>START_OF_SUBSCRIPTION</alignment>
+			</changeAlignmentCase>
+		</changeAlignment>
 	</rules>
 
 	<plans>
@@ -86,6 +68,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice> <!-- empty price implies $0 -->
 					</fixedPrice>
 				</phase>
@@ -110,6 +93,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice></fixedPrice>
 				    <!-- no price implies $0 -->
 				</phase>
@@ -135,6 +119,7 @@
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 5f4d4ef..7483255 100644
--- a/entitlement/pom.xml
+++ b/entitlement/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.13-SNAPSHOT</version>
+        <version>0.0.14-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
index 0d9ac8d..01ec2c0 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/PlanAligner.java
@@ -139,13 +139,9 @@ public class PlanAligner implements IPlanAligner {
             planStartDate = subscription.getBundleStartDate();
             break;
         case CHANGE_OF_PLAN:
-            // STEPH
             throw new EntitlementError(String.format("Not implemented yet %s", alignment));
-            //break;
         case CHANGE_OF_PRICELIST:
-            // STEPH
             throw new EntitlementError(String.format("Not implemented yet %s", alignment));
-            //break;
         default:
             throw new EntitlementError(String.format("Unknwon PlanAlignmentChange %s", alignment));
         }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java
index 8ba459e..61cee03 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/test/EntitlementTestApi.java
@@ -19,6 +19,7 @@ package com.ning.billing.entitlement.api.test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.inject.Inject;
 import com.ning.billing.config.IEntitlementConfig;
 import com.ning.billing.entitlement.engine.core.IApiEventProcessor;
 
@@ -29,6 +30,7 @@ public class EntitlementTestApi implements IEntitlementTestApi {
     private final IApiEventProcessor apiEventProcessor;
     private final IEntitlementConfig config;
 
+    @Inject
     public EntitlementTestApi(IApiEventProcessor apiEventProcessor, IEntitlementConfig config) {
         this.apiEventProcessor = apiEventProcessor;
         this.config = config;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
index 19f2ec8..93fe561 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/EntitlementUserApi.java
@@ -22,53 +22,42 @@ import java.util.UUID;
 
 import org.joda.time.DateTime;
 
+import com.google.inject.Inject;
 import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.IAccount;
 import com.ning.billing.catalog.api.BillingPeriod;
-import com.ning.billing.catalog.api.ICatalog;
+import com.ning.billing.catalog.api.ICatalogService;
 import com.ning.billing.catalog.api.IPlan;
 import com.ning.billing.catalog.api.IPlanPhase;
 import com.ning.billing.catalog.api.IPriceListSet;
 import com.ning.billing.entitlement.alignment.IPlanAligner;
 import com.ning.billing.entitlement.alignment.IPlanAligner.TimedPhase;
-import com.ning.billing.entitlement.engine.core.Engine;
+import com.ning.billing.entitlement.api.user.ISubscription;
+import com.ning.billing.entitlement.api.user.ISubscriptionBundle;
+import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
 import com.ning.billing.entitlement.engine.dao.IEntitlementDao;
 import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.phase.IPhaseEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEvent;
 import com.ning.billing.entitlement.events.user.ApiEventCreate;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
-import com.ning.billing.entitlement.glue.InjectorMagic;
 import com.ning.billing.util.clock.IClock;
 
 public class EntitlementUserApi implements IEntitlementUserApi {
 
-    private final Engine engine;
     private final IClock clock;
     private final IEntitlementDao dao;
     private final IPlanAligner planAligner;
+    private final ICatalogService catalogService;
 
-    private boolean initialized;
-
-    private ICatalog catalog;
-
-    public EntitlementUserApi(Engine engine, IClock clock, IPlanAligner planAligner, IEntitlementDao dao) {
+    @Inject
+    public EntitlementUserApi(IClock clock, IPlanAligner planAligner,
+            IEntitlementDao dao, ICatalogService catalogService) {
         super();
-        this.engine = engine;
         this.clock = clock;
         this.dao = dao;
         this.planAligner = planAligner;
-        this.initialized = false;
-
-    }
-
-    @Override
-    public synchronized void initialize(List<IApiListener> listeners) {
-        if (!initialized) {
-            this.catalog = InjectorMagic.getCatlog();
-            engine.registerApiObservers(listeners);
-            initialized = true;
-        }
+        this.catalogService = catalogService;
     }
 
     @Override
@@ -102,7 +91,6 @@ public class EntitlementUserApi implements IEntitlementUserApi {
     public ISubscription createSubscription(UUID bundleId, String productName,
             BillingPeriod term, String priceList, DateTime requestedDate) throws EntitlementUserApiException {
 
-        // STEPH Should really get 'standard' from catalog
         String realPriceList = (priceList == null) ? IPriceListSet.DEFAULT_PRICELIST_NAME : priceList;
 
         DateTime now = clock.getUTCNow();
@@ -112,7 +100,7 @@ public class EntitlementUserApi implements IEntitlementUserApi {
 
         requestedDate = (requestedDate == null) ? now : requestedDate;
 
-        IPlan plan = catalog.getPlan(productName, term, realPriceList);
+        IPlan plan = catalogService.getCatalog().getPlan(productName, term, realPriceList);
         if (plan == null) {
             throw new EntitlementUserApiException(ErrorCode.ENT_CREATE_BAD_CATALOG, productName, term, realPriceList);
         }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 2c04998..afcf053 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -47,7 +47,7 @@ import com.ning.billing.entitlement.events.user.ApiEventCancel;
 import com.ning.billing.entitlement.events.user.ApiEventChange;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.events.user.ApiEventUncancel;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.entitlement.glue.InjectorMagic;
 import com.ning.billing.util.clock.IClock;
@@ -295,8 +295,8 @@ public class Subscription extends PrivateFields  implements ISubscription {
         TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(this, newPlan, realPriceList, effectiveDate);
         IPhaseEvent nextPhaseEvent = PhaseEvent.getNextPhaseEvent(nextTimedPhase, this, now);
         List<IEvent> changeEvents = new ArrayList<IEvent>();
-        // Add phase event first so we expect to see PHASE event first-- mostly for test expectation
-        if (nextPhaseEvent != null) {
+        // Only add the PHASE if it does not coincide with the CHANGE, if not this is 'just' a CHANGE.
+        if (nextPhaseEvent != null && ! nextPhaseEvent.getEffectiveDate().equals(changeEvent.getEffectiveDate())) {
             changeEvents.add(nextPhaseEvent);
         }
         changeEvents.add(changeEvent);
@@ -479,7 +479,7 @@ public class Subscription extends PrivateFields  implements ISubscription {
                 break;
 
             case API_USER:
-                IUserEvent userEV = (IUserEvent) cur;
+                IApiEvent userEV = (IApiEvent) cur;
                 apiEventType = userEV.getEventType();
                 switch(apiEventType) {
                 case CREATE:
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
index 63f42f6..616e871 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionTransition.java
@@ -25,6 +25,7 @@ import com.ning.billing.catalog.api.IPlanPhase;
 import com.ning.billing.entitlement.api.user.ISubscription.SubscriptionState;
 import com.ning.billing.entitlement.events.IEvent.EventType;
 import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
 
 public class SubscriptionTransition implements ISubscriptionTransition {
 
@@ -117,6 +118,18 @@ public class SubscriptionTransition implements ISubscriptionTransition {
         return nextPriceList;
     }
 
+    @Override
+    public SubscriptionTransitionType getTransitionType() {
+        switch(eventType) {
+        case API_USER:
+            return apiEventType.getSubscriptionTransitionType();
+        case PHASE:
+            return SubscriptionTransitionType.PHASE;
+        default:
+            throw new EntitlementError("Unexpected event type " + eventType);
+        }
+    }
+
     public ApiEventType getApiEventType() {
         return apiEventType;
     }
@@ -151,5 +164,4 @@ public class SubscriptionTransition implements ISubscriptionTransition {
                 + ", nextPriceList " + nextPriceList
                 + ", nextPhase=" + ((nextPhase != null) ? nextPhase.getName() : null) + "]";
     }
-
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java
index c5b9bd4..f3faf8e 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java
@@ -141,8 +141,8 @@ public abstract class ApiEventProcessorBase implements IApiEventProcessor {
                             Thread.currentThread().getId()));
                 } catch (Throwable e) {
                     log.error(API_EVENT_THREAD_NAME + " got an exception exiting...", e);
-                    // STEPH let's review that later...
-                    System.exit(1);
+                    // Just to make it really obvious in the log
+                    e.printStackTrace();
                 }
             }
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
index fee014e..622fa12 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/Engine.java
@@ -16,27 +16,21 @@
 
 package com.ning.billing.entitlement.engine.core;
 
-import java.util.List;
-
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.inject.Inject;
-import com.ning.billing.catalog.api.ICatalog;
-import com.ning.billing.catalog.api.ICatalogService;
 import com.ning.billing.config.IEntitlementConfig;
 
 import com.ning.billing.entitlement.alignment.IPlanAligner;
 import com.ning.billing.entitlement.alignment.IPlanAligner.TimedPhase;
-import com.ning.billing.entitlement.alignment.PlanAligner;
 import com.ning.billing.entitlement.api.IEntitlementService;
-import com.ning.billing.entitlement.api.billing.BillingApi;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
 import com.ning.billing.entitlement.api.billing.IEntitlementBillingApi;
 import com.ning.billing.entitlement.api.test.EntitlementTestApi;
 import com.ning.billing.entitlement.api.test.IEntitlementTestApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
-import com.ning.billing.entitlement.api.user.IApiListener;
 import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
 import com.ning.billing.entitlement.api.user.Subscription;
 import com.ning.billing.entitlement.engine.dao.IEntitlementDao;
@@ -44,11 +38,13 @@ import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.IEvent.EventType;
 import com.ning.billing.entitlement.events.phase.IPhaseEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEvent;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.lifecycle.IService;
 import com.ning.billing.lifecycle.LyfecycleHandlerType;
 import com.ning.billing.lifecycle.LyfecycleHandlerType.LyfecycleLevel;
 import com.ning.billing.util.clock.IClock;
+import com.ning.billing.util.eventbus.IEventBus;
+import com.ning.billing.util.eventbus.IEventBus.EventBusException;
 
 public class Engine implements IEventListener, IEntitlementService {
 
@@ -63,24 +59,21 @@ public class Engine implements IEventListener, IEntitlementService {
     private final IEntitlementUserApi userApi;
     private final IEntitlementBillingApi billingApi;
     private final IEntitlementTestApi testApi;
-    private final IEntitlementConfig config;
-    private List<IApiListener> observers;
-
+    private final IEventBus eventBus;
 
     @Inject
     public Engine(IClock clock, IEntitlementDao dao, IApiEventProcessor apiEventProcessor,
-            IPlanAligner planAligner, IEntitlementConfig config) {
+            IPlanAligner planAligner, IEntitlementConfig config, EntitlementUserApi userApi,
+            EntitlementBillingApi billingApi, EntitlementTestApi testApi, IEventBus eventBus) {
         super();
         this.clock = clock;
         this.dao = dao;
         this.apiEventProcessor = apiEventProcessor;
         this.planAligner = planAligner;
-        this.config = config;
-        this.observers = null;
-        this.userApi = new EntitlementUserApi(this, clock, planAligner, dao);
-        this.billingApi = new BillingApi(this, clock, dao);
-        this.testApi = new EntitlementTestApi(apiEventProcessor, config);
-
+        this.userApi = userApi;
+        this.testApi = testApi;
+        this.billingApi = billingApi;
+        this.eventBus = eventBus;
     }
 
     @Override
@@ -104,8 +97,7 @@ public class Engine implements IEventListener, IEntitlementService {
     }
 
     @Override
-    public IEntitlementUserApi getUserApi(List<IApiListener> listeners) {
-        userApi.initialize(listeners);
+    public IEntitlementUserApi getUserApi() {
         return userApi;
     }
 
@@ -120,51 +112,22 @@ public class Engine implements IEventListener, IEntitlementService {
         return testApi;
     }
 
-    public void registerApiObservers(List<IApiListener> observers) {
-        this.observers = observers;
-    }
-
-
     @Override
     public void processEventReady(IEvent event) {
-        if (observers == null) {
-            return;
-        }
         Subscription subscription = (Subscription) dao.getSubscriptionFromId(event.getSubscriptionId());
         if (subscription == null) {
             log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
             return;
         }
-        if (event.getType() == EventType.API_USER) {
-            dispatchApiEvent((IUserEvent) event, subscription);
-        } else {
-            dispatchPhaseEvent((IPhaseEvent) event, subscription);
+        if (event.getType() == EventType.PHASE) {
             insertNextPhaseEvent(subscription);
         }
-    }
-
-    private void dispatchApiEvent(IUserEvent event, Subscription subscription) {
-        for (IApiListener listener : observers) {
-            switch(event.getEventType()) {
-            case CREATE:
-                listener.subscriptionCreated(subscription.getLatestTranstion());
-                break;
-            case CHANGE:
-                listener.subscriptionChanged(subscription.getLatestTranstion());
-                break;
-            case CANCEL:
-                listener.subscriptionCancelled(subscription.getLatestTranstion());
-                break;
-            default:
-                break;
-            }
+        try {
+            eventBus.post(subscription.getLatestTranstion());
+        } catch (EventBusException e) {
+            log.warn("Failed to post entitlement event " + event, e);
         }
-    }
 
-    private void dispatchPhaseEvent(IPhaseEvent event, Subscription subscription) {
-        for (IApiListener listener : observers) {
-            listener.subscriptionPhaseChanged(subscription.getLatestTranstion());
-        }
     }
 
     private void insertNextPhaseEvent(Subscription subscription) {
@@ -174,7 +137,6 @@ public class Engine implements IEventListener, IEntitlementService {
         TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, subscription.getCurrentPlan(), now, subscription.getCurrentPlanStart());
         IPhaseEvent nextPhaseEvent = PhaseEvent.getNextPhaseEvent(nextTimedPhase, subscription, now);
         if (nextPhaseEvent != null) {
-            // STEPH Harden since event could be processed twice
             dao.createNextPhaseEvent(subscription.getId(), nextPhaseEvent);
         }
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
index db4e375..197e4bd 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementDao.java
@@ -41,7 +41,7 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.IEvent.EventType;
 import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.Hostname;
 import com.ning.billing.util.clock.IClock;
@@ -249,7 +249,7 @@ public class EntitlementDao implements IEntitlementDao {
                 List<IEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
 
                 for (IEvent cur : events) {
-                    if (cur.getType() == EventType.API_USER && ((IUserEvent) cur).getEventType() == ApiEventType.CANCEL) {
+                    if (cur.getType() == EventType.API_USER && ((IApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
                         if (existingCancelId != null) {
                             throw new EntitlementError(String.format("Found multiple cancel active events for subscriptions %s", subscriptionId.toString()));
                         }
@@ -299,7 +299,7 @@ public class EntitlementDao implements IEntitlementDao {
         List<IEvent> events = dao.getFutureActiveEventForSubscription(subscriptionId.toString(), now);
         for (IEvent cur : events) {
             if (cur.getType() == type &&
-                    (apiType == null || apiType == ((IUserEvent) cur).getEventType() )) {
+                    (apiType == null || apiType == ((IApiEvent) cur).getEventType() )) {
                 if (futureEventId != null) {
                     throw new EntitlementError(
                             String.format("Found multiple future events for type %s for subscriptions %s",
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java
index 007d5b9..3c2180d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/IEventSqlDao.java
@@ -50,7 +50,7 @@ import com.ning.billing.entitlement.events.user.ApiEventPause;
 import com.ning.billing.entitlement.events.user.ApiEventResume;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.events.user.ApiEventUncancel;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 
 @ExternalizedSqlViaStringTemplate3()
@@ -99,15 +99,15 @@ public interface IEventSqlDao extends Transactional<IEventSqlDao>, CloseMe, Tran
         public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, IEvent evt) {
             stmt.bind("event_id", evt.getId().toString());
             stmt.bind("event_type", evt.getType().toString());
-            stmt.bind("user_type", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getEventType().toString() : null);
+            stmt.bind("user_type", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getEventType().toString() : null);
             stmt.bind("created_dt", getDate(evt.getProcessedDate()));
             stmt.bind("updated_dt", getDate(evt.getProcessedDate()));
             stmt.bind("requested_dt", getDate(evt.getRequestedDate()));
             stmt.bind("effective_dt", getDate(evt.getEffectiveDate()));
             stmt.bind("subscription_id", evt.getSubscriptionId().toString());
-            stmt.bind("plan_name", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getEventPlan() : null);
-            stmt.bind("phase_name", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getEventPlanPhase() : ((IPhaseEvent) evt).getPhase());
-            stmt.bind("plist_name", (evt.getType() == EventType.API_USER) ? ((IUserEvent) evt).getPriceList() : null);
+            stmt.bind("plan_name", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getEventPlan() : null);
+            stmt.bind("phase_name", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getEventPlanPhase() : ((IPhaseEvent) evt).getPhase());
+            stmt.bind("plist_name", (evt.getType() == EventType.API_USER) ? ((IApiEvent) evt).getPriceList() : null);
             stmt.bind("current_version", evt.getActiveVersion());
             stmt.bind("is_active", evt.isActive());
             stmt.bind("processing_available_dt", getDate(evt.getNextAvailableDate()));
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
index a0c526a..b1e9f02 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBase.java
@@ -22,7 +22,7 @@ import org.joda.time.DateTime;
 
 import com.ning.billing.catalog.api.IPlan;
 import com.ning.billing.entitlement.events.IEventLyfecycle.IEventLyfecycleState;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 
 public abstract class EventBase implements IEvent {
@@ -213,7 +213,7 @@ public abstract class EventBase implements IEvent {
         } else if (getType() != other.getType()) {
             return (getType() == EventType.PHASE) ? -1 : 1;
         } else if (getType() == EventType.API_USER) {
-            return ((IUserEvent) this).getEventType().compareTo(((IUserEvent) other).getEventType());
+            return ((IApiEvent) this).getEventType().compareTo(((IApiEvent) other).getEventType());
         } else {
             return uuid.compareTo(other.getId());
         }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
index 4e2944e..e1b9177 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventBase.java
@@ -27,7 +27,7 @@ import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.events.EventBase;
 import com.ning.billing.entitlement.events.IEventLyfecycle.IEventLyfecycleState;
 
-public class ApiEventBase extends EventBase implements IUserEvent {
+public class ApiEventBase extends EventBase implements IApiEvent {
 
     private final ApiEventType eventType;
     // Only valid for CREATE/CHANGE
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
index e07e39f..3d66a5b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/user/ApiEventType.java
@@ -16,16 +16,35 @@
 
 package com.ning.billing.entitlement.events.user;
 
+import com.ning.billing.entitlement.api.user.ISubscriptionTransition.SubscriptionTransitionType;
+
 
 public enum ApiEventType {
-    /*
-     * STEPH should be changed
-     * Ordering is important for unit tests today.
-     */
-    CREATE,
-    CHANGE,
-    PAUSE,
-    RESUME,
-    CANCEL,
-    UNCANCEL
+    CREATE {
+        @Override
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CREATE; }
+    },
+    CHANGE {
+        @Override
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CHANGE; }
+    },
+    PAUSE {
+        @Override
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.PAUSE; }
+    },
+    RESUME {
+        @Override
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.RESUME; }
+    },
+    CANCEL {
+        @Override
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.CANCEL; }
+    },
+    UNCANCEL {
+        @Override
+        public SubscriptionTransitionType getSubscriptionTransitionType() { return SubscriptionTransitionType.UNCANCEL; }
+    };
+
+    // Used to map from internal events to User visible events (both user and phase)
+    public abstract SubscriptionTransitionType getSubscriptionTransitionType();
 }
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 d303b30..89b58c8 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
@@ -23,6 +23,10 @@ import com.ning.billing.config.IEntitlementConfig;
 import com.ning.billing.entitlement.alignment.IPlanAligner;
 import com.ning.billing.entitlement.alignment.PlanAligner;
 import com.ning.billing.entitlement.api.IEntitlementService;
+import com.ning.billing.entitlement.api.billing.EntitlementBillingApi;
+import com.ning.billing.entitlement.api.billing.IEntitlementBillingApi;
+import com.ning.billing.entitlement.api.test.EntitlementTestApi;
+import com.ning.billing.entitlement.api.test.IEntitlementTestApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.IEntitlementUserApi;
 import com.ning.billing.entitlement.engine.core.ApiEventProcessor;
@@ -59,6 +63,9 @@ public class EntitlementModule extends AbstractModule {
         bind(IEntitlementService.class).to(Engine.class).asEagerSingleton();
         bind(Engine.class).asEagerSingleton();
         bind(IPlanAligner.class).to(PlanAligner.class).asEagerSingleton();
+        bind(IEntitlementTestApi.class).to(EntitlementTestApi.class).asEagerSingleton();
+        bind(IEntitlementUserApi.class).to(EntitlementUserApi.class).asEagerSingleton();
+        bind(IEntitlementBillingApi.class).to(EntitlementBillingApi.class).asEagerSingleton();
     }
 
     protected void installInjectorMagic() {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
index 9591ee6..5b5100b 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/ApiTestListener.java
@@ -22,10 +22,11 @@ import java.util.Stack;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.ning.billing.entitlement.api.user.IApiListener;
+import com.google.common.eventbus.Subscribe;
 import com.ning.billing.entitlement.api.user.ISubscriptionTransition;
+import com.ning.billing.util.eventbus.IEventBus;
 
-public class ApiTestListener implements IApiListener {
+public class ApiTestListener {
 
     private static final Logger log = LoggerFactory.getLogger(ApiTestListener.class);
 
@@ -42,11 +43,40 @@ public class ApiTestListener implements IApiListener {
         PHASE
     }
 
-    public ApiTestListener() {
+    public ApiTestListener(IEventBus eventBus) {
         this.nextExpectedEvent = new Stack<NextEvent>();
         this.completed = false;
     }
 
+    @Subscribe
+    public void handleEntitlementEvent(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 pushExpectedEvent(NextEvent next) {
         synchronized (this) {
             nextExpectedEvent.push(next);
@@ -98,42 +128,42 @@ public class ApiTestListener implements IApiListener {
         }
     }
 
-    @Override
+
     public void subscriptionCreated(ISubscriptionTransition created) {
         log.debug("-> Got event CREATED");
         assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.CREATE);
         notifyIfStackEmpty();
     }
 
-    @Override
+
     public void subscriptionCancelled(ISubscriptionTransition cancelled) {
         log.debug("-> Got event CANCEL");
         assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.CANCEL);
         notifyIfStackEmpty();
     }
 
-    @Override
+
     public void subscriptionChanged(ISubscriptionTransition changed) {
         log.debug("-> Got event CHANGE");
         assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.CHANGE);
         notifyIfStackEmpty();
     }
 
-    @Override
+
     public void subscriptionPaused(ISubscriptionTransition paused) {
         log.debug("-> Got event PAUSE");
         assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.PAUSE);
         notifyIfStackEmpty();
     }
 
-    @Override
+
     public void subscriptionResumed(ISubscriptionTransition resumed) {
         log.debug("-> Got event RESUME");
         assertEqualsNicely(nextExpectedEvent.pop(), NextEvent.RESUME);
         notifyIfStackEmpty();
     }
 
-    @Override
+
     public void subscriptionPhaseChanged(
             ISubscriptionTransition phaseChanged) {
         log.debug("-> Got event PHASE");
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
index 568d08a..21a5064 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiBase.java
@@ -31,11 +31,13 @@ import com.ning.billing.entitlement.engine.dao.IEntitlementDaoMock;
 import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.phase.IPhaseEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.entitlement.glue.InjectorMagic;
 import com.ning.billing.lifecycle.IService.ServiceException;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.clock.IClock;
+import com.ning.billing.util.eventbus.EventBusService;
+import com.ning.billing.util.eventbus.IEventBusService;
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -54,6 +56,7 @@ import java.util.UUID;
 
 import static org.testng.Assert.*;
 
+
 public abstract class TestUserApiBase {
 
     protected static final Logger log = LoggerFactory.getLogger(TestUserApiBase.class);
@@ -67,14 +70,13 @@ public abstract class TestUserApiBase {
     protected IEntitlementConfig config;
     protected IEntitlementDao dao;
     protected ClockMock clock;
+    protected IEventBusService busService;
 
     protected IAccount account;
     protected ICatalog catalog;
     protected ApiTestListener testListener;
     protected ISubscriptionBundle bundle;
 
-    private InjectorMagic injectorMagic;
-
     public static void loadSystemPropertiesFromClasspath( final String resource )
     {
         final URL url = TestUserApiBase.class.getResource(resource);
@@ -89,7 +91,14 @@ public abstract class TestUserApiBase {
 
     @AfterClass(groups={"setup"})
     public void tearDown() {
-        InjectorMagic.instance = null;
+        try {
+            InjectorMagic.instance = null;
+            busService.getEventBus().register(testListener);
+            ((EventBusService) busService).stopBus();
+        } catch (Exception e) {
+            log.warn("Failed to tearDown test properly ", e);
+        }
+
     }
 
     @BeforeClass(groups={"setup"})
@@ -98,18 +107,17 @@ public abstract class TestUserApiBase {
         loadSystemPropertiesFromClasspath("/entitlement.properties");
         final Injector g = getInjector();
 
-        injectorMagic = g.getInstance(InjectorMagic.class);
-
-
         entitlementService = g.getInstance(IEntitlementService.class);
         catalogService = g.getInstance(ICatalogService.class);
+        busService = g.getInstance(IEventBusService.class);
         config = g.getInstance(IEntitlementConfig.class);
         dao = g.getInstance(IEntitlementDao.class);
         clock = (ClockMock) g.getInstance(IClock.class);
         try {
 
             ((CatalogService) catalogService).loadCatalog();
-            ((Engine)entitlementService).initialize();
+            ((EventBusService) busService).startBus();
+            ((Engine) entitlementService).initialize();
             init();
         } catch (EntitlementUserApiException e) {
             Assert.fail(e.getMessage());
@@ -127,10 +135,9 @@ public abstract class TestUserApiBase {
         catalog = catalogService.getCatalog();
         assertNotNull(catalog);
 
-        testListener = new ApiTestListener();
-        List<IApiListener> listeners =  new ArrayList<IApiListener>();
-        listeners.add(testListener);
-        entitlementApi = entitlementService.getUserApi(listeners);
+
+        testListener = new ApiTestListener(busService.getEventBus());
+        entitlementApi = entitlementService.getUserApi();
         billingApi = entitlementService.getBillingApi();
 
     }
@@ -142,11 +149,13 @@ public abstract class TestUserApiBase {
         log.warn("RESET TEST FRAMEWORK\n\n");
 
         testListener.reset();
+
         clock.resetDeltaFromReality();
         ((IEntitlementDaoMock) dao).reset();
         try {
+            busService.getEventBus().register(testListener);
             bundle = entitlementApi.createBundleForAccount(account, "myDefaultBundle");
-        } catch (EntitlementUserApiException e) {
+        } catch (Exception e) {
             Assert.fail(e.getMessage());
         }
         assertNotNull(bundle);
@@ -207,8 +216,8 @@ public abstract class TestUserApiBase {
                     assertEquals(foundPhase, false);
                     foundPhase = true;
                     assertEquals(cur.getEffectiveDate(), expPhaseChange);
-                } else if (cur instanceof IUserEvent) {
-                    IUserEvent uEvent = (IUserEvent) cur;
+                } else if (cur instanceof IApiEvent) {
+                    IApiEvent uEvent = (IApiEvent) cur;
                     assertEquals(ApiEventType.CHANGE, uEvent.getEventType());
                     assertEquals(foundChange, false);
                     foundChange = true;
@@ -232,7 +241,7 @@ public abstract class TestUserApiBase {
                 return TimeUnit.DAYS;
             }
             @Override
-            public int getLength() {
+            public int getNumber() {
                 return days;
             }
         };
@@ -246,7 +255,7 @@ public abstract class TestUserApiBase {
                 return TimeUnit.MONTHS;
             }
             @Override
-            public int getLength() {
+            public int getNumber() {
                 return months;
             }
         };
@@ -261,7 +270,7 @@ public abstract class TestUserApiBase {
                 return TimeUnit.YEARS;
             }
             @Override
-            public int getLength() {
+            public int getNumber() {
                 return years;
             }
         };
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
index 373db87..32f1dc9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlan.java
@@ -36,7 +36,7 @@ import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.ProductCategory;
 import com.ning.billing.entitlement.api.ApiTestListener.NextEvent;
 import com.ning.billing.entitlement.events.IEvent;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.util.clock.Clock;
 
 public abstract class TestUserApiChangePlan extends TestUserApiBase {
@@ -145,7 +145,7 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
 
             // ALSO VERIFY PENDING CHANGE EVENT
             List<IEvent> events = dao.getPendingEventsForSubscription(subscription.getId());
-            assertTrue(events.get(0) instanceof IUserEvent);
+            assertTrue(events.get(0) instanceof IApiEvent);
 
 
             // MOVE TO EOT
@@ -341,7 +341,6 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
             IPlanPhase trialPhase = subscription.getCurrentPhase();
             assertEquals(trialPhase.getPhaseType(), PhaseType.TRIAL);
 
-            // MOVE TO NEXT PHASE
             testListener.pushExpectedEvent(NextEvent.PHASE);
             clock.setDeltaFromReality(trialPhase.getDuration(), DAY_IN_MS);
             assertTrue(testListener.isCompleted(2000));
@@ -378,9 +377,25 @@ public abstract class TestUserApiChangePlan extends TestUserApiBase {
             assertNotNull(currentPhase);
             assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
 
+            // ACTIVATE CHNAGE BY MOVING AFTER CTD
+            testListener.pushExpectedEvent(NextEvent.CHANGE);
+            clock.addDeltaFromReality(ctd);
+            assertTrue(testListener.isCompleted(3000));
+
+            currentPlan = subscription.getCurrentPlan();
+            assertNotNull(currentPlan);
+            assertEquals(currentPlan.getProduct().getName(), "Pistol");
+            assertEquals(currentPlan.getProduct().getCategory(), ProductCategory.BASE);
+            assertEquals(currentPlan.getBillingPeriod(), BillingPeriod.ANNUAL);
+
+            currentPhase = subscription.getCurrentPhase();
+            assertNotNull(currentPhase);
+            assertEquals(currentPhase.getPhaseType(), PhaseType.DISCOUNT);
+
+
+
             // MOVE TO NEXT PHASE
             testListener.pushExpectedEvent(NextEvent.PHASE);
-            testListener.pushExpectedEvent(NextEvent.CHANGE);
             clock.addDeltaFromReality(currentPhase.getDuration());
             assertTrue(testListener.isCompleted(3000));
             subscription = (Subscription) entitlementApi.getSubscriptionFromId(subscription.getId());
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
index 4455436..8847612 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanMemory.java
@@ -57,7 +57,7 @@ public class TestUserApiChangePlanMemory extends TestUserApiChangePlan {
         invokeRealMethod(this);
     }
 
-    // STEPH set to false until we implement rescue example.
+    // Set to false until we implement rescue example.
     @Test(enabled=false, groups={"fast"})
     public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
         invokeRealMethod(this);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
index 174b85e..60d777d 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiChangePlanSql.java
@@ -79,7 +79,7 @@ public class TestUserApiChangePlanSql extends TestUserApiChangePlan {
         invokeRealMethod(this);
     }
 
-    // STEPH rescue not implemented
+    // rescue not implemented yet
     @Test(enabled=false, groups={"sql"})
     public void testChangePlanChangePlanAlignEOTWithChargeThroughDate() {
         invokeRealMethod(this);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
index cc574ee..1e043eb 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/EntitlementDaoMemoryMock.java
@@ -40,7 +40,7 @@ import com.ning.billing.entitlement.events.IEvent;
 import com.ning.billing.entitlement.events.IEvent.EventType;
 import com.ning.billing.entitlement.events.IEventLyfecycle.IEventLyfecycleState;
 import com.ning.billing.entitlement.events.user.ApiEventType;
-import com.ning.billing.entitlement.events.user.IUserEvent;
+import com.ning.billing.entitlement.events.user.IApiEvent;
 import com.ning.billing.util.clock.IClock;
 
 public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDaoMock {
@@ -294,7 +294,7 @@ public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDa
                     continue;
                 }
                 if (cur.getType() == EventType.API_USER &&
-                        ApiEventType.CHANGE == ((IUserEvent) cur).getEventType() &&
+                        ApiEventType.CHANGE == ((IApiEvent) cur).getEventType() &&
                         cur.getProcessingState() == IEventLyfecycleState.AVAILABLE) {
                     cur.deactivate();
                     break;
@@ -315,7 +315,7 @@ public class EntitlementDaoMemoryMock implements IEntitlementDao, IEntitlementDa
                     continue;
                 }
                 if (cur.getType() == EventType.API_USER &&
-                        ((IUserEvent) cur).getEventType() == ApiEventType.CANCEL) {
+                        ((IApiEvent) cur).getEventType() == ApiEventType.CANCEL) {
                     cur.deactivate();
                     foundCancel = true;
                     break;
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/EngineModuleMock.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/EngineModuleMock.java
index d70a2f7..de812ee 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/EngineModuleMock.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/EngineModuleMock.java
@@ -18,6 +18,7 @@ package com.ning.billing.entitlement.glue;
 
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.clock.IClock;
+import com.ning.billing.util.glue.EventBusModule;
 
 public class EngineModuleMock extends EntitlementModule {
 
@@ -29,6 +30,7 @@ public class EngineModuleMock extends EntitlementModule {
     @Override
     protected void configure() {
         super.configure();
+        install(new EventBusModule());
     }
 
 }
diff --git a/entitlement/src/test/resources/testInput.xml b/entitlement/src/test/resources/testInput.xml
index cfd445b..740a903 100644
--- a/entitlement/src/test/resources/testInput.xml
+++ b/entitlement/src/test/resources/testInput.xml
@@ -86,76 +86,100 @@ Use Cases to do:
 	</products>
 	 
 	<rules>
-		<tiers>
-			<tier>
-				<product>Pistol</product>
-				<product>Shotgun</product>
-				<product>Assault-Rifle</product>
-			</tier>
-		</tiers>
-		<changePolicyRule>
-			<qualifier>DEFAULT</qualifier>
-			<policy>END_OF_TERM</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>TERM_FROM_SHORT_TO_LONG</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyRule>
-			<qualifier>PRODUCT_FROM_LOW_TO_HIGH</qualifier>
-			<policy>IMMEDIATE</policy>
-		</changePolicyRule>
-		<changePolicyCase> 
-			<fromBillingPeriod>MONTHLY</fromBillingPeriod>
-			<toProduct>Assault-Rifle</toProduct>
-			<toBillingPeriod>MONTHLY</toBillingPeriod>
-			<policy>END_OF_TERM</policy>
-		</changePolicyCase>
-		<changePolicyCase> 
-			<toPriceList>rescue</toPriceList>
-			<policy>END_OF_TERM</policy>
-		</changePolicyCase>		
-		<changePolicyCase> 
-			<phaseType>TRIAL</phaseType>
-			<policy>IMMEDIATE</policy>
-		</changePolicyCase>
-		<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>
-		<cancelPolicyCase>
-			<policy>END_OF_TERM</policy>
-		</cancelPolicyCase>
-		<cancelPolicyCase>
-			<phaseType>TRIAL</phaseType>
-			<policy>IMMEDIATE</policy>
-		</cancelPolicyCase>
-		<createAlignmentCase>
-			<alignment>START_OF_BUNDLE</alignment>
-		</createAlignmentCase>
+		<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>
 			<alignment>ACCOUNT</alignment>
 		</billingAlignmentCase>
-		<billingAlignmentCase>
-			<billingPeriod>ANNUAL</billingPeriod>
-			<alignment>SUBSCRIPTION</alignment>
-		</billingAlignmentCase>
-		<billingAlignmentCase>
-			<productCategory>ADD_ON</productCategory>
-			<alignment>BUNDLE</alignment>
-		</billingAlignmentCase>
-		<priceListCase>
-			<fromPriceList>rescue</fromPriceList>
-			<toPriceList>DEFAULT</toPriceList>
-		</priceListCase>
+			<billingAlignmentCase>
+				<billingPeriod>ANNUAL</billingPeriod>
+				<alignment>SUBSCRIPTION</alignment>
+			</billingAlignmentCase>
+			<billingAlignmentCase>
+				<productCategory>ADD_ON</productCategory>
+				<alignment>BUNDLE</alignment>
+			</billingAlignmentCase>
+		</billingAlignment>
+		<priceList>
+			<priceListCase>
+				<fromPriceList>rescue</fromPriceList>
+				<toPriceList>DEFAULT</toPriceList>
+			</priceListCase>
+		</priceList>
 	</rules>
 
 	<plans>
@@ -167,6 +191,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice> <!-- empty price implies $0 -->
 					</fixedPrice>
 				</phase>
@@ -191,6 +216,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice></fixedPrice>
 				    <!-- no price implies $0 -->
 				</phase>
@@ -216,6 +242,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -240,6 +267,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -264,6 +292,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -288,6 +317,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -312,6 +342,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -348,6 +379,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -384,6 +416,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -463,6 +496,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>
@@ -487,6 +521,7 @@ Use Cases to do:
 						<unit>DAYS</unit>
 						<number>30</number>
 					</duration>
+					<billingPeriod>NO_BILLING_PERIOD</billingPeriod>
 					<fixedPrice>
 					</fixedPrice>
 				</phase>

invoice/pom.xml 2(+1 -1)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 919e422..62b0c51 100644
--- a/invoice/pom.xml
+++ b/invoice/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.13-SNAPSHOT</version>
+        <version>0.0.14-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/InternationalPriceMock.java b/invoice/src/test/java/com/ning/billing/invoice/tests/InternationalPriceMock.java
index c2df6ef..2217b6c 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/InternationalPriceMock.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/InternationalPriceMock.java
@@ -21,6 +21,7 @@ import com.ning.billing.catalog.api.IInternationalPrice;
 import com.ning.billing.catalog.api.IPrice;
 
 import java.math.BigDecimal;
+import java.util.Date;
 
 import static org.testng.Assert.fail;
 
@@ -42,4 +43,9 @@ public class InternationalPriceMock implements IInternationalPrice {
     public BigDecimal getPrice(Currency currency) {
         return rate;
     }
+
+	@Override
+	public Date getEffectiveDateForExistingSubscriptons() {
+		return new Date();
+	}
 }

payment/pom.xml 2(+1 -1)

diff --git a/payment/pom.xml b/payment/pom.xml
index b902c81..e69d221 100644
--- a/payment/pom.xml
+++ b/payment/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.13-SNAPSHOT</version>
+        <version>0.0.14-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>

pom.xml 3(+2 -1)

diff --git a/pom.xml b/pom.xml
index bf8e215..8038652 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
     <groupId>com.ning.billing</groupId>
     <artifactId>killbill</artifactId>
     <packaging>pom</packaging>
-    <version>0.0.13-SNAPSHOT</version>
+    <version>0.0.14-SNAPSHOT</version>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
     <url>http://github.com/ning/killbill</url>
@@ -373,6 +373,7 @@
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>2.6</version>
                 <configuration>
+                    <useManifestOnlyJar>false</useManifestOnlyJar>
                     <systemPropertyVariables>
                         <log4j.configuration>file:${project.basedir}/src/test/resources/log4j.xml</log4j.configuration>
                     </systemPropertyVariables>

util/pom.xml 11(+5 -6)

diff --git a/util/pom.xml b/util/pom.xml
index e451499..3f3a40e 100644
--- a/util/pom.xml
+++ b/util/pom.xml
@@ -13,7 +13,7 @@
     <parent>
         <groupId>com.ning.billing</groupId>
         <artifactId>killbill</artifactId>
-        <version>0.0.13-SNAPSHOT</version>
+        <version>0.0.14-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
@@ -29,6 +29,10 @@
             <artifactId>bonecp</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.jdbi</groupId>
+            <artifactId>jdbi</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.inject</groupId>
             <artifactId>guice</artifactId>
             <version>3.0</version>
@@ -47,11 +51,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.jdbi</groupId>
-            <artifactId>jdbi</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <scope>test</scope>
diff --git a/util/src/main/java/com/ning/billing/util/clock/Clock.java b/util/src/main/java/com/ning/billing/util/clock/Clock.java
index 6b5b2f1..43819ba 100644
--- a/util/src/main/java/com/ning/billing/util/clock/Clock.java
+++ b/util/src/main/java/com/ning/billing/util/clock/Clock.java
@@ -44,15 +44,15 @@ public class Clock implements IClock {
         for (IDuration cur : durations) {
             switch (cur.getUnit()) {
             case DAYS:
-                result = result.plusDays(cur.getLength());
+                result = result.plusDays(cur.getNumber());
                 break;
 
             case MONTHS:
-                result = result.plusMonths(cur.getLength());
+                result = result.plusMonths(cur.getNumber());
                 break;
 
             case YEARS:
-                result = result.plusYears(cur.getLength());
+                result = result.plusYears(cur.getNumber());
                 break;
             case UNLIMITED:
             default:
diff --git a/util/src/main/java/com/ning/billing/util/config/ValidationError.java b/util/src/main/java/com/ning/billing/util/config/ValidationError.java
index f2e2346..4a68dac 100644
--- a/util/src/main/java/com/ning/billing/util/config/ValidationError.java
+++ b/util/src/main/java/com/ning/billing/util/config/ValidationError.java
@@ -17,6 +17,8 @@ package com.ning.billing.util.config;
 
 import java.net.URI;
 
+import org.slf4j.Logger;
+
 
 public class ValidationError {
 	private final String description;
@@ -44,5 +46,7 @@ public class ValidationError {
 		return objectName;
 	}
 	
-	
+	public void log(Logger log) {
+		log.error(String.format("%s [%s] (%s:%s)", description, sourceURI, objectType, objectName));
+	}
 }
diff --git a/util/src/main/java/com/ning/billing/util/config/ValidationErrors.java b/util/src/main/java/com/ning/billing/util/config/ValidationErrors.java
index 72cf752..901f636 100644
--- a/util/src/main/java/com/ning/billing/util/config/ValidationErrors.java
+++ b/util/src/main/java/com/ning/billing/util/config/ValidationErrors.java
@@ -19,6 +19,8 @@ package com.ning.billing.util.config;
 import java.net.URI;
 import java.util.ArrayList;
 
+import org.slf4j.Logger;
+
 public class ValidationErrors extends ArrayList<ValidationError>{
 	private static final long serialVersionUID = 1L;
 
@@ -28,4 +30,10 @@ public class ValidationErrors extends ArrayList<ValidationError>{
 		
 	}
 
+	public void log(Logger log) {
+		for(ValidationError error : this) {
+			error.log(log);
+		}	
+	}
+
 }
\ No newline at end of file
diff --git a/util/src/main/java/com/ning/billing/util/eventbus/EventBusService.java b/util/src/main/java/com/ning/billing/util/eventbus/EventBusService.java
new file mode 100644
index 0000000..53c07c9
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/eventbus/EventBusService.java
@@ -0,0 +1,54 @@
+/*
+ * 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.util.eventbus;
+
+import com.google.inject.Inject;
+import com.ning.billing.lifecycle.LyfecycleHandlerType;
+import com.ning.billing.lifecycle.LyfecycleHandlerType.LyfecycleLevel;
+
+public class EventBusService implements IEventBusService {
+
+    private final static String EVENT_BUS_SERVICE = "eventbus-service";
+
+    private final IEventBus eventBus;
+
+    @Inject
+    public EventBusService(IEventBus eventBus) {
+        this.eventBus = eventBus;
+    }
+
+    @Override
+    public String getName() {
+        return EVENT_BUS_SERVICE;
+    }
+
+    @LyfecycleHandlerType(LyfecycleLevel.INIT_BUS)
+    public void startBus() {
+        eventBus.start();
+    }
+
+    @LyfecycleHandlerType(LyfecycleLevel.STOP_BUS)
+    public void stopBus() {
+        eventBus.stop();
+    }
+
+    @Override
+    public IEventBus getEventBus() {
+        return eventBus;
+    }
+
+}
diff --git a/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java b/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java
new file mode 100644
index 0000000..7cc0453
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/eventbus/MemoryEventBus.java
@@ -0,0 +1,131 @@
+/*
+ * 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.util.eventbus;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.eventbus.AsyncEventBus;
+
+public class MemoryEventBus implements IEventBus {
+
+    // STEPH config ?
+    private final static int MAX_EVENT_THREADS = 1;
+
+    private final static String EVENT_BUS_IDENTIFIER = "eventbus-service";
+    private final static String EVENT_BUS_GROUP_NAME = "eventbus-grp";
+    private final static String EVENT_BUS_TH_NAME = "eventbus-th";
+
+    private static final Logger log = LoggerFactory.getLogger(MemoryEventBus.class);
+
+    private final EventBusDelegate delegate;
+    private final AtomicBoolean isInitialized;
+
+    public class EventBusDelegate extends AsyncEventBus {
+
+        private final Executor executor;
+        private final ThreadGroup grp;
+
+        public EventBusDelegate(String name, ThreadGroup grp, Executor executor) {
+            super(name, executor);
+            this.executor = executor;
+            this.grp = grp;
+        }
+
+        public void completeDispatch() {
+            dispatchQueuedEvents();
+        }
+
+        // No way to really 'stop' an executor; what we do is:
+        // i) disallow any new events into the queue
+        // ii) empty the queue
+        //
+        // That will only work if the event submitter handles EventBusException correctly when posting.
+        //
+        public void stop() {
+        }
+    }
+
+    public MemoryEventBus() {
+
+        final ThreadGroup group = new ThreadGroup(EVENT_BUS_GROUP_NAME);
+        Executor executor = Executors.newFixedThreadPool(MAX_EVENT_THREADS, new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                return new Thread(group, r, EVENT_BUS_TH_NAME);
+            }
+        });
+
+        this.delegate = new EventBusDelegate(EVENT_BUS_IDENTIFIER, group, executor);
+        this.isInitialized = new AtomicBoolean(false);
+    }
+
+    @Override
+    public void register(Object handlerInstnace) throws EventBusException {
+        checkInitialized("register");
+        delegate.register(handlerInstnace);
+    }
+
+    @Override
+    public void unregister(Object handlerInstance) throws EventBusException {
+        checkInitialized("unregister");
+        delegate.unregister(handlerInstance);
+    }
+
+    @Override
+    public void post(IEventBusType event) throws EventBusException {
+        checkInitialized("post");
+        delegate.post(event);
+    }
+
+    @Override
+    public void postFromTransaction(IEventBusType event, Transmogrifier dao) throws EventBusException {
+        checkInitialized("postFromTransaction");
+        delegate.post(event);
+    }
+
+    @Override
+    public void start() {
+        if (isInitialized.compareAndSet(false, true)) {
+            log.info("MemoryEventBus started...");
+
+        }
+    }
+
+
+    private void checkInitialized(String operation) throws EventBusException {
+        if (!isInitialized.get()) {
+            throw new EventBusException(String.format("Attempting operation %s on an non initialized eventbus",
+                    operation));
+        }
+    }
+    @Override
+    public void stop() {
+        if (isInitialized.compareAndSet(true, false)) {
+            log.info("MemoryEventBus stopping...");
+            delegate.completeDispatch();
+            delegate.stop();
+            log.info("MemoryEventBus stoped...");
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/glue/EventBusModule.java b/util/src/main/java/com/ning/billing/util/glue/EventBusModule.java
new file mode 100644
index 0000000..e6e995a
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/glue/EventBusModule.java
@@ -0,0 +1,34 @@
+/*
+ * 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.util.glue;
+
+import com.google.inject.AbstractModule;
+import com.ning.billing.util.eventbus.EventBusService;
+import com.ning.billing.util.eventbus.IEventBus;
+import com.ning.billing.util.eventbus.IEventBusService;
+import com.ning.billing.util.eventbus.MemoryEventBus;
+
+public class EventBusModule extends AbstractModule {
+
+    @Override
+    protected void configure() {
+        bind(IEventBusService.class).to(EventBusService.class);
+        bind(IEventBus.class).to(MemoryEventBus.class).asEagerSingleton();
+
+    }
+
+}
diff --git a/util/src/test/java/com/ning/billing/util/clock/ClockMock.java b/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
index 7e41d1c..eb7216e 100644
--- a/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
+++ b/util/src/test/java/com/ning/billing/util/clock/ClockMock.java
@@ -102,18 +102,18 @@ public class ClockMock extends Clock {
         DateTime result = input;
         for (IDuration cur : deltaFromRealityDuration) {
 
-            int length = cur.getLength();
+            int length = cur.getNumber();
             switch (cur.getUnit()) {
             case DAYS:
-                result = result.plusDays(cur.getLength());
+                result = result.plusDays(cur.getNumber());
                 break;
 
             case MONTHS:
-                result = result.plusMonths(cur.getLength());
+                result = result.plusMonths(cur.getNumber());
                 break;
 
             case YEARS:
-                result = result.plusYears(cur.getLength());
+                result = result.plusYears(cur.getNumber());
                 break;
 
             case UNLIMITED:
diff --git a/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java b/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java
new file mode 100644
index 0000000..e4e41df
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java
@@ -0,0 +1,110 @@
+/*
+ * 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.util.eventbus;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.eventbus.Subscribe;
+
+public class TestEventBus {
+
+    private static final Logger log = LoggerFactory.getLogger(TestEventBus.class);
+
+    private IEventBus eventBus;
+
+
+    @BeforeClass
+    public void setup() {
+        eventBus = new MemoryEventBus();
+        eventBus.start();
+    }
+
+    @AfterClass
+    public void tearDown() {
+        eventBus.stop();
+    }
+
+    public static final class MyEvent implements IEventBusType {
+        String name;
+        Long value;
+
+        public MyEvent(String name, Long value) {
+            this.name = name;
+            this.value = value;
+        }
+    }
+
+    public static class MyEventHandler {
+
+        private final int expectedEvents;
+
+        private int gotEvents;
+
+
+        public MyEventHandler(int exp) {
+            this.expectedEvents = exp;
+            this.gotEvents = 0;
+        }
+
+        public synchronized int getEvents() {
+            return gotEvents;
+        }
+
+        @Subscribe
+        public synchronized void processEvent(MyEvent event) {
+            gotEvents++;
+            log.info("Got event {} {}", event.name, event.value);
+        }
+
+        public synchronized boolean waitForCompletion(long timeoutMs) {
+
+            while (gotEvents < expectedEvents) {
+                try {
+                    wait(timeoutMs);
+                    break;
+                } catch (InterruptedException ignore) {
+                }
+            }
+            return (gotEvents == expectedEvents);
+        }
+    }
+
+    @Test()
+    public void test() {
+        try {
+
+            int nbEvents = 127;
+            MyEventHandler handler = new MyEventHandler(nbEvents);
+            eventBus.register(handler);
+
+            for (int i = 0; i < nbEvents; i++) {
+                eventBus.post(new MyEvent("my-event", (long) i));
+            }
+
+            boolean completed = handler.waitForCompletion(3000);
+            Assert.assertEquals(completed, true);
+        } catch (Exception e) {
+            Assert.fail("",e);
+        }
+
+    }
+}
diff --git a/util/src/test/resources/log4j.xml b/util/src/test/resources/log4j.xml
index 75abc76..ded1f80 100644
--- a/util/src/test/resources/log4j.xml
+++ b/util/src/test/resources/log4j.xml
@@ -25,7 +25,7 @@
     </appender>
 
 
-    <logger name="com.ning.billing.entitlement">
+    <logger name="com.ning.billing.util">
         <level value="info"/>
     </logger>