killbill-uncached

Merge branch 'inv-ent-integration' into addon_from_migration Not

1/16/2012 10:22:28 PM

Changes

account/pom.xml 13(+12 -1)

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

api/src/main/java/com/ning/billing/invoice/api/DefaultBillingEvent.java 136(+0 -136)

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

catalog/pom.xml 17(+16 -1)

entitlement/pom.xml 23(+22 -1)

entitlement/src/main/java/com/ning/billing/entitlement/engine/core/ApiEventProcessorBase.java 253(+0 -253)

entitlement/src/main/java/com/ning/billing/entitlement/engine/core/DefaultApiEventProcessor.java 63(+0 -63)

invoice/pom.xml 17(+16 -1)

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

pom.xml 16(+15 -1)

util/pom.xml 17(+15 -2)

Details

account/pom.xml 13(+12 -1)

diff --git a/account/pom.xml b/account/pom.xml
index 2107a34..44b4543 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.21-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-account</artifactId>
@@ -107,6 +107,17 @@
                     <groups>setup,fast</groups>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
index 1c7a397..b953707 100644
--- a/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
+++ b/account/src/main/java/com/ning/billing/account/api/DefaultAccount.java
@@ -133,7 +133,9 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
 
     @Override
     public void addTags(List<Tag> tags) {
-        this.tags.add(tags);
+        if (tags != null) {
+            this.tags.add(tags);
+        }
     }
 
     @Override
@@ -160,4 +162,4 @@ public class DefaultAccount extends CustomizableEntityBase implements Account {
     public BigDecimal getBalance() {
         return balance;
     }
-}
+}
\ No newline at end of file
diff --git a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
index a20a519..d6bf995 100644
--- a/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
+++ b/account/src/main/java/com/ning/billing/account/api/user/DefaultAccountUserApi.java
@@ -19,33 +19,47 @@ package com.ning.billing.account.api.user;
 import java.util.List;
 import java.util.UUID;
 import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.account.dao.AccountDao;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
 
 public class DefaultAccountUserApi implements com.ning.billing.account.api.AccountUserApi {
     private final AccountDao dao;
 
     @Inject
-    public DefaultAccountUserApi(AccountDao dao) {
+    public DefaultAccountUserApi(final AccountDao dao) {
         this.dao = dao;
     }
 
     @Override
-    public Account createAccount(AccountData data) {
-        Account account = new DefaultAccount(data);
-        dao.save(account);
-        return account;
+    public Account createAccount(final AccountData data, final List<CustomField> fields, List<Tag> tags) throws AccountApiException {
+        String key = data.getExternalKey();
+        Account existingAccount = dao.getAccountByKey(key);
+
+        if (existingAccount == null) {
+            Account account = new DefaultAccount(data);
+            account.addFields(fields);
+            account.addTags(tags);
+
+            dao.create(account);
+            return account;
+        } else {
+            throw new AccountApiException(ErrorCode.ACCOUNT_ALREADY_EXISTS, key);
+        }
     }
 
     @Override
-    public Account getAccountByKey(String key) {
+    public Account getAccountByKey(final String key) {
         return dao.getAccountByKey(key);
     }
 
     @Override
-    public Account getAccountById(UUID id) {
+    public Account getAccountById(final UUID id) {
         return dao.getById(id.toString());
     }
 
@@ -55,12 +69,12 @@ public class DefaultAccountUserApi implements com.ning.billing.account.api.Accou
     }
 
     @Override
-    public UUID getIdFromKey(String externalKey) {
+    public UUID getIdFromKey(final String externalKey) {
         return dao.getIdFromKey(externalKey);
     }
 
     @Override
-    public void saveAccount(Account account) {
-        dao.save(account);
+    public void updateAccount(final Account account) {
+        dao.update(account);
     }
 }
diff --git a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
index 20eb90b..21122e2 100644
--- a/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/AccountSqlDao.java
@@ -17,6 +17,7 @@
 package com.ning.billing.account.dao;
 
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.user.AccountBuilder;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.UuidMapper;
@@ -56,7 +57,11 @@ public interface AccountSqlDao extends EntityDao<Account>, Transactional<Account
 
     @Override
     @SqlUpdate
-    public void save(@AccountBinder Account account);
+    public void create(@AccountBinder Account account);
+
+    @Override
+    @SqlUpdate
+    public void update(@AccountBinder Account account);
 
     public static class AccountMapper implements ResultSetMapper<Account> {
         @Override
diff --git a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
index c108bb4..c4671ea 100644
--- a/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
+++ b/account/src/main/java/com/ning/billing/account/dao/DefaultAccountDao.java
@@ -50,27 +50,10 @@ public class DefaultAccountDao implements AccountDao {
             @Override
             public Account inTransaction(AccountSqlDao accountSqlDao, TransactionStatus status) throws Exception {
                 Account account = accountSqlDao.getAccountByKey(key);
-
                 if (account != null) {
-                    FieldStoreDao fieldStoreDao = accountSqlDao.become(FieldStoreDao.class);
-                    List<CustomField> fields = fieldStoreDao.load(account.getId().toString(), account.getObjectName());
-
-                    account.clearFields();
-                    if (fields != null) {
-                        for (CustomField field : fields) {
-                            account.setFieldValue(field.getName(), field.getValue());
-                        }
-                    }
-
-                    TagStoreDao tagStoreDao = fieldStoreDao.become(TagStoreDao.class);
-                    List<Tag> tags = tagStoreDao.load(account.getId().toString(), account.getObjectName());
-                    account.clearTags();
-
-                    if (tags != null) {
-                        account.addTags(tags);
-                    }
+                    setCustomFieldsFromWithinTransaction(account, accountSqlDao);
+                    setTagsFromWithinTransaction(account, accountSqlDao);
                 }
-
                 return account;
             }
         });
@@ -87,71 +70,97 @@ public class DefaultAccountDao implements AccountDao {
             @Override
             public Account inTransaction(AccountSqlDao accountSqlDao, TransactionStatus status) throws Exception {
                 Account account = accountSqlDao.getById(id);
-
                 if (account != null) {
-                    FieldStoreDao fieldStoreDao = accountSqlDao.become(FieldStoreDao.class);
-                    List<CustomField> fields = fieldStoreDao.load(account.getId().toString(), account.getObjectName());
-
-                    account.clearFields();
-                    if (fields != null) {
-                        for (CustomField field : fields) {
-                            account.setFieldValue(field.getName(), field.getValue());
-                        }
-                    }
-
-                    TagStoreDao tagStoreDao = fieldStoreDao.become(TagStoreDao.class);
-                    List<Tag> tags = tagStoreDao.load(account.getId().toString(), account.getObjectName());
-                    account.clearTags();
-
-                    if (tags != null) {
-                        account.addTags(tags);
-                    }
+                    setCustomFieldsFromWithinTransaction(account, accountSqlDao);
+                    setTagsFromWithinTransaction(account, accountSqlDao);
                 }
-
                 return account;
             }
         });
     }
 
+
     @Override
     public List<Account> get() {
         return accountDao.get();
     }
 
     @Override
-    public void test() {
-        accountDao.test();
+    public void create(final Account account) {
+        final String accountId = account.getId().toString();
+        final String objectType = DefaultAccount.OBJECT_TYPE;
+
+        accountDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
+            @Override
+            public Void inTransaction(AccountSqlDao accountSqlDao, TransactionStatus status) throws Exception {
+                accountSqlDao.create(account);
+
+                FieldStoreDao fieldStoreDao = accountSqlDao.become(FieldStoreDao.class);
+                fieldStoreDao.save(accountId, objectType, account.getFieldList());
+
+                TagStoreDao tagStoreDao = accountSqlDao.become(TagStoreDao.class);
+                tagStoreDao.save(accountId, objectType, account.getTagList());
+
+                AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account);
+                eventBus.post(creationEvent);
+                return null;
+            }
+        });
     }
 
     @Override
-    public void save(final Account account) {
+    public void update(final Account account) {
         final String accountId = account.getId().toString();
         final String objectType = DefaultAccount.OBJECT_TYPE;
 
         accountDao.inTransaction(new Transaction<Void, AccountSqlDao>() {
             @Override
-            public Void inTransaction(AccountSqlDao accountDao, TransactionStatus status) throws Exception {
-                Account currentAccount = accountDao.getById(accountId);
-                accountDao.save(account);
+            public Void inTransaction(AccountSqlDao accountSqlDao, TransactionStatus status) throws Exception {
+                Account currentAccount = accountSqlDao.getById(accountId);
 
-                FieldStoreDao fieldStoreDao = accountDao.become(FieldStoreDao.class);
+                accountSqlDao.update(account);
+
+                FieldStoreDao fieldStoreDao = accountSqlDao.become(FieldStoreDao.class);
+                fieldStoreDao.clear(accountId, objectType);
                 fieldStoreDao.save(accountId, objectType, account.getFieldList());
 
                 TagStoreDao tagStoreDao = fieldStoreDao.become(TagStoreDao.class);
+                tagStoreDao.clear(accountId, objectType);
                 tagStoreDao.save(accountId, objectType, account.getTagList());
 
-                if (currentAccount == null) {
-                    AccountCreationNotification creationEvent = new DefaultAccountCreationEvent(account);
-                    eventBus.post(creationEvent);
-                } else {
-                    AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
-                    if (changeEvent.hasChanges()) {
-                        eventBus.post(changeEvent);
-                    }
+                AccountChangeNotification changeEvent = new DefaultAccountChangeNotification(account.getId(), currentAccount, account);
+                if (changeEvent.hasChanges()) {
+                    eventBus.post(changeEvent);
                 }
-
                 return null;
             }
         });
     }
+
+    @Override
+    public void test() {
+        accountDao.test();
+    }
+
+    private void setCustomFieldsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao) {
+        FieldStoreDao fieldStoreDao = transactionalDao.become(FieldStoreDao.class);
+        List<CustomField> fields = fieldStoreDao.load(account.getId().toString(), account.getObjectName());
+
+        account.clearFields();
+        if (fields != null) {
+            for (CustomField field : fields) {
+                account.setFieldValue(field.getName(), field.getValue());
+            }
+        }
+    }
+
+    private void setTagsFromWithinTransaction(final Account account, final AccountSqlDao transactionalDao) {
+        TagStoreDao tagStoreDao = transactionalDao.become(TagStoreDao.class);
+        List<Tag> tags = tagStoreDao.load(account.getId().toString(), account.getObjectName());
+        account.clearTags();
+
+        if (tags != null) {
+            account.addTags(tags);
+        }
+    }
 }
diff --git a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
index 3e0ebdd..7c0e76e 100644
--- a/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
+++ b/account/src/main/resources/com/ning/billing/account/dao/AccountSqlDao.sql.stg
@@ -1,13 +1,17 @@
 group AccountDaoSql;
 
-save() ::= <<
+create() ::= <<
     INSERT INTO accounts
     (id, external_key, email, name, first_name_length, phone, currency, billing_cycle_day, payment_provider_name)
     VALUES
-    (:id, :externalKey, :email, :name, :firstNameLength, :phone, :currency, :billingCycleDay, :paymentProviderName)
-    ON DUPLICATE KEY UPDATE
-      external_key = :externalKey, email = :email, name = :name, first_name_length = :firstNameLength,
-      phone = :phone, currency = :currency, payment_provider_name = :paymentProviderName;
+    (:id, :externalKey, :email, :name, :firstNameLength, :phone, :currency, :billingCycleDay, :paymentProviderName);
+>>
+
+update() ::= <<
+    UPDATE accounts
+    SET email = :email, name = :name, first_name_length = :firstNameLength, phone = :phone,
+        currency = :currency, billing_cycle_day = :billingCycleDay, payment_provider_name = :paymentProviderName
+    WHERE id = :id;
 >>
 
 getAccountByKey() ::= <<
diff --git a/account/src/main/resources/com/ning/billing/account/ddl.sql b/account/src/main/resources/com/ning/billing/account/ddl.sql
index acca205..ccfed72 100644
--- a/account/src/main/resources/com/ning/billing/account/ddl.sql
+++ b/account/src/main/resources/com/ning/billing/account/ddl.sql
@@ -12,42 +12,4 @@ CREATE TABLE accounts (
     PRIMARY KEY(id)
 ) ENGINE=innodb;
 CREATE UNIQUE INDEX accounts_external_key ON accounts(external_key);
-CREATE UNIQUE INDEX accounts_email ON accounts(email);
-
-DROP TABLE IF EXISTS custom_fields;
-CREATE TABLE custom_fields (
-  id char(36) NOT NULL,
-  object_id char(36) NOT NULL,
-  object_type varchar(30) NOT NULL,
-  field_name varchar(30) NOT NULL,
-  field_value varchar(255) NOT NULL,
-  PRIMARY KEY(id)
-) ENGINE=innodb;
-CREATE INDEX custom_fields_object_id_object_type ON custom_fields(object_id, object_type);
-CREATE UNIQUE INDEX custom_fields_unique ON custom_fields(object_id, object_type, field_name);
-
-DROP TABLE IF EXISTS tag_descriptions;
-CREATE TABLE tag_descriptions (
-  id char(36) NOT NULL,
-  name varchar(20) NOT NULL,
-  created_by varchar(50) NOT NULL,
-  creation_date datetime NOT NULL,
-  description varchar(200) NOT NULL,
-  generate_invoice boolean DEFAULT false,
-  process_payment boolean DEFAULT false,
-  PRIMARY KEY(id)
-) ENGINE=innodb;
-CREATE UNIQUE INDEX tag_descriptions_name ON tag_descriptions(name);
-
-DROP TABLE IF EXISTS tags;
-CREATE TABLE tags (
-  id char(36) NOT NULL,
-  tag_description_id char(36) NOT NULL,
-  object_id char(36) NOT NULL,
-  object_type varchar(30) NOT NULL,
-  date_added datetime NOT NULL,
-  added_by varchar(50) NOT NULL,
-  PRIMARY KEY(id)
-) ENGINE = innodb;
-CREATE INDEX tags_by_object ON tags(object_id);
-CREATE UNIQUE INDEX tags_unique ON tags(tag_description_id, object_id);
\ No newline at end of file
+CREATE UNIQUE INDEX accounts_email ON accounts(email);
\ No newline at end of file
diff --git a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
index 77efe00..b18a41b 100644
--- a/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
+++ b/account/src/test/java/com/ning/billing/account/dao/AccountDaoTestBase.java
@@ -38,14 +38,17 @@ public abstract class AccountDaoTestBase {
 
     @BeforeClass(alwaysRun = true)
     protected void setup() throws IOException {
-        // Healthcheck test to make sure MySQL is setup properly
+        // Health check test to make sure MySQL is setup properly
         try {
             module = new AccountModuleMock();
             final String accountDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
             final String invoiceDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/invoice/ddl.sql"));
+            final String utilDdl = IOUtils.toString(AccountSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+
             module.startDb();
             module.initDb(accountDdl);
             module.initDb(invoiceDdl);
+            module.initDb(utilDdl);
 
             final Injector injector = Guice.createInjector(Stage.DEVELOPMENT, module);
             dbi = injector.getInstance(IDBI.class);
diff --git a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
index 7a82f98..a4b0b98 100644
--- a/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
+++ b/account/src/test/java/com/ning/billing/account/dao/TestSimpleAccountDao.java
@@ -21,6 +21,7 @@ import java.util.UUID;
 import org.joda.time.DateTime;
 import org.testng.annotations.Test;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.util.tag.DefaultTagDescription;
@@ -30,7 +31,6 @@ import com.ning.billing.account.api.user.AccountBuilder;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.util.clock.DefaultClock;
 import com.ning.billing.util.tag.dao.TagDescriptionDao;
-import com.ning.billing.util.tag.dao.TagDescriptionDao;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
@@ -57,7 +57,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     public void testBasic() {
 
         Account a = createTestAccount();
-        accountDao.save(a);
+        accountDao.create(a);
         String key = a.getExternalKey();
 
         Account r = accountDao.getAccountByKey(key);
@@ -74,14 +74,14 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     }
 
     @Test
-    public void testGetById() {
+    public void testGetById() throws AccountApiException {
         Account account = createTestAccount();
         UUID id = account.getId();
         String key = account.getExternalKey();
         String name = account.getName();
         int firstNameLength = account.getFirstNameLength();
 
-        accountDao.save(account);
+        accountDao.create(account);
 
         account = accountDao.getById(id.toString());
         assertNotNull(account);
@@ -99,7 +99,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         String fieldValue = "testField1_value";
         account.setFieldValue(fieldName, fieldValue);
 
-        accountDao.save(account);
+        accountDao.create(account);
 
         Account thisAccount = accountDao.getAccountByKey(account.getExternalKey());
         assertNotNull(thisAccount);
@@ -112,13 +112,13 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         Account account = createTestAccount();
         TagDescription description = new DefaultTagDescription("Test Tag", "For testing only", true, true, "Test System", new DateTime());
         TagDescriptionDao tagDescriptionDao = dbi.onDemand(TagDescriptionDao.class);
-        tagDescriptionDao.save(description);
+        tagDescriptionDao.create(description);
 
         String addedBy = "testTags()";
         DateTime dateAdded = new DefaultClock().getUTCNow();
         account.addTag(description, addedBy, dateAdded);
         assertEquals(account.getTagList().size(), 1);
-        accountDao.save(account);
+        accountDao.create(account);
 
         Account thisAccount = accountDao.getById(account.getId().toString());
         List<Tag> tagList = thisAccount.getTagList();
@@ -135,16 +135,16 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
     @Test
     public void testGetIdFromKey() {
         Account account = createTestAccount();
-        accountDao.save(account);
+        accountDao.create(account);
 
         UUID accountId = accountDao.getIdFromKey(account.getExternalKey());
         assertEquals(accountId, account.getId());
     }
 
     @Test
-    public void testUpdate() {
+    public void testUpdate() throws AccountApiException {
         final Account account = createTestAccount();
-        accountDao.save(account);
+        accountDao.create(account);
 
         AccountData accountData = new AccountData() {
             @Override
@@ -182,7 +182,7 @@ public class TestSimpleAccountDao extends AccountDaoTestBase {
         };
 
         Account updatedAccount = new DefaultAccount(account.getId(), accountData);
-        accountDao.save(updatedAccount);
+        accountDao.update(updatedAccount);
 
         Account savedAccount = accountDao.getAccountByKey(account.getExternalKey());
 
diff --git a/analytics/pom.xml b/analytics/pom.xml
index 03e7d72..278348b 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.21-SNAPSHOT</version>
+        <version>0.1.2-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 262e9d9..d623338 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/AnalyticsListener.java
@@ -18,6 +18,7 @@ package com.ning.billing.analytics;
 
 import com.google.common.eventbus.Subscribe;
 import com.google.inject.Inject;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountChangeNotification;
 import com.ning.billing.account.api.AccountCreationNotification;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition;
@@ -35,8 +36,7 @@ public class AnalyticsListener
     }
 
     @Subscribe
-    public void handleSubscriptionTransitionChange(final SubscriptionTransition event)
-    {
+    public void handleSubscriptionTransitionChange(final SubscriptionTransition event) throws AccountApiException {
         switch (event.getTransitionType()) {
             case MIGRATE_ENTITLEMENT:
                 // TODO do nothing for now
diff --git a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
index 33b8f4d..0384fab 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/BusinessSubscriptionTransitionRecorder.java
@@ -18,6 +18,7 @@ package com.ning.billing.analytics;
 
 import com.google.inject.Inject;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionDao;
 import com.ning.billing.catalog.api.Currency;
@@ -46,44 +47,37 @@ public class BusinessSubscriptionTransitionRecorder
         this.accountApi = accountApi;
     }
 
-    public void subscriptionCreated(final SubscriptionTransition created)
-    {
+    public void subscriptionCreated(final SubscriptionTransition created) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCreated(created.getNextPlan());
         recordTransition(event, created);
     }
 
-    public void subscriptionCancelled(final SubscriptionTransition cancelled)
-    {
+    public void subscriptionCancelled(final SubscriptionTransition cancelled) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionCancelled(cancelled.getNextPlan());
         recordTransition(event, cancelled);
     }
 
-    public void subscriptionChanged(final SubscriptionTransition changed)
-    {
+    public void subscriptionChanged(final SubscriptionTransition changed) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionChanged(changed.getNextPlan());
         recordTransition(event, changed);
     }
 
-    public void subscriptionPaused(final SubscriptionTransition paused)
-    {
+    public void subscriptionPaused(final SubscriptionTransition paused) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPaused(paused.getNextPlan());
         recordTransition(event, paused);
     }
 
-    public void subscriptionResumed(final SubscriptionTransition resumed)
-    {
+    public void subscriptionResumed(final SubscriptionTransition resumed) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionResumed(resumed.getNextPlan());
         recordTransition(event, resumed);
     }
 
-    public void subscriptionPhaseChanged(final SubscriptionTransition phaseChanged)
-    {
+    public void subscriptionPhaseChanged(final SubscriptionTransition phaseChanged) throws AccountApiException {
         final BusinessSubscriptionEvent event = BusinessSubscriptionEvent.subscriptionPhaseChanged(phaseChanged.getNextPlan(), phaseChanged.getNextState());
         recordTransition(event, phaseChanged);
     }
 
-    public void recordTransition(final BusinessSubscriptionEvent event, final SubscriptionTransition transition)
-    {
+    public void recordTransition(final BusinessSubscriptionEvent event, final SubscriptionTransition transition) throws AccountApiException {
         Currency currency = null;
         String transitionKey = null;
         String accountKey = null;
diff --git a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
index 3c3de7e..6ec95b9 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/AnalyticsTestModule.java
@@ -22,6 +22,7 @@ import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.dbi.MysqlTestingHelper;
 import com.ning.billing.entitlement.glue.EntitlementModule;
 import com.ning.billing.util.glue.EventBusModule;
+import com.ning.billing.util.glue.NotificationQueueModule;
 import com.ning.billing.util.glue.TagStoreModule;
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.IDBI;
@@ -39,6 +40,7 @@ public class AnalyticsTestModule extends AnalyticsModule
         install(new EventBusModule());
         install(new EntitlementModule());
         install(new TagStoreModule());
+        install(new NotificationQueueModule());
 
         // Install the Dao layer
         final MysqlTestingHelper helper = new MysqlTestingHelper();
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
index 03a27d0..c146de7 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/TestAnalyticsService.java
@@ -48,7 +48,9 @@ import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.util.eventbus.EventBus;
+import com.ning.billing.util.tag.DefaultTag;
 import com.ning.billing.util.tag.DefaultTagDescription;
+import com.ning.billing.util.tag.Tag;
 import com.ning.billing.util.tag.dao.TagDescriptionDao;
 import org.apache.commons.io.IOUtils;
 import org.joda.time.DateTime;
@@ -61,13 +63,17 @@ import org.testng.annotations.Test;
 
 import java.io.IOException;
 import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.UUID;
 
+import static org.testng.Assert.fail;
+
 @Guice(modules = AnalyticsTestModule.class)
 public class TestAnalyticsService
 {
-    private static final String KEY = "1234";
-    private static final String ACCOUNT_KEY = "pierre-1234";
+    private static final String KEY = "12345";
+    private static final String ACCOUNT_KEY = "pierre-12345";
     private static final DefaultTagDescription TAG_ONE = new DefaultTagDescription("batch20", "something", false, false, "pierre", new DateTime(DateTimeZone.UTC));
     private static final DefaultTagDescription TAG_TWO = new DefaultTagDescription("awesome", "something", false, false, "pierre", new DateTime(DateTimeZone.UTC));
 
@@ -106,18 +112,23 @@ public class TestAnalyticsService
         // Killbill generic setup
         setupBusAndMySQL();
 
-        tagDao.save(TAG_ONE);
-        tagDao.save(TAG_TWO);
+        tagDao.create(TAG_ONE);
+        tagDao.create(TAG_TWO);
 
         final MockAccount account = new MockAccount(UUID.randomUUID(), ACCOUNT_KEY, Currency.USD);
-        final Account storedAccount = accountApi.createAccount(account);
-        storedAccount.addTag(TAG_ONE, "pierre", new DateTime(DateTimeZone.UTC));
-        storedAccount.addTag(TAG_TWO, "pierre", new DateTime(DateTimeZone.UTC));
-        accountApi.saveAccount(storedAccount);
-
-        // Create events for the bus and expected results
-        createSubscriptionTransitionEvent(storedAccount);
-        createAccountCreationEvent(storedAccount);
+        try {
+            List<Tag> tags = new ArrayList<Tag>();
+            tags.add(new DefaultTag(TAG_ONE, "pierre", new DateTime(DateTimeZone.UTC)));
+            tags.add(new DefaultTag(TAG_TWO, "pierre", new DateTime(DateTimeZone.UTC)));
+
+            final Account storedAccount = accountApi.createAccount(account, null, tags);
+
+            // Create events for the bus and expected results
+            createSubscriptionTransitionEvent(storedAccount);
+            createAccountCreationEvent(storedAccount);
+        } catch (Throwable t) {
+            fail("Initializing accounts failed.", t);
+        }
     }
 
     private void setupBusAndMySQL() throws IOException
@@ -127,11 +138,13 @@ public class TestAnalyticsService
         final String analyticsDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/analytics/ddl.sql"));
         final String accountDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/account/ddl.sql"));
         final String entitlementDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/entitlement/ddl.sql"));
+        final String utilDdl = IOUtils.toString(BusinessSubscriptionTransitionDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
 
         helper.startMysql();
         helper.initDb(analyticsDdl);
         helper.initDb(accountDdl);
         helper.initDb(entitlementDdl);
+        helper.initDb(utilDdl);
     }
 
     private void createSubscriptionTransitionEvent(final Account account) throws EntitlementUserApiException
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
index faf4dc8..3b38c8c 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockAccount.java
@@ -109,6 +109,11 @@ public class MockAccount implements Account
     }
 
     @Override
+    public void addFields(List<CustomField> fields) {
+        throw new NotImplementedException();
+    }
+
+    @Override
     public void clearFields() {
         throw new NotImplementedException();
     }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
index 4c39456..791d191 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockIAccountUserApi.java
@@ -21,10 +21,13 @@ import sun.reflect.generics.reflectiveObjects.NotImplementedException;
 import java.util.List;
 import java.util.UUID;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
 import com.ning.billing.account.api.AccountData;
 import com.ning.billing.account.api.AccountUserApi;
 import com.ning.billing.account.api.DefaultAccount;
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
 
 public class MockIAccountUserApi implements AccountUserApi
 {
@@ -38,14 +41,14 @@ public class MockIAccountUserApi implements AccountUserApi
     }
 
     @Override
-    public Account createAccount(final AccountData data)
+    public Account createAccount(final AccountData data, final List<CustomField> fields, final List<Tag> tags)
     {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public void saveAccount(Account account) {
-        throw new NotImplementedException();
+    public void updateAccount(final Account account) {
+        throw new UnsupportedOperationException();
     }
 
     @Override
@@ -55,8 +58,7 @@ public class MockIAccountUserApi implements AccountUserApi
     }
 
     @Override
-    public Account getAccountById(final UUID uid)
-    {
+    public Account getAccountById(final UUID uid) {
         return new DefaultAccount(account);
     }
 
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 7eb9b44..3f11c0e 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockPhase.java
@@ -53,12 +53,7 @@ public class MockPhase implements PlanPhase
                 return BigDecimal.valueOf(price);
             }
 
-            @Override
-            public Date getEffectiveDateForExistingSubscriptons()
-            {
-                return new Date();
-            }
-        };
+         };
     }
 
     @Override
@@ -78,11 +73,6 @@ public class MockPhase implements PlanPhase
                 return BigDecimal.valueOf(price);
             }
 
-            @Override
-            public Date getEffectiveDateForExistingSubscriptons()
-            {
-                return new Date();
-            }
         };
     }
 
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockPlan.java b/analytics/src/test/java/com/ning/billing/analytics/MockPlan.java
index 0e0809c..72ce7ca 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockPlan.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockPlan.java
@@ -17,10 +17,12 @@
 package com.ning.billing.analytics;
 
 import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.Product;
 
+import java.util.Date;
 import java.util.Iterator;
 
 public class MockPlan implements Plan
@@ -51,6 +53,12 @@ public class MockPlan implements Plan
     {
         return name;
     }
+    
+    @Override
+    public Date getEffectiveDateForExistingSubscriptons()
+    {
+        return new Date();
+    }
 
     @Override
     public Iterator<PlanPhase> getInitialPhaseIterator()
@@ -80,4 +88,14 @@ public class MockPlan implements Plan
 	public PlanPhase[] getAllPhases() {
 		 throw new UnsupportedOperationException();
 	}
+
+	@Override
+	public PlanPhase findPhase(String name) throws CatalogApiException {
+		 throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public boolean isRetired() {
+		return false;
+	}
 }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockProduct.java b/analytics/src/test/java/com/ning/billing/analytics/MockProduct.java
index 27ace04..36fba0d 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockProduct.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockProduct.java
@@ -61,4 +61,9 @@ public class MockProduct implements Product
     {
         return null;
     }
+
+	@Override
+	public boolean isRetired() {
+		return false;
+	}
 }
diff --git a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
index 5982bcd..6420325 100644
--- a/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
+++ b/analytics/src/test/java/com/ning/billing/analytics/MockSubscription.java
@@ -129,6 +129,11 @@ public class MockSubscription implements Subscription
     }
 
     @Override
+
+    public List<SubscriptionTransition> getAllTransitions() {
+        throw new UnsupportedOperationException();
+     }
+    
     public SubscriptionTransition getPendingTransition() {
         throw new UnsupportedOperationException();
     }

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

diff --git a/api/pom.xml b/api/pom.xml
index a998c67..dce65e7 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.21-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-api</artifactId>
diff --git a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
index b34d9a3..a535ada 100644
--- a/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
+++ b/api/src/main/java/com/ning/billing/account/api/AccountUserApi.java
@@ -18,12 +18,14 @@ package com.ning.billing.account.api;
 
 import java.util.List;
 import java.util.UUID;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
 
 public interface AccountUserApi {
 
-    public Account createAccount(AccountData data);
+    public Account createAccount(AccountData data, List<CustomField> fields, List<Tag> tags) throws AccountApiException;
 
-    public void saveAccount(Account account);
+    public void updateAccount(Account account);
 
     public Account getAccountByKey(String key);
 
diff --git a/api/src/main/java/com/ning/billing/catalog/api/Catalog.java b/api/src/main/java/com/ning/billing/catalog/api/Catalog.java
index 0fe2e25..3c04a74 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/Catalog.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/Catalog.java
@@ -16,45 +16,65 @@
 
 package com.ning.billing.catalog.api;
 
-import java.util.Date;
+import org.joda.time.DateTime;
 
 public interface Catalog {
+	//
+    // Simple getters
+    //
+    public abstract String getCatalogName();
 
-	public abstract Product[] getProducts();
-	
-	public abstract Plan findPlan(String productName, BillingPeriod term, String priceList) throws CatalogApiException;
+    public abstract Currency[] getSupportedCurrencies(DateTime requestedDate);
 
-	public abstract Plan findPlan(String name) throws CatalogApiException;
+	public abstract Product[] getProducts(DateTime requestedDate);
+	
+	public abstract Plan[] getPlans(DateTime requestedDate);
 
-    public abstract Product findProduct(String name) throws CatalogApiException;
+	
+	//
+	// Find a plan
+	//
 
-    public abstract PlanPhase findPhase(String name) throws CatalogApiException;
+	public abstract Plan findPlan(String name, DateTime requestedDate) throws CatalogApiException;
 
+	public abstract Plan findPlan(String productName, BillingPeriod term, String priceListName,
+									DateTime requestedDate) throws CatalogApiException;
 	
-	public abstract Currency[] getSupportedCurrencies();
-
-	public abstract Plan[] getPlans();
+	public abstract Plan findPlan(String name, DateTime effectiveDate, DateTime subscriptionStartDate) throws CatalogApiException;
 
+	public abstract Plan findPlan(String productName, BillingPeriod term, String priceListName,
+									DateTime requestedDate, DateTime subscriptionStartDate) throws CatalogApiException;
+	
+	//
+	// Find a product
+	//
+    public abstract Product findProduct(String name, DateTime requestedDate) throws CatalogApiException;
+
+    //
+    // Find a phase
+    //  
+    public abstract PlanPhase findPhase(String name, DateTime requestedDate, DateTime subscriptionStartDate) throws CatalogApiException;
+
+    //
+    // Rules
+    //
 	public abstract ActionPolicy planChangePolicy(PlanPhaseSpecifier from,
-			PlanSpecifier to) throws CatalogApiException;
+			PlanSpecifier to, DateTime requestedDate) throws CatalogApiException;
 
 	public abstract PlanChangeResult planChange(PlanPhaseSpecifier from,
-			PlanSpecifier to) throws IllegalPlanChange, CatalogApiException;
+			PlanSpecifier to, DateTime requestedDate) throws CatalogApiException;
 
-    public abstract Date getEffectiveDate();
+    public abstract ActionPolicy planCancelPolicy(PlanPhaseSpecifier planPhase, DateTime requestedDate) throws CatalogApiException;
 
-    public abstract ActionPolicy planCancelPolicy(PlanPhaseSpecifier planPhase) throws CatalogApiException;
+    public abstract PlanAlignmentCreate planCreateAlignment(PlanSpecifier specifier, DateTime requestedDate) throws CatalogApiException;
 
-    public abstract void configureEffectiveDate(Date date);
-
-    public abstract String getCatalogName();
-
-    public abstract PlanAlignmentCreate planCreateAlignment(PlanSpecifier specifier) throws CatalogApiException;
-
-    public abstract BillingAlignment billingAlignment(PlanPhaseSpecifier planPhase) throws CatalogApiException;
+    public abstract BillingAlignment billingAlignment(PlanPhaseSpecifier planPhase, DateTime requestedDate) throws CatalogApiException;
 
     public abstract PlanAlignmentChange planChangeAlignment(PlanPhaseSpecifier from,
-			PlanSpecifier to) throws CatalogApiException;
+			PlanSpecifier to, DateTime requestedDate) throws CatalogApiException;
 
+    public abstract boolean canCreatePlan(PlanSpecifier specifier, DateTime requestedDate) throws CatalogApiException;
+	
+	
 	
-}
\ No newline at end of file
+}
diff --git a/api/src/main/java/com/ning/billing/catalog/api/CatalogService.java b/api/src/main/java/com/ning/billing/catalog/api/CatalogService.java
index ab70569..c363a87 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/CatalogService.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/CatalogService.java
@@ -20,6 +20,7 @@ import com.ning.billing.lifecycle.KillbillService;
 
 public interface CatalogService extends KillbillService {
 
-	public abstract Catalog getCatalog();
+	public abstract Catalog getFullCatalog();
+	public abstract StaticCatalog getCurrentCatalog();
 
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/catalog/api/Currency.java b/api/src/main/java/com/ning/billing/catalog/api/Currency.java
index 0e7cd7d..3bef242 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/Currency.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/Currency.java
@@ -31,7 +31,7 @@ public enum Currency {
 //	CAD,
 //	JPY
 
-    public Currency getDefaultCurrency() {
+    public static Currency getDefaultCurrency() {
         return Currency.USD;
     }
 }
diff --git a/api/src/main/java/com/ning/billing/catalog/api/InternationalPrice.java b/api/src/main/java/com/ning/billing/catalog/api/InternationalPrice.java
index 44252fb..2876f1d 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/InternationalPrice.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/InternationalPrice.java
@@ -17,7 +17,6 @@
 package com.ning.billing.catalog.api;
 
 import java.math.BigDecimal;
-import java.util.Date;
 
 
 public interface InternationalPrice {
@@ -26,6 +25,4 @@ public interface InternationalPrice {
 
 	public abstract BigDecimal getPrice(Currency currency) throws CatalogApiException;
 
-	public abstract Date getEffectiveDateForExistingSubscriptons();
-
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/catalog/api/Plan.java b/api/src/main/java/com/ning/billing/catalog/api/Plan.java
index a1b6815..c0dbce5 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/Plan.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/Plan.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.catalog.api;
 
+import java.util.Date;
 import java.util.Iterator;
 
 public interface Plan {
@@ -26,6 +27,8 @@ public interface Plan {
 
 	public abstract String getName();
 
+	public abstract boolean isRetired();
+
 	public abstract Iterator<PlanPhase> getInitialPhaseIterator();
 
 	public abstract PlanPhase getFinalPhase();
@@ -35,4 +38,8 @@ public interface Plan {
 	public abstract int getPlansAllowedInBundle();
 
 	public abstract PlanPhase[] getAllPhases();
+
+	public abstract Date getEffectiveDateForExistingSubscriptons();
+
+	public abstract PlanPhase findPhase(String name) throws CatalogApiException;
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/catalog/api/PriceList.java b/api/src/main/java/com/ning/billing/catalog/api/PriceList.java
index 2d7192b..58a98fa 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/PriceList.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/PriceList.java
@@ -21,6 +21,8 @@ public interface PriceList {
 
 	public abstract String getName();
 
+	public abstract boolean isRetired();
+
 	public abstract Plan findPlan(Product product, BillingPeriod period);
 
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/catalog/api/Product.java b/api/src/main/java/com/ning/billing/catalog/api/Product.java
index 8483678..e226a0d 100644
--- a/api/src/main/java/com/ning/billing/catalog/api/Product.java
+++ b/api/src/main/java/com/ning/billing/catalog/api/Product.java
@@ -21,6 +21,8 @@ public interface Product {
 
 	public abstract String getName();
 
+	public abstract boolean isRetired();
+
 	public abstract Product[] getAvailable();
 
 	public abstract Product[] getIncluded();
@@ -29,4 +31,5 @@ public interface Product {
 
 	public abstract String getCatalogName();
 
+
 }
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/catalog/api/StaticCatalog.java b/api/src/main/java/com/ning/billing/catalog/api/StaticCatalog.java
new file mode 100644
index 0000000..0db7db5
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/catalog/api/StaticCatalog.java
@@ -0,0 +1,76 @@
+/*
+ * 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 java.util.Date;
+
+
+public interface StaticCatalog {
+    //
+    // Simple getters
+    //
+    public abstract String getCatalogName();
+    
+    public abstract Date getEffectiveDate();
+
+    public abstract Currency[] getCurrentSupportedCurrencies();
+
+	public abstract Product[] getCurrentProducts();
+	
+	public abstract Plan[] getCurrentPlans();
+	
+	//
+	// Find a plan
+	//
+	public abstract Plan findCurrentPlan(String productName, BillingPeriod term, String priceList) throws CatalogApiException;
+
+	public abstract Plan findCurrentPlan(String name) throws CatalogApiException;
+
+	//
+	// Find a product
+	//
+    public abstract Product findCurrentProduct(String name) throws CatalogApiException;
+
+    //
+    // Find a phase
+    //
+    public abstract PlanPhase findCurrentPhase(String name) throws CatalogApiException;
+    
+    //
+    // Rules
+    //
+	public abstract ActionPolicy planChangePolicy(PlanPhaseSpecifier from,
+			PlanSpecifier to) throws CatalogApiException;
+
+	public abstract PlanChangeResult planChange(PlanPhaseSpecifier from,
+			PlanSpecifier to) throws CatalogApiException;
+
+
+    public abstract ActionPolicy planCancelPolicy(PlanPhaseSpecifier planPhase) throws CatalogApiException;
+
+    public abstract PlanAlignmentCreate planCreateAlignment(PlanSpecifier specifier) throws CatalogApiException;
+
+    public abstract BillingAlignment billingAlignment(PlanPhaseSpecifier planPhase) throws CatalogApiException;
+
+    public abstract PlanAlignmentChange planChangeAlignment(PlanPhaseSpecifier from,
+			PlanSpecifier to) throws CatalogApiException;
+
+    public abstract boolean canCreatePlan(PlanSpecifier specifier) throws CatalogApiException;
+
+
+	
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
index 086bcd8..d3425bb 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/BillingEvent.java
@@ -16,13 +16,17 @@
 
 package com.ning.billing.entitlement.api.billing;
 
+import java.math.BigDecimal;
+
+import org.joda.time.DateTime;
+
 import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.catalog.api.InternationalPrice;
-import org.joda.time.DateTime;
-
-import java.math.BigDecimal;
-import java.util.UUID;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.user.Subscription;
 
 public interface BillingEvent extends Comparable<BillingEvent> {
 
@@ -36,9 +40,9 @@ public interface BillingEvent extends Comparable<BillingEvent> {
 
     /**
      *
-     * @return the id for the matching subscription
+     * @return the subscription
      */
-    public UUID getSubscriptionId();
+    public Subscription getSubscription();
 
     /**
      *
@@ -48,30 +52,16 @@ public interface BillingEvent extends Comparable<BillingEvent> {
 
     /**
      *
-     * @return the name of the plan phase
+     * @return the plan phase
      */
-    public String getPlanPhaseName();
+    public PlanPhase getPlanPhase();
 
 
     /**
      *
-     * @return the name of the plan
-     */
-    public String getPlanName();
-
-    /**
-     *
-     * @return the international price for the event
-     *
-     */
-    public InternationalPrice getPrice();
-
-    /**
-     *
-     * @param currency the target currency for invoicing
-     * @return the price of the plan phase in the specified currency
+     * @return the plan
      */
-    public BigDecimal getPrice(Currency currency);
+    public Plan getPlan();
 
     /**
      *
@@ -90,4 +80,32 @@ public interface BillingEvent extends Comparable<BillingEvent> {
      * @return the description of the billing event
      */
     public String getDescription();
+
+    /**
+     * 
+     * @return the fixed price for the phase
+     */
+    public InternationalPrice getFixedPrice();
+
+    /**
+     * 
+     * @return the recurring price for the phase
+     */
+    public InternationalPrice getRecurringPrice();
+
+    /**
+     * Syntactic sugar to wrap currency access call
+     * 
+     * @param currency
+     * @return price value
+     */
+    public BigDecimal getRecurringPrice(Currency currency) throws CatalogApiException ;
+
+    /**
+     * Syntactic sugar to wrap currency access call
+     * 
+     * @param currency
+     * @return price value
+     */
+    public BigDecimal getFixedPrice(Currency currency) throws CatalogApiException ;
 }
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/DefaultBillingEvent.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/DefaultBillingEvent.java
new file mode 100644
index 0000000..5f64dc7
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/DefaultBillingEvent.java
@@ -0,0 +1,146 @@
+/*
+ * 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.entitlement.api.billing;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.InternationalPrice;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+
+public class DefaultBillingEvent implements BillingEvent {
+    final private int billCycleDay;
+    final private Subscription subscription;
+    final private DateTime effectiveDate;
+    final private PlanPhase planPhase;
+    final private Plan plan;
+    final private InternationalPrice fixedPrice;
+    final private InternationalPrice recurringPrice;
+    final private String description;
+    final private BillingModeType billingModeType;
+    final private BillingPeriod billingPeriod;
+    
+    public DefaultBillingEvent(SubscriptionTransition transition, Subscription subscription, int billCycleDay) {
+        this.billCycleDay = billCycleDay;
+        this.subscription = subscription;
+        effectiveDate = transition.getEffectiveTransitionTime();
+        planPhase = transition.getNextPhase();
+        plan = transition.getNextPlan();
+        fixedPrice = transition.getNextPhase().getFixedPrice();
+        recurringPrice = transition.getNextPhase().getRecurringPrice();
+        description = transition.getTransitionType().toString();
+        billingModeType=BillingModeType.IN_ADVANCE;
+        billingPeriod = transition.getNextPhase().getBillingPeriod();
+        
+    }
+
+    // Intended for test only
+    public DefaultBillingEvent(Subscription subscription, DateTime effectiveDate, Plan plan, PlanPhase planPhase, InternationalPrice fixedPrice,
+            InternationalPrice recurringPrice, BillingPeriod billingPeriod, int billCycleDay, BillingModeType billingModeType, String description) {
+        this.subscription = subscription;
+        this.effectiveDate = effectiveDate;
+        this.plan = plan;
+        this.planPhase = planPhase;
+        this.fixedPrice = fixedPrice;
+        this.recurringPrice = recurringPrice;
+        this.billingPeriod = billingPeriod;
+        this.billCycleDay = billCycleDay;
+        this.billingModeType = billingModeType;
+        this.description = description;
+    }
+
+    @Override
+    public int compareTo(BillingEvent e1) {
+        if (getSubscription().getId().equals(e1.getSubscription().getId())) {
+            if (getEffectiveDate().equals(e1.getEffectiveDate())) { //ordering with a HashSet fails if we get equality
+                return hashCode() - e1.hashCode();
+            } else {
+                return getEffectiveDate().compareTo(e1.getEffectiveDate());
+            }
+        } else {
+            return getSubscription().getId().compareTo(e1.getSubscription().getId());
+        }
+    }
+
+    @Override
+    public int getBillCycleDay() {
+         return billCycleDay;
+    }
+
+    @Override
+    public Subscription getSubscription() {
+        return subscription;
+    }
+
+    @Override
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+
+    @Override
+    public PlanPhase getPlanPhase() {
+        return planPhase;
+    }
+
+    @Override
+    public Plan getPlan() {
+        return plan;
+    }
+
+    @Override
+    public BillingPeriod getBillingPeriod() {
+        return billingPeriod;
+    }
+
+    @Override
+    public BillingModeType getBillingMode() {
+        return billingModeType;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public InternationalPrice getFixedPrice() {
+        return fixedPrice;
+    }
+
+    @Override
+    public InternationalPrice getRecurringPrice() {
+        return recurringPrice;
+    }
+    @Override
+    public BigDecimal getFixedPrice(Currency currency) throws CatalogApiException {
+        return fixedPrice.getPrice(currency);
+    }
+
+    @Override
+    public BigDecimal getRecurringPrice(Currency currency) throws CatalogApiException {
+        return recurringPrice.getPrice(currency);
+    }
+    
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
index 874a7bc..2b07182 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/billing/EntitlementBillingApi.java
@@ -16,29 +16,24 @@
 
 package com.ning.billing.entitlement.api.billing;
 
-import com.ning.billing.account.api.Account;
-import org.joda.time.DateTime;
-
 import java.util.List;
 import java.util.SortedSet;
 import java.util.UUID;
 
+import org.joda.time.DateTime;
+
+import com.ning.billing.account.api.Account;
+
 public interface EntitlementBillingApi {
 
-    /**
-     *
-     * @return the list of accounts which have active subscriptions
-     */
-    public List<Account> getActiveAccounts();
 
     /**
      *
-     * @param subscriptionId the subscriptionId of interest for a gievn account
-     * @return an ordered list of billing event
+     * @param accountId 
+     * @return an ordered list of billing event for the given accounts
      *
-     * Note: The user api allows to get list of subscription bundle / subscriptions for an account
      */
-    public SortedSet<BillingEvent> getBillingEventsForSubscription(UUID subscriptionId);
+    public SortedSet<BillingEvent> getBillingEventsForAccount(UUID accountId);
 
 
     public void setChargedThroughDate(UUID subscriptionId, DateTime ctd);
diff --git a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
index 4b56301..d19de5f 100644
--- a/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
+++ b/api/src/main/java/com/ning/billing/entitlement/api/user/Subscription.java
@@ -67,6 +67,8 @@ public interface Subscription {
 
     public List<SubscriptionTransition> getActiveTransitions();
 
+    public List<SubscriptionTransition> getAllTransitions();
+
     public SubscriptionTransition getPendingTransition();
 
 }
diff --git a/api/src/main/java/com/ning/billing/ErrorCode.java b/api/src/main/java/com/ning/billing/ErrorCode.java
index f2c6add..0c60808 100644
--- a/api/src/main/java/com/ning/billing/ErrorCode.java
+++ b/api/src/main/java/com/ning/billing/ErrorCode.java
@@ -71,7 +71,8 @@ public enum ErrorCode {
 
     /* Price value explicitly set to NULL meaning there is no price available in that currency */
     CAT_PRICE_VALUE_NULL_FOR_CURRENCY(2011, "The value for the currency '%s' is NULL. This plan cannot be bought in this currnency."),
-
+    CAT_NULL_PRICE_LIST_NAME(2012,"Price list name was null"),
+    CAT_PRICE_LIST_NOT_FOUND(2013, "Could not find a pricelist with name '%s'"),
     /*
      * Plans
      */
@@ -81,14 +82,33 @@ public enum ErrorCode {
     /*
      * Products
      */
-    CAT_NO_SUCH_PRODUCT(2030,"Could not find any plans named '%s'"),
-
+    CAT_NO_SUCH_PRODUCT(2030,"Could not find any product named '%s'"),
+    CAT_NULL_PRODUCT_NAME(2031,"Product name was null"),
     /*
      * Phases
      */
-    CAT_NO_SUCH_PHASE(2040,"Could not find any phases named '%s'")
-    ;
+    CAT_NO_SUCH_PHASE(2040,"Could not find any phases named '%s'"),
+    CAT_BAD_PHASE_NAME(2041,"Bad phase name '%s'"),
+    /*
+     * Versioned Catalog
+     */
+    CAT_NO_CATALOG_FOR_GIVEN_DATE(2050, "There is no catalog version that applies for the given date '%s'"),
+    CAT_NO_CATALOG_ENTRIES_FOR_GIVEN_DATE(2051, "The are no catalog entries that apply for the given date '%s'"),
+    CAT_CATALOG_NAME_MISMATCH(2052, "The catalog name '%s' does not match the name of the catalog we are trying to add '%s'"),  
+    /*
+     * Billing Alignment
+     */
+    CAT_INVALID_BILLING_ALIGNMENT(2060, "Invalid billing alignment '%s'"),
+    
+   /*
+    *
+    * Range 3000 : ACCOUNT
+    *
+    */
+    ACCOUNT_ALREADY_EXISTS(3000, "Account already exists for key %s"),
+    ACCOUNT_INVALID_NAME(3001, "An invalid name was specified when creating or updating an account.")
 
+    ;
     private int code;
     private String format;
 
diff --git a/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java b/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
index 7b8f37a..e21fd83 100644
--- a/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
+++ b/api/src/main/java/com/ning/billing/util/customfield/CustomizableEntity.java
@@ -26,6 +26,8 @@ public interface CustomizableEntity extends Entity {
 
     public List<CustomField> getFieldList();
 
+    public void addFields(List<CustomField> fields);
+
     public void clearFields();
 
     public String getObjectName();

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

diff --git a/beatrix/pom.xml b/beatrix/pom.xml
index ec79685..32ceef9 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.21-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-beatrix</artifactId>

catalog/pom.xml 17(+16 -1)

diff --git a/catalog/pom.xml b/catalog/pom.xml
index 8d5c9c1..1b5b8d6 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.21-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-catalog</artifactId>
@@ -43,4 +43,19 @@
             <scope>provided</scope>
         </dependency>
     </dependencies>
+     <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java
index 813ed2b..7f88f07 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultCatalogService.java
@@ -20,6 +20,7 @@ import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.ning.billing.catalog.api.Catalog;
 import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.StaticCatalog;
 import com.ning.billing.catalog.io.VersionedCatalogLoader;
 import com.ning.billing.config.CatalogConfig;
 import com.ning.billing.lifecycle.KillbillService;
@@ -30,7 +31,7 @@ public class DefaultCatalogService implements KillbillService, Provider<Catalog>
 
     private static final String CATALOG_SERVICE_NAME = "catalog-service";
 
-    private static Catalog catalog;
+    private static VersionedCatalog catalog;
 
     private final CatalogConfig config;
     private boolean isInitialized;
@@ -74,7 +75,7 @@ public class DefaultCatalogService implements KillbillService, Provider<Catalog>
      * @see com.ning.billing.catalog.ICatlogService#getCatalog()
      */
     @Override
-    public Catalog getCatalog() {
+    public Catalog getFullCatalog() {
         return catalog;
     }
 
@@ -85,4 +86,9 @@ public class DefaultCatalogService implements KillbillService, Provider<Catalog>
         return catalog;
     }
 
+	@Override
+	public StaticCatalog getCurrentCatalog() {
+		return catalog;
+	}
+
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultInternationalPrice.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultInternationalPrice.java
index b53dcd3..6990d3c 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultInternationalPrice.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultInternationalPrice.java
@@ -16,26 +16,25 @@
 
 package com.ning.billing.catalog;
 
-import com.ning.billing.ErrorCode;
-import com.ning.billing.catalog.api.*;
-import com.ning.billing.util.config.ValidatingConfig;
-import com.ning.billing.util.config.ValidationError;
-import com.ning.billing.util.config.ValidationErrors;
+import java.math.BigDecimal;
+import java.net.URI;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
-import java.math.BigDecimal;
-import java.net.URI;
-import java.util.Date;
+
+import com.ning.billing.ErrorCode;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.CurrencyValueNull;
+import com.ning.billing.catalog.api.InternationalPrice;
+import com.ning.billing.catalog.api.Price;
+import com.ning.billing.util.config.ValidatingConfig;
+import com.ning.billing.util.config.ValidationErrors;
 
 @XmlAccessorType(XmlAccessType.NONE)
 public class DefaultInternationalPrice extends ValidatingConfig<StandaloneCatalog> implements InternationalPrice {
 
-	//TODO MDW Validation - effectiveDateForExistingSubscriptons > catalog effectiveDate 
-	@XmlElement(required=false)
-	private Date effectiveDateForExistingSubscriptons;
-
 	//TODO: Must have a price point for every configured currency
 	//TODO: No prices is a zero cost plan
 	@XmlElement(name="price")
@@ -50,13 +49,7 @@ public class DefaultInternationalPrice extends ValidatingConfig<StandaloneCatalo
 		return prices;
 	}
 
-	/* (non-Javadoc)
-	 * @see com.ning.billing.catalog.IInternationalPrice#getEffectiveDateForExistingSubscriptons()
-	 */
-	@Override
-	public Date getEffectiveDateForExistingSubscriptons() {
-		return effectiveDateForExistingSubscriptons;
-	}
+
 
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IInternationalPrice#getPrice(com.ning.billing.catalog.api.Currency)
@@ -71,12 +64,6 @@ public class DefaultInternationalPrice extends ValidatingConfig<StandaloneCatalo
 		throw new CatalogApiException(ErrorCode.CAT_NO_PRICE_FOR_CURRENCY, currency);
 	}
 
-
-	protected void setEffectiveDateForExistingSubscriptons(
-			Date effectiveDateForExistingSubscriptons) {
-		this.effectiveDateForExistingSubscriptons = effectiveDateForExistingSubscriptons;
-	}
-
 	protected DefaultInternationalPrice setPrices(DefaultPrice[] prices) {
 		this.prices = prices;
 		return this;
@@ -85,7 +72,7 @@ public class DefaultInternationalPrice extends ValidatingConfig<StandaloneCatalo
 
 	@Override
 	public ValidationErrors validate(StandaloneCatalog catalog, ValidationErrors errors)  {
-		Currency[] supportedCurrencies = catalog.getSupportedCurrencies();
+		Currency[] supportedCurrencies = catalog.getCurrentSupportedCurrencies();
 		for (Price p : prices) {
 			Currency currency = p.getCurrency();
 			if(!currencyIsSupported(currency, supportedCurrencies)) {
@@ -99,14 +86,6 @@ public class DefaultInternationalPrice extends ValidatingConfig<StandaloneCatalo
 				// No currency => nothing to check, ignore exception
 			}
 		}
-		if(effectiveDateForExistingSubscriptons != null &&
-				catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptons.getTime()) {
-			errors.add(new ValidationError(String.format("Price effective date %s is before catalog effective date '%s'",
-					effectiveDateForExistingSubscriptons,
-					catalog.getEffectiveDate().getTime()), 
-					catalog.getCatalogURI(), DefaultInternationalPrice.class, ""));
-		}
-		
 		return errors;
 	}
 	
@@ -129,7 +108,7 @@ public class DefaultInternationalPrice extends ValidatingConfig<StandaloneCatalo
 	}
 
 	private synchronized DefaultPrice[] getZeroPrice(StandaloneCatalog root) {
-		Currency[] currencies = root.getSupportedCurrencies();
+		Currency[] currencies = root.getCurrentSupportedCurrencies();
 		DefaultPrice[] zeroPrice = new DefaultPrice[currencies.length];
 		for(int i = 0; i < currencies.length; i++) {
 			zeroPrice[i] = new DefaultPrice();
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlan.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlan.java
index 3620162..4972e26 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlan.java
@@ -16,25 +16,42 @@
 
 package com.ning.billing.catalog;
 
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlID;
+import javax.xml.bind.annotation.XmlIDREF;
+
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Plan;
 import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.util.config.ValidatingConfig;
+import com.ning.billing.util.config.ValidationError;
 import com.ning.billing.util.config.ValidationErrors;
 
-import javax.xml.bind.annotation.*;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Iterator;
-
 @XmlAccessorType(XmlAccessType.NONE)
 public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements Plan {
 
-
 	@XmlAttribute(required=true)
 	@XmlID
 	private String name;
+	
+	@XmlAttribute(required=false)
+	private Boolean retired = false;
+	
+	//TODO MDW Validation - effectiveDateForExistingSubscriptons > catalog effectiveDate 
+	@XmlElement(required=false)
+	private Date effectiveDateForExistingSubscriptons;
 
 	@XmlElement(required=true)
 	@XmlIDREF
@@ -55,6 +72,12 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 	private Integer plansAllowedInBundle = 1;
 
 	/* (non-Javadoc)
+	 * @see com.ning.billing.catalog.IPlan#getEffectiveDateForExistingSubscriptons()
+	 */
+	@Override
+	public Date getEffectiveDateForExistingSubscriptons() {
+		return effectiveDateForExistingSubscriptons;
+	}	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlan#getPhases()
 	 */
     @Override
@@ -77,6 +100,12 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 	public String getName() {
 		return name;
 	}
+	
+	@Override
+	public boolean isRetired() {
+		return retired;
+	}
+	
 	@Override
 	public DefaultPlanPhase getFinalPhase() {
 		return finalPhase;
@@ -97,6 +126,17 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 	}
 
 	@Override
+	public PlanPhase findPhase(String name) throws CatalogApiException {
+		for(PlanPhase pp : getAllPhases()) {
+			if(pp.getName().equals(name)) {
+				return pp;
+			}
+
+		}
+		throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PHASE, name);
+	}
+
+	@Override
 	public BillingPeriod getBillingPeriod(){
 		return finalPhase.getBillingPeriod();
 	}
@@ -138,9 +178,21 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 
 	@Override
 	public ValidationErrors validate(StandaloneCatalog catalog, ValidationErrors errors) {
+		if(effectiveDateForExistingSubscriptons != null &&
+				catalog.getEffectiveDate().getTime() > effectiveDateForExistingSubscriptons.getTime()) {
+			errors.add(new ValidationError(String.format("Price effective date %s is before catalog effective date '%s'",
+					effectiveDateForExistingSubscriptons,
+					catalog.getEffectiveDate().getTime()), 
+					catalog.getCatalogURI(), DefaultInternationalPrice.class, ""));
+		}
+		
 		return errors;
 	}
 
+	protected void setEffectiveDateForExistingSubscriptons(
+			Date effectiveDateForExistingSubscriptons) {
+		this.effectiveDateForExistingSubscriptons = effectiveDateForExistingSubscriptons;
+	}
 
 	protected DefaultPlan setName(String name) {
 		this.name = name;
@@ -166,4 +218,16 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
 		this.initialPhases = phases;
 		return this;
 	}
+
+	public DefaultPlan setRetired(boolean retired) {
+		this.retired = retired;
+		return this;
+	}
+	
+	public DefaultPlan setPlansAllowedInBundle(Integer plansAllowedInBundle) {
+		this.plansAllowedInBundle = plansAllowedInBundle;
+		return this;
+	}
+	
+	
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java
index e086bfe..4afe278 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultPlanPhase.java
@@ -16,6 +16,7 @@
 
 package com.ning.billing.catalog;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.*;
 import com.ning.billing.util.config.ValidatingConfig;
 import com.ning.billing.util.config.ValidationError;
@@ -51,6 +52,20 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
 
 	//Not exposed in XML
 	private Plan plan;
+	
+	public static String phaseName(Plan plan, PlanPhase phase) {
+		return plan.getName() + "-" + phase.getPhaseType().toString().toLowerCase();
+	}
+	
+	public static String planName(String phaseName) throws CatalogApiException {
+		for(PhaseType type : PhaseType.values()) {
+			if(phaseName.endsWith(type.toString().toLowerCase())) {
+				return phaseName.substring(0, phaseName.length() - type.toString().length() - 1);
+			}
+		}
+		throw new CatalogApiException(ErrorCode.CAT_BAD_PHASE_NAME, phaseName);
+	}
+
 
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPlanPhase#getRecurringPrice()
@@ -89,7 +104,7 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
 	 */
 	@Override
 	public String getName() {
-		return plan.getName() + "-" + type.toString().toLowerCase();
+		return phaseName(plan,this);
 	}
 
 	/* (non-Javadoc)
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceList.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceList.java
index 04916ba..48de5d4 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceList.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceList.java
@@ -32,6 +32,9 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
 	@XmlID
 	private String name;
 
+	@XmlAttribute(required=false)
+	private Boolean retired = false;
+	
 	@XmlElementWrapper(name="plans", required=true)
 	@XmlElement(name="plan", required=true)
 	@XmlIDREF
@@ -47,6 +50,10 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
 	protected DefaultPlan[] getPlans() {
 		return plans;
 	}
+
+	public boolean isRetired() {
+		return retired;
+	}
 	
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.IPriceList#getName()
@@ -94,6 +101,11 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
         }
         return count;
     }
+	
+	public DefaultPriceList setRetired(boolean retired) {
+		this.retired = retired;
+		return this;
+	}
 
 
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceListSet.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceListSet.java
index b876fa9..f0636dd 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceListSet.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultPriceListSet.java
@@ -16,17 +16,19 @@
 
 package com.ning.billing.catalog;
 
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.Product;
 import com.ning.billing.util.config.ValidatingConfig;
 import com.ning.billing.util.config.ValidationError;
 import com.ning.billing.util.config.ValidationErrors;
 
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-
 @XmlAccessorType(XmlAccessType.NONE)
 public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> {
 	@XmlElement(required=true, name="defaultPriceList")
@@ -47,7 +49,7 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> {
 	}
 
 	public DefaultPlan getPlanListFrom(String priceListName, Product product,
-			BillingPeriod period) {
+			BillingPeriod period) throws CatalogApiException {
 		DefaultPlan result = null;
 		DefaultPriceList pl = findPriceListFrom(priceListName);
 		if(pl != null) {
@@ -60,7 +62,10 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> {
 		return defaultPricelist.findPlan(product, period);
 	}
 
-	public DefaultPriceList findPriceListFrom (String priceListName) {
+	public DefaultPriceList findPriceListFrom (String priceListName) throws CatalogApiException {
+		if(priceListName == null) {
+			throw new CatalogApiException(ErrorCode.CAT_NULL_PRICE_LIST_NAME);
+		}
 		if (defaultPricelist.getName().equals(priceListName)) {
 			return defaultPricelist;
 		} 
@@ -69,7 +74,7 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> {
 				return pl;
 			}
 		}
-		return null;
+		throw new CatalogApiException(ErrorCode.CAT_PRICE_LIST_NOT_FOUND, priceListName);
 	}
 
 	@Override
diff --git a/catalog/src/main/java/com/ning/billing/catalog/DefaultProduct.java b/catalog/src/main/java/com/ning/billing/catalog/DefaultProduct.java
index 79520ce..bf8cfd4 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/DefaultProduct.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/DefaultProduct.java
@@ -22,6 +22,7 @@ import com.ning.billing.util.config.ValidatingConfig;
 import com.ning.billing.util.config.ValidationErrors;
 
 import javax.xml.bind.annotation.*;
+
 import java.net.URI;
 
 @XmlAccessorType(XmlAccessType.NONE)
@@ -32,6 +33,9 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
 	@XmlID
     private String name;
 
+	@XmlAttribute(required=false)
+	private Boolean retired = false;
+	
 	@XmlElement(required=true)
     private ProductCategory category;
 	
@@ -53,6 +57,11 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
 	}
 
 	@Override
+	public boolean isRetired() {
+		return retired;
+	}
+	
+	@Override
     public ProductCategory getCategory() {
 		return category;
 	}
@@ -140,4 +149,9 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
 		return this;
 	}
 
+	public DefaultProduct setRetired(boolean retired) {
+		this.retired = retired;
+		return this;
+	}
+
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/io/VersionedCatalogLoader.java b/catalog/src/main/java/com/ning/billing/catalog/io/VersionedCatalogLoader.java
index 4ad78df..7ef0600 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/io/VersionedCatalogLoader.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/io/VersionedCatalogLoader.java
@@ -60,13 +60,11 @@ public class VersionedCatalogLoader implements ICatalogLoader  {
 				xmlURIs = findXmlReferences(directoryContents, new URL(uriString));
 			}
 			
-			VersionedCatalog result = new VersionedCatalog();
+			VersionedCatalog result = new VersionedCatalog(clock);
 			for(URI u : xmlURIs) {
 				StandaloneCatalog catalog = XMLLoader.getObjectFromUri(u, StandaloneCatalog.class);
 				result.add(catalog);
 			}
-			Date now = clock.getUTCNow().toDate();
-			result.configureEffectiveDate(now);
 			return result;
 		} catch (Exception e) {
 			throw new ServiceException("Problem encountered loading catalog", e);
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 737373e..7ae442f 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,6 +16,7 @@
 
 package com.ning.billing.catalog.rules;
 
+
 import com.ning.billing.catalog.DefaultPriceList;
 import com.ning.billing.catalog.DefaultProduct;
 import com.ning.billing.catalog.StandaloneCatalog;
@@ -28,12 +29,15 @@ import com.ning.billing.util.config.ValidationErrors;
 
 public abstract class Case<T> extends ValidatingConfig<StandaloneCatalog> {
 
-	protected DefaultProduct product;
-	protected ProductCategory productCategory;
-	protected BillingPeriod billingPeriod;
-	protected DefaultPriceList priceList;
-
 	protected abstract T getResult();
+	
+	public abstract DefaultProduct getProduct();
+
+	public abstract ProductCategory getProductCategory();
+
+	public abstract BillingPeriod getBillingPeriod();
+	    
+	public abstract DefaultPriceList getPriceList();
 
 	public T getResult(PlanSpecifier planPhase, StandaloneCatalog c) throws CatalogApiException {
 		if (satisfiesCase(planPhase, c)	) {
@@ -43,10 +47,10 @@ public abstract class Case<T> extends ValidatingConfig<StandaloneCatalog> {
 	}
 	
 	protected boolean satisfiesCase(PlanSpecifier planPhase, StandaloneCatalog c) throws CatalogApiException {
-		return (product         == null || product.equals(c.findProduct(planPhase.getProductName()))) &&
-		(productCategory == null || productCategory.equals(planPhase.getProductCategory())) &&
-		(billingPeriod   == null || billingPeriod.equals(planPhase.getBillingPeriod())) &&
-		(priceList       == null || priceList.equals(c.getPriceListFromName(planPhase.getPriceListName())));
+		return (getProduct()         == null || getProduct().equals(c.findCurrentProduct(planPhase.getProductName()))) &&
+		(getProductCategory() == null || getProductCategory().equals(planPhase.getProductCategory())) &&
+		(getBillingPeriod()   == null || getBillingPeriod().equals(planPhase.getBillingPeriod())) &&
+		(getPriceList()       == null || getPriceList().equals(c.findCurrentPriceList(planPhase.getPriceListName())));
 	}
 
 	public static <K> K getResult(Case<K>[] cases, PlanSpecifier planSpec, StandaloneCatalog catalog) throws CatalogApiException {
@@ -67,26 +71,11 @@ public abstract class Case<T> extends ValidatingConfig<StandaloneCatalog> {
 		return errors;
 	}
 
-	protected Case<T> setProduct(DefaultProduct product) {
-		this.product = product;
-		return this;
-	}
-
-	protected Case<T> setProductCategory(ProductCategory productCategory) {
-		this.productCategory = productCategory;
-		return this;
-	}
+	protected abstract Case<T> setProduct(DefaultProduct product);
 
-	protected Case<T> setBillingPeriod(BillingPeriod billingPeriod) {
-		this.billingPeriod = billingPeriod;
-		return this;
-	}
+	protected abstract  Case<T> setProductCategory(ProductCategory productCategory);
 
-	protected Case<T> setPriceList(DefaultPriceList priceList) {
-		this.priceList = priceList;
-		return this;
-	}
+	protected abstract  Case<T> setBillingPeriod(BillingPeriod billingPeriod);
 
-	
-	
+	protected abstract  Case<T> setPriceList(DefaultPriceList priceList);
 }
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 a340fdc..f101ef1 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
@@ -68,14 +68,14 @@ public abstract class CaseChange<T>  extends ValidatingConfig<StandaloneCatalog>
 			PlanSpecifier to, StandaloneCatalog catalog) throws CatalogApiException {
 		if(	
 				(phaseType     	     == null || from.getPhaseType() == phaseType) &&
-				(fromProduct 	     == null || fromProduct.equals(catalog.findProduct(from.getProductName()))) &&
+				(fromProduct 	     == null || fromProduct.equals(catalog.findCurrentProduct(from.getProductName()))) &&
 				(fromProductCategory == null || fromProductCategory.equals(from.getProductCategory())) &&
 				(fromBillingPeriod   == null || fromBillingPeriod.equals(from.getBillingPeriod())) &&
-				(toProduct           == null || toProduct.equals(catalog.findProduct(to.getProductName()))) &&
+				(toProduct           == null || toProduct.equals(catalog.findCurrentProduct(to.getProductName()))) &&
 				(toProductCategory   == null || toProductCategory.equals(to.getProductCategory())) &&
 				(toBillingPeriod     == null || toBillingPeriod.equals(to.getBillingPeriod())) &&
-				(fromPriceList       == null || fromPriceList.equals(catalog.getPriceListFromName(from.getPriceListName()))) &&
-				(toPriceList         == null || toPriceList.equals(catalog.getPriceListFromName(to.getPriceListName())))
+				(fromPriceList       == null || fromPriceList.equals(catalog.findCurrentPriceList(from.getPriceListName()))) &&
+				(toPriceList         == null || toPriceList.equals(catalog.findCurrentPriceList(to.getPriceListName())))
 				) {
 			return getResult();
 		}
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 b38c1f8..ebd7561 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,9 +31,8 @@ public abstract class CasePhase<T> extends CaseStandardNaming<T> {
 	private PhaseType phaseType;	
 	
 	public T getResult(PlanPhaseSpecifier specifier, StandaloneCatalog c) throws CatalogApiException {
-		if ((phaseType       == null || 
-				specifier.getPhaseType() == null || specifier.getPhaseType() == phaseType) &&
-				satisfiesCase(new PlanSpecifier(specifier), c)
+		if ((phaseType       == null ||  specifier.getPhaseType() == phaseType) 
+				&& satisfiesCase(new PlanSpecifier(specifier), c)
 				) {
 			return getResult(); 
 		}
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 fbb9cab..5063131 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
@@ -25,37 +25,66 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlIDREF;
 
 public class CasePriceList extends Case<DefaultPriceList> {
+    @XmlElement(required=false, name="fromProduct")
+    @XmlIDREF
+    private DefaultProduct fromProduct;
+    
+    @XmlElement(required=false, name="fromProductCategory")
+    private ProductCategory fromProductCategory;
+    
+    @XmlElement(required=false, name="fromBillingPeriod")
+    private BillingPeriod fromBillingPeriod;
+    
+    @XmlElement(required=false, name="fromPriceList")
+    @XmlIDREF
+    private DefaultPriceList fromPriceList;
 
-	private DefaultPriceList toPriceList;
+    @XmlElement(required=true, name="toPriceList")
+    @XmlIDREF
+    private DefaultPriceList toPriceList;
 
-	@XmlElement(required=false, name="fromProduct")
-	@XmlIDREF
-	public DefaultProduct getProduct(){
-		return product;
-	}
+    public DefaultProduct getProduct(){
+        return fromProduct;
+    }
 
-	@XmlElement(required=false, name="fromProductCategory")
-	public ProductCategory getProductCategory() {
-		return productCategory;
-	}
+    public ProductCategory getProductCategory() {
+        return fromProductCategory;
+    }
 
-	@XmlElement(required=false, name="fromBillingPeriod")
-	public BillingPeriod getBillingPeriod() {
-		return billingPeriod;
-	}
-	
-	@XmlElement(required=false, name="fromPriceList")
-	@XmlIDREF
-	public DefaultPriceList getPriceList() {
-		return priceList;
-	}
+    public BillingPeriod getBillingPeriod() {
+        return fromBillingPeriod;
+    }
+    
+    public DefaultPriceList getPriceList() {
+        return fromPriceList;
+    }
+
+    protected DefaultPriceList getResult() {
+        return toPriceList;
+    }
+
+    protected CasePriceList setProduct(DefaultProduct product) {
+        this.fromProduct = product;
+        return this;
+    }
+
+    protected CasePriceList setProductCategory(ProductCategory productCategory) {
+        this.fromProductCategory = productCategory;
+        return this;
+    }
+
+    protected CasePriceList setBillingPeriod(BillingPeriod billingPeriod) {
+        this.fromBillingPeriod = billingPeriod;
+        return this;
+    }
+
+    protected CasePriceList setPriceList(DefaultPriceList priceList) {
+        this.fromPriceList = priceList;
+        return this;
+    }
+    
+    
 
-	@Override
-	@XmlElement(required=true, name="toPriceList")
-	@XmlIDREF
-	protected DefaultPriceList getResult() {
-		return toPriceList;
-	}
 
 	protected CasePriceList setToPriceList(DefaultPriceList toPriceList) {
 		this.toPriceList = toPriceList;
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 cf61fcd..8e161ce 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
@@ -25,27 +25,53 @@ import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlIDREF;
 
 public abstract class CaseStandardNaming<T> extends Case<T> {
+    @XmlElement(required=false, name="product")
+    @XmlIDREF
+    private DefaultProduct product;
+    @XmlElement(required=false, name="productCategory")
+    private ProductCategory productCategory;
+    
+    @XmlElement(required=false, name="billingPeriod")
+    private BillingPeriod billingPeriod;
+    
+    @XmlElement(required=false, name="priceList")
+    @XmlIDREF
+    private DefaultPriceList priceList;
 
-	@XmlElement(required=false, name="product")
-	@XmlIDREF
 	public DefaultProduct getProduct(){
 		return product;
 	}
 
-	@XmlElement(required=false, name="productCategory")
 	public ProductCategory getProductCategory() {
 		return productCategory;
 	}
 
-	@XmlElement(required=false, name="billingPeriod")
 	public BillingPeriod getBillingPeriod() {
 		return billingPeriod;
 	}
 	
-	@XmlElement(required=false, name="priceList")
-	@XmlIDREF
 	public DefaultPriceList getPriceList() {
 		return priceList;
 	}
 
+    protected CaseStandardNaming<T> setProduct(DefaultProduct product) {
+        this.product = product;
+        return this;
+    }
+
+    protected CaseStandardNaming<T> setProductCategory(ProductCategory productCategory) {
+        this.productCategory = productCategory;
+        return this;
+    }
+
+    protected CaseStandardNaming<T> setBillingPeriod(BillingPeriod billingPeriod) {
+        this.billingPeriod = billingPeriod;
+        return this;
+    }
+
+    protected CaseStandardNaming<T> setPriceList(DefaultPriceList priceList) {
+        this.priceList = priceList;
+        return this;
+    }
+
 }
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 54aa5c7..c47529a 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
@@ -16,17 +16,28 @@
 
 package com.ning.billing.catalog.rules;
 
-import com.ning.billing.catalog.DefaultPriceList;
-import com.ning.billing.catalog.StandaloneCatalog;
-import com.ning.billing.catalog.api.*;
-import com.ning.billing.util.config.ValidatingConfig;
-import com.ning.billing.util.config.ValidationErrors;
-
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 
+import org.joda.time.DateTime;
+
+import com.ning.billing.catalog.DefaultPriceList;
+import com.ning.billing.catalog.StandaloneCatalog;
+import com.ning.billing.catalog.api.ActionPolicy;
+import com.ning.billing.catalog.api.BillingAlignment;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.IllegalPlanChange;
+import com.ning.billing.catalog.api.Plan;
+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;
+import com.ning.billing.util.config.ValidationErrors;
+
 @XmlAccessorType(XmlAccessType.NONE)
 public class PlanRules extends ValidatingConfig<StandaloneCatalog>  {
 
@@ -67,11 +78,13 @@ public class PlanRules extends ValidatingConfig<StandaloneCatalog>  {
 	}
 
 	public PlanChangeResult planChange(PlanPhaseSpecifier from, PlanSpecifier to, StandaloneCatalog catalog) throws CatalogApiException {
-		DefaultPriceList priceList = catalog.getPriceListFromName(to.getPriceListName());
-		if( priceList== null ) {
-			priceList = findPriceList(from.toPlanSpecifier(), catalog);
-			to = new PlanSpecifier(to.getProductName(), to.getProductCategory(), to.getBillingPeriod(), priceList.getName());
-		} 
+		DefaultPriceList toPriceList;
+		if( to.getPriceListName() == null ) { // Pricelist may be null because it is unspecified this is the principal use-case
+			toPriceList = findPriceList(from.toPlanSpecifier(), catalog);
+			to = new PlanSpecifier(to.getProductName(), to.getProductCategory(), to.getBillingPeriod(), toPriceList.getName());
+		} else {
+			toPriceList = catalog.findCurrentPriceList(to.getPriceListName());
+		}
 		
 		ActionPolicy policy = getPlanChangePolicy(from, to, catalog);
 		if(policy == ActionPolicy.ILLEGAL) {
@@ -80,7 +93,7 @@ public class PlanRules extends ValidatingConfig<StandaloneCatalog>  {
 		
 		PlanAlignmentChange alignment = getPlanChangeAlignment(from, to, catalog);
 		
-		return new PlanChangeResult(priceList, policy, alignment);
+		return new PlanChangeResult(toPriceList, policy, alignment);
 	}
 	
 	public PlanAlignmentChange getPlanChangeAlignment(PlanPhaseSpecifier from,
@@ -95,6 +108,7 @@ public class PlanRules extends ValidatingConfig<StandaloneCatalog>  {
 				from.getPriceListName().equals(to.getPriceListName())) {
 			return ActionPolicy.ILLEGAL;
 		}
+		//Plan toPlan = catalog.findPlan()
 		
 		return CaseChange.getResult(changeCase, from, to, catalog); 
 	}
@@ -102,7 +116,7 @@ public class PlanRules extends ValidatingConfig<StandaloneCatalog>  {
 	private DefaultPriceList findPriceList(PlanSpecifier specifier, StandaloneCatalog catalog) throws CatalogApiException {
 		DefaultPriceList result = Case.getResult(priceListCase, specifier, catalog);
 		if (result == null) {
-			result = catalog.getPriceListFromName(specifier.getPriceListName());
+			result = catalog.findCurrentPriceList(specifier.getPriceListName());
 		}
 		return result;
 	}
diff --git a/catalog/src/main/java/com/ning/billing/catalog/StandaloneCatalog.java b/catalog/src/main/java/com/ning/billing/catalog/StandaloneCatalog.java
index 06b24bb..b7f7b79 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/StandaloneCatalog.java
@@ -16,21 +16,39 @@
 
 package com.ning.billing.catalog;
 
+import java.net.URI;
+import java.util.Collection;
+import java.util.Date;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
 import com.ning.billing.ErrorCode;
-import com.ning.billing.catalog.api.*;
+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.CatalogApiException;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.Plan;
+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.PlanPhase;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.PlanSpecifier;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.catalog.api.StaticCatalog;
 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 javax.xml.bind.annotation.*;
-import java.net.URI;
-import java.util.Collection;
-import java.util.Date;
-
 @XmlRootElement(name="catalog")
 @XmlAccessorType(XmlAccessType.NONE)
-public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> implements Catalog {
+public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> implements StaticCatalog {
 	@XmlElement(required=true)
 	private Date effectiveDate;
 
@@ -80,18 +98,18 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
 	 * @see com.ning.billing.catalog.ICatalog#getProducts()
 	 */
 	@Override
-	public DefaultProduct[] getProducts() {
+	public DefaultProduct[] getCurrentProducts() {
 		return products;
 	}
 
 
 	@Override
-	public Currency[] getSupportedCurrencies() {
+	public Currency[] getCurrentSupportedCurrencies() {
 		return supportedCurrencies;
 	}
 
 	@Override
-	public DefaultPlan[] getPlans() {
+	public DefaultPlan[] getCurrentPlans() {
 		return plans;
 	}
 
@@ -103,7 +121,7 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
 		return planRules;
 	}
 	
-	public DefaultPriceList getPriceListFromName(String priceListName) {
+	public DefaultPriceList findCurrentPriceList(String priceListName) throws CatalogApiException {
 		return priceLists.findPriceListFrom(priceListName);
 	}
 	
@@ -111,29 +129,30 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
 		return this.priceLists;
 	}
 
-	@Override
-	public void configureEffectiveDate(Date date) {
-		// Nothing to do here this is a method that is only implemented on VersionedCatalog
-	}
-
-
 	/* (non-Javadoc)
 	 * @see com.ning.billing.catalog.ICatalog#getPlan(java.lang.String, java.lang.String)
 	 */
 	@Override
-	public DefaultPlan findPlan(String productName, BillingPeriod period, String priceListName) throws CatalogApiException {
-		Product product = findProduct(productName);
+	public DefaultPlan findCurrentPlan(String productName, BillingPeriod period, String priceListName) throws CatalogApiException {
+		if (productName == null ) {
+			throw new CatalogApiException(ErrorCode.CAT_NULL_PRODUCT_NAME);
+		}
+		if (priceLists == null) {
+			throw new CatalogApiException(ErrorCode.CAT_PRICE_LIST_NOT_FOUND,priceListName);
+		}
+		Product product = findCurrentProduct(productName);
 		DefaultPlan result = priceLists.getPlanListFrom(priceListName, product, period);
 		if ( result == null) {
-			throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND, productName, period.toString(), priceListName);
+			String periodString = (period == null) ? "NULL" :  period.toString();
+			throw new CatalogApiException(ErrorCode.CAT_PLAN_NOT_FOUND, productName, periodString, priceListName);
 		}
 		return result;
 	}
 	
 	@Override
-	public DefaultPlan findPlan(String name) throws CatalogApiException {
-		if (name == null) {
-			return null;
+	public DefaultPlan findCurrentPlan(String name) throws CatalogApiException {
+		if (name == null || plans == null) {
+			throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PLAN, name);
 		}
 		for(DefaultPlan p : plans) {
 			if(p.getName().equals(name)) {
@@ -144,7 +163,10 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
 	}
 	
 	@Override
-	public Product findProduct(String name) throws CatalogApiException {
+	public Product findCurrentProduct(String name) throws CatalogApiException {
+		if (name == null || products == null) {
+			throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PRODUCT, name);
+		}
 		for(DefaultProduct p : products) {
 			if (p.getName().equals(name)) {
 				return p;
@@ -154,24 +176,18 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
 	}
 
 	@Override
-	public DefaultPlanPhase findPhase(String name) throws CatalogApiException {
-		for(DefaultPlan p : plans) {
-
-			if(p.getFinalPhase().getName().equals(name)) {
-				return p.getFinalPhase();
-			}
-			if (p.getInitialPhases() != null) {
-				for(DefaultPlanPhase pp : p.getInitialPhases()) {
-					if(pp.getName().equals(name)) {
-						return pp;
-					}
-				}
-			}
+	public PlanPhase findCurrentPhase(String name) throws CatalogApiException {
+		if (name == null || plans == null) {
+			throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PHASE, name);
 		}
-
-		throw new CatalogApiException(ErrorCode.CAT_NO_SUCH_PHASE, name);
+		
+		String  planName = DefaultPlanPhase.planName(name);
+		Plan plan = findCurrentPlan(planName);
+		return plan.findPhase(name);
 	}
 
+
+
 	//////////////////////////////////////////////////////////////////////////////
 	//
 	// RULES
@@ -220,9 +236,9 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
 	private Collection<? extends ValidationError> validate(StandaloneCatalog catalog,
 			ValidationErrors errors, ValidatingConfig<StandaloneCatalog>[] configs) {
 		for(ValidatingConfig<StandaloneCatalog> config: configs) {
-
+			config.validate(catalog, errors);
 		}
-		return null;
+		return errors;
 	}
 	
 
@@ -275,6 +291,14 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
 		return this;
 	}
 
-
-
+	@Override
+	public boolean canCreatePlan(PlanSpecifier specifier) throws CatalogApiException {
+		Product product = findCurrentProduct(specifier.getProductName());
+		Plan plan = findCurrentPlan(specifier.getProductName(), specifier.getBillingPeriod(), specifier.getPriceListName());
+		DefaultPriceList priceList = findCurrentPriceList(specifier.getPriceListName());
+		
+		return (!product.isRetired()) &&
+				(!plan.isRetired()) &&
+				(!priceList.isRetired());
+	}
 }
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 bb48b9b..f652347 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/VersionedCatalog.java
@@ -28,7 +28,11 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlRootElement;
 
-import com.google.inject.Inject;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.api.ActionPolicy;
 import com.ning.billing.catalog.api.BillingAlignment;
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -43,40 +47,137 @@ import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.catalog.api.PlanPhaseSpecifier;
 import com.ning.billing.catalog.api.PlanSpecifier;
 import com.ning.billing.catalog.api.Product;
+import com.ning.billing.catalog.api.StaticCatalog;
+import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.config.ValidatingConfig;
 import com.ning.billing.util.config.ValidationErrors;
 
 
 @XmlRootElement(name="catalog")
 @XmlAccessorType(XmlAccessType.NONE)
-public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implements Catalog {
+public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implements Catalog, StaticCatalog {
+	private static final Logger log = LoggerFactory.getLogger(VersionedCatalog.class);
 	
-	private StandaloneCatalog currentCatalog;
+	final private Clock clock;
+	private String catalogName;
 	
 	@XmlElement(name="catalogVersion", required=true)
 	private final List<StandaloneCatalog> versions = new ArrayList<StandaloneCatalog>();
-	
-	@Inject
-	public VersionedCatalog() {
+
+	public VersionedCatalog(Clock clock) {
+		this.clock = clock;
 		StandaloneCatalog baseline = new StandaloneCatalog(new Date(0)); // init with an empty catalog may need to 
 													 // populate some empty pieces here to make validation work
-		add(baseline); 
+		try {
+			add(baseline);
+		} catch (CatalogApiException e) {
+			// This should never happen
+			log.error("This error should never happpen", e);
+		} 
 	}
-	
-	private StandaloneCatalog versionForDate(Date date) {
-		StandaloneCatalog previous = versions.get(0);
-		for(StandaloneCatalog c : versions) {
+
+	//
+	// Private methods
+	//
+	private StandaloneCatalog versionForDate(DateTime date) {
+		return versions.get(indexOfVersionForDate(date.toDate()));
+	}
+
+	private List<StandaloneCatalog> versionsBeforeDate(Date date) {
+		List<StandaloneCatalog> result = new ArrayList<StandaloneCatalog>();
+		int index = indexOfVersionForDate(date);
+		for(int i = 0; i <= index; i++) {
+			result.add(versions.get(i));
+		}
+		return result;
+	}
+
+	private int indexOfVersionForDate(Date date) {
+		for(int i = 1; i < versions.size(); i++) {
+			StandaloneCatalog c = versions.get(i);
 			if(c.getEffectiveDate().getTime() > date.getTime()) {
-				return previous;
+				return i - 1;
 			}
-			previous = c;
 		}
-		return versions.get(versions.size() - 1);
+		return versions.size() - 1;
 	}
+	
+	private class PlanRequestWrapper {
+		String name;
+		String productName;
+		BillingPeriod bp;
+		String priceListName;
+		
+		public PlanRequestWrapper(String name) {
+			super();
+			this.name = name;
+		}
 
-	public void add(StandaloneCatalog e) {
-		if(currentCatalog == null) {
-			currentCatalog = e;
+		public PlanRequestWrapper(String productName, BillingPeriod bp,
+				String priceListName) {
+			super();
+			this.productName = productName;
+			this.bp = bp;
+			this.priceListName = priceListName;
+		}
+		
+		public Plan findPlan(StandaloneCatalog catalog) throws CatalogApiException {
+			if(name != null) {
+				return catalog.findCurrentPlan(name);
+			} else {
+				return catalog.findCurrentPlan(productName, bp, priceListName);
+			}
+		}
+	}
+	
+	private Plan findPlan(PlanRequestWrapper wrapper, 
+			DateTime requestedDate,
+			DateTime subscriptionStartDate) 
+					throws CatalogApiException {
+		List<StandaloneCatalog> catalogs = versionsBeforeDate(requestedDate.toDate());
+		if(catalogs.size() == 0) {
+			throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
+		}
+		
+		for(int i = catalogs.size() - 1; i >= 0 ; i--) { // Working backwards to find the latest applicable plan
+			StandaloneCatalog c = catalogs.get(i);
+			Plan plan = null;
+			try {
+				 plan = wrapper.findPlan(c);
+			} catch (CatalogApiException e) {
+				if(e.getCode() != ErrorCode.CAT_NO_SUCH_PLAN.getCode()) {
+					throw e;
+				} else { 
+					break;
+				}
+			}
+				
+			DateTime catalogEffectiveDate = new DateTime(c.getEffectiveDate());
+			if(subscriptionStartDate.isAfter(catalogEffectiveDate)) { // Its a new subscription this plan always applies
+				return plan;
+			} else { //Its an existing subscription
+				if(plan.getEffectiveDateForExistingSubscriptons() != null) { //if it is null any change to this does not apply to existing subscriptions
+					DateTime existingSubscriptionDate = new DateTime(plan.getEffectiveDateForExistingSubscriptons());
+					if(requestedDate.isAfter(existingSubscriptionDate)){ // this plan is now applicable to existing subs
+						return plan;
+					}
+				}
+			}
+		}
+		
+		throw new CatalogApiException(ErrorCode.CAT_NO_CATALOG_FOR_GIVEN_DATE, requestedDate.toDate().toString());
+	}
+	
+	//
+	// Public methods not exposed in interface
+	//
+	public void add(StandaloneCatalog e) throws CatalogApiException {
+		if(catalogName == null) {
+			catalogName = e.getCatalogName();
+		} else {
+			if(!catalogName.equals(getCatalogName())) {
+				throw new CatalogApiException(ErrorCode.CAT_CATALOG_NAME_MISMATCH,catalogName, e.getCatalogName());
+			}
 		}
 		versions.add(e);
 		Collections.sort(versions,new Comparator<StandaloneCatalog>() {
@@ -86,61 +187,144 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
 			}
 		});
 	}
-
+		
 	public Iterator<StandaloneCatalog> iterator() {
 		return versions.iterator();
 	}
-	
-	@Override
-	public void configureEffectiveDate(Date date) {
-		currentCatalog = versionForDate(date); // 
-	}
 
 	public int size() {
 		return versions.size();
 	}
 
+	//
+    // Simple getters
+    //
 	@Override
-	public DefaultProduct[] getProducts() {
-		return currentCatalog.getProducts();
+	public String getCatalogName() {
+		return catalogName;
 	}
 
 	@Override
-	public Currency[] getSupportedCurrencies() {
-		return currentCatalog.getSupportedCurrencies();
+	public DefaultProduct[] getProducts(DateTime requestedDate) {
+		return versionForDate(requestedDate).getCurrentProducts();
 	}
 
 	@Override
-	public DefaultPlan[] getPlans() {
-		return currentCatalog.getPlans();
+	public Currency[] getSupportedCurrencies(DateTime requestedDate) {
+		return versionForDate(requestedDate).getCurrentSupportedCurrencies();
 	}
 
 	@Override
-	public Date getEffectiveDate() {
-		return currentCatalog.getEffectiveDate();
+	public DefaultPlan[] getPlans(DateTime requestedDate) {
+		return versionForDate(requestedDate).getCurrentPlans();
+	}
+
+	//
+	// Find a plan
+	//
+	@Override
+	public Plan findPlan(String name, 
+			DateTime requestedDate) 
+					throws CatalogApiException {
+		return versionForDate(requestedDate).findCurrentPlan(name);
+	}
+
+	@Override
+	public Plan findPlan(String productName, 
+			BillingPeriod term,
+			String priceListName,
+			DateTime requestedDate) 
+					throws CatalogApiException {
+		return versionForDate(requestedDate).findCurrentPlan(productName, term, priceListName);
+	}
+
+	@Override
+	public Plan findPlan(String name, 
+			DateTime requestedDate,
+			DateTime subscriptionStartDate) 
+					throws CatalogApiException {
+		return findPlan(new PlanRequestWrapper(name), requestedDate, subscriptionStartDate);
+	}
+
+	@Override
+	public Plan findPlan(String productName, 
+			BillingPeriod term,
+			String priceListName,
+			DateTime requestedDate,
+			DateTime subscriptionStartDate) 
+					throws CatalogApiException {
+		return findPlan(new PlanRequestWrapper(productName, term, priceListName), requestedDate, subscriptionStartDate);
+	}
+
+	//
+	// Find a product
+	//
+	@Override
+	public Product findProduct(String name, DateTime requestedDate) throws CatalogApiException {
+		return versionForDate(requestedDate).findCurrentProduct(name);
+	}
+
+
+    //
+    // Find a phase
+    //
+	@Override
+	public PlanPhase findPhase(String phaseName, 
+			DateTime requestedDate,
+			DateTime subscriptionStartDate) 
+					throws CatalogApiException {
+		String planName = DefaultPlanPhase.planName(phaseName);
+		Plan plan = findPlan(planName, requestedDate, subscriptionStartDate);
+		return plan.findPhase(phaseName);
+	}
+	
+    //
+    // Rules
+    //
+	@Override
+	public ActionPolicy planChangePolicy(PlanPhaseSpecifier from,
+			PlanSpecifier to, DateTime requestedDate) throws CatalogApiException {
+		return versionForDate(requestedDate).planChangePolicy(from, to);
+	}
+
+	@Override
+	public ActionPolicy planCancelPolicy(PlanPhaseSpecifier planPhase, DateTime requestedDate) throws CatalogApiException {
+		return versionForDate(requestedDate).planCancelPolicy(planPhase);
+	}
+
+	@Override
+	public PlanAlignmentChange planChangeAlignment(PlanPhaseSpecifier from,
+			PlanSpecifier to, DateTime requestedDate) throws CatalogApiException {
+		return versionForDate(requestedDate).planChangeAlignment(from, to);
 	}
 
 	@Override
-	public Plan findPlan(String productName, BillingPeriod term,
-			String planSetName) throws CatalogApiException {
-		return currentCatalog.findPlan(productName, term, planSetName);
+	public PlanAlignmentCreate planCreateAlignment(PlanSpecifier specifier, DateTime requestedDate) throws CatalogApiException {
+		return versionForDate(requestedDate).planCreateAlignment(specifier);
 	}
 
+	
+
 	@Override
-	public DefaultPlan findPlan(String name) throws CatalogApiException {
-		return currentCatalog.findPlan(name);
+	public BillingAlignment billingAlignment(PlanPhaseSpecifier planPhase, DateTime requestedDate) throws CatalogApiException {
+		return versionForDate(requestedDate).billingAlignment(planPhase);
 	}
 
 	@Override
-	public PlanPhase findPhase(String name) throws CatalogApiException {
-		return currentCatalog.findPhase(name);
+	public PlanChangeResult planChange(PlanPhaseSpecifier from, PlanSpecifier to, DateTime requestedDate)
+			throws CatalogApiException {
+		return versionForDate(requestedDate).planChange(from, to);
 	}
 
 	@Override
-	public Product findProduct(String name) throws CatalogApiException {
-		return currentCatalog.findProduct(name);
+	public boolean canCreatePlan(PlanSpecifier specifier, DateTime requestedDate)
+			throws CatalogApiException {
+		return versionForDate(requestedDate).canCreatePlan(specifier);
 	}
 
+	//
+	// VerifiableConfig API
+	//
 	@Override
 	public void initialize(StandaloneCatalog catalog, URI sourceURI) {
 		for(StandaloneCatalog c : versions) {
@@ -153,48 +337,103 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalog> implem
 		for(StandaloneCatalog c : versions) {
 			errors.addAll(c.validate(c, errors));
 		}
+		//TODO MDW validation - ensure all catalog versions have a single name
+		//TODO MDW validation - ensure effective dates are different (actually do we want this?)
+		//TODO MDW validation - check that all products are there
+		//TODO MDW validation - check that all plans are there
+		//TODO MDW validation - check that all currencies are there
+		//TODO MDW validation - check that all pricelists are there
 		return errors;
 	}
-	
+
+	//
+	// Static catalog API
+	//
+	@Override
+	public Date getEffectiveDate() {
+		return versionForDate(clock.getUTCNow()).getEffectiveDate();
+	}
+
+	@Override
+	public Currency[] getCurrentSupportedCurrencies() {
+		return versionForDate(clock.getUTCNow()).getCurrentSupportedCurrencies();
+	}
+
+	@Override
+	public Product[] getCurrentProducts() {
+		return versionForDate(clock.getUTCNow()).getCurrentProducts() ;
+	}
+
+	@Override
+	public Plan[] getCurrentPlans() {
+		return versionForDate(clock.getUTCNow()).getCurrentPlans();
+	}
+
+	@Override
+	public Plan findCurrentPlan(String productName, BillingPeriod term,
+			String priceList) throws CatalogApiException {
+		return versionForDate(clock.getUTCNow()).findCurrentPlan(productName, term, priceList);
+	}
+
+	@Override
+	public Plan findCurrentPlan(String name) throws CatalogApiException {
+		return versionForDate(clock.getUTCNow()).findCurrentPlan(name);
+	}
+
+	@Override
+	public Product findCurrentProduct(String name) throws CatalogApiException {
+		return versionForDate(clock.getUTCNow()).findCurrentProduct(name);
+	}
+
+	@Override
+	public PlanPhase findCurrentPhase(String name) throws CatalogApiException {
+		return versionForDate(clock.getUTCNow()).findCurrentPhase(name);
+	}
+
 	@Override
 	public ActionPolicy planChangePolicy(PlanPhaseSpecifier from,
 			PlanSpecifier to) throws CatalogApiException {
-		return currentCatalog.planChangePolicy(from, to);
+		return versionForDate(clock.getUTCNow()).planChangePolicy(from, to);
 	}
 
 	@Override
-	public ActionPolicy planCancelPolicy(PlanPhaseSpecifier planPhase) throws CatalogApiException {
-		return currentCatalog.planCancelPolicy(planPhase);
+	public PlanChangeResult planChange(PlanPhaseSpecifier from, PlanSpecifier to)
+			throws CatalogApiException {
+		return versionForDate(clock.getUTCNow()).planChange(from, to);
 	}
 
 	@Override
-	public PlanAlignmentChange planChangeAlignment(PlanPhaseSpecifier from,
-			PlanSpecifier to) throws CatalogApiException {
-		return currentCatalog.planChangeAlignment(from, to);
+	public ActionPolicy planCancelPolicy(PlanPhaseSpecifier planPhase)
+			throws CatalogApiException {
+		return versionForDate(clock.getUTCNow()).planCancelPolicy(planPhase);
 	}
 
 	@Override
-	public PlanAlignmentCreate planCreateAlignment(PlanSpecifier specifier) throws CatalogApiException {
-		return currentCatalog.planCreateAlignment(specifier);
+	public PlanAlignmentCreate planCreateAlignment(PlanSpecifier specifier)
+			throws CatalogApiException {
+		return versionForDate(clock.getUTCNow()).planCreateAlignment(specifier);
 	}
 
 	@Override
-	public String getCatalogName() {
-		return currentCatalog.getCatalogName();
+	public BillingAlignment billingAlignment(PlanPhaseSpecifier planPhase)
+			throws CatalogApiException {
+		return versionForDate(clock.getUTCNow()).billingAlignment(planPhase);
 	}
 
 	@Override
-	public BillingAlignment billingAlignment(PlanPhaseSpecifier planPhase) throws CatalogApiException {
-		return currentCatalog.billingAlignment(planPhase);
+	public PlanAlignmentChange planChangeAlignment(PlanPhaseSpecifier from,
+			PlanSpecifier to) throws CatalogApiException {
+		return versionForDate(clock.getUTCNow()).planChangeAlignment(from, to);
 	}
 
 	@Override
-	public PlanChangeResult planChange(PlanPhaseSpecifier from, PlanSpecifier to)
+	public boolean canCreatePlan(PlanSpecifier specifier)
 			throws CatalogApiException {
-		return currentCatalog.planChange(from, to);
+		return versionForDate(clock.getUTCNow()).canCreatePlan(specifier);
 	}
+
 	
-	//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/MockCatalog.java b/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java
index 089c1e4..765d4bd 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockCatalog.java
@@ -57,7 +57,7 @@ public class MockCatalog extends StandaloneCatalog {
 	}
 	
 	public void populatePlans() {
-		DefaultProduct[] products = getProducts();
+		DefaultProduct[] products = getCurrentProducts();
 		DefaultPlan[] plans = new DefaultPlan[products.length];
 		for(int i = 0; i < products.length; i++) {
 			DefaultPlanPhase phase = new DefaultPlanPhase().setPhaseType(PhaseType.EVERGREEN).setBillingPeriod(BillingPeriod.MONTHLY).setReccuringPrice(new DefaultInternationalPrice());
@@ -67,7 +67,7 @@ public class MockCatalog extends StandaloneCatalog {
 	}
 
 	public void populatePriceLists() {
-		DefaultPlan[] plans = getPlans();
+		DefaultPlan[] plans = getCurrentPlans();
 		
 		DefaultPriceList[] priceList = new DefaultPriceList[plans.length - 1];
 		for(int i = 1; i < plans.length; i++) {
diff --git a/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java b/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java
index 5d1af74..773c58b 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/MockInternationalPrice.java
@@ -24,19 +24,12 @@ import java.util.Date;
 public class MockInternationalPrice extends DefaultInternationalPrice {
 	
 	MockInternationalPrice() {
-		setEffectiveDateForExistingSubscriptons(new Date());
 		setPrices(new DefaultPrice[] {
 			new DefaultPrice().setCurrency(Currency.USD).setValue(new BigDecimal(1))	
 		});
 	}
 	
-	MockInternationalPrice(Date effectiveDateForExistingSubscriptions, DefaultPrice[] price) {
-		setEffectiveDateForExistingSubscriptons(effectiveDateForExistingSubscriptions);
-		setPrices(price);
-	}
-
 	MockInternationalPrice(DefaultPrice... price) {
-		setEffectiveDateForExistingSubscriptons(new Date());
 		setPrices(price);
 	}
 
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 c00f1c4..10229ef 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
@@ -16,209 +16,275 @@
 
 package com.ning.billing.catalog.rules;
 
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNull;
+
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlIDREF;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.DefaultPriceList;
 import com.ning.billing.catalog.DefaultProduct;
 import com.ning.billing.catalog.MockCatalog;
 import com.ning.billing.catalog.StandaloneCatalog;
-import com.ning.billing.catalog.api.*;
-import org.testng.annotations.Test;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.PlanSpecifier;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.api.ProductCategory;
 
-import javax.xml.bind.annotation.XmlElement;
+public class TestCase {
 
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertNull;
+    protected class CaseResult extends Case<Result>  {
+
+        @XmlElement(required=true)
+        private Result policy;
+
+        public CaseResult(DefaultProduct product, ProductCategory productCategory, BillingPeriod billingPeriod, DefaultPriceList priceList,
+                Result policy) {
+            setProduct(product);
+            setProductCategory(productCategory);
+            setBillingPeriod(billingPeriod);
+            setPriceList(priceList);
+            this.policy = policy;
+        }
+
+        @Override
+        protected Result getResult() {
+            return policy;
+        }
+
+        @XmlElement(required=false, name="product")
+        @XmlIDREF
+        protected DefaultProduct product;
+        @XmlElement(required=false, name="productCategory")
+        protected ProductCategory productCategory;
+
+        @XmlElement(required=false, name="billingPeriod")
+        protected BillingPeriod billingPeriod;
+
+        @XmlElement(required=false, name="priceList")
+        @XmlIDREF
+        protected DefaultPriceList priceList;
+
+        public DefaultProduct getProduct(){
+            return product;
+        }
+
+        public ProductCategory getProductCategory() {
+            return productCategory;
+        }
+
+        public BillingPeriod getBillingPeriod() {
+            return billingPeriod;
+        }
+
+        public DefaultPriceList getPriceList() {
+            return priceList;
+        }
+
+        protected CaseResult setProduct(DefaultProduct product) {
+            this.product = product;
+            return this;
+        }
 
-public class TestCase {
+        protected CaseResult setProductCategory(ProductCategory productCategory) {
+            this.productCategory = productCategory;
+            return this;
+        }
+
+        protected CaseResult setBillingPeriod(BillingPeriod billingPeriod) {
+            this.billingPeriod = billingPeriod;
+            return this;
+        }
+
+        protected CaseResult setPriceList(DefaultPriceList priceList) {
+            this.priceList = priceList;
+            return this;
+        }
+    }
+
+    @Test(enabled=true)
+    public void testBasic() throws CatalogApiException{
+        MockCatalog cat = new MockCatalog();
+
+        DefaultProduct product = cat.getCurrentProducts()[0];
+        DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+
+        CaseResult cr = new CaseResult(
+                product, 
+                ProductCategory.BASE,
+                BillingPeriod.MONTHLY, 
+                priceList,
+                Result.FOO);
+
+        assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), cat);
+        assertionException(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", cat);
+    }
+
+    @Test(enabled=true)
+    public void testWildCardProduct() throws CatalogApiException{
+        MockCatalog cat = new MockCatalog();
+
+        DefaultProduct product = cat.getCurrentProducts()[0];
+        DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+
+        CaseResult cr = new CaseResult(
+                null, 
+                ProductCategory.BASE,
+                BillingPeriod.MONTHLY, 
+                priceList,
+
+                Result.FOO);
+
+        assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertion(Result.FOO, cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), cat);
+        assertionException(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", cat);
+    }
+
+    @Test(enabled=true)
+    public void testWildCardProductCategory() throws CatalogApiException{
+        MockCatalog cat = new MockCatalog();
+
+        DefaultProduct product = cat.getCurrentProducts()[0];
+        DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+
+        CaseResult cr = new CaseResult(
+                product, 
+                null,
+                BillingPeriod.MONTHLY, 
+                priceList,
+
+                Result.FOO);
+
+        assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr,  cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertion(Result.FOO, cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), cat);
+        assertionException(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", cat);
+    }
+
+    @Test(enabled=true)
+    public void testWildCardBillingPeriod() throws CatalogApiException{
+        MockCatalog cat = new MockCatalog();
+
+        DefaultProduct product = cat.getCurrentProducts()[0];
+        DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+
+        CaseResult cr = new CaseResult(
+                product, 
+                ProductCategory.BASE,
+                null, 
+                priceList,
+
+                Result.FOO);
+
+        assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr,  cat.getCurrentProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertion(Result.FOO,cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), cat);
+        assertionException(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", cat);
+    }
+
+    @Test(enabled=true)
+    public void testWildCardPriceList() throws CatalogApiException{
+        MockCatalog cat = new MockCatalog();
+
+        DefaultProduct product = cat.getCurrentProducts()[0];
+        DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
+
+
+        CaseResult cr = new CaseResult(
+                product, 
+                ProductCategory.BASE,
+                BillingPeriod.MONTHLY, 
+                null,
+
+                Result.FOO);
+
+        assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr,  cat.getCurrentProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
+        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() throws CatalogApiException {
+        MockCatalog cat = new MockCatalog();
+
+        DefaultProduct product = cat.getCurrentProducts()[0];
+        DefaultPriceList priceList = cat.findCurrentPriceList(PriceListSet.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);
 
-	protected class CaseResult extends Case<Result>  {
-
-		@XmlElement(required=true)
-		private Result policy;
-
-		public CaseResult(DefaultProduct product, ProductCategory productCategory, BillingPeriod billingPeriod, DefaultPriceList priceList,
-				 Result policy) {
-			setProduct(product);
-			setProductCategory(productCategory);
-			setBillingPeriod(billingPeriod);
-			setPriceList(priceList);
-			this.policy = policy;
-		}
-
-		@Override
-		protected Result getResult() {
-			return policy;
-		}
-	}
-
-	@Test(enabled=true)
-	public void testBasic() throws CatalogApiException{
-		MockCatalog cat = new MockCatalog();
-
-		DefaultProduct product = cat.getProducts()[0];
-		DefaultPriceList priceList = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
-
-
-		CaseResult cr = new CaseResult(
-				product, 
-				ProductCategory.BASE,
-				BillingPeriod.MONTHLY, 
-				priceList,
-				Result.FOO);
-
-		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr, cat.getProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", cat);
-	}
-
-	@Test(enabled=true)
-	public void testWildCardProduct() throws CatalogApiException{
-		MockCatalog cat = new MockCatalog();
-
-		DefaultProduct product = cat.getProducts()[0];
-		DefaultPriceList priceList = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
-
-
-		CaseResult cr = new CaseResult(
-				null, 
-				ProductCategory.BASE,
-				BillingPeriod.MONTHLY, 
-				priceList,
-
-				Result.FOO);
-
-		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertion(Result.FOO, cr, cat.getProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", cat);
-	}
-	
-	@Test(enabled=true)
-	public void testWildCardProductCategory() throws CatalogApiException{
-		MockCatalog cat = new MockCatalog();
-
-		DefaultProduct product = cat.getProducts()[0];
-		DefaultPriceList priceList = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
-
-
-		CaseResult cr = new CaseResult(
-				product, 
-				null,
-				BillingPeriod.MONTHLY, 
-				priceList,
-
-				Result.FOO);
-
-		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr,  cat.getProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertion(Result.FOO, cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", cat);
-	}
-	
-	@Test(enabled=true)
-	public void testWildCardBillingPeriod() throws CatalogApiException{
-		MockCatalog cat = new MockCatalog();
-
-		DefaultProduct product = cat.getProducts()[0];
-		DefaultPriceList priceList = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
-
-
-		CaseResult cr = new CaseResult(
-				product, 
-				ProductCategory.BASE,
-				null, 
-				priceList,
-
-				Result.FOO);
-
-		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr,  cat.getProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertion(Result.FOO,cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", cat);
-	}
-
-	@Test(enabled=true)
-	public void testWildCardPriceList() throws CatalogApiException{
-		MockCatalog cat = new MockCatalog();
-
-		DefaultProduct product = cat.getProducts()[0];
-		DefaultPriceList priceList = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
-
-
-		CaseResult cr = new CaseResult(
-				product, 
-				ProductCategory.BASE,
-				BillingPeriod.MONTHLY, 
-				null,
-
-				Result.FOO);
-
-		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr,  cat.getProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), cat);
-		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() throws CatalogApiException {
-		MockCatalog cat = new MockCatalog();
-
-		DefaultProduct product = cat.getProducts()[0];
-		DefaultPriceList priceList = cat.getPriceListFromName(PriceListSet.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, StandaloneCatalog cat) throws CatalogApiException{
-		assertNull(cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat));
-	}
-
-	protected void assertion(Result result, CaseResult cr, String productName, ProductCategory productCategory, BillingPeriod bp, String priceListName,StandaloneCatalog cat) throws CatalogApiException{
-		assertEquals(result, cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat));
-	}
+        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, StandaloneCatalog cat) throws CatalogApiException{
+        assertNull(cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat));
+    }
+
+    protected void assertionException(CaseResult cr, String productName, ProductCategory productCategory, BillingPeriod bp, String priceListName, StandaloneCatalog cat) {
+        try{
+        	cr.getResult(new PlanSpecifier(productName, productCategory, bp, priceListName), cat);  	
+        	Assert.fail("Expecting an exception");
+        } catch (CatalogApiException e) {
+        	Assert.assertEquals(e.getCode(), ErrorCode.CAT_PRICE_LIST_NOT_FOUND.getCode());
+        }
+    }
+
+    protected void assertion(Result result, CaseResult cr, String productName, ProductCategory productCategory, BillingPeriod bp, String priceListName,StandaloneCatalog cat) throws CatalogApiException{
+        assertEquals(result, 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 af13162..f684503 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
@@ -16,11 +16,14 @@
 
 package com.ning.billing.catalog.rules;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.DefaultPriceList;
 import com.ning.billing.catalog.DefaultProduct;
 import com.ning.billing.catalog.MockCatalog;
 import com.ning.billing.catalog.StandaloneCatalog;
 import com.ning.billing.catalog.api.*;
+import com.ning.billing.catalog.rules.TestCase.CaseResult;
+
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -60,13 +63,13 @@ public class TestCaseChange {
 		}
 	}
 	@Test(enabled=true)
-	public void testBasic(){
+	public void testBasic() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -86,14 +89,14 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -127,18 +130,18 @@ public class TestCaseChange {
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
@@ -150,13 +153,13 @@ public class TestCaseChange {
 	}
 	
 	@Test(enabled=true)
-	public void testWildcardFromProduct(){
+	public void testWildcardFromProduct()throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -176,7 +179,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertion(Result.FOO,cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -197,7 +200,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 
 		assertionNull(cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.ANNUAL, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -210,18 +213,18 @@ public class TestCaseChange {
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
@@ -233,13 +236,13 @@ public class TestCaseChange {
 	}
 	
 	@Test(enabled=true)
-	public void testWildcardToProduct(){
+	public void testWildcardToProduct() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -259,7 +262,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -280,7 +283,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertion(Result.FOO, cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -300,18 +303,18 @@ public class TestCaseChange {
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
@@ -323,13 +326,13 @@ public class TestCaseChange {
 	}
 	
 	@Test(enabled=true)
-	public void testWildcardFromProductCategory(){
+	public void testWildcardFromProductCategory() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -349,14 +352,14 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -390,18 +393,18 @@ public class TestCaseChange {
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
@@ -413,13 +416,13 @@ public class TestCaseChange {
 	}
 	
 	@Test(enabled=true)
-	public void testWildcardToProductCategory(){
+	public void testWildcardToProductCategory() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -439,14 +442,14 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -480,18 +483,18 @@ public class TestCaseChange {
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
@@ -503,13 +506,13 @@ public class TestCaseChange {
 	}
 	
 	@Test(enabled=true)
-	public void testWildcardFromBillingPeriod(){
+	public void testWildcardFromBillingPeriod() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -529,7 +532,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -550,7 +553,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -570,18 +573,18 @@ public class TestCaseChange {
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
@@ -594,13 +597,13 @@ public class TestCaseChange {
 	
 	
 	@Test(enabled=true)
-	public void testWildCardToBillingPeriod(){
+	public void testWildCardToBillingPeriod() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -620,7 +623,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -641,7 +644,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -661,18 +664,18 @@ public class TestCaseChange {
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
@@ -684,13 +687,13 @@ public class TestCaseChange {
 	}
 	
 	@Test(enabled=true)
-	public void testWildCardFromPriceList(){
+	public void testWildCardFromPriceList() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -710,7 +713,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -731,7 +734,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -755,14 +758,14 @@ public class TestCaseChange {
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
@@ -774,13 +777,13 @@ public class TestCaseChange {
 	}
 	
 	@Test(enabled=true)
-	public void testWildcardToPriceList(){
+	public void testWildcardToPriceList() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -800,7 +803,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -821,7 +824,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -841,18 +844,18 @@ public class TestCaseChange {
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertion(Result.FOO,cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
@@ -864,13 +867,13 @@ public class TestCaseChange {
 	}
 	
 	@Test(enabled=true)
-	public void testWildcardPlanPhase(){
+	public void testWildcardPlanPhase() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -890,7 +893,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				 cat.getProducts()[1].getName(), product2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -911,7 +914,7 @@ public class TestCaseChange {
 				PhaseType.EVERGREEN, cat);
 		
 		assertionNull(cr, 
-				product1.getName(),  cat.getProducts()[1].getName(), 
+				product1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
 				priceList1.getName(), priceList2.getName(), 
@@ -931,18 +934,18 @@ public class TestCaseChange {
 				priceList1.getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				 cat.getProducts()[1].getName(), priceList2.getName(), 
+				 cat.getCurrentProducts()[1].getName(), priceList2.getName(), 
 				PhaseType.EVERGREEN, cat);
 		
-		assertionNull(cr, 
+		assertionException(cr, 
 				product1.getName(), product2.getName(), 
 				ProductCategory.BASE, ProductCategory.BASE,
 				BillingPeriod.MONTHLY, BillingPeriod.MONTHLY, 
-				priceList1.getName(),  cat.getProducts()[1].getName(), 
+				priceList1.getName(),  cat.getCurrentProducts()[1].getName(), 
 				PhaseType.EVERGREEN, cat);
 		
 		assertion(Result.FOO,cr, 
@@ -958,10 +961,10 @@ public class TestCaseChange {
 	public void testOrder() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 
-		DefaultProduct product2 = cat.getProducts()[2];
+		DefaultProduct product2 = cat.getCurrentProducts()[2];
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[1];
 
 
@@ -1033,7 +1036,20 @@ public class TestCaseChange {
 			Assert.fail("", e);
 		}
 	}
-
+	   protected void assertionException(CaseChangeResult cr, 
+				String fromProductName, String toProductName,
+				ProductCategory fromProductCategory, ProductCategory toProductCategory, 
+				BillingPeriod fromBp, BillingPeriod toBp,
+				String fromPriceListName, String toPriceListName,
+				PhaseType phaseType, StandaloneCatalog cat){
+	        try{
+	        	cr.getResult(new PlanPhaseSpecifier(fromProductName, fromProductCategory, fromBp, fromPriceListName, phaseType), 
+						new PlanSpecifier(toProductName, toProductCategory, toBp, toPriceListName),cat);	
+	        	Assert.fail("Expecting an exception");
+	        } catch (CatalogApiException e) {
+	        	Assert.assertEquals(e.getCode(), ErrorCode.CAT_PRICE_LIST_NOT_FOUND.getCode());
+	        }
+	    }
 	protected void assertion(Result result, 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 fe505f4..02f7ab5 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
@@ -16,6 +16,7 @@
 
 package com.ning.billing.catalog.rules;
 
+import com.ning.billing.ErrorCode;
 import com.ning.billing.catalog.DefaultPriceList;
 import com.ning.billing.catalog.DefaultProduct;
 import com.ning.billing.catalog.MockCatalog;
@@ -53,7 +54,7 @@ public class TestCasePhase {
 	public void testBasic(){
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product = cat.getProducts()[0];
+		DefaultProduct product = cat.getCurrentProducts()[0];
 		DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
 
@@ -66,10 +67,10 @@ public class TestCasePhase {
 				Result.FOO);
 
 		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr,  cat.getProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+		assertionNull(cr,  cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
+		assertionException(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
 	}
 
@@ -77,7 +78,7 @@ public class TestCasePhase {
 	public void testWildCardProduct(){
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product = cat.getProducts()[0];
+		DefaultProduct product = cat.getCurrentProducts()[0];
 		DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
 
@@ -90,10 +91,10 @@ public class TestCasePhase {
 				Result.FOO);
 
 		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertion(Result.FOO, cr, cat.getProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+		assertion(Result.FOO, cr, cat.getCurrentProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
+		assertionException(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
 	}
 	
@@ -101,7 +102,7 @@ public class TestCasePhase {
 	public void testWildCardProductCategory(){
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product = cat.getProducts()[0];
+		DefaultProduct product = cat.getCurrentProducts()[0];
 		DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
 
@@ -114,10 +115,10 @@ public class TestCasePhase {
 				Result.FOO);
 
 		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr,  cat.getProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+		assertionNull(cr,  cat.getCurrentProducts()[1].getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertion(Result.FOO, cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
+		assertionException(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
 	}
 	
@@ -125,7 +126,7 @@ public class TestCasePhase {
 	public void testWildCardBillingPeriod(){
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product = cat.getProducts()[0];
+		DefaultProduct product = cat.getCurrentProducts()[0];
 		DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
 
@@ -138,10 +139,10 @@ public class TestCasePhase {
 				Result.FOO);
 
 		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr,  cat.getProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+		assertionNull(cr,  cat.getCurrentProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertion(Result.FOO,cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
+		assertionException(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.TRIAL, cat);
 	}
 
@@ -149,7 +150,7 @@ public class TestCasePhase {
 	public void testWildCardPriceList(){
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product = cat.getProducts()[0];
+		DefaultProduct product = cat.getCurrentProducts()[0];
 		DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
 
@@ -162,7 +163,7 @@ public class TestCasePhase {
 				Result.FOO);
 
 		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr,  cat.getProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+		assertionNull(cr,  cat.getCurrentProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
@@ -173,7 +174,7 @@ public class TestCasePhase {
 	public void testWildCardPhaseType(){
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product = cat.getProducts()[0];
+		DefaultProduct product = cat.getCurrentProducts()[0];
 		DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
 
@@ -186,10 +187,10 @@ public class TestCasePhase {
 				Result.FOO);
 
 		assertion(Result.FOO, cr, product.getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr,  cat.getProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
+		assertionNull(cr,  cat.getCurrentProducts()[1].getName(), ProductCategory.BASE,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.ADD_ON,BillingPeriod.MONTHLY, priceList.getName(), PhaseType.EVERGREEN, cat);
 		assertionNull(cr, product.getName(), ProductCategory.BASE,BillingPeriod.ANNUAL, priceList.getName(), PhaseType.EVERGREEN, cat);
-		assertionNull(cr, product.getName(), ProductCategory.BASE, BillingPeriod.MONTHLY, "dipsy", PhaseType.EVERGREEN, cat);
+		assertionException(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);
 	}
 	
@@ -197,7 +198,7 @@ public class TestCasePhase {
 	public void testOrder() throws CatalogApiException{
 		MockCatalog cat = new MockCatalog();
 
-		DefaultProduct product = cat.getProducts()[0];
+		DefaultProduct product = cat.getCurrentProducts()[0];
 		DefaultPriceList priceList = cat.getPriceLists().getDefaultPricelist();
 
 
@@ -262,6 +263,16 @@ public class TestCasePhase {
 		}
 	}
 
+
+	protected void assertionException(CaseResult cr, String productName, ProductCategory productCategory, BillingPeriod bp, String priceListName, PhaseType phaseType, StandaloneCatalog cat){
+		try {
+			Assert.assertNull(cr.getResult(new PlanPhaseSpecifier(productName, productCategory, bp, priceListName, phaseType), cat));
+			Assert.fail("Exception expected");
+		} catch (CatalogApiException e) {
+			Assert.assertEquals(e.getCode(), ErrorCode.CAT_PRICE_LIST_NOT_FOUND.getCode());
+		}
+	}
+
 	protected void assertion(Result result, CaseResult cr, String productName, ProductCategory productCategory, BillingPeriod bp, String priceListName, PhaseType phaseType, StandaloneCatalog cat){
 		try {
 			Assert.assertEquals(result, cr.getResult(new PlanPhaseSpecifier(productName, productCategory, bp, priceListName, phaseType), cat));
diff --git a/catalog/src/test/java/com/ning/billing/catalog/rules/TestLoadRules.java b/catalog/src/test/java/com/ning/billing/catalog/rules/TestLoadRules.java
new file mode 100644
index 0000000..6f1d9e2
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/rules/TestLoadRules.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.catalog.rules;
+
+import java.io.File;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.StandaloneCatalog;
+import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanAlignmentCreate;
+import com.ning.billing.catalog.api.PlanSpecifier;
+import com.ning.billing.catalog.api.ProductCategory;
+import com.ning.billing.util.config.XMLLoader;
+
+public class TestLoadRules {
+
+    @Test
+    public void test() throws Exception {
+        StandaloneCatalog catalog = XMLLoader.getObjectFromUri(new File("src/test/resources/WeaponsHireSmall.xml").toURI(), StandaloneCatalog.class);
+        Assert.assertNotNull(catalog);
+        PlanRules rules = catalog.getPlanRules();
+        
+        PlanSpecifier specifier = new PlanSpecifier("Laser-Scope", ProductCategory.ADD_ON , BillingPeriod.MONTHLY,
+                "DEFAULT");
+        
+        PlanAlignmentCreate alignment=  rules.getPlanCreateAlignment(specifier, catalog);
+        Assert.assertEquals(alignment, PlanAlignmentCreate.START_OF_SUBSCRIPTION);
+        
+        PlanSpecifier specifier2 = new PlanSpecifier("Extra-Ammo", ProductCategory.ADD_ON , BillingPeriod.MONTHLY,
+                "DEFAULT");
+        
+        PlanAlignmentCreate alignment2 = rules.getPlanCreateAlignment(specifier2, catalog);
+        Assert.assertEquals(alignment2, PlanAlignmentCreate.START_OF_BUNDLE);
+    }
+}
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 e41d506..7ac9cc0 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
@@ -20,6 +20,7 @@ import com.ning.billing.catalog.DefaultPriceList;
 import com.ning.billing.catalog.DefaultProduct;
 import com.ning.billing.catalog.MockCatalog;
 import com.ning.billing.catalog.api.*;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testng.Assert;
@@ -48,9 +49,9 @@ public class TestPlanRules {
 	}
 
 	@Test
-	public void testCannotChangeToSamePlan() {
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+	public void testCannotChangeToSamePlan() throws CatalogApiException {
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.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());
@@ -67,9 +68,9 @@ public class TestPlanRules {
 	}
 	
 	@Test
-	public void testExistingPriceListIsKept() {
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+	public void testExistingPriceListIsKept() throws CatalogApiException {
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.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());
@@ -92,10 +93,10 @@ public class TestPlanRules {
 	
 	
 	@Test
-	public void testBaseCase() {
-		DefaultProduct product1 = cat.getProducts()[0];
-		DefaultProduct product2 = cat.getProducts()[1];
-		DefaultPriceList priceList1 = cat.getPriceListFromName(PriceListSet.DEFAULT_PRICELIST_NAME);
+	public void testBaseCase() throws CatalogApiException {
+		DefaultProduct product1 = cat.getCurrentProducts()[0];
+		DefaultProduct product2 = cat.getCurrentProducts()[1];
+		DefaultPriceList priceList1 = cat.findCurrentPriceList(PriceListSet.DEFAULT_PRICELIST_NAME);
 		DefaultPriceList priceList2 = cat.getPriceLists().getChildPriceLists()[0];
 		
 		PlanPhaseSpecifier from = new PlanPhaseSpecifier(product1.getName(), product1.getCategory(), BillingPeriod.MONTHLY, priceList1.getName(), PhaseType.EVERGREEN);
@@ -103,7 +104,7 @@ public class TestPlanRules {
 
 		PlanChangeResult result = null;
 		try {
-			result = cat.getPlanRules().planChange(from, to, cat);		
+			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");
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestCatalogService.java b/catalog/src/test/java/com/ning/billing/catalog/TestCatalogService.java
index 754b03a..7bb45a6 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestCatalogService.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestCatalogService.java
@@ -35,8 +35,8 @@ public class TestCatalogService {
 			
 		}, new VersionedCatalogLoader(new DefaultClock()));
 		service.loadCatalog();
-		Assert.assertNotNull(service.getCatalog());
-		Assert.assertEquals(service.getCatalog().getCatalogName(), "WeaponsHireSmall");
+		Assert.assertNotNull(service.getFullCatalog());
+		Assert.assertEquals(service.getFullCatalog().getCatalogName(), "WeaponsHireSmall");
 	}
 	
 	@Test
@@ -49,7 +49,7 @@ public class TestCatalogService {
 			
 		}, new VersionedCatalogLoader(new DefaultClock()));
 		service.loadCatalog();
-		Assert.assertNotNull(service.getCatalog());
-		Assert.assertEquals(service.getCatalog().getCatalogName(), "Firearms");
+		Assert.assertNotNull(service.getFullCatalog());
+		Assert.assertEquals(service.getFullCatalog().getCatalogName(), "Firearms");
 	}
 }
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestInternationalPrice.java b/catalog/src/test/java/com/ning/billing/catalog/TestInternationalPrice.java
index 0f75459..3e6a755 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestInternationalPrice.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestInternationalPrice.java
@@ -67,7 +67,7 @@ public class TestInternationalPrice {
 	  StandaloneCatalog c = new MockCatalog();
 	  c.setSupportedCurrencies(new Currency[]{Currency.GBP, Currency.EUR, Currency.USD, Currency.BRL, Currency.MXN});
 	  c.initialize(c, new URI("foo://bar"));
-	  Assert.assertEquals(c.getPlans()[0].getFinalPhase().getRecurringPrice().getPrice(Currency.GBP), new BigDecimal(0));
+	  Assert.assertEquals(c.getCurrentPlans()[0].getFinalPhase().getRecurringPrice().getPrice(Currency.GBP), new BigDecimal(0));
   }
   
   @Test
@@ -88,16 +88,7 @@ public class TestInternationalPrice {
 	 errors.log(log);
 	 Assert.assertEquals(errors.size(), 3);
   }
-  @Test
-  public void testDateValidation(){
-	 StandaloneCatalog c = new MockCatalog();
-	 c.setSupportedCurrencies(new Currency[]{Currency.GBP, Currency.EUR, Currency.USD, Currency.BRL, Currency.MXN});
-	 DefaultInternationalPrice p1 =  new MockInternationalPrice();
-	 p1.setEffectiveDateForExistingSubscriptons(new Date((new Date().getTime()) - (1000 * 60 * 60 * 24)));
-	 ValidationErrors errors = p1.validate(c, new ValidationErrors());
-	 Assert.assertEquals(errors.size(), 1);
-	 errors.log(log);
-  }
+
   
   
   
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java b/catalog/src/test/java/com/ning/billing/catalog/TestPlan.java
new file mode 100644
index 0000000..00dd1b6
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestPlan.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.util.Date;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.config.ValidationErrors;
+
+public class TestPlan {
+	private static final Logger log = LoggerFactory.getLogger(TestPlan.class);
+	@Test
+	public void testDateValidation() {
+
+		StandaloneCatalog c = new MockCatalog();
+		c.setSupportedCurrencies(new Currency[]{Currency.GBP, Currency.EUR, Currency.USD, Currency.BRL, Currency.MXN});
+		DefaultPlan p1 =  new MockPlan();
+		p1.setEffectiveDateForExistingSubscriptons(new Date((new Date().getTime()) - (1000 * 60 * 60 * 24)));
+		ValidationErrors errors = p1.validate(c, new ValidationErrors());
+		Assert.assertEquals(errors.size(), 1);
+		errors.log(log);
+
+	}
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java b/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java
index 4bfcf30..d778c4b 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestPlanPhase.java
@@ -17,6 +17,8 @@
 package com.ning.billing.catalog;
 
 import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.util.config.ValidationErrors;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,5 +46,37 @@ public class TestPlanPhase {
 		errors = pp.validate(new MockCatalog(), new ValidationErrors());
 		errors.log(log);
 		Assert.assertEquals(errors.size(), 1);
-}
+	}
+	
+	@Test
+	public void testPhaseNames() throws CatalogApiException {
+		String planName = "Foo";
+		String planNameExt = planName + "-";
+		
+		DefaultPlan p = new MockPlan().setName(planName);
+		DefaultPlanPhase ppDiscount = new MockPlanPhase().setPhaseType(PhaseType.DISCOUNT).setPlan(p);
+		DefaultPlanPhase ppTrial = new MockPlanPhase().setPhaseType(PhaseType.TRIAL).setPlan(p);
+		DefaultPlanPhase ppEvergreen = new MockPlanPhase().setPhaseType(PhaseType.EVERGREEN).setPlan(p);
+		DefaultPlanPhase ppFixedterm = new MockPlanPhase().setPhaseType(PhaseType.FIXEDTERM).setPlan(p);
+		
+		String ppnDiscount = DefaultPlanPhase.phaseName(p, ppDiscount);
+		String ppnTrial = DefaultPlanPhase.phaseName(p, ppTrial);
+		String ppnEvergreen = DefaultPlanPhase.phaseName(p, ppEvergreen);
+		String ppnFixedterm = DefaultPlanPhase.phaseName(p, ppFixedterm);
+		
+		Assert.assertEquals(ppnTrial, planNameExt + "trial");
+		Assert.assertEquals(ppnEvergreen, planNameExt + "evergreen");
+		Assert.assertEquals(ppnFixedterm, planNameExt + "fixedterm");
+		Assert.assertEquals(ppnDiscount, planNameExt + "discount");
+		
+		
+		Assert.assertEquals(DefaultPlanPhase.planName(ppnDiscount),planName);
+		Assert.assertEquals(DefaultPlanPhase.planName(ppnTrial),planName);
+		Assert.assertEquals(DefaultPlanPhase.planName(ppnEvergreen), planName);
+		Assert.assertEquals(DefaultPlanPhase.planName(ppnFixedterm), planName);
+		
+		
+		
+		
+	}
 }
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 09024ce..3cb8d2f 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestPriceListSet.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestPriceListSet.java
@@ -17,6 +17,7 @@
 package com.ning.billing.catalog;
 
 import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.PhaseType;
 import com.ning.billing.catalog.api.PriceListSet;
 import com.ning.billing.catalog.api.ProductCategory;
@@ -30,7 +31,7 @@ import static com.ning.billing.catalog.api.PhaseType.EVERGREEN;
 
 public class TestPriceListSet {
 	@Test(enabled=true)
-	public void testOverriding() {
+	public void testOverriding() throws CatalogApiException {
 		DefaultProduct foo = new DefaultProduct("Foo", ProductCategory.BASE);
 		DefaultProduct bar = new DefaultProduct("Bar", ProductCategory.BASE);
 		DefaultPlan[] defaultPlans = new DefaultPlan[]{ 
@@ -55,7 +56,7 @@ public class TestPriceListSet {
 		Assert.assertEquals(set.getPlanListFrom("child", foo, BillingPeriod.MONTHLY).getFinalPhase().getPhaseType(), PhaseType.EVERGREEN);
 	}
 	
-	public void testForNullBillingPeriod() {
+	public void testForNullBillingPeriod() throws CatalogApiException {
 		DefaultProduct foo = new DefaultProduct("Foo", ProductCategory.BASE);
 		DefaultProduct bar = new DefaultProduct("Bar", ProductCategory.BASE);
 		DefaultPlan[] defaultPlans = new DefaultPlan[]{ 
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestStandaloneCatalog.java b/catalog/src/test/java/com/ning/billing/catalog/TestStandaloneCatalog.java
new file mode 100644
index 0000000..fd5d8fb
--- /dev/null
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestStandaloneCatalog.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.catalog;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.PhaseType;
+
+public class TestStandaloneCatalog {
+	
+		@Test
+		public void testFindPhase() throws CatalogApiException{
+			DefaultPlanPhase phaseTrial1 = new MockPlanPhase().setPhaseType(PhaseType.TRIAL);
+			DefaultPlanPhase phaseTrial2 = new MockPlanPhase().setPhaseType(PhaseType.TRIAL);
+			DefaultPlanPhase phaseDiscount1 = new MockPlanPhase().setPhaseType(PhaseType.DISCOUNT);
+			DefaultPlanPhase phaseDiscount2 = new MockPlanPhase().setPhaseType(PhaseType.DISCOUNT);
+			
+			DefaultPlan plan1 = new MockPlan().setName("TestPlan1").setFinalPhase(phaseDiscount1).setInitialPhases(new DefaultPlanPhase[]{phaseTrial1});
+			DefaultPlan plan2 = new MockPlan().setName("TestPlan2").setFinalPhase(phaseDiscount2).setInitialPhases(new DefaultPlanPhase[]{phaseTrial2});
+			phaseTrial1.setPlan(plan1);
+			phaseTrial2.setPlan(plan2);
+			phaseDiscount1.setPlan(plan1);
+			phaseDiscount2.setPlan(plan2);
+			
+			StandaloneCatalog cat = new MockCatalog().setPlans(new DefaultPlan[]{plan1, plan2});
+			
+			Assert.assertEquals(cat.findCurrentPhase("TestPlan1-discount"), phaseDiscount1);
+			Assert.assertEquals(cat.findCurrentPhase("TestPlan2-discount"), phaseDiscount2);
+			Assert.assertEquals(cat.findCurrentPhase("TestPlan1-trial"), phaseTrial1);
+			Assert.assertEquals(cat.findCurrentPhase("TestPlan2-trial"), phaseTrial2);
+			
+			
+		}
+		
+		
+}
diff --git a/catalog/src/test/java/com/ning/billing/catalog/TestVersionedCatalog.java b/catalog/src/test/java/com/ning/billing/catalog/TestVersionedCatalog.java
index bc07e3e..ddd623e 100644
--- a/catalog/src/test/java/com/ning/billing/catalog/TestVersionedCatalog.java
+++ b/catalog/src/test/java/com/ning/billing/catalog/TestVersionedCatalog.java
@@ -15,56 +15,88 @@
  */
 package com.ning.billing.catalog;
 
-import com.google.common.io.Resources;
-import com.ning.billing.catalog.api.InvalidConfigException;
-import com.ning.billing.catalog.io.VersionedCatalogLoader;
-import com.ning.billing.lifecycle.KillbillService.ServiceException;
-import com.ning.billing.util.clock.DefaultClock;
-import org.joda.time.DateTime;
-import org.testng.annotations.Test;
-import org.xml.sax.SAXException;
+import static org.testng.AssertJUnit.assertEquals;
 
-import javax.xml.bind.JAXBException;
-import javax.xml.transform.TransformerException;
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.net.MalformedURLException;
 import java.net.URISyntaxException;
 import java.util.Date;
 
-import static org.testng.AssertJUnit.assertEquals;
+import javax.xml.bind.JAXBException;
+import javax.xml.transform.TransformerException;
+
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import org.xml.sax.SAXException;
+
+import com.google.common.io.Resources;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.InvalidConfigException;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.io.VersionedCatalogLoader;
+import com.ning.billing.lifecycle.KillbillService.ServiceException;
+import com.ning.billing.util.clock.DefaultClock;
 
 public class TestVersionedCatalog {
+	private static final Logger log = LoggerFactory.getLogger(TestVersionedCatalog.class);
 	private final VersionedCatalogLoader loader = new VersionedCatalogLoader(new DefaultClock());
+	private VersionedCatalog vc;
+	
+	@BeforeClass(groups={"setup"})
+	public void setUp() throws ServiceException {
+		vc = loader.load(Resources.getResource("versionedCatalog").toString());
+	}
 
 	@Test(enabled=true)
-	public void testAddCatalog() throws MalformedURLException, IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, ServiceException {
-		VersionedCatalog vc = loader.load(Resources.getResource("versionedCatalog").toString());
+	public void testAddCatalog() throws MalformedURLException, IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, ServiceException, CatalogApiException {
 		vc.add(new StandaloneCatalog(new Date()));
 		assertEquals(5, vc.size());
 	}
 	
+		
 	@Test(enabled=true)
-	public void testApplyEffectiveDate() throws MalformedURLException, IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, ServiceException {
-		VersionedCatalog vc = loader.load(Resources.getResource("versionedCatalog").toString());
-		Date d = new Date(1L);
-		vc.configureEffectiveDate(d);
-		assertEquals(new Date(0), vc.getEffectiveDate()); // Start at the begining of time
+	public void testFindPlanWithDates() throws Exception {
+		DateTime dt0= new DateTime("2010-01-01T00:00:00+00:00");
+		DateTime dt1 = new DateTime("2011-01-01T00:01:00+00:00");
+		DateTime dt2 = new DateTime("2011-02-02T00:01:00+00:00");
+		DateTime dt214 = new DateTime("2011-02-14T00:01:00+00:00");
+		DateTime dt3 = new DateTime("2011-03-03T00:01:00+00:00");
 		
-		DateTime dt = new DateTime("2011-01-01T00:00:00+00:00");
-		d = new Date(dt.getMillis() + 1000);
-		vc.configureEffectiveDate(d);
-		assertEquals(dt.toDate(),vc.getEffectiveDate());
+		// New subscription
+		try {
+			vc.findPlan("pistol-monthly", dt0, dt0);	
+			Assert.fail("Exception should have been thrown there are no plans for this date");
+		} catch (CatalogApiException e) {
+			// Expected behaviour
+			log.error("Expected exception", e);
+			
+		}
+		Plan newSubPlan1 = vc.findPlan("pistol-monthly", dt1, dt1);
+		Plan newSubPlan2 = vc.findPlan("pistol-monthly", dt2, dt2);
+		Plan newSubPlan214 = vc.findPlan("pistol-monthly", dt214, dt214);
+		Plan newSubPlan3 = vc.findPlan("pistol-monthly", dt3, dt3);
 		
-		dt = new DateTime("2011-02-02T00:00:00+00:00");
-		d = new Date(dt.getMillis() + 1000);
-		vc.configureEffectiveDate(d);
-		assertEquals(dt.toDate(),vc.getEffectiveDate());
+		Assert.assertEquals(newSubPlan1.getAllPhases()[1].getRecurringPrice().getPrice(Currency.USD), new BigDecimal("1.0"));
+		Assert.assertEquals(newSubPlan2.getAllPhases()[1].getRecurringPrice().getPrice(Currency.USD), new BigDecimal("2.0"));
+		Assert.assertEquals(newSubPlan214.getAllPhases()[1].getRecurringPrice().getPrice(Currency.USD), new BigDecimal("2.0"));
+		Assert.assertEquals(newSubPlan3.getAllPhases()[1].getRecurringPrice().getPrice(Currency.USD), new BigDecimal("3.0"));
 		
-		dt = new DateTime("2011-03-03T00:00:00+00:00");
-		d = new Date(dt.getMillis() + 1000);
-		vc.configureEffectiveDate(d);
-		assertEquals(dt.toDate(),vc.getEffectiveDate());
+		// Existing subscription
 		
-	}
+		Plan exSubPlan2 = vc.findPlan("pistol-monthly", dt2, dt1);
+		Plan exSubPlan214 = vc.findPlan("pistol-monthly", dt214, dt1);
+		Plan exSubPlan3 = vc.findPlan("pistol-monthly", dt3, dt1);
+		
+		Assert.assertEquals(exSubPlan2.getAllPhases()[1].getRecurringPrice().getPrice(Currency.USD), new BigDecimal("1.0"));
+		Assert.assertEquals(exSubPlan214.getAllPhases()[1].getRecurringPrice().getPrice(Currency.USD), new BigDecimal("2.0"));
+		Assert.assertEquals(exSubPlan3.getAllPhases()[1].getRecurringPrice().getPrice(Currency.USD), new BigDecimal("2.0"));
 
+		
+	}
 }
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
index a29e929..c21aac1 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
@@ -57,6 +57,15 @@
 				<alignment>START_OF_SUBSCRIPTION</alignment>
 			</changeAlignmentCase>
 		</changeAlignment>
+		 <createAlignment>
+            <createAlignmentCase>
+                <product>Laser-Scope</product>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
 	</rules>
 
 
@@ -80,9 +89,9 @@
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>GBP</currency><value>29.95</value></price>
-					<price><currency>EUR</currency><value>29.95</value></price> 
-					<price><currency>USD</currency><value>29.95</value></price>								
+					<price><currency>GBP</currency><value>1.0</value></price>
+					<price><currency>EUR</currency><value>1.0</value></price> 
+					<price><currency>USD</currency><value>1.0</value></price>								
 				</recurringPrice>
 			</finalPhase>
 		</plan>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
index 135a89e..0e650f1 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
@@ -57,10 +57,20 @@
 				<alignment>START_OF_SUBSCRIPTION</alignment>
 			</changeAlignmentCase>
 		</changeAlignment>
+		 <createAlignment>
+            <createAlignmentCase>
+                <product>Laser-Scope</product>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
 	</rules>
 
 	<plans>
 		<plan name="pistol-monthly">
+			<effectiveDateForExistingSubscriptons>2011-02-14T00:00:00+00:00</effectiveDateForExistingSubscriptons>
 			<product>Pistol</product>
 			<initialPhases>
 				<phase type="TRIAL">
@@ -79,9 +89,9 @@
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>GBP</currency><value>29.95</value></price>
-					<price><currency>EUR</currency><value>29.95</value></price> 
-					<price><currency>USD</currency><value>29.95</value></price>								
+					<price><currency>GBP</currency><value>2.0</value></price>
+					<price><currency>EUR</currency><value>2.0</value></price> 
+					<price><currency>USD</currency><value>2.0</value></price>								
 				</recurringPrice>
 			</finalPhase>
 		</plan>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
index 747f54b..f7ae066 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
@@ -57,6 +57,15 @@
 				<alignment>START_OF_SUBSCRIPTION</alignment>
 			</changeAlignmentCase>
 		</changeAlignment>
+		 <createAlignment>
+            <createAlignmentCase>
+                <product>Laser-Scope</product>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
 	</rules>
 
 
@@ -80,9 +89,9 @@
 				</duration>
 				<billingPeriod>MONTHLY</billingPeriod>
 				<recurringPrice>
-					<price><currency>GBP</currency><value>29.95</value></price>
-					<price><currency>EUR</currency><value>29.95</value></price> 
-					<price><currency>USD</currency><value>29.95</value></price>								
+					<price><currency>GBP</currency><value>3.0</value></price>
+					<price><currency>EUR</currency><value>3.0</value></price> 
+					<price><currency>USD</currency><value>3.0</value></price>								
 				</recurringPrice>
 			</finalPhase>
 		</plan>
diff --git a/catalog/src/test/resources/WeaponsHireSmall.xml b/catalog/src/test/resources/WeaponsHireSmall.xml
index 0a3ed52..21a9f08 100644
--- a/catalog/src/test/resources/WeaponsHireSmall.xml
+++ b/catalog/src/test/resources/WeaponsHireSmall.xml
@@ -37,6 +37,9 @@
 		<product name="Laser-Scope">
 			<category>ADD_ON</category>
 		</product>
+        <product name="Extra-Ammo">
+            <category>ADD_ON</category>
+        </product>
 	</products>
 	 
 	<rules>
@@ -52,11 +55,20 @@
 				<policy>IMMEDIATE</policy>
 			</changePolicyCase>	
 		</changePolicy>
-		<changeAlignment>
-			<changeAlignmentCase>
-				<alignment>START_OF_SUBSCRIPTION</alignment>
-			</changeAlignmentCase>
-		</changeAlignment>
+        <changeAlignment>
+            <changeAlignmentCase>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <createAlignment>
+            <createAlignmentCase>
+                <product>Laser-Scope</product>
+                <alignment>START_OF_SUBSCRIPTION</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
 	</rules>
 
 	<plans>
@@ -136,6 +148,34 @@
 				</recurringPrice>
 			</finalPhase>
 		</plan>
+		<plan name="laser-scope-monthly">
+        <product>Laser-Scope</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <billingPeriod>MONTHLY</billingPeriod>
+                <recurringPrice>
+                    <price><currency>USD</currency><value>1999.95</value></price>                               
+                    <price><currency>EUR</currency><value>1499.95</value></price>
+                    <price><currency>GBP</currency><value>1999.95</value></price>
+                </recurringPrice>
+            </finalPhase>
+        </plan>
+        <plan name="extra-ammo-monthly">
+        <product>Extra-Ammo</product>
+            <finalPhase type="EVERGREEN">
+                <duration>
+                    <unit>UNLIMITED</unit>
+                </duration>
+                <billingPeriod>MONTHLY</billingPeriod>
+                <recurringPrice>
+                    <price><currency>USD</currency><value>1999.95</value></price>                               
+                    <price><currency>EUR</currency><value>1499.95</value></price>
+                    <price><currency>GBP</currency><value>1999.95</value></price>
+                </recurringPrice>
+            </finalPhase>
+        </plan>
 	</plans>
 	<priceLists>
 		<defaultPriceList name="DEFAULT">

entitlement/pom.xml 23(+22 -1)

diff --git a/entitlement/pom.xml b/entitlement/pom.xml
index 7e97852..f0d0aae 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.21-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-entitlement</artifactId>
@@ -61,6 +61,16 @@
             <artifactId>killbill-util</artifactId>
         </dependency>
         <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-account</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>joda-time</groupId>
             <artifactId>joda-time</artifactId>
         </dependency>
@@ -108,6 +118,17 @@
                     <groups>setup,fast</groups>
                 </configuration>
             </plugin>
+             <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
     <profiles>
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
index 4290f73..5730399 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/alignment/MigrationPlanAligner.java
@@ -48,11 +48,11 @@ public class MigrationPlanAligner {
 
         try {
             TimedMigration [] events = null;
-            Plan plan0 = catalogService.getCatalog().findPlan(input[0].getPlanPhaseSpecifer().getProductName(),
-                    input[0].getPlanPhaseSpecifer().getBillingPeriod(), input[0].getPlanPhaseSpecifer().getPriceListName());
+            Plan plan0 = catalogService.getFullCatalog().findPlan(input[0].getPlanPhaseSpecifer().getProductName(),
+                    input[0].getPlanPhaseSpecifer().getBillingPeriod(), input[0].getPlanPhaseSpecifer().getPriceListName(), now);
 
-            Plan plan1 = (input.length > 1) ? catalogService.getCatalog().findPlan(input[1].getPlanPhaseSpecifer().getProductName(),
-                    input[1].getPlanPhaseSpecifer().getBillingPeriod(), input[1].getPlanPhaseSpecifer().getPriceListName()) :
+            Plan plan1 = (input.length > 1) ? catalogService.getFullCatalog().findPlan(input[1].getPlanPhaseSpecifer().getProductName(),
+                    input[1].getPlanPhaseSpecifer().getBillingPeriod(), input[1].getPlanPhaseSpecifer().getPriceListName(), now) :
                         null;
 
             DateTime migrationStartDate = now;
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 541c57c..7553e0d 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
@@ -22,7 +22,6 @@ import com.ning.billing.catalog.api.*;
 import com.ning.billing.entitlement.api.user.EntitlementUserApiException;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionTransition;
-import com.ning.billing.entitlement.api.user.SubscriptionTransition.SubscriptionTransitionType;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
 import com.ning.billing.util.clock.DefaultClock;
 import org.joda.time.DateTime;
@@ -63,11 +62,11 @@ public class PlanAligner  {
      * @throws CatalogApiException
      * @throws EntitlementUserApiException
      */
-    public TimedPhase [] getCurrentAndNextTimedPhaseOnCreate(SubscriptionData subscription,
-            Plan plan, PhaseType initialPhase, String priceList, DateTime effectiveDate)
+    public TimedPhase [] getCurrentAndNextTimedPhaseOnCreate(final SubscriptionData subscription,
+            final Plan plan, final PhaseType initialPhase, final String priceList, final DateTime requestedDate, final DateTime effectiveDate)
         throws CatalogApiException, EntitlementUserApiException {
         List<TimedPhase> timedPhases = getTimedPhaseOnCreate(subscription.getStartDate(),
-                subscription.getBundleStartDate(), plan, initialPhase, priceList);
+                subscription.getBundleStartDate(), plan, initialPhase, priceList, requestedDate);
         TimedPhase [] result = new TimedPhase[2];
         result[0] = getTimedPhase(timedPhases, effectiveDate, WhichPhase.CURRENT);
         result[1] = getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
@@ -86,10 +85,10 @@ public class PlanAligner  {
      * @throws CatalogApiException
      * @throws EntitlementUserApiException
      */
-    public TimedPhase getCurrentTimedPhaseOnChange(SubscriptionData subscription,
-            Plan plan, String priceList, DateTime effectiveDate)
+    public TimedPhase getCurrentTimedPhaseOnChange(final SubscriptionData subscription,
+            final Plan plan, final String priceList, final DateTime requestedDate, final DateTime effectiveDate)
         throws CatalogApiException, EntitlementUserApiException {
-        return getTimedPhaseOnChange(subscription, plan, priceList, effectiveDate, WhichPhase.CURRENT);
+        return getTimedPhaseOnChange(subscription, plan, priceList, requestedDate, effectiveDate, WhichPhase.CURRENT);
     }
 
     /**
@@ -103,10 +102,10 @@ public class PlanAligner  {
      * @throws CatalogApiException
      * @throws EntitlementUserApiException
      */
-    public TimedPhase getNextTimedPhaseOnChange(SubscriptionData subscription,
-            Plan plan, String priceList, DateTime effectiveDate)
+    public TimedPhase getNextTimedPhaseOnChange(final SubscriptionData subscription,
+            final Plan plan, final String priceList, final DateTime requestedDate, final DateTime effectiveDate)
         throws CatalogApiException, EntitlementUserApiException {
-        return getTimedPhaseOnChange(subscription, plan, priceList, effectiveDate, WhichPhase.NEXT);
+        return getTimedPhaseOnChange(subscription, plan, priceList, requestedDate, effectiveDate, WhichPhase.NEXT);
     }
 
 
@@ -117,7 +116,7 @@ public class PlanAligner  {
      * @param effectiveDate the date at which we look to compute that event. effective needs to be after last Plan change or initial Plan
      * @return The PhaseEvent at the correct point in time
      */
-    public TimedPhase getNextTimedPhase(SubscriptionData subscription, DateTime effectiveDate) {
+    public TimedPhase getNextTimedPhase(final SubscriptionData subscription, final DateTime requestedDate, final DateTime effectiveDate) {
         try {
 
             SubscriptionTransition lastPlanTransition = subscription.getInitialTransitionForCurrentPlan();
@@ -133,7 +132,8 @@ public class PlanAligner  {
                         subscription.getBundleStartDate(),
                         lastPlanTransition.getNextPlan(),
                         lastPlanTransition.getNextPhase().getPhaseType(),
-                        lastPlanTransition.getNextPriceList());
+                        lastPlanTransition.getNextPriceList(),
+                        requestedDate);
                 return getTimedPhase(timedPhases, effectiveDate, WhichPhase.NEXT);
             // If we went through Plan changes, borrow the logics for changePlan alignement
             case CHANGE:
@@ -144,7 +144,9 @@ public class PlanAligner  {
                         lastPlanTransition.getPreviousPriceList(),
                         lastPlanTransition.getNextPlan(),
                         lastPlanTransition.getNextPriceList(),
-                        effectiveDate, WhichPhase.NEXT);
+                        requestedDate,
+                        effectiveDate,
+                        WhichPhase.NEXT);
             default:
                 throw new EntitlementError(String.format("Unexpectd initial transition %s for current plan %s on subscription %s",
                         lastPlanTransition.getTransitionType(), subscription.getCurrentPlan(), subscription.getId()));
@@ -154,12 +156,13 @@ public class PlanAligner  {
         }
     }
 
+
     private List<TimedPhase> getTimedPhaseOnCreate(DateTime subscriptionStartDate,
             DateTime bundleStartDate,
-            Plan plan, PhaseType initialPhase, String priceList)
+            Plan plan, PhaseType initialPhase, String priceList, DateTime requestedDate)
         throws CatalogApiException, EntitlementUserApiException  {
 
-        Catalog catalog = catalogService.getCatalog();
+        Catalog catalog = catalogService.getFullCatalog();
 
         PlanSpecifier planSpecifier = new PlanSpecifier(plan.getProduct().getName(),
                 plan.getProduct().getCategory(),
@@ -167,8 +170,7 @@ public class PlanAligner  {
                 priceList);
 
         DateTime planStartDate = null;
-        PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier);
-
+        PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, requestedDate);
         switch(alignement) {
         case START_OF_SUBSCRIPTION:
             planStartDate = subscriptionStartDate;
@@ -182,9 +184,8 @@ public class PlanAligner  {
         return getPhaseAlignments(plan, initialPhase, planStartDate);
     }
 
-
     private TimedPhase getTimedPhaseOnChange(SubscriptionData subscription,
-            Plan nextPlan, String nextPriceList, DateTime effectiveDate, WhichPhase which)
+            Plan nextPlan, String nextPriceList, DateTime requestedDate, DateTime effectiveDate, WhichPhase which)
         throws CatalogApiException, EntitlementUserApiException {
         return getTimedPhaseOnChange(subscription.getStartDate(),
                 subscription.getBundleStartDate(),
@@ -193,19 +194,21 @@ public class PlanAligner  {
                 subscription.getCurrentPriceList(),
                 nextPlan,
                 nextPriceList,
+                requestedDate,
                 effectiveDate,
                 which);
     }
 
+
     private TimedPhase getTimedPhaseOnChange(DateTime subscriptionStartDate,
             DateTime bundleStartDate,
             PlanPhase currentPhase,
             Plan currentPlan,
             String currentPriceList,
-            Plan nextPlan, String priceList, DateTime effectiveDate, WhichPhase which)
+            Plan nextPlan, String priceList, DateTime requestedDate, DateTime effectiveDate, WhichPhase which)
         throws CatalogApiException, EntitlementUserApiException {
 
-        Catalog catalog = catalogService.getCatalog();
+        Catalog catalog = catalogService.getFullCatalog();
         ProductCategory currentCategory = currentPlan.getProduct().getCategory();
         // STEPH tiered ADDON not implemented yet
         if (currentCategory != ProductCategory.BASE) {
@@ -226,7 +229,7 @@ public class PlanAligner  {
         DateTime planStartDate = null;
 
         PlanAlignmentChange alignment = null;
-        alignment = catalog.planChangeAlignment(fromPlanPhaseSpecifier, toPlanSpecifier);
+        alignment = catalog.planChangeAlignment(fromPlanPhaseSpecifier, toPlanSpecifier, requestedDate);
         switch(alignment) {
         case START_OF_SUBSCRIPTION:
             planStartDate = subscriptionStartDate;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
index 22fdd40..fd130a9 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/billing/DefaultEntitlementBillingApi.java
@@ -16,37 +16,111 @@
 
 package com.ning.billing.entitlement.api.billing;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.google.inject.Inject;
+import com.ning.billing.ErrorCode;
 import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.catalog.api.BillingAlignment;
+import com.ning.billing.catalog.api.Catalog;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PlanPhaseSpecifier;
+import com.ning.billing.catalog.api.Product;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
-import org.joda.time.DateTime;
-
-import java.util.List;
-import java.util.SortedSet;
-import java.util.UUID;
 
 public class DefaultEntitlementBillingApi implements EntitlementBillingApi {
-
+	private Logger log = LoggerFactory.getLogger(DefaultEntitlementBillingApi.class);
+	
     private final EntitlementDao dao;
+    private final AccountUserApi accountApi;
+    private final CatalogService catalogService;
 
     @Inject
-    public DefaultEntitlementBillingApi(EntitlementDao dao) {
+    public DefaultEntitlementBillingApi(EntitlementDao dao, AccountUserApi accountApi, CatalogService catalogService) {
         super();
         this.dao = dao;
+        this.accountApi = accountApi;
+        this.catalogService = catalogService;
     }
 
     @Override
-    public List<Account> getActiveAccounts() {
-        return null;
-    }
+    public SortedSet<BillingEvent> getBillingEventsForAccount(
+            UUID accountId) {
+        
+        List<SubscriptionBundle> bundles = dao.getSubscriptionBundleForAccount(accountId);
+        List<Subscription> subscriptions = new ArrayList<Subscription>();
+        for (SubscriptionBundle bundle: bundles) {
+            subscriptions.addAll(dao.getSubscriptions(bundle.getId()));
+        }
 
-    @Override
-    public SortedSet<BillingEvent> getBillingEventsForSubscription(
-            UUID subscriptionId) {
-        return null;
+        SortedSet<BillingEvent> result = new TreeSet<BillingEvent>();        
+        for (Subscription subscription: subscriptions) {
+        	for (SubscriptionTransition transition : subscription.getAllTransitions()) {
+        		try {
+        			result.add(new DefaultBillingEvent(transition, subscription, calculateBCD(transition, accountId)));
+        		} catch (CatalogApiException e) {
+        			log.error("Failing to identify catalog components while creating BillingEvent from transition: " + 
+        					transition.getId().toString(), e);
+        		}
+        	}
+        }
+        return result;
+    }
+    
+    private int calculateBCD(SubscriptionTransition transition, UUID accountId) throws CatalogApiException {
+    	Catalog catalog = catalogService.getFullCatalog();
+    	Plan plan = transition.getNextPlan();
+    	Product product = plan.getProduct();
+    	PlanPhase phase = transition.getNextPhase();
+    	
+    	BillingAlignment alignment = catalog.billingAlignment(
+    			new PlanPhaseSpecifier(product.getName(), 
+    					product.getCategory(), 
+    					phase.getBillingPeriod(), 
+    					transition.getNextPriceList(), 
+    					phase.getPhaseType()), 
+    					transition.getRequestedTransitionTime());
+    	int result = 0;
+    	Account account = accountApi.getAccountById(accountId);
+    	switch (alignment) {
+    		case ACCOUNT : 
+    			result = account.getBillCycleDay();
+    		break;
+    		case BUNDLE : 
+    			SubscriptionBundle bundle = dao.getSubscriptionBundleFromId(transition.getBundleId());
+    			//TODO result = bundle.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
+    			result = bundle.getStartDate().getDayOfMonth();
+    		break;
+    		case SUBSCRIPTION :
+    			Subscription subscription = dao.getSubscriptionFromId(transition.getSubscriptionId());
+    			//TODO result = subscription.getStartDate().toDateTime(account.getTimeZone()).getDayOfMonth();
+    			result = subscription.getStartDate().getDayOfMonth();
+    		break;
+    	}
+    	if(result == 0) {
+    		throw new CatalogApiException(ErrorCode.CAT_INVALID_BILLING_ALIGNMENT, alignment.toString());
+    	}
+    	return result;
+    		
     }
+    
 
     @Override
     public void setChargedThroughDate(UUID subscriptionId, DateTime ctd) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
index d8a0100..05a063d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/migration/DefaultEntitlementMigrationApi.java
@@ -111,7 +111,7 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
                     // Not implemented yet
                     break;
                 case STANDALONE:
-                    // Not implemented yet
+                    data = createStandaloneSubscriptionMigrationData(bundleData.getId(), curSub.getCategory(), curSub.getSubscriptionCases(), now);
                     break;
                 default:
                     throw new EntitlementMigrationApiException(String.format("Unkown product type ", curSub.getCategory()));
@@ -144,6 +144,22 @@ public class DefaultEntitlementMigrationApi implements EntitlementMigrationApi {
         return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, events));
     }
 
+    private SubscriptionMigrationData createStandaloneSubscriptionMigrationData(UUID bundleId, ProductCategory productCategory,
+            EntitlementSubscriptionMigrationCase [] input, DateTime now)
+    throws EntitlementMigrationApiException {
+        TimedMigration [] events = migrationAligner.getEventsMigration(input, now);
+        DateTime migrationStartDate= events[0].getEventTime();
+        List<EntitlementEvent> emptyEvents =  Collections.emptyList();
+        SubscriptionData subscriptionData = factory.createSubscription(new SubscriptionBuilder()
+            .setId(UUID.randomUUID())
+            .setBundleId(bundleId)
+            .setCategory(productCategory)
+            .setBundleStartDate(migrationStartDate)
+            .setStartDate(migrationStartDate),
+            emptyEvents);
+        return new SubscriptionMigrationData(subscriptionData, toEvents(subscriptionData, now, events));
+    }
+
     private List<EntitlementEvent> toEvents(SubscriptionData subscriptionData, DateTime now, TimedMigration [] migrationEvents) {
 
         List<EntitlementEvent> events = new ArrayList<EntitlementEvent>(migrationEvents.length);
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/test/DefaultEntitlementTestApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/test/DefaultEntitlementTestApi.java
index c2f5878..2843ec1 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/test/DefaultEntitlementTestApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/test/DefaultEntitlementTestApi.java
@@ -18,30 +18,50 @@ package com.ning.billing.entitlement.api.test;
 
 import com.google.inject.Inject;
 import com.ning.billing.config.EntitlementConfig;
-import com.ning.billing.entitlement.engine.core.EventNotifier;
+import com.ning.billing.entitlement.engine.core.Engine;
+import com.ning.billing.entitlement.exceptions.EntitlementError;
+import com.ning.billing.util.notificationq.NotificationQueue;
+import com.ning.billing.util.notificationq.NotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 import java.util.UUID;
 
 public class DefaultEntitlementTestApi implements EntitlementTestApi {
 
     private final static Logger log = LoggerFactory.getLogger(DefaultEntitlementTestApi.class);
 
-    private final EventNotifier apiEventProcessor;
     private final EntitlementConfig config;
+    private final NotificationQueueService notificationQueueService;
 
     @Inject
-    public DefaultEntitlementTestApi(EventNotifier apiEventProcessor, EntitlementConfig config) {
-        this.apiEventProcessor = apiEventProcessor;
+    public DefaultEntitlementTestApi(NotificationQueueService notificationQueueService, EntitlementConfig config) {
         this.config = config;
+        this.notificationQueueService = notificationQueueService;
     }
 
     @Override
     public void doProcessReadyEvents(UUID [] subscriptionsIds, Boolean recursive, Boolean oneEventOnly) {
+        if (recursive || oneEventOnly) {
+            throw new EntitlementError("Not implemented");
+        }
         if (config.isEventProcessingOff()) {
             log.warn("Running event processing loop");
-            apiEventProcessor.processAllReadyEvents(subscriptionsIds, recursive, oneEventOnly);
+            NotificationQueue queue = getNotificationQueue();
+            queue.processReadyNotification();
+        }
+    }
+
+    private NotificationQueue getNotificationQueue() {
+        try {
+            NotificationQueue subscritionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
+                    Engine.NOTIFICATION_QUEUE_NAME);
+            return subscritionEventQueue;
+        } catch (NoSuchNotificationQueue e) {
+            throw new RuntimeException(e);
         }
     }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
index 51010db..deedb6d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/DefaultEntitlementUserApi.java
@@ -107,7 +107,7 @@ public class DefaultEntitlementUserApi implements EntitlementUserApi {
             requestedDate = (requestedDate == null) ? now : requestedDate;
             DateTime effectiveDate = requestedDate;
 
-            Plan plan = catalogService.getCatalog().findPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList);
+            Plan plan = catalogService.getFullCatalog().findPlan(spec.getProductName(), spec.getBillingPeriod(), realPriceList, requestedDate);
 
 
             PlanPhase phase = plan.getAllPhases()[0];
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
index 548675b..1b95f6b 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionApiService.java
@@ -60,7 +60,7 @@ public class SubscriptionApiService {
 
             SubscriptionData subscription = new SubscriptionData(builder, this, clock);
 
-            TimedPhase [] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(subscription, plan, initialPhase, realPriceList, effectiveDate);
+            TimedPhase [] curAndNextPhases = planAligner.getCurrentAndNextTimedPhaseOnCreate(subscription, plan, initialPhase, realPriceList, requestedDate, effectiveDate);
             ApiEventCreate creationEvent = new ApiEventCreate(new ApiEventBuilder()
             .setSubscriptionId(subscription.getId())
             .setEventPlan(plan.getName())
@@ -81,7 +81,7 @@ public class SubscriptionApiService {
                 events.add(nextPhaseEvent);
             }
             dao.createSubscription(subscription, events);
-            subscription.rebuildTransitions(events, catalogService.getCatalog());
+            subscription.rebuildTransitions(events, catalogService.getFullCatalog());
             return subscription;
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
@@ -112,7 +112,7 @@ public class SubscriptionApiService {
                     subscription.getCurrentPhase().getPhaseType());
 
             ActionPolicy policy = null;
-            policy = catalogService.getCatalog().planCancelPolicy(planPhase);
+            policy = catalogService.getFullCatalog().planCancelPolicy(planPhase, requestedDate);
             DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, requestedDate);
 
             EntitlementEvent cancelEvent = new ApiEventCancel(new ApiEventBuilder()
@@ -123,7 +123,7 @@ public class SubscriptionApiService {
             .setRequestedDate(now));
 
             dao.cancelSubscription(subscription.getId(), cancelEvent);
-            subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getCatalog());
+            subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
         }
@@ -148,7 +148,7 @@ public class SubscriptionApiService {
         List<EntitlementEvent> uncancelEvents = new ArrayList<EntitlementEvent>();
         uncancelEvents.add(uncancelEvent);
 
-        TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now);
+        TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, now);
         PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
                 PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
                     null;
@@ -156,7 +156,7 @@ public class SubscriptionApiService {
             uncancelEvents.add(nextPhaseEvent);
         }
         dao.uncancelSubscription(subscription.getId(), uncancelEvents);
-        subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getCatalog());
+        subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
     }
 
     public void changePlan(SubscriptionData subscription, String productName, BillingPeriod term,
@@ -186,7 +186,7 @@ public class SubscriptionApiService {
             PlanChangeResult planChangeResult = null;
             try {
 
-                Product destProduct = catalogService.getCatalog().findProduct(productName);
+                Product destProduct = catalogService.getFullCatalog().findProduct(productName, requestedDate);
                 Plan currentPlan = subscription.getCurrentPlan();
                 PlanPhaseSpecifier fromPlanPhase = new PlanPhaseSpecifier(currentPlan.getProduct().getName(),
                         currentPlan.getProduct().getCategory(),
@@ -197,7 +197,7 @@ public class SubscriptionApiService {
                         term,
                         priceList);
 
-                planChangeResult = catalogService.getCatalog().planChange(fromPlanPhase, toPlanPhase);
+                planChangeResult = catalogService.getFullCatalog().planChange(fromPlanPhase, toPlanPhase, requestedDate);
             } catch (CatalogApiException e) {
                 throw new EntitlementUserApiException(e);
             }
@@ -205,10 +205,10 @@ public class SubscriptionApiService {
             ActionPolicy policy = planChangeResult.getPolicy();
             PriceList newPriceList = planChangeResult.getNewPriceList();
 
-            Plan newPlan = catalogService.getCatalog().findPlan(productName, term, newPriceList.getName());
+            Plan newPlan = catalogService.getFullCatalog().findPlan(productName, term, newPriceList.getName(), requestedDate);
             DateTime effectiveDate = subscription.getPlanChangeEffectiveDate(policy, now);
 
-            TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), effectiveDate);
+            TimedPhase currentTimedPhase = planAligner.getCurrentTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
 
             EntitlementEvent changeEvent = new ApiEventChange(new ApiEventBuilder()
             .setSubscriptionId(subscription.getId())
@@ -220,7 +220,7 @@ public class SubscriptionApiService {
             .setEffectiveDate(effectiveDate)
             .setRequestedDate(now));
 
-            TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), effectiveDate);
+            TimedPhase nextTimedPhase = planAligner.getNextTimedPhaseOnChange(subscription, newPlan, newPriceList.getName(), requestedDate, effectiveDate);
             PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
                     PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
                         null;
@@ -231,7 +231,7 @@ public class SubscriptionApiService {
                     }
                     changeEvents.add(changeEvent);
                     dao.changePlan(subscription.getId(), changeEvents);
-                    subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getCatalog());
+                    subscription.rebuildTransitions(dao.getEventsForSubscription(subscription.getId()), catalogService.getFullCatalog());
         } catch (CatalogApiException e) {
             throw new EntitlementUserApiException(e);
         }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
index 8789537..0cee49d 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionData.java
@@ -172,6 +172,18 @@ public class SubscriptionData implements Subscription {
     }
 
     @Override
+    public List<SubscriptionTransition> getAllTransitions() {
+        if (transitions == null) {
+            return Collections.emptyList();
+        }
+
+        List<SubscriptionTransition> result = new ArrayList<SubscriptionTransition>();
+        for (SubscriptionTransition cur : transitions) {
+            result.add(cur);
+               }
+        return result;
+    }
+
     public SubscriptionTransition getPendingTransition() {
         if (transitions == null) {
             return null;
@@ -323,11 +335,14 @@ public class SubscriptionData implements Subscription {
         String nextPriceList = null;
 
         SubscriptionState previousState = null;
-        String previousPlanName = null;
-        String previousPhaseName = null;
+        //String previousPlanName = null;
+        //String previousPhaseName = null;
         String previousPriceList = null;
 
         transitions = new LinkedList<SubscriptionTransitionData>();
+        Plan previousPlan = null;
+        PlanPhase previousPhase = null;
+        
         for (final EntitlementEvent cur : events) {
 
             if (!cur.isActive() || cur.getActiveVersion() < activeVersion) {
@@ -383,15 +398,12 @@ public class SubscriptionData implements Subscription {
                         cur.getType()));
             }
 
-            Plan previousPlan = null;
-            PlanPhase previousPhase = null;
+
             Plan nextPlan = null;
             PlanPhase nextPhase = null;
             try {
-                previousPlan = (previousPlanName != null) ? catalog.findPlan(previousPlanName) : null;
-                previousPhase = (previousPhaseName != null) ? catalog.findPhase(previousPhaseName) : null;
-                nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName) : null;
-                nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName) : null;
+                nextPlan = (nextPlanName != null) ? catalog.findPlan(nextPlanName, cur.getRequestedDate(), getStartDate()) : null;
+                nextPhase = (nextPhaseName != null) ? catalog.findPhase(nextPhaseName, cur.getRequestedDate(), getStartDate()) : null;
             } catch (CatalogApiException e) {
                 log.error(String.format("Failed to build transition for subscription %s", id), e);
             }
@@ -414,8 +426,8 @@ public class SubscriptionData implements Subscription {
             transitions.add(transition);
 
             previousState = nextState;
-            previousPlanName = nextPlanName;
-            previousPhaseName = nextPhaseName;
+            previousPlan = nextPlan;
+            previousPhase = nextPhase;
             previousPriceList = nextPriceList;
         }
     }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java
index 207b4b2..cef1b43 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/api/user/SubscriptionFactory.java
@@ -44,7 +44,7 @@ public class SubscriptionFactory {
     public SubscriptionData createSubscription(SubscriptionBuilder builder, List<EntitlementEvent> events) {
         SubscriptionData subscription = new SubscriptionData(builder, apiService, clock);
         if (events.size() > 0) {
-            subscription.rebuildTransitions(events, catalogService.getCatalog());
+            subscription.rebuildTransitions(events, catalogService.getFullCatalog());
         }
         return subscription;
     }
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 73e488b..cb37037 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,8 +16,10 @@
 
 package com.ning.billing.entitlement.engine.core;
 
+
 import java.util.Iterator;
 import java.util.List;
+import java.util.UUID;
 
 import org.joda.time.DateTime;
 import org.slf4j.Logger;
@@ -57,10 +59,16 @@ import com.ning.billing.lifecycle.LifecycleHandlerType.LifecycleLevel;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.eventbus.EventBus;
 import com.ning.billing.util.eventbus.EventBus.EventBusException;
+import com.ning.billing.util.notificationq.NotificationConfig;
+import com.ning.billing.util.notificationq.NotificationQueue;
+import com.ning.billing.util.notificationq.NotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotficationQueueAlreadyExists;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
 
 public class Engine implements EventListener, EntitlementService {
 
-    private static final String ENTITLEMENT_SERVICE_NAME = "entitlement-service";
+    public static final String NOTIFICATION_QUEUE_NAME = "subscription-events";
+    public static final String ENTITLEMENT_SERVICE_NAME = "entitlement-service";
 
     private final long MAX_NOTIFICATION_THREAD_WAIT_MS = 10000; // 10 secs
     private final long NOTIFICATION_THREAD_WAIT_INCREMENT_MS = 1000; // 1 sec
@@ -70,7 +78,6 @@ public class Engine implements EventListener, EntitlementService {
 
     private final Clock clock;
     private final EntitlementDao dao;
-    private final EventNotifier apiEventProcessor;
     private final PlanAligner planAligner;
     private final EntitlementUserApi userApi;
     private final EntitlementBillingApi billingApi;
@@ -78,28 +85,31 @@ public class Engine implements EventListener, EntitlementService {
     private final EntitlementMigrationApi migrationApi;
     private final AddonUtils addonUtils;
     private final EventBus eventBus;
+    private final EntitlementConfig config;
+    private final NotificationQueueService notificationQueueService;
 
     private boolean startedNotificationThread;
+    private boolean stoppedNotificationThread;
+    private NotificationQueue subscritionEventQueue;
 
     @Inject
-    public Engine(Clock clock, EntitlementDao dao, EventNotifier apiEventProcessor,
-            PlanAligner planAligner, EntitlementConfig config, DefaultEntitlementUserApi userApi,
+    public Engine(Clock clock, EntitlementDao dao, PlanAligner planAligner,
+            EntitlementConfig config, DefaultEntitlementUserApi userApi,
             DefaultEntitlementBillingApi billingApi, DefaultEntitlementTestApi testApi,
-            DefaultEntitlementMigrationApi migrationApi, AddonUtils addonUtils, EventBus eventBus) {
-
+            DefaultEntitlementMigrationApi migrationApi, AddonUtils addonUtils, EventBus eventBus,
+            NotificationQueueService notificationQueueService) {
         super();
         this.clock = clock;
         this.dao = dao;
-        this.apiEventProcessor = apiEventProcessor;
         this.planAligner = planAligner;
         this.userApi = userApi;
         this.testApi = testApi;
         this.billingApi = billingApi;
         this.migrationApi = migrationApi;
         this.addonUtils = addonUtils;
+        this.config = config;
         this.eventBus = eventBus;
-
-        this.startedNotificationThread = false;
+        this.notificationQueueService = notificationQueueService;
     }
 
     @Override
@@ -107,20 +117,75 @@ public class Engine implements EventListener, EntitlementService {
         return ENTITLEMENT_SERVICE_NAME;
     }
 
-
     @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
     public void initialize() {
+
+        try {
+            this.stoppedNotificationThread = false;
+            this.startedNotificationThread = false;
+            subscritionEventQueue = notificationQueueService.createNotificationQueue(ENTITLEMENT_SERVICE_NAME,
+                    NOTIFICATION_QUEUE_NAME,
+                    new NotificationQueueHandler() {
+                @Override
+                public void handleReadyNotification(String notificationKey) {
+                    EntitlementEvent event = dao.getEventById(UUID.fromString(notificationKey));
+                    if (event == null) {
+                        log.warn("Failed to extract event for notification key {}", notificationKey);
+                    } else {
+                        processEventReady(event);
+                    }
+                }
+
+                @Override
+                public void completedQueueStop() {
+                    synchronized (this) {
+                        stoppedNotificationThread = true;
+                        this.notifyAll();
+                    }
+                }
+                @Override
+                public void completedQueueStart() {
+                    synchronized (this) {
+                        startedNotificationThread = true;
+                        this.notifyAll();
+                    }
+                }
+            },
+            new NotificationConfig() {
+                @Override
+                public boolean isNotificationProcessingOff() {
+                    return config.isEventProcessingOff();
+                }
+                @Override
+                public long getNotificationSleepTimeMs() {
+                    return config.getNotificationSleepTimeMs();
+                }
+                @Override
+                public int getDaoMaxReadyEvents() {
+                    return config.getDaoMaxReadyEvents();
+                }
+                @Override
+                public long getDaoClaimTimeMs() {
+                    return config.getDaoMaxReadyEvents();
+                }
+            });
+        } catch (NotficationQueueAlreadyExists e) {
+            throw new RuntimeException(e);
+        }
     }
 
     @LifecycleHandlerType(LifecycleLevel.START_SERVICE)
     public void start() {
-        apiEventProcessor.startNotifications(this);
+        subscritionEventQueue.startQueue();
         waitForNotificationStartCompletion();
     }
 
     @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
     public void stop() {
-        apiEventProcessor.stopNotifications();
+        if (subscritionEventQueue != null) {
+            subscritionEventQueue.stopQueue();
+            waitForNotificationStopCompletion();
+        }
         startedNotificationThread = false;
     }
 
@@ -148,6 +213,9 @@ public class Engine implements EventListener, EntitlementService {
 
     @Override
     public void processEventReady(EntitlementEvent event) {
+        if (!event.isActive()) {
+            return;
+        }
         SubscriptionData subscription = (SubscriptionData) dao.getSubscriptionFromId(event.getSubscriptionId());
         if (subscription == null) {
             log.warn("Failed to retrieve subscription for id %s", event.getSubscriptionId());
@@ -169,23 +237,20 @@ public class Engine implements EventListener, EntitlementService {
         }
     }
 
-    //
-    // We want to ensure the notification thread is indeed started when we return from start()
-    //
-    @Override
-    public void completedNotificationStart() {
-        synchronized (this) {
-            startedNotificationThread = true;
-            this.notifyAll();
-        }
+    private void waitForNotificationStartCompletion() {
+        waitForNotificationEventCompletion(true);
     }
 
-    private void waitForNotificationStartCompletion() {
+    private void waitForNotificationStopCompletion() {
+        waitForNotificationEventCompletion(false);
+    }
+
+    private void waitForNotificationEventCompletion(boolean startEvent) {
 
         long ini = System.nanoTime();
         synchronized(this) {
             do {
-                if (startedNotificationThread) {
+                if ((startEvent ? startedNotificationThread : stoppedNotificationThread)) {
                     break;
                 }
                 try {
@@ -194,21 +259,25 @@ public class Engine implements EventListener, EntitlementService {
                     Thread.currentThread().interrupt();
                     throw new EntitlementError(e);
                 }
-            } while (!startedNotificationThread &&
+            } while (!(startEvent ? startedNotificationThread : stoppedNotificationThread) &&
                     (System.nanoTime() - ini) / NANO_TO_MS < MAX_NOTIFICATION_THREAD_WAIT_MS);
 
-            if (!startedNotificationThread) {
-                log.error("Could not start notification thread in %d msec !!!", MAX_NOTIFICATION_THREAD_WAIT_MS);
+            if (!(startEvent ? startedNotificationThread : stoppedNotificationThread)) {
+                log.error("Could not {} notification thread in {} msec !!!",
+                        (startEvent ? "start" : "stop"),
+                        MAX_NOTIFICATION_THREAD_WAIT_MS);
                 throw new EntitlementError("Failed to start service!!");
             }
-            log.info("Notification thread has been started in {} ms", (System.nanoTime() - ini) / NANO_TO_MS);
+            log.info("Notification thread has been {} in {} ms",
+                    (startEvent ? "started" : "stopped"),
+                    (System.nanoTime() - ini) / NANO_TO_MS);
         }
     }
 
     private void onPhaseEvent(SubscriptionData subscription) {
         try {
             DateTime now = clock.getUTCNow();
-            TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now);
+            TimedPhase nextTimedPhase = planAligner.getNextTimedPhase(subscription, now, now);
             PhaseEvent nextPhaseEvent = (nextTimedPhase != null) ?
                     PhaseEventData.getNextPhaseEvent(nextTimedPhase.getPhase().getName(), subscription, now, nextTimedPhase.getStartPhase()) :
                         null;
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java
index 7a84c51..e9962d8 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/core/EventListener.java
@@ -24,5 +24,4 @@ public interface EventListener {
 
     public void processEventReady(EntitlementEvent event);
 
-    public void completedNotificationStart();
 }
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 b118110..ea62b84 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
@@ -54,14 +54,12 @@ public interface EntitlementDao {
     // Event apis
     public void createNextPhaseEvent(UUID subscriptionId, EntitlementEvent nextPhase);
 
+    public EntitlementEvent getEventById(UUID eventId);
+
     public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId);
 
     public List<EntitlementEvent> getPendingEventsForSubscription(UUID subscriptionId);
 
-    public List<EntitlementEvent> getEventsReady(UUID ownerId, int sequenceId);
-
-    public void clearEventsReady(UUID ownerId, Collection<EntitlementEvent> cleared);
-
     // Subscription creation, cancellation, changePlan apis
     public void createSubscription(SubscriptionData subscription, List<EntitlementEvent> initialEvents);
 
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
index a987fe6..df3ee92 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EntitlementSqlDao.java
@@ -16,9 +16,14 @@
 
 package com.ning.billing.entitlement.engine.dao;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
 import com.google.inject.Inject;
 import com.ning.billing.catalog.api.ProductCategory;
-import com.ning.billing.config.EntitlementConfig;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.BundleMigrationData;
 import com.ning.billing.entitlement.api.migration.AccountMigrationData.SubscriptionMigrationData;
@@ -28,20 +33,26 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
 import com.ning.billing.entitlement.api.user.SubscriptionData;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
 import com.ning.billing.entitlement.events.user.ApiEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.entitlement.exceptions.EntitlementError;
-import com.ning.billing.util.Hostname;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.notificationq.NotificationKey;
+import com.ning.billing.util.notificationq.NotificationQueue;
+import com.ning.billing.util.notificationq.NotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
+
+import org.joda.time.DateTime;
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.*;
 
 public class EntitlementSqlDao implements EntitlementDao {
 
@@ -51,19 +62,17 @@ public class EntitlementSqlDao implements EntitlementDao {
     private final SubscriptionSqlDao subscriptionsDao;
     private final BundleSqlDao bundlesDao;
     private final EventSqlDao eventsDao;
-    private final EntitlementConfig config;
-    private final String hostname;
     private final SubscriptionFactory factory;
+    private final NotificationQueueService notificationQueueService;
 
     @Inject
-    public EntitlementSqlDao(DBI dbi, Clock clock, EntitlementConfig config, SubscriptionFactory factory) {
+    public EntitlementSqlDao(DBI dbi, Clock clock, SubscriptionFactory factory, NotificationQueueService notificationQueueService) {
         this.clock = clock;
-        this.config = config;
         this.factory = factory;
         this.subscriptionsDao = dbi.onDemand(SubscriptionSqlDao.class);
         this.eventsDao = dbi.onDemand(EventSqlDao.class);
         this.bundlesDao = dbi.onDemand(BundleSqlDao.class);
-        this.hostname = Hostname.get();
+        this.notificationQueueService = notificationQueueService;
     }
 
     @Override
@@ -140,11 +149,24 @@ public class EntitlementSqlDao implements EntitlementDao {
                     TransactionStatus status) throws Exception {
                 cancelNextPhaseEventFromTransaction(subscriptionId, dao);
                 dao.insertEvent(nextPhase);
+                recordFutureNotificationFromTransaction(dao,
+                        nextPhase.getEffectiveDate(),
+                        new NotificationKey() {
+                            @Override
+                            public String toString() {
+                                return nextPhase.getId().toString();
+                            }
+                        });
                 return null;
             }
         });
     }
 
+    @Override
+    public EntitlementEvent getEventById(UUID eventId) {
+        return eventsDao.getEventById(eventId.toString());
+    }
+
 
     @Override
     public List<EntitlementEvent> getEventsForSubscription(UUID subscriptionId) {
@@ -159,61 +181,6 @@ public class EntitlementSqlDao implements EntitlementDao {
         return results;
     }
 
-    @Override
-    public List<EntitlementEvent> getEventsReady(final UUID ownerId, final int sequenceId) {
-
-        final Date now = clock.getUTCNow().toDate();
-        final Date nextAvailable = clock.getUTCNow().plus(config.getDaoClaimTimeMs()).toDate();
-
-        log.debug(String.format("EntitlementDao getEventsReady START effectiveNow =  %s", now));
-
-        List<EntitlementEvent> events = eventsDao.inTransaction(new Transaction<List<EntitlementEvent>, EventSqlDao>() {
-
-            @Override
-            public List<EntitlementEvent> inTransaction(EventSqlDao dao,
-                    TransactionStatus status) throws Exception {
-
-                List<EntitlementEvent> claimedEvents = new ArrayList<EntitlementEvent>();
-                List<EntitlementEvent> input = dao.getReadyEvents(now, config.getDaoMaxReadyEvents());
-                for (EntitlementEvent cur : input) {
-                    final boolean claimed = (dao.claimEvent(ownerId.toString(), nextAvailable, cur.getId().toString(), now) == 1);
-                    if (claimed) {
-                        claimedEvents.add(cur);
-                        dao.insertClaimedHistory(sequenceId, ownerId.toString(), hostname, now, cur.getId().toString());
-                    }
-                }
-                return claimedEvents;
-            }
-        });
-
-        for (EntitlementEvent cur : events) {
-            log.debug(String.format("EntitlementDao %s [host %s] claimed events %s", ownerId, hostname, cur.getId()));
-            if (cur.getOwner() != null && !cur.getOwner().equals(ownerId)) {
-                log.warn(String.format("EventProcessor %s stealing event %s from %s", ownerId, cur, cur.getOwner()));
-            }
-        }
-        return events;
-    }
-
-    @Override
-    public void clearEventsReady(final UUID ownerId, final Collection<EntitlementEvent> cleared) {
-
-        log.debug(String.format("EntitlementDao clearEventsReady START cleared size = %d", cleared.size()));
-
-        eventsDao.inTransaction(new Transaction<Void, EventSqlDao>() {
-
-            @Override
-            public Void inTransaction(EventSqlDao dao,
-                    TransactionStatus status) throws Exception {
-                // STEPH Same here batch would nice
-                for (EntitlementEvent cur : cleared) {
-                    dao.clearEvent(cur.getId().toString(), ownerId.toString());
-                    log.debug(String.format("EntitlementDao %s [host %s] cleared events %s", ownerId, hostname, cur.getId()));
-                }
-                return null;
-            }
-        });
-    }
 
     @Override
     public void createSubscription(final SubscriptionData subscription,
@@ -228,8 +195,16 @@ public class EntitlementSqlDao implements EntitlementDao {
                 dao.insertSubscription(subscription);
                 // STEPH batch as well
                 EventSqlDao eventsDaoFromSameTranscation = dao.become(EventSqlDao.class);
-                for (EntitlementEvent cur : initialEvents) {
+                for (final EntitlementEvent cur : initialEvents) {
                     eventsDaoFromSameTranscation.insertEvent(cur);
+                    recordFutureNotificationFromTransaction(dao,
+                            cur.getEffectiveDate(),
+                            new NotificationKey() {
+                                @Override
+                                public String toString() {
+                                    return cur.getId().toString();
+                                }
+                            });
                 }
                 return null;
             }
@@ -247,6 +222,14 @@ public class EntitlementSqlDao implements EntitlementDao {
                 cancelNextChangeEventFromTransaction(subscriptionId, dao);
                 cancelNextPhaseEventFromTransaction(subscriptionId, dao);
                 dao.insertEvent(cancelEvent);
+                recordFutureNotificationFromTransaction(dao,
+                        cancelEvent.getEffectiveDate(),
+                        new NotificationKey() {
+                            @Override
+                            public String toString() {
+                                return cancelEvent.getId().toString();
+                            }
+                        });
                 return null;
             }
         });
@@ -276,8 +259,16 @@ public class EntitlementSqlDao implements EntitlementDao {
 
                 if (existingCancelId != null) {
                     dao.unactiveEvent(existingCancelId.toString(), now);
-                    for (EntitlementEvent cur : uncancelEvents) {
+                    for (final EntitlementEvent cur : uncancelEvents) {
                         dao.insertEvent(cur);
+                        recordFutureNotificationFromTransaction(dao,
+                                cur.getEffectiveDate(),
+                                new NotificationKey() {
+                            @Override
+                            public String toString() {
+                                return cur.getId().toString();
+                            }
+                        });
                     }
                 }
                 return null;
@@ -293,8 +284,16 @@ public class EntitlementSqlDao implements EntitlementDao {
                     TransactionStatus status) throws Exception {
                 cancelNextChangeEventFromTransaction(subscriptionId, dao);
                 cancelNextPhaseEventFromTransaction(subscriptionId, dao);
-                for (EntitlementEvent cur : changeEvents) {
+                for (final EntitlementEvent cur : changeEvents) {
                     dao.insertEvent(cur);
+                    recordFutureNotificationFromTransaction(dao,
+                            cur.getEffectiveDate(),
+                            new NotificationKey() {
+                        @Override
+                        public String toString() {
+                            return cur.getId().toString();
+                        }
+                    });
                 }
                 return null;
             }
@@ -367,8 +366,16 @@ public class EntitlementSqlDao implements EntitlementDao {
                     SubscriptionBundleData bundleData = curBundle.getData();
                     for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
                         SubscriptionData subData = curSubscription.getData();
-                        for (EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
+                        for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
                             transEventDao.insertEvent(curEvent);
+                            recordFutureNotificationFromTransaction(transEventDao,
+                                    curEvent.getEffectiveDate(),
+                                    new NotificationKey() {
+                                @Override
+                                public String toString() {
+                                    return curEvent.getId().toString();
+                                }
+                            });
                         }
                         transSubDao.insertSubscription(subData);
                     }
@@ -379,6 +386,7 @@ public class EntitlementSqlDao implements EntitlementDao {
         });
     }
 
+
     @Override
     public void undoMigration(final UUID accountId) {
 
@@ -407,4 +415,14 @@ public class EntitlementSqlDao implements EntitlementDao {
             transBundleDao.removeBundle(curBundle.getId().toString());
         }
     }
+
+    private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate, final NotificationKey notificationKey) {
+        try {
+            NotificationQueue subscritionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
+                Engine.NOTIFICATION_QUEUE_NAME);
+            subscritionEventQueue.recordFutureNotificationFromTransaction(transactionalDao, effectiveDate, notificationKey);
+        } catch (NoSuchNotificationQueue e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
index de61217..5f485e5 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/engine/dao/EventSqlDao.java
@@ -19,7 +19,6 @@ package com.ning.billing.entitlement.engine.dao;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
 import com.ning.billing.entitlement.events.EventBaseBuilder;
-import com.ning.billing.entitlement.events.EventLifecycle.EventLifecycleState;
 import com.ning.billing.entitlement.events.phase.PhaseEvent;
 import com.ning.billing.entitlement.events.phase.PhaseEventBuilder;
 import com.ning.billing.entitlement.events.phase.PhaseEventData;
@@ -49,43 +48,31 @@ import java.util.UUID;
 @ExternalizedSqlViaStringTemplate3()
 public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transmogrifier  {
 
-    //
-    // APIs for event notifications
-    //
     @SqlQuery
-    @Mapper(IEventSqlMapper.class)
-    public List<EntitlementEvent> getReadyEvents(@Bind("now") Date now, @Bind("max") int max);
+    @Mapper(EventSqlMapper.class)
+    public EntitlementEvent getEventById(@Bind("event_id") String eventId);
 
     @SqlUpdate
-    public int claimEvent(@Bind("owner") String owner, @Bind("next_available") Date nextAvailable, @Bind("event_id") String eventId, @Bind("now") Date now);
+    public void insertEvent(@Bind(binder = EventSqlDaoBinder.class) EntitlementEvent evt);
 
     @SqlUpdate
     public void removeEvents(@Bind("subscription_id") String subscriptionId);
 
     @SqlUpdate
-    public void clearEvent(@Bind("event_id") String eventId, @Bind("owner") String owner);
-
-    @SqlUpdate
-    public void insertEvent(@Bind(binder = IEventSqlDaoBinder.class) EntitlementEvent evt);
-
-    @SqlUpdate
-    public void insertClaimedHistory(@Bind("sequence_id") int sequenceId, @Bind("owner_id") String ownerId, @Bind("hostname") String hostname, @Bind("claimed_dt") Date clainedDate, @Bind("event_id") String eventId);
-
-    @SqlUpdate
     public void unactiveEvent(@Bind("event_id")String eventId, @Bind("now") Date now);
 
     @SqlUpdate
     public void reactiveEvent(@Bind("event_id")String eventId, @Bind("now") Date now);
 
     @SqlQuery
-    @Mapper(IEventSqlMapper.class)
+    @Mapper(EventSqlMapper.class)
     public List<EntitlementEvent> getFutureActiveEventForSubscription(@Bind("subscription_id") String subscriptionId, @Bind("now") Date now);
 
     @SqlQuery
-    @Mapper(IEventSqlMapper.class)
+    @Mapper(EventSqlMapper.class)
     public List<EntitlementEvent> getEventsForSubscription(@Bind("subscription_id") String subscriptionId);
 
-    public static class IEventSqlDaoBinder implements Binder<Bind, EntitlementEvent> {
+    public static class EventSqlDaoBinder implements Binder<Bind, EntitlementEvent> {
 
         private Date getDate(DateTime dateTime) {
             return dateTime == null ? null : dateTime.toDate();
@@ -106,13 +93,10 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
             stmt.bind("plist_name", (evt.getType() == EventType.API_USER) ? ((ApiEvent) evt).getPriceList() : null);
             stmt.bind("current_version", evt.getActiveVersion());
             stmt.bind("is_active", evt.isActive());
-            stmt.bind("processing_available_dt", getDate(evt.getNextAvailableDate()));
-            stmt.bind("processing_owner", (String) null);
-            stmt.bind("processing_state", EventLifecycleState.AVAILABLE.toString());
         }
     }
 
-    public static class IEventSqlMapper implements ResultSetMapper<EntitlementEvent> {
+    public static class EventSqlMapper implements ResultSetMapper<EntitlementEvent> {
 
         private DateTime getDate(ResultSet r, String fieldName) throws SQLException {
             final Timestamp resultStamp = r.getTimestamp(fieldName);
@@ -135,9 +119,6 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
             String priceListName = r.getString("plist_name");
             long currentVersion = r.getLong("current_version");
             boolean isActive = r.getBoolean("is_active");
-            DateTime nextAvailableDate = getDate(r, "processing_available_dt");
-            UUID processingOwner = (r.getString("processing_owner") != null) ? UUID.fromString(r.getString("processing_owner")) : null;
-            EventLifecycleState processingState = EventLifecycleState.valueOf(r.getString("processing_state"));
 
             EventBaseBuilder<?> base = ((eventType == EventType.PHASE) ?
                     new PhaseEventBuilder() :
@@ -148,11 +129,7 @@ public interface EventSqlDao extends Transactional<EventSqlDao>, CloseMe, Transm
                         .setEffectiveDate(effectiveDate)
                         .setProcessedDate(createdDate)
                         .setActiveVersion(currentVersion)
-                        .setActive(isActive)
-                        .setProcessingOwner(processingOwner)
-                        .setNextAvailableProcessingTime(nextAvailableDate)
-                        .setProcessingState(processingState);
-
+                        .setActive(isActive);
 
             EntitlementEvent result = null;
             if (eventType == EventType.PHASE) {
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/EntitlementEvent.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/EntitlementEvent.java
index af74752..b7bfece 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/EntitlementEvent.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/EntitlementEvent.java
@@ -21,7 +21,7 @@ import org.joda.time.DateTime;
 import java.util.UUID;
 
 
-public interface EntitlementEvent extends EventLifecycle, Comparable<EntitlementEvent> {
+public interface EntitlementEvent extends Comparable<EntitlementEvent> {
 
     public enum EventType {
         API_USER,
@@ -32,6 +32,16 @@ public interface EntitlementEvent extends EventLifecycle, Comparable<Entitlement
 
     public UUID getId();
 
+    public long getActiveVersion();
+
+    public void setActiveVersion(long activeVersion);
+
+    public boolean isActive();
+
+    public void deactivate();
+
+    public void reactivate();
+
     public DateTime getProcessedDate();
 
     public DateTime getRequestedDate();
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 93dc9e1..9420fbf 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
@@ -30,12 +30,8 @@ public abstract class EventBase implements EntitlementEvent {
     private final DateTime effectiveDate;
     private final DateTime processedDate;
 
-    // Lifecyle of the event
     private long activeVersion;
     private boolean isActive;
-    private UUID processingOwner;
-    private DateTime nextAvailableProcessingTime;
-    private EventLifecycleState processingState;
 
     public EventBase(EventBaseBuilder<?> builder) {
         this.uuid = builder.getUuid();
@@ -46,31 +42,17 @@ public abstract class EventBase implements EntitlementEvent {
 
         this.activeVersion = builder.getActiveVersion();
         this.isActive = builder.isActive();
-        this.processingOwner = builder.getProcessingOwner();
-        this.nextAvailableProcessingTime = builder.getNextAvailableProcessingTime();
-        this.processingState = builder.getProcessingState();
     }
 
     public EventBase(UUID subscriptionId, DateTime requestedDate,
             DateTime effectiveDate, DateTime processedDate,
             long activeVersion, boolean isActive) {
-        this(subscriptionId, requestedDate, effectiveDate, processedDate, activeVersion, isActive, null, null, EventLifecycleState.AVAILABLE);
-    }
-
-    private EventBase(UUID subscriptionId, DateTime requestedDate,
-            DateTime effectiveDate, DateTime processedDate,
-            long activeVersion, boolean isActive,
-            UUID processingOwner, DateTime nextAvailableProcessingTime,
-            EventLifecycleState processingState) {
-        this(UUID.randomUUID(), subscriptionId, requestedDate, effectiveDate, processedDate, activeVersion, isActive,
-                processingOwner, nextAvailableProcessingTime, processingState);
+        this(UUID.randomUUID(), subscriptionId, requestedDate, effectiveDate, processedDate, activeVersion, isActive);
     }
 
     public EventBase(UUID id, UUID subscriptionId, DateTime requestedDate,
             DateTime effectiveDate, DateTime processedDate,
-            long activeVersion, boolean isActive,
-            UUID processingOwner, DateTime nextAvailableProcessingTime,
-            EventLifecycleState processingState) {
+            long activeVersion, boolean isActive) {
         this.uuid = id;
         this.subscriptionId = subscriptionId;
         this.requestedDate = requestedDate;
@@ -79,10 +61,6 @@ public abstract class EventBase implements EntitlementEvent {
 
         this.activeVersion = activeVersion;
         this.isActive = isActive;
-        this.processingOwner = processingOwner;
-        this.nextAvailableProcessingTime = nextAvailableProcessingTime;
-        this.processingState = processingState;
-
     }
 
 
@@ -138,64 +116,9 @@ public abstract class EventBase implements EntitlementEvent {
     }
 
 
-    @Override
-    public UUID getOwner() {
-        return processingOwner;
-    }
-
-    @Override
-    public void setOwner(UUID owner) {
-        this.processingOwner = owner;
-    }
-
-    @Override
-    public DateTime getNextAvailableDate() {
-        return nextAvailableProcessingTime;
-    }
-
-    @Override
-    public void setNextAvailableDate(DateTime dateTime) {
-        this.nextAvailableProcessingTime = dateTime;
-    }
-
-
-    @Override
-    public EventLifecycleState getProcessingState() {
-        return processingState;
-    }
-
-    @Override
-    public void setProcessingState(EventLifecycleState processingState) {
-        this.processingState = processingState;
-    }
-
-    @Override
-    public boolean isAvailableForProcessing(DateTime now) {
-
-        // Event got deactivated, will never be processed
-        if (!isActive) {
-            return false;
-        }
-
-        switch(processingState) {
-        case AVAILABLE:
-            break;
-        case IN_PROCESSING:
-            // Somebody already got the event, not available yet
-            if (nextAvailableProcessingTime.isAfter(now)) {
-                return false;
-            }
-            break;
-        case PROCESSED:
-            return false;
-        default:
-            throw new EntitlementError(String.format("Unkwnon IEvent processing state %s", processingState));
-        }
-        return effectiveDate.isBefore(now);
-    }
 
     //
-    // Really used for unit tesrs only as the sql implementation relies on date first and then event insertion
+    // Really used for unit tests only as the sql implementation relies on date first and then event insertion
     //
     // Order first by:
     // - effectiveDate, followed by processedDate, requestedDate
diff --git a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBaseBuilder.java b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBaseBuilder.java
index 104fbef..17f5e15 100644
--- a/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBaseBuilder.java
+++ b/entitlement/src/main/java/com/ning/billing/entitlement/events/EventBaseBuilder.java
@@ -16,7 +16,6 @@
 
 package com.ning.billing.entitlement.events;
 
-import com.ning.billing.entitlement.events.EventLifecycle.EventLifecycleState;
 import org.joda.time.DateTime;
 
 import java.util.UUID;
@@ -32,15 +31,11 @@ public class EventBaseBuilder<T extends EventBaseBuilder<T>> {
 
     private long activeVersion;
     private boolean isActive;
-    private UUID processingOwner;
-    private DateTime nextAvailableProcessingTime;
-    private EventLifecycleState processingState;
 
 
     public EventBaseBuilder() {
         this.uuid = UUID.randomUUID();
         this.isActive = true;
-        this.processingState = EventLifecycleState.AVAILABLE;
     }
 
     public EventBaseBuilder(EventBaseBuilder<?> copy) {
@@ -52,9 +47,6 @@ public class EventBaseBuilder<T extends EventBaseBuilder<T>> {
 
         this.activeVersion = copy.activeVersion;
         this.isActive = copy.isActive;
-        this.processingOwner = copy.processingOwner;
-        this.nextAvailableProcessingTime = copy.nextAvailableProcessingTime;
-        this.processingState = copy.processingState;
     }
 
     public T setUuid(UUID uuid) {
@@ -92,21 +84,6 @@ public class EventBaseBuilder<T extends EventBaseBuilder<T>> {
         return (T) this;
     }
 
-    public T setProcessingOwner(UUID processingOwner) {
-        this.processingOwner = processingOwner;
-        return (T) this;
-    }
-
-    public T setNextAvailableProcessingTime(DateTime nextAvailableProcessingTime) {
-        this.nextAvailableProcessingTime = nextAvailableProcessingTime;
-        return (T) this;
-    }
-
-    public T setProcessingState(EventLifecycleState processingState) {
-        this.processingState = processingState;
-        return (T) this;
-    }
-
     public UUID getUuid() {
         return uuid;
     }
@@ -134,16 +111,4 @@ public class EventBaseBuilder<T extends EventBaseBuilder<T>> {
     public boolean isActive() {
         return isActive;
     }
-
-    public UUID getProcessingOwner() {
-        return processingOwner;
-    }
-
-    public DateTime getNextAvailableProcessingTime() {
-        return nextAvailableProcessingTime;
-    }
-
-    public EventLifecycleState getProcessingState() {
-        return processingState;
-    }
 }
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 3f033a4..2438ddf 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
@@ -58,17 +58,6 @@ public class ApiEventBase extends EventBase implements ApiEvent {
     }
 
 
-    public ApiEventBase(UUID id, UUID subscriptionId, DateTime processed, String eventPlan, String eventPhase,
-            String priceList, DateTime requestedDate,  ApiEventType eventType, DateTime effectiveDate, long activeVersion,
-            boolean isActive, UUID processingOwner, DateTime nextAvailableProcessingTime,EventLifecycleState processingState) {
-        super(id, subscriptionId, requestedDate, effectiveDate, processed, activeVersion, isActive, processingOwner, nextAvailableProcessingTime, processingState);
-        this.eventType = eventType;
-        this.eventPlan = eventPlan;
-        this.eventPlanPhase = eventPhase;
-        this.eventPriceList = priceList;
-    }
-
-
     @Override
     public ApiEventType getEventType() {
         return eventType;
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 1098c15..0135ebf 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
@@ -31,9 +31,7 @@ import com.ning.billing.entitlement.api.user.DefaultEntitlementUserApi;
 import com.ning.billing.entitlement.api.user.EntitlementUserApi;
 import com.ning.billing.entitlement.api.user.SubscriptionApiService;
 import com.ning.billing.entitlement.engine.addon.AddonUtils;
-import com.ning.billing.entitlement.engine.core.DefaultApiEventProcessor;
 import com.ning.billing.entitlement.engine.core.Engine;
-import com.ning.billing.entitlement.engine.core.EventNotifier;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.EntitlementSqlDao;
 import com.ning.billing.util.clock.Clock;
@@ -54,10 +52,6 @@ public class EntitlementModule extends AbstractModule {
         bind(EntitlementConfig.class).toInstance(config);
     }
 
-    protected void installApiEventProcessor() {
-        bind(EventNotifier.class).to(DefaultApiEventProcessor.class).asEagerSingleton();
-    }
-
     protected void installEntitlementDao() {
         bind(EntitlementDao.class).to(EntitlementSqlDao.class).asEagerSingleton();
     }
@@ -79,7 +73,6 @@ public class EntitlementModule extends AbstractModule {
     protected void configure() {
         installConfig();
         installClock();
-        installApiEventProcessor();
         installEntitlementDao();
         installEntitlementCore();
     }
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
index 4540ad8..55ad7f4 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/ddl.sql
@@ -14,24 +14,9 @@ CREATE TABLE events (
     plist_name varchar(64) DEFAULT NULL,
     current_version int(11) DEFAULT 1,
     is_active bool DEFAULT 1,
-    processing_owner char(36) DEFAULT NULL,
-    processing_available_dt datetime DEFAULT NULL,
-    processing_state varchar(14) DEFAULT 'AVAILABLE',
     PRIMARY KEY(id)
 ) ENGINE=innodb;
 
-DROP TABLE IF EXISTS claimed_events;
-CREATE TABLE claimed_events (
-    id int(11) unsigned NOT NULL AUTO_INCREMENT,    
-    sequence_id int(11) unsigned NOT NULL,    
-    owner_id char(36) NOT NULL,
-    hostname varchar(64) NOT NULL,
-    claimed_dt datetime NOT NULL,
-    event_id char(36) NOT NULL,
-    PRIMARY KEY(id)
-) ENGINE=innodb;
-
-
 DROP TABLE IF EXISTS subscriptions;
 CREATE TABLE subscriptions (
     id char(36) NOT NULL,
diff --git a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
index 31e9eaf..704e2c7 100644
--- a/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
+++ b/entitlement/src/main/resources/com/ning/billing/entitlement/engine/dao/EventSqlDao.sql.stg
@@ -1,8 +1,8 @@
 group EventSqlDao;
 
-getReadyEvents(now, max) ::= <<
-    select
-      event_id
+getEventById(event_id) ::= <<
+  select
+     event_id
       , event_type
       , user_type
       , created_dt
@@ -14,48 +14,11 @@ getReadyEvents(now, max) ::= <<
       , phase_name
       , plist_name
       , current_version
-      , is_active
-      , processing_owner
-      , processing_available_dt
-      , processing_state
-    from events
-    where
-      effective_dt \<= :now
-      and is_active = 1
-      and processing_state != 'PROCESSED'
-      and (processing_owner IS NULL OR processing_available_dt \<= :now)
-    order by
-      effective_dt asc
-      , created_dt asc
-      , requested_dt asc
-      , id asc
-    limit :max
-    ;
->>
-
-claimEvent(owner, next_available, event_id, now) ::= <<
-    update events
-    set
-      processing_owner = :owner
-      , processing_available_dt = :next_available
-      , processing_state = 'IN_PROCESSING'
-    where
-      event_id = :event_id
-      and is_active = 1
-      and processing_state != 'PROCESSED'
-      and (processing_owner IS NULL OR processing_available_dt \<= :now)
-    ;
->>
-
-clearEvent(event_id, owner) ::= <<
-    update events
-    set
-      processing_owner = NULL
-      , processing_state = 'PROCESSED'
-    where
+      , is_active  
+  from events
+  where
       event_id = :event_id
-      and processing_owner = :owner
-    ;
+  ;
 >>
 
 insertEvent() ::= <<
@@ -73,9 +36,6 @@ insertEvent() ::= <<
       , plist_name
       , current_version
       , is_active
-      , processing_owner
-      , processing_available_dt
-      , processing_state
     ) values (
       :event_id
       , :event_type
@@ -90,9 +50,6 @@ insertEvent() ::= <<
       , :plist_name
       , :current_version
       , :is_active
-      , :processing_owner
-      , :processing_available_dt
-      , :processing_state
     );   
 >>
 
@@ -103,22 +60,6 @@ removeEvents(subscription_id) ::= <<
     ;
 >>
 
-insertClaimedHistory(sequence_id, owner_id, hostname, claimed_dt, event_id) ::= <<
-    insert into claimed_events (
-        sequence_id
-        , owner_id
-        , hostname
-        , claimed_dt
-        , event_id
-      ) values (
-        :sequence_id
-        , :owner_id
-        , :hostname
-        , :claimed_dt
-        , :event_id
-      );
->>
-
 unactiveEvent(event_id, now) ::= <<
     update events
     set
@@ -154,9 +95,6 @@ getFutureActiveEventForSubscription(subscription_id, now) ::= <<
       , plist_name
       , current_version
       , is_active
-      , processing_owner
-      , processing_available_dt
-      , processing_state    
     from events
     where
       subscription_id = :subscription_id
@@ -185,9 +123,6 @@ getEventsForSubscription(subscription_id) ::= <<
       , plist_name
       , current_version
       , is_active
-      , processing_owner
-      , processing_available_dt
-      , processing_state       
     from events
     where
       subscription_id = :subscription_id
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java
new file mode 100644
index 0000000..cc08699
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccount.java
@@ -0,0 +1,154 @@
+/*
+ * 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.entitlement.api.billing;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
+import com.ning.billing.util.tag.TagDescription;
+
+public class BrainDeadAccount implements Account {
+
+	@Override
+	public String getExternalKey() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public String getName() {
+		throw new UnsupportedOperationException();	}
+
+	@Override
+	public int getFirstNameLength() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public String getEmail() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public String getPhone() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public int getBillCycleDay() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public Currency getCurrency() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public String getPaymentProviderName() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public String getFieldValue(String fieldName) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void setFieldValue(String fieldName, String fieldValue) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public List<CustomField> getFieldList() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void clearFields() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public String getObjectName() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public UUID getId() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public List<Tag> getTagList() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public boolean hasTag(String tagName) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void addTag(TagDescription description, String addedBy,
+			DateTime dateAdded) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void addTags(List<Tag> tags) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void clearTags() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void removeTag(TagDescription description) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public boolean generateInvoice() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public boolean processPayment() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public BigDecimal getBalance() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void addFields(List<CustomField> fields) {
+		throw new UnsupportedOperationException();
+		
+	}
+
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java
new file mode 100644
index 0000000..94f0ea7
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadAccountUserApi.java
@@ -0,0 +1,63 @@
+/*
+ * 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.entitlement.api.billing;
+
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountApiException;
+import com.ning.billing.account.api.AccountData;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.util.customfield.CustomField;
+import com.ning.billing.util.tag.Tag;
+
+public class BrainDeadAccountUserApi implements AccountUserApi {
+
+
+	@Override
+	public Account getAccountByKey(String key) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public Account getAccountById(UUID accountId) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public List<Account> getAccounts() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public UUID getIdFromKey(String externalKey) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public Account createAccount(AccountData data, List<CustomField> fields,
+			List<Tag> tags) throws AccountApiException {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void updateAccount(Account account) {
+		throw new UnsupportedOperationException();
+	}
+
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadMockEntitlementDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadMockEntitlementDao.java
new file mode 100644
index 0000000..94fd234
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/BrainDeadMockEntitlementDao.java
@@ -0,0 +1,141 @@
+/*
+ * 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.entitlement.api.billing;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.UUID;
+
+import com.ning.billing.entitlement.api.migration.AccountMigrationData;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent;
+
+class BrainDeadMockEntitlementDao implements EntitlementDao {
+
+	@Override
+	public List<SubscriptionBundle> getSubscriptionBundleForAccount(
+			UUID accountId) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public SubscriptionBundle getSubscriptionBundleFromKey(String bundleKey) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public SubscriptionBundle createSubscriptionBundle(
+			SubscriptionBundleData bundle) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public Subscription getSubscriptionFromId(UUID subscriptionId) {
+		throw new UnsupportedOperationException();
+
+	}
+
+	@Override
+	public Subscription getBaseSubscription(UUID bundleId) {
+		throw new UnsupportedOperationException();
+
+	}
+
+	@Override
+	public List<Subscription> getSubscriptions(UUID bundleId) {
+		throw new UnsupportedOperationException();
+
+	}
+
+	@Override
+	public List<Subscription> getSubscriptionsForKey(String bundleKey) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void updateSubscription(SubscriptionData subscription) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void createNextPhaseEvent(UUID subscriptionId,
+			EntitlementEvent nextPhase) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public List<EntitlementEvent> getEventsForSubscription(
+			UUID subscriptionId) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public List<EntitlementEvent> getPendingEventsForSubscription(
+			UUID subscriptionId) {
+		throw new UnsupportedOperationException();
+	}
+
+
+	@Override
+	public void createSubscription(SubscriptionData subscription,
+			List<EntitlementEvent> initialEvents) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void cancelSubscription(UUID subscriptionId,
+			EntitlementEvent cancelEvent) {
+		throw new UnsupportedOperationException();
+
+	}
+
+	@Override
+	public void uncancelSubscription(UUID subscriptionId,
+			List<EntitlementEvent> uncancelEvents) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void changePlan(UUID subscriptionId,
+			List<EntitlementEvent> changeEvents) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void migrate(UUID acountId, AccountMigrationData data) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public void undoMigration(UUID accountId) {
+        throw new UnsupportedOperationException();
+	}
+
+    @Override
+    public EntitlementEvent getEventById(UUID eventId) {
+        throw new UnsupportedOperationException();
+    }
+}
\ No newline at end of file
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
new file mode 100644
index 0000000..08b05d6
--- /dev/null
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/billing/TestDefaultEntitlementBillingApi.java
@@ -0,0 +1,281 @@
+/*
+ * 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.entitlement.api.billing;
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Stage;
+import com.ning.billing.account.api.Account;
+import com.ning.billing.account.api.AccountUserApi;
+import com.ning.billing.catalog.DefaultCatalogService;
+import com.ning.billing.catalog.api.CatalogApiException;
+import com.ning.billing.catalog.api.CatalogService;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
+import com.ning.billing.catalog.api.PriceListSet;
+import com.ning.billing.catalog.glue.CatalogModule;
+import com.ning.billing.entitlement.api.TestApiBase;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.Subscription.SubscriptionState;
+import com.ning.billing.entitlement.api.user.SubscriptionBundle;
+import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.api.user.SubscriptionTransition;
+import com.ning.billing.entitlement.api.user.SubscriptionTransitionData;
+import com.ning.billing.entitlement.engine.dao.EntitlementDao;
+import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
+import com.ning.billing.entitlement.events.user.ApiEventType;
+import com.ning.billing.lifecycle.KillbillService.ServiceException;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.DefaultClock;
+
+public class TestDefaultEntitlementBillingApi {
+	private static final UUID zeroId = new UUID(0L,0L);
+	private static final UUID oneId = new UUID(1L,0L);
+	private static final UUID twoId = new UUID(2L,0L);
+	
+	private CatalogService catalogService;
+	private ArrayList<SubscriptionBundle> bundles;
+	private ArrayList<Subscription> subscriptions;
+	private ArrayList<SubscriptionTransition> transitions;
+	private BrainDeadMockEntitlementDao dao;
+
+	private Clock clock;
+	private SubscriptionData subscription;
+	private DateTime subscriptionStartDate;
+
+	@BeforeClass(groups={"setup"})
+	public void setup() throws ServiceException {
+		TestApiBase.loadSystemPropertiesFromClasspath("/entitlement.properties");
+        final Injector g = Guice.createInjector(Stage.PRODUCTION, new CatalogModule(), new AbstractModule() {
+			protected void configure() {
+				 bind(Clock.class).to(DefaultClock.class).asEagerSingleton();
+			}  	
+        });
+
+        
+        catalogService = g.getInstance(CatalogService.class);
+        clock = g.getInstance(Clock.class);
+        
+        ((DefaultCatalogService)catalogService).loadCatalog();
+	}
+	
+	@BeforeMethod
+	public void setupEveryTime() {
+		bundles = new ArrayList<SubscriptionBundle>();
+		final SubscriptionBundle bundle = new SubscriptionBundleData( zeroId,"TestKey", oneId,  new DateTime().minusDays(4));
+		bundles.add(bundle);
+		
+		
+		transitions = new ArrayList<SubscriptionTransition>();
+		
+		
+		subscriptions = new ArrayList<Subscription>();
+		
+		SubscriptionBuilder builder = new SubscriptionBuilder();
+		subscriptionStartDate = new DateTime().minusDays(3);
+		builder.setStartDate(subscriptionStartDate);
+		subscription = new SubscriptionData(builder) {
+		    public List<SubscriptionTransition> getAllTransitions() {
+		    	return transitions;
+		    }
+		};
+
+		subscriptions.add(subscription);
+		
+		dao = new BrainDeadMockEntitlementDao() {
+			public List<SubscriptionBundle> getSubscriptionBundleForAccount(
+					UUID accountId) {
+				return bundles;
+				
+			}
+
+			public List<Subscription> getSubscriptions(UUID bundleId) {
+				return subscriptions;
+			}
+
+			public Subscription getSubscriptionFromId(UUID subscriptionId) {
+				return subscription;
+
+			}
+			
+			@Override
+			public SubscriptionBundle getSubscriptionBundleFromId(UUID bundleId) {
+				return bundle;
+			}
+
+
+		};
+
+	}
+	
+	@Test
+	public void testBillingEventsEmpty() {
+		EntitlementDao dao = new BrainDeadMockEntitlementDao() {
+			public List<SubscriptionBundle> getSubscriptionBundleForAccount(
+					UUID accountId) {
+				return new ArrayList<SubscriptionBundle>();
+			}
+			
+		};
+		AccountUserApi accountApi = new BrainDeadAccountUserApi() ;
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
+		Assert.assertEquals(events.size(), 0);
+	}
+	
+	@Test
+	public void testBillingEventsNoBillingPeriod() throws CatalogApiException {
+		DateTime now = clock.getUTCNow();
+		DateTime then = now.minusDays(1);
+		Plan nextPlan = catalogService.getFullCatalog().findPlan("shotgun-annual", now);
+		PlanPhase nextPhase = nextPlan.getAllPhases()[0]; // The trial has no billing period
+		String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+		SubscriptionTransition t = new SubscriptionTransitionData(
+				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
+		transitions.add(t);
+		
+		AccountUserApi accountApi = new BrainDeadAccountUserApi(){
+
+			@Override
+			public Account getAccountById(UUID accountId) {
+				return new BrainDeadAccount(){@Override
+				public int getBillCycleDay() {
+					return 32;
+				}};
+			}} ;
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
+		checkFirstEvent(events, nextPlan, 32, oneId, now, nextPhase, ApiEventType.CREATE.toString());
+	}
+
+	@Test
+	public void testBillingEventsAnual() throws CatalogApiException {
+		DateTime now = clock.getUTCNow();
+		DateTime then = now.minusDays(1);
+		Plan nextPlan = catalogService.getFullCatalog().findPlan("shotgun-annual", now);
+		PlanPhase nextPhase = nextPlan.getAllPhases()[1];
+		String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+		SubscriptionTransition t = new SubscriptionTransitionData(
+				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
+		transitions.add(t);
+		
+		AccountUserApi accountApi = new BrainDeadAccountUserApi(){
+
+			@Override
+			public Account getAccountById(UUID accountId) {
+				return new BrainDeadAccount(){@Override
+				public int getBillCycleDay() {
+					return 1;
+				}};
+			}} ;
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
+		checkFirstEvent(events, nextPlan, subscription.getStartDate().getDayOfMonth(), oneId, now, nextPhase, ApiEventType.CREATE.toString());
+	}
+	
+	@Test
+	public void testBillingEventsMonthly() throws CatalogApiException {
+		DateTime now = clock.getUTCNow();
+		DateTime then = now.minusDays(1);
+		Plan nextPlan = catalogService.getFullCatalog().findPlan("shotgun-annual", now);
+		PlanPhase nextPhase = nextPlan.getAllPhases()[1];
+		String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+		SubscriptionTransition t = new SubscriptionTransitionData(
+				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
+		transitions.add(t);
+		
+		AccountUserApi accountApi = new BrainDeadAccountUserApi(){
+
+			@Override
+			public Account getAccountById(UUID accountId) {
+				return new BrainDeadAccount(){@Override
+				public int getBillCycleDay() {
+					return 32;
+				}};
+			}} ;
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
+		checkFirstEvent(events, nextPlan, 32, oneId, now, nextPhase, ApiEventType.CREATE.toString());
+	}
+	
+	@Test
+	public void testBillingEventsAddOn() throws CatalogApiException {
+		DateTime now = clock.getUTCNow();
+		DateTime then = now.minusDays(1);
+		Plan nextPlan = catalogService.getFullCatalog().findPlan("shotgun-annual", now);
+		PlanPhase nextPhase = nextPlan.getAllPhases()[0];
+		String nextPriceList = PriceListSet.DEFAULT_PRICELIST_NAME;
+		SubscriptionTransition t = new SubscriptionTransitionData(
+				zeroId, oneId, twoId, EventType.API_USER, ApiEventType.CREATE, then, now, null, null, null, null, SubscriptionState.ACTIVE, nextPlan, nextPhase, nextPriceList);
+		transitions.add(t);
+		
+		AccountUserApi accountApi = new BrainDeadAccountUserApi(){
+
+			@Override
+			public Account getAccountById(UUID accountId) {
+				return new BrainDeadAccount(){@Override
+				public int getBillCycleDay() {
+					return 1;
+				}};
+			}} ;
+		DefaultEntitlementBillingApi api = new DefaultEntitlementBillingApi(dao,accountApi,catalogService);
+		SortedSet<BillingEvent> events = api.getBillingEventsForAccount(new UUID(0L,0L));
+		checkFirstEvent(events, nextPlan, bundles.get(0).getStartDate().getDayOfMonth(), oneId, now, nextPhase, ApiEventType.CREATE.toString());
+	}
+
+
+	private void checkFirstEvent(SortedSet<BillingEvent> events, Plan nextPlan,
+			int BCD, UUID id, DateTime time, PlanPhase nextPhase, String desc) throws CatalogApiException {
+		Assert.assertEquals(events.size(), 1);
+		BillingEvent event = events.first();
+		if(nextPhase.getFixedPrice() != null) {
+			Assert.assertEquals(nextPhase.getFixedPrice().getPrice(Currency.USD), event.getFixedPrice().getPrice(Currency.USD));
+		}
+		if(nextPhase.getRecurringPrice() != null) {
+			Assert.assertEquals(nextPhase.getRecurringPrice().getPrice(Currency.USD), event.getRecurringPrice().getPrice(Currency.USD));
+		}
+		
+		Assert.assertEquals(BCD, event.getBillCycleDay());
+		Assert.assertEquals(id, event.getSubscription().getId());
+		Assert.assertEquals(time, event.getEffectiveDate());
+		Assert.assertEquals(nextPhase, event.getPlanPhase());
+		Assert.assertEquals(nextPlan, event.getPlan());
+		Assert.assertEquals(nextPhase.getBillingPeriod(), event.getBillingPeriod());
+		Assert.assertEquals(BillingModeType.IN_ADVANCE, event.getBillingMode());
+		Assert.assertEquals(desc, event.getDescription());
+		Assert.assertEquals(nextPhase.getFixedPrice(), event.getFixedPrice());
+		Assert.assertEquals(nextPhase.getRecurringPrice(), event.getRecurringPrice());
+	}
+	
+	
+}
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
index e4cc948..30df1f9 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/TestApiBase.java
@@ -93,7 +93,7 @@ public abstract class TestApiBase {
     protected ApiTestListener testListener;
     protected SubscriptionBundle bundle;
 
-    public static void loadSystemPropertiesFromClasspath( final String resource )
+    public static void loadSystemPropertiesFromClasspath(final String resource)
     {
         final URL url = TestApiBase.class.getResource(resource);
         assertNotNull(url);
@@ -147,7 +147,7 @@ public abstract class TestApiBase {
         accountData = getAccountData();
         assertNotNull(accountData);
 
-        catalog = catalogService.getCatalog();
+        catalog = catalogService.getFullCatalog();
         assertNotNull(catalog);
 
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
index d7b9bf4..4b251f4 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiAddOn.java
@@ -237,7 +237,7 @@ public class TestUserApiAddOn extends TestApiBase {
                     ProductCategory.ADD_ON,
                     aoTerm,
                     aoPriceList);
-            PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier);
+            PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, clock.getUTCNow());
             assertEquals(alignement, PlanAlignmentCreate.START_OF_BUNDLE);
 
             testAddonCreateInternal(aoProduct, aoTerm, aoPriceList, alignement);
@@ -269,7 +269,7 @@ public class TestUserApiAddOn extends TestApiBase {
                     ProductCategory.ADD_ON,
                     aoTerm,
                     aoPriceList);
-            PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier);
+            PlanAlignmentCreate alignement = catalog.planCreateAlignment(planSpecifier, clock.getUTCNow());
             assertEquals(alignement, PlanAlignmentCreate.START_OF_SUBSCRIPTION);
 
             testAddonCreateInternal(aoProduct, aoTerm, aoPriceList, alignement);
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
index 187c080..87491c7 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCancelSql.java
@@ -32,7 +32,7 @@ public class TestUserApiCancelSql extends TestUserApiCancel {
         return Guice.createInjector(Stage.DEVELOPMENT, new MockEngineModuleSql());
     }
 
-    @Test(enabled= true, groups={"stress"})
+    @Test(enabled= false, groups={"stress"})
     public void stressTest() {
         for (int i = 0; i < MAX_STRESS_ITERATIONS; i++) {
             cleanupTest();
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 ab32386..2c17255 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
@@ -198,6 +198,15 @@ public abstract class TestUserApiChangePlan extends TestApiBase {
             testListener.pushExpectedEvent(NextEvent.PHASE);
             clock.addDeltaFromReality(currentPhase.getDuration());
             DateTime futureNow = clock.getUTCNow();
+
+            /*
+            try {
+                Thread.sleep(1000 * 3000);
+            } catch (Exception e) {
+
+            }
+            */
+
             assertTrue(futureNow.isAfter(nextExpectedPhaseChange));
             assertTrue(testListener.isCompleted(3000));
 
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
index ff55ca0..d5546d7 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiCreate.java
@@ -23,6 +23,8 @@ import static org.testng.Assert.assertTrue;
 
 import java.util.List;
 import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.testng.Assert;
 
 import com.ning.billing.catalog.api.BillingPeriod;
@@ -38,7 +40,7 @@ import com.ning.billing.entitlement.events.phase.PhaseEvent;
 import com.ning.billing.util.clock.DefaultClock;
 
 public abstract class TestUserApiCreate extends TestApiBase {
-
+	Logger log = LoggerFactory.getLogger(TestUserApiCreate.class);
 
     public void testCreateWithRequestedDate() {
         log.info("Starting testCreateWithRequestedDate");
@@ -68,6 +70,7 @@ public abstract class TestUserApiCreate extends TestApiBase {
             assertTrue(testListener.isCompleted(5000));
 
         } catch (EntitlementUserApiException e) {
+        	log.error("Unexpected exception",e);
             Assert.fail(e.getMessage());
         }
     }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
index 9917228..81ee501 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/api/user/TestUserApiError.java
@@ -50,17 +50,16 @@ public class TestUserApiError extends TestApiBase {
     @Test(enabled=true, groups={"fast"})
     public void testCreateSubscriptionBadCatalog() {
         // WRONG PRODUTCS
-        tCreateSubscriptionInternal(bundle.getId(), null, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NO_SUCH_PRODUCT);
+        tCreateSubscriptionInternal(bundle.getId(), null, BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NULL_PRODUCT_NAME);
         tCreateSubscriptionInternal(bundle.getId(), "Whatever", BillingPeriod.ANNUAL, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_NO_SUCH_PRODUCT);
 
 
         // TODO: MARTIN TO FIX WITH CORRECT ERROR CODE. RIGHT NOW NPE
 
         // WRONG BILLING PERIOD
-        //tCreateSubscriptionInternal(bundle.getId(), "Shotgun", null, IPriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.);
+        tCreateSubscriptionInternal(bundle.getId(), "Shotgun", null, PriceListSet.DEFAULT_PRICELIST_NAME, ErrorCode.CAT_PLAN_NOT_FOUND);
         // WRONG PLAN SET
-        //tCreateSubscriptionInternal(bundle.getId(), "Shotgun", BillingPeriod.ANNUAL, null, ErrorCode.);
-        //tCreateSubscriptionInternal(bundle.getId(), "Shotgun", BillingPeriod.ANNUAL, "Whatever", ErrorCode.);
+        tCreateSubscriptionInternal(bundle.getId(), "Shotgun", BillingPeriod.ANNUAL, "Whatever", ErrorCode.CAT_PRICE_LIST_NOT_FOUND);
 
     }
 
@@ -118,7 +117,7 @@ public class TestUserApiError extends TestApiBase {
             entitlementApi.createSubscription(bundleId,
                     getProductSpecifier(productName, planSet, term, null),
                     clock.getUTCNow());
-            assertFalse(true);
+            Assert.fail("Exception expected, error code: " + expected);
         } catch (EntitlementUserApiException e) {
             assertEquals(e.getCode(), expected.getCode());
             try {
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDao.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDao.java
index 74ac1e7..3623da2 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDao.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDao.java
@@ -17,5 +17,6 @@
 package com.ning.billing.entitlement.engine.dao;
 
 public interface MockEntitlementDao {
+
     public void reset();
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
index 7fc8342..86e458f 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoMemory.java
@@ -31,16 +31,26 @@ import com.ning.billing.entitlement.api.user.SubscriptionBundleData;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory;
 
 import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
+import com.ning.billing.entitlement.engine.core.Engine;
 import com.ning.billing.entitlement.events.EntitlementEvent;
 import com.ning.billing.entitlement.events.EntitlementEvent.EventType;
-import com.ning.billing.entitlement.events.EventLifecycle.EventLifecycleState;
 import com.ning.billing.entitlement.events.user.ApiEvent;
 import com.ning.billing.entitlement.events.user.ApiEventType;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.notificationq.NotificationKey;
+import com.ning.billing.util.notificationq.NotificationLifecycle;
+import com.ning.billing.util.notificationq.NotificationQueue;
+import com.ning.billing.util.notificationq.NotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService.NoSuchNotificationQueue;
+
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.*;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 
 public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlementDao {
 
@@ -52,15 +62,15 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     private final Clock clock;
     private final EntitlementConfig config;
     private final SubscriptionFactory factory;
-
-
+    private final NotificationQueueService notificationQueueService;
 
     @Inject
-    public MockEntitlementDaoMemory(Clock clock, EntitlementConfig config, SubscriptionFactory factory) {
+    public MockEntitlementDaoMemory(Clock clock, EntitlementConfig config, SubscriptionFactory factory, NotificationQueueService notificationQueueService) {
         super();
         this.clock = clock;
         this.config = config;
         this.factory = factory;
+        this.notificationQueueService = notificationQueueService;
         this.bundles = new ArrayList<SubscriptionBundle>();
         this.subscriptions = new ArrayList<Subscription>();
         this.events = new TreeSet<EntitlementEvent>();
@@ -138,6 +148,14 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
 
         synchronized(events) {
             events.addAll(initalEvents);
+            for (final EntitlementEvent cur : initalEvents) {
+                recordFutureNotificationFromTransaction(null, cur.getEffectiveDate(), new NotificationKey() {
+                    @Override
+                    public String toString() {
+                        return cur.getId().toString();
+                    }
+                });
+            }
         }
         Subscription updatedSubscription = buildSubscription(subscription);
         subscriptions.add(updatedSubscription);
@@ -174,7 +192,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
             List<EntitlementEvent> results = new LinkedList<EntitlementEvent>();
             for (EntitlementEvent cur : events) {
                 if (cur.isActive() &&
-                        cur.getProcessingState() == EventLifecycleState.AVAILABLE &&
+                        cur.getEffectiveDate().isAfter(clock.getUTCNow()) &&
                             cur.getSubscriptionId().equals(subscriptionId)) {
                     results.add(cur);
                 }
@@ -202,39 +220,6 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
     }
 
 
-    @Override
-    public List<EntitlementEvent> getEventsReady(UUID ownerId, int sequenceId) {
-        synchronized(events) {
-            List<EntitlementEvent> readyList = new LinkedList<EntitlementEvent>();
-            for (EntitlementEvent cur : events) {
-                if (cur.isAvailableForProcessing(clock.getUTCNow())) {
-
-                    if (cur.getOwner() != null) {
-                        log.warn(String.format("EventProcessor %s stealing event %s from %s", ownerId, cur, cur.getOwner()));
-                    }
-                    cur.setOwner(ownerId);
-                    cur.setNextAvailableDate(clock.getUTCNow().plus(config.getDaoClaimTimeMs()));
-                    cur.setProcessingState(EventLifecycleState.IN_PROCESSING);
-                    readyList.add(cur);
-                }
-            }
-            Collections.sort(readyList);
-            return readyList;
-        }
-    }
-
-    @Override
-    public void clearEventsReady(UUID ownerId, Collection<EntitlementEvent> cleared) {
-        synchronized(events) {
-            for (EntitlementEvent cur : cleared) {
-                if (cur.getOwner().equals(ownerId)) {
-                    cur.setProcessingState(EventLifecycleState.PROCESSED);
-                } else {
-                    log.warn(String.format("EventProcessor %s trying to clear event %s that it does not own", ownerId, cur));
-                }
-            }
-        }
-    }
 
     private Subscription buildSubscription(SubscriptionData in) {
         return factory.createSubscription(new SubscriptionBuilder(in), getEventsForSubscription(in.getId()));
@@ -272,12 +257,26 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
             cancelNextChangeEvent(subscriptionId);
             cancelNextPhaseEvent(subscriptionId);
             events.addAll(changeEvents);
+            for (final EntitlementEvent cur : changeEvents) {
+                recordFutureNotificationFromTransaction(null, cur.getEffectiveDate(), new NotificationKey() {
+                    @Override
+                    public String toString() {
+                        return cur.getId().toString();
+                    }
+                });
+            }
         }
     }
 
-    private void insertEvent(EntitlementEvent event) {
+    private void insertEvent(final EntitlementEvent event) {
         synchronized(events) {
             events.add(event);
+            recordFutureNotificationFromTransaction(null, event.getEffectiveDate(), new NotificationKey() {
+                @Override
+                public String toString() {
+                    return event.getId().toString();
+                }
+            });
         }
     }
 
@@ -298,10 +297,11 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
                     continue;
                 }
                 if (cur.getType() == EventType.PHASE &&
-                        cur.getProcessingState() == EventLifecycleState.AVAILABLE) {
+                        cur.getEffectiveDate().isAfter(clock.getUTCNow())) {
                     cur.deactivate();
                     break;
                 }
+
             }
         }
     }
@@ -319,7 +319,7 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
                 }
                 if (cur.getType() == EventType.API_USER &&
                         ApiEventType.CHANGE == ((ApiEvent) cur).getEventType() &&
-                        cur.getProcessingState() == EventLifecycleState.AVAILABLE) {
+                        cur.getEffectiveDate().isAfter(clock.getUTCNow())) {
                     cur.deactivate();
                     break;
                 }
@@ -364,8 +364,15 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
                 SubscriptionBundleData bundleData = curBundle.getData();
                 for (SubscriptionMigrationData curSubscription : curBundle.getSubscriptions()) {
                     SubscriptionData subData = curSubscription.getData();
-                    for (EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
+                    for (final EntitlementEvent curEvent : curSubscription.getInitialEvents()) {
                         events.add(curEvent);
+                        recordFutureNotificationFromTransaction(null, curEvent.getEffectiveDate(), new NotificationKey() {
+                            @Override
+                            public String toString() {
+                                return curEvent.getId().toString();
+                            }
+                        });
+
                     }
                     subscriptions.add(subData);
                 }
@@ -393,4 +400,26 @@ public class MockEntitlementDaoMemory implements EntitlementDao, MockEntitlement
         }
 
     }
+
+    @Override
+    public EntitlementEvent getEventById(UUID eventId) {
+        synchronized(events) {
+            for (EntitlementEvent cur : events) {
+                if (cur.getId().equals(eventId)) {
+                    return cur;
+                }
+            }
+        }
+        return null;
+    }
+
+    private void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao, final DateTime effectiveDate, final NotificationKey notificationKey) {
+        try {
+            NotificationQueue subscritionEventQueue = notificationQueueService.getNotificationQueue(Engine.ENTITLEMENT_SERVICE_NAME,
+                Engine.NOTIFICATION_QUEUE_NAME);
+            subscritionEventQueue.recordFutureNotificationFromTransaction(transactionalDao, effectiveDate, notificationKey);
+        } catch (NoSuchNotificationQueue e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
index 2ebdea9..503fd1a 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/engine/dao/MockEntitlementDaoSql.java
@@ -20,6 +20,8 @@ import com.google.inject.Inject;
 import com.ning.billing.config.EntitlementConfig;
 import com.ning.billing.entitlement.api.user.SubscriptionFactory;
 import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.notificationq.NotificationQueueService;
+
 import org.skife.jdbi.v2.DBI;
 import org.skife.jdbi.v2.Transaction;
 import org.skife.jdbi.v2.TransactionStatus;
@@ -32,11 +34,12 @@ public class MockEntitlementDaoSql extends EntitlementSqlDao implements MockEnti
     private final ResetSqlDao resetDao;
 
     @Inject
-    public MockEntitlementDaoSql(DBI dbi, Clock clock, EntitlementConfig config, SubscriptionFactory factory) {
-        super(dbi, clock, config, factory);
+    public MockEntitlementDaoSql(DBI dbi, Clock clock, SubscriptionFactory factory, NotificationQueueService notificationQueueService) {
+        super(dbi, clock, factory, notificationQueueService);
         this.resetDao = dbi.onDemand(ResetSqlDao.class);
     }
 
+
     @Override
     public void reset() {
         resetDao.inTransaction(new Transaction<Void, ResetSqlDao>() {
@@ -45,9 +48,10 @@ public class MockEntitlementDaoSql extends EntitlementSqlDao implements MockEnti
             public Void inTransaction(ResetSqlDao dao, TransactionStatus status)
                     throws Exception {
                 resetDao.resetEvents();
-                resetDao.resetClaimedEvents();
                 resetDao.resetSubscriptions();
                 resetDao.resetBundles();
+                resetDao.resetClaimedNotifications();
+                resetDao.resetNotifications();
                 return null;
             }
         });
@@ -58,14 +62,17 @@ public class MockEntitlementDaoSql extends EntitlementSqlDao implements MockEnti
         @SqlUpdate("truncate table events")
         public void resetEvents();
 
-        @SqlUpdate("truncate table claimed_events")
-        public void resetClaimedEvents();
-
         @SqlUpdate("truncate table subscriptions")
         public void resetSubscriptions();
 
         @SqlUpdate("truncate table bundles")
         public void resetBundles();
-    }
 
+        @SqlUpdate("truncate table notifications")
+        public void resetNotifications();
+
+        @SqlUpdate("truncate table claimed_notifications")
+        public void resetClaimedNotifications();
+
+    }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
index dc422a6..1555bdb 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModule.java
@@ -16,10 +16,12 @@
 
 package com.ning.billing.entitlement.glue;
 
+import com.ning.billing.account.glue.AccountModuleMock;
 import com.ning.billing.catalog.glue.CatalogModule;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
 import com.ning.billing.util.glue.EventBusModule;
+import com.ning.billing.util.glue.NotificationQueueModule;
 
 public class MockEngineModule extends EntitlementModule {
 
@@ -33,6 +35,7 @@ public class MockEngineModule extends EntitlementModule {
         super.configure();
         install(new EventBusModule());
         install(new CatalogModule());
+        install(new AccountModuleMock());
     }
 
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
index c6ce269..786f1e3 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleMemory.java
@@ -17,16 +17,12 @@
 package com.ning.billing.entitlement.glue;
 
 
-import com.ning.billing.entitlement.engine.core.EventNotifier;
-import com.ning.billing.entitlement.engine.core.MockApiEventProcessorMemory;
 import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoMemory;
+import com.ning.billing.util.notificationq.MockNotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService;
 
 public class MockEngineModuleMemory extends MockEngineModule {
-    @Override
-    protected void installApiEventProcessor() {
-        bind(EventNotifier.class).to(MockApiEventProcessorMemory.class).asEagerSingleton();
-    }
 
     @Override
     protected void installEntitlementDao() {
@@ -34,8 +30,12 @@ public class MockEngineModuleMemory extends MockEngineModule {
     }
 
 
+    private void installNotificationQueue() {
+        bind(NotificationQueueService.class).to(MockNotificationQueueService.class).asEagerSingleton();
+    }
     @Override
     protected void configure() {
         super.configure();
+        installNotificationQueue();
     }
 }
diff --git a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
index f1cd237..dbe2938 100644
--- a/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
+++ b/entitlement/src/test/java/com/ning/billing/entitlement/glue/MockEngineModuleSql.java
@@ -22,6 +22,8 @@ import com.ning.billing.entitlement.engine.dao.EntitlementDao;
 import com.ning.billing.entitlement.engine.dao.MockEntitlementDaoSql;
 import com.ning.billing.util.clock.Clock;
 import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.glue.NotificationQueueModule;
+
 import org.skife.config.ConfigurationObjectFactory;
 import org.skife.jdbi.v2.DBI;
 
@@ -47,6 +49,7 @@ public class MockEngineModuleSql extends MockEngineModule {
     @Override
     protected void configure() {
         installDBI();
+        install(new NotificationQueueModule());
         super.configure();
     }
 }
diff --git a/entitlement/src/test/resources/testInput.xml b/entitlement/src/test/resources/testInput.xml
index 3d36b9d..8a97d57 100644
--- a/entitlement/src/test/resources/testInput.xml
+++ b/entitlement/src/test/resources/testInput.xml
@@ -39,7 +39,7 @@ Use Cases to do:
 <catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
 
-	<effectiveDate>2011-10-08T00:00:00+00:00</effectiveDate>
+	<effectiveDate>2011-01-01T00:00:00+00:00</effectiveDate>
 	<catalogName>Firearms</catalogName>
 
 	<currencies>
@@ -151,16 +151,16 @@ Use Cases to do:
             </createAlignmentCase>
 		</createAlignment>
 		<billingAlignment>
-		<billingAlignmentCase>
-			<alignment>ACCOUNT</alignment>
-		</billingAlignmentCase>
+			<billingAlignmentCase>
+				<productCategory>ADD_ON</productCategory>
+				<alignment>BUNDLE</alignment>
+			</billingAlignmentCase>
 			<billingAlignmentCase>
 				<billingPeriod>ANNUAL</billingPeriod>
 				<alignment>SUBSCRIPTION</alignment>
 			</billingAlignmentCase>
 			<billingAlignmentCase>
-				<productCategory>ADD_ON</productCategory>
-				<alignment>BUNDLE</alignment>
+				<alignment>ACCOUNT</alignment>
 			</billingAlignmentCase>
 		</billingAlignment>
 		<priceList>

invoice/pom.xml 17(+16 -1)

diff --git a/invoice/pom.xml b/invoice/pom.xml
index 3d7dc27..8c939ca 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.21-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-invoice</artifactId>
@@ -35,6 +35,21 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-entitlement</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-catalog</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ning.billing</groupId>
+            <artifactId>killbill-catalog</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        
+        <dependency>
             <groupId>org.testng</groupId>
             <artifactId>testng</artifactId>
             <scope>test</scope>
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
index 1186456..20f33c7 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/DefaultInvoiceDao.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.invoice.dao;
 
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
 import com.google.inject.Inject;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceCreationNotification;
@@ -40,7 +42,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     private final static Logger log = LoggerFactory.getLogger(DefaultInvoiceDao.class);
 
     @Inject
-    public DefaultInvoiceDao(IDBI dbi, EventBus eventBus) {
+    public DefaultInvoiceDao(final IDBI dbi, final EventBus eventBus) {
         this.invoiceDao = dbi.onDemand(InvoiceSqlDao.class);
         this.eventBus = eventBus;
     }
@@ -54,7 +56,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     public List<Invoice> get() {
         return invoiceDao.inTransaction(new Transaction<List<Invoice>, InvoiceSqlDao>() {
              @Override
-             public List<Invoice> inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
+             public List<Invoice> inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                  List<Invoice> invoices = invoiceDao.get();
 
                  InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
@@ -72,7 +74,7 @@ public class DefaultInvoiceDao implements InvoiceDao {
     public Invoice getById(final String invoiceId) {
         return invoiceDao.inTransaction(new Transaction<Invoice, InvoiceSqlDao>() {
              @Override
-             public Invoice inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
+             public Invoice inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                  Invoice invoice = invoiceDao.getById(invoiceId);
 
                  if (invoice != null) {
@@ -87,25 +89,24 @@ public class DefaultInvoiceDao implements InvoiceDao {
     }
 
     @Override
-    public void save(final Invoice invoice) {
+    public void create(final Invoice invoice) {
          invoiceDao.inTransaction(new Transaction<Void, InvoiceSqlDao>() {
              @Override
-             public Void inTransaction(InvoiceSqlDao invoiceDao, TransactionStatus status) throws Exception {
+             public Void inTransaction(final InvoiceSqlDao invoiceDao, final TransactionStatus status) throws Exception {
                 Invoice currentInvoice = invoiceDao.getById(invoice.getId().toString());
-                invoiceDao.save(invoice);
-
-                List<InvoiceItem> invoiceItems = invoice.getItems();
-                InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
-                invoiceItemDao.save(invoiceItems);
 
                 if (currentInvoice == null) {
+                    invoiceDao.create(invoice);
+
+                    List<InvoiceItem> invoiceItems = invoice.getItems();
+                    InvoiceItemSqlDao invoiceItemDao = invoiceDao.become(InvoiceItemSqlDao.class);
+                    invoiceItemDao.create(invoiceItems);
+
                     InvoiceCreationNotification event;
                     event = new DefaultInvoiceCreationNotification(invoice.getId(), invoice.getAccountId(),
                                                                   invoice.getAmountOutstanding(), invoice.getCurrency(),
                                                                   invoice.getInvoiceDate());
                     eventBus.post(event);
-                } else {
-
                 }
 
                 return null;
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
index b59e4ed..e9306b2 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceDao.java
@@ -23,7 +23,7 @@ import java.util.UUID;
 import com.ning.billing.invoice.api.Invoice;
 
 public interface InvoiceDao {
-    void save(Invoice invoice);
+    void create(Invoice invoice);
 
     Invoice getById(final String id);
 
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
index a89749e..6f4e47b 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceItemSqlDao.java
@@ -50,10 +50,14 @@ public interface InvoiceItemSqlDao extends EntityDao<InvoiceItem> {
 
     @Override
     @SqlUpdate
-    void save(@InvoiceItemBinder final InvoiceItem invoiceItem);
+    void create(@InvoiceItemBinder final InvoiceItem invoiceItem);
+
+    @Override
+    @SqlUpdate
+    void update(@InvoiceItemBinder final InvoiceItem invoiceItem);
 
     @SqlBatch
-    void save(@InvoiceItemBinder final List<InvoiceItem> items);
+    void create(@InvoiceItemBinder final List<InvoiceItem> items);
 
     @BindingAnnotation(InvoiceItemBinder.InvoiceItemBinderFactory.class)
     @Retention(RetentionPolicy.RUNTIME)
diff --git a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
index aa0051a..9ede145 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/dao/InvoiceSqlDao.java
@@ -53,7 +53,11 @@ import java.util.UUID;
 public interface InvoiceSqlDao extends EntityDao<Invoice>, Transactional<InvoiceSqlDao>, Transmogrifier, CloseMe {
     @Override
     @SqlUpdate
-    void save(@InvoiceBinder Invoice invoice);
+    void create(@InvoiceBinder Invoice invoice);
+
+    @Override
+    @SqlUpdate
+    void update(@InvoiceBinder Invoice invoice);
 
     @SqlQuery
     List<Invoice> getInvoicesByAccount(@Bind("accountId") final String accountId);
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
index 797f587..97f82e0 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoice.java
@@ -126,6 +126,10 @@ public class DefaultInvoice implements Invoice {
 
     @Override
     public boolean isDueForPayment(final DateTime targetDate, final int numberOfDays) {
+        if (getTotalAmount().compareTo(BigDecimal.ZERO) == 0) {
+            return false;
+        }
+
         if (lastPaymentAttempt == null) {
             return true;
         }
diff --git a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
index 54ca894..3b9311e 100644
--- a/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
+++ b/invoice/src/main/java/com/ning/billing/invoice/model/DefaultInvoiceGenerator.java
@@ -16,22 +16,29 @@
 
 package com.ning.billing.invoice.model;
 
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.ISODateTimeFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import com.ning.billing.catalog.api.BillingPeriod;
+import com.ning.billing.catalog.api.CatalogApiException;
 import com.ning.billing.catalog.api.Currency;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
 import com.ning.billing.invoice.api.BillingEventSet;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
-import org.joda.time.DateTime;
-
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.UUID;
 
 public class DefaultInvoiceGenerator implements InvoiceGenerator {
+    private static final Logger log = LoggerFactory.getLogger(DefaultInvoiceGenerator.class); 
     @Override
     public Invoice generateInvoice(final UUID accountId, final BillingEventSet events, final InvoiceItemList existingItems, final DateTime targetDate, final Currency targetCurrency) {
         if (events == null) {return new DefaultInvoice(accountId, targetDate, targetCurrency);}
@@ -96,7 +103,7 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
             BillingEvent thisEvent = events.get(i);
             BillingEvent nextEvent = events.get(i + 1);
 
-            if (thisEvent.getSubscriptionId() == nextEvent.getSubscriptionId()) {
+            if (thisEvent.getSubscription().getId() == nextEvent.getSubscription().getId()) {
                 processEvents(invoiceId, thisEvent, nextEvent, items, targetDate, targetCurrency);
             } else {
                 processEvent(invoiceId, thisEvent, items, targetDate, targetCurrency);
@@ -112,26 +119,40 @@ public class DefaultInvoiceGenerator implements InvoiceGenerator {
     }
 
     private void processEvent(UUID invoiceId, BillingEvent event, List<InvoiceItem> items, DateTime targetDate, Currency targetCurrency) {
-        BigDecimal rate = event.getPrice(targetCurrency);
-        BigDecimal invoiceItemAmount = calculateInvoiceItemAmount(event, targetDate, rate);
-        BillingMode billingMode = getBillingMode(event.getBillingMode());
-        DateTime billThroughDate = billingMode.calculateEffectiveEndDate(event.getEffectiveDate(), targetDate, event.getBillCycleDay(), event.getBillingPeriod());
-
-        addInvoiceItem(invoiceId, items, event, billThroughDate, invoiceItemAmount, rate, targetCurrency);
+    	try {
+    		//TODO: Jeff getPrice() -> getRecurringPrice()
+    		BigDecimal rate = event.getRecurringPrice(targetCurrency);
+    		BigDecimal invoiceItemAmount = calculateInvoiceItemAmount(event, targetDate, rate);
+    		BillingMode billingMode = getBillingMode(event.getBillingMode());
+    		DateTime billThroughDate = billingMode.calculateEffectiveEndDate(event.getEffectiveDate(), targetDate, event.getBillCycleDay(), event.getBillingPeriod());
+
+    		addInvoiceItem(invoiceId, items, event, billThroughDate, invoiceItemAmount, rate, targetCurrency);
+    	} catch (CatalogApiException e) {
+            log.error(String.format("Encountered a catalog error processing invoice %s for billing event on date %s", 
+                    invoiceId.toString(), 
+                    ISODateTimeFormat.basicDateTime().print(event.getEffectiveDate())), e);
+        }
     }
 
     private void processEvents(UUID invoiceId, BillingEvent firstEvent, BillingEvent secondEvent, List<InvoiceItem> items, DateTime targetDate, Currency targetCurrency) {
-        BigDecimal rate = firstEvent.getPrice(targetCurrency);
-        BigDecimal invoiceItemAmount = calculateInvoiceItemAmount(firstEvent, secondEvent, targetDate, rate);
-        BillingMode billingMode = getBillingMode(firstEvent.getBillingMode());
-        DateTime billThroughDate = billingMode.calculateEffectiveEndDate(firstEvent.getEffectiveDate(), secondEvent.getEffectiveDate(), targetDate, firstEvent.getBillCycleDay(), firstEvent.getBillingPeriod());
-
-        addInvoiceItem(invoiceId, items, firstEvent, billThroughDate, invoiceItemAmount, rate, targetCurrency);
+    	//TODO: Jeff getPrice() -> getRecurringPrice()
+    	try {
+    		BigDecimal rate = firstEvent.getRecurringPrice(targetCurrency);
+    		BigDecimal invoiceItemAmount = calculateInvoiceItemAmount(firstEvent, secondEvent, targetDate, rate);
+    		BillingMode billingMode = getBillingMode(firstEvent.getBillingMode());
+    		DateTime billThroughDate = billingMode.calculateEffectiveEndDate(firstEvent.getEffectiveDate(), secondEvent.getEffectiveDate(), targetDate, firstEvent.getBillCycleDay(), firstEvent.getBillingPeriod());
+
+    		addInvoiceItem(invoiceId, items, firstEvent, billThroughDate, invoiceItemAmount, rate, targetCurrency);
+    	} catch (CatalogApiException e) {
+    		log.error(String.format("Encountered a catalog error processing invoice %s for billing event on date %s", 
+                    invoiceId.toString(), 
+                    ISODateTimeFormat.basicDateTime().print(firstEvent.getEffectiveDate())), e);
+        }
     }
 
     private void addInvoiceItem(UUID invoiceId, List<InvoiceItem> items, BillingEvent event, DateTime billThroughDate, BigDecimal amount, BigDecimal rate, Currency currency) {
         if (!(amount.compareTo(BigDecimal.ZERO) == 0)) {
-            DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, event.getSubscriptionId(), event.getEffectiveDate(), billThroughDate, event.getDescription(), amount, rate, currency);
+            DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, event.getSubscription().getId(), event.getEffectiveDate(), billThroughDate, event.getDescription(), amount, rate, currency);
             items.add(item);
         }
     }
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
index 490d075..4dc783f 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceItemSqlDao.sql.stg
@@ -25,12 +25,16 @@ getInvoiceItemsBySubscription() ::= <<
   WHERE subscription_id = :subscriptionId;
 >>
 
-save() ::= <<
+create() ::= <<
   INSERT INTO invoice_items(id, invoice_id, subscription_id, start_date, end_date, description, amount, rate, currency)
-  VALUES(:id, :invoiceId, :subscriptionId, :startDate, :endDate, :description, :amount, :rate, :currency)
-  ON DUPLICATE KEY UPDATE
-    start_date = :startDate, end_date = :endDate, description = :description,
-    amount = :amount, rate = :rate, currency = :currency''
+  VALUES(:id, :invoiceId, :subscriptionId, :startDate, :endDate, :description, :amount, :rate, :currency);
+>>
+
+update() ::= <<
+  UPDATE invoice_items
+  SET invoice_id = :invoiceId, subscription_id = :subscriptionId, start_date = :startDate, end_date = :endDate,
+      description = :description, amount = :amount, rate = :rate, currency = :currency
+  WHERE id = :id;
 >>
 
 test() ::= <<
diff --git a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
index 0323040..8766e18 100644
--- a/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
+++ b/invoice/src/main/resources/com/ning/billing/invoice/dao/InvoiceSqlDao.sql.stg
@@ -52,11 +52,15 @@ getById() ::= <<
   GROUP BY i.id, i.account_id, i.invoice_date, i.target_date, i.currency;
 >>
 
-save() ::= <<
+create() ::= <<
   INSERT INTO invoices(id, account_id, invoice_date, target_date, currency)
-  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency)
-  ON DUPLICATE KEY UPDATE
-    invoice_date = :invoiceDate, target_date = :targetDate, currency = :currency;
+  VALUES (:id, :accountId, :invoiceDate, :targetDate, :currency);
+>>
+
+update() ::= <<
+  UPDATE invoices
+  SET account_id = :accountId, invoice_date = :invoiceDate, target_date = :targetDate, currency = :currency
+  WHERE id = :id;
 >>
 
 notifySuccessfulPayment() ::= <<
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
index 0666ada..3fe72ed 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceDaoTests.java
@@ -45,7 +45,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         Invoice invoice = new DefaultInvoice(accountId, new DefaultClock().getUTCNow(), Currency.USD);
         DateTime invoiceDate = invoice.getInvoiceDate();
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
 
         List<Invoice> invoices = invoiceDao.getInvoicesByAccount(accountId.toString());
         assertNotNull(invoices);
@@ -68,7 +68,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime endDate = new DateTime(2010, 4, 1, 0, 0, 0, 0);
         InvoiceItem invoiceItem = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, endDate, "test", new BigDecimal("21.00"), new BigDecimal("7.00"), Currency.USD);
         invoice.add(invoiceItem);
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
 
         Invoice savedInvoice = invoiceDao.getById(invoiceId.toString());
         assertNotNull(savedInvoice);
@@ -105,12 +105,12 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
         BigDecimal paymentAmount = new BigDecimal("14.0");
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
         invoiceDao.notifySuccessfulPayment(invoice.getId().toString(), paymentAmount, Currency.USD.toString(), paymentId, paymentAttemptDate.toDate());
 
         invoice = invoiceDao.getById(invoice.getId().toString());
-//        assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
-//        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
+        assertEquals(invoice.getAmountPaid().compareTo(paymentAmount), 0);
+        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
     }
 
     @Test
@@ -121,11 +121,11 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         DateTime paymentAttemptDate = new DateTime(2011, 6, 24, 12, 14, 36, 0);
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
         invoiceDao.notifyFailedPayment(invoice.getId().toString(), UUID.randomUUID().toString(), paymentAttemptDate.toDate());
 
         invoice = invoiceDao.getById(invoice.getId().toString());
-//        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
+        assertTrue(invoice.getLastPaymentAttempt().equals(paymentAttemptDate));
     }
 
     @Test
@@ -140,7 +140,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         UUID accountId = UUID.randomUUID();
         Invoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
         invoices = invoiceDao.getInvoicesForPayment(notionalDate.toDate(), NUMBER_OF_DAY_BETWEEN_RETRIES);
         assertEquals(invoices.size(), existingInvoiceCount);
     }
@@ -163,7 +163,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, targetDate, endDate, "test", amount, rate, Currency.USD);
         invoice.add(item);
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
 
         // ensure that the number of invoices for payment has increased by 1
         int count;
@@ -248,7 +248,7 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
 
         // create invoice 1 (subscriptions 1-4)
         Invoice invoice1 = new DefaultInvoice(accountId, targetDate, Currency.USD);
-        invoiceDao.save(invoice1);
+        invoiceDao.create(invoice1);
 
         UUID invoiceId1 = invoice1.getId();
 
@@ -256,34 +256,34 @@ public class InvoiceDaoTests extends InvoiceDaoTestBase {
         DateTime endDate = startDate.plusMonths(1);
 
         DefaultInvoiceItem item1 = new DefaultInvoiceItem(invoiceId1, subscriptionId1, startDate, endDate, "test A", rate1, rate1, Currency.USD);
-        invoiceItemDao.save(item1);
+        invoiceItemDao.create(item1);
 
         DefaultInvoiceItem item2 = new DefaultInvoiceItem(invoiceId1, subscriptionId2, startDate, endDate, "test B", rate2, rate2, Currency.USD);
-        invoiceItemDao.save(item2);
+        invoiceItemDao.create(item2);
 
         DefaultInvoiceItem item3 = new DefaultInvoiceItem(invoiceId1, subscriptionId3, startDate, endDate, "test C", rate3, rate3, Currency.USD);
-        invoiceItemDao.save(item3);
+        invoiceItemDao.create(item3);
 
         DefaultInvoiceItem item4 = new DefaultInvoiceItem(invoiceId1, subscriptionId4, startDate, endDate, "test D", rate4, rate4, Currency.USD);
-        invoiceItemDao.save(item4);
+        invoiceItemDao.create(item4);
 
         // create invoice 2 (subscriptions 1-3)
-        DefaultInvoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
-        invoiceDao.save(invoice);
+        DefaultInvoice invoice2 = new DefaultInvoice(accountId, targetDate, Currency.USD);
+        invoiceDao.create(invoice2);
 
-        UUID invoiceId2 = invoice.getId();
+        UUID invoiceId2 = invoice2.getId();
 
         startDate = endDate;
         endDate = startDate.plusMonths(1);
 
         DefaultInvoiceItem item5 = new DefaultInvoiceItem(invoiceId2, subscriptionId1, startDate, endDate, "test A", rate1, rate1, Currency.USD);
-        invoiceItemDao.save(item5);
+        invoiceItemDao.create(item5);
 
         DefaultInvoiceItem item6 = new DefaultInvoiceItem(invoiceId2, subscriptionId2, startDate, endDate, "test B", rate2, rate2, Currency.USD);
-        invoiceItemDao.save(item6);
+        invoiceItemDao.create(item6);
 
         DefaultInvoiceItem item7 = new DefaultInvoiceItem(invoiceId2, subscriptionId3, startDate, endDate, "test C", rate3, rate3, Currency.USD);
-        invoiceItemDao.save(item7);
+        invoiceItemDao.create(item7);
 
         // check that each subscription returns the correct number of invoices
         List<Invoice> items1 = invoiceDao.getInvoicesBySubscription(subscriptionId1.toString());
diff --git a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
index 0f30753..fa743ff 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/dao/InvoiceItemDaoTests.java
@@ -46,7 +46,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         BigDecimal rate = new BigDecimal("20.00");
 
         InvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, endDate, "test", rate, rate, Currency.USD);
-        invoiceItemDao.save(item);
+        invoiceItemDao.create(item);
 
         InvoiceItem thisItem = invoiceItemDao.getById(item.getId().toString());
         assertNotNull(thisItem);
@@ -70,7 +70,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         for (int i = 0; i < 3; i++) {
             UUID invoiceId = UUID.randomUUID();
             DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate.plusMonths(i), startDate.plusMonths(i + 1), "test", rate, rate, Currency.USD);
-            invoiceItemDao.save(item);
+            invoiceItemDao.create(item);
         }
 
         List<InvoiceItem> items = invoiceItemDao.getInvoiceItemsBySubscription(subscriptionId.toString());
@@ -87,7 +87,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
             UUID subscriptionId = UUID.randomUUID();
             BigDecimal amount = rate.multiply(new BigDecimal(i + 1));
             DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, startDate.plusMonths(1), "test", amount, amount, Currency.USD);
-            invoiceItemDao.save(item);
+            invoiceItemDao.create(item);
         }
 
         List<InvoiceItem> items = invoiceItemDao.getInvoiceItemsByInvoice(invoiceId.toString());
@@ -100,7 +100,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
         DateTime targetDate = new DateTime(2011, 5, 23, 0, 0, 0, 0);
         DefaultInvoice invoice = new DefaultInvoice(accountId, targetDate, Currency.USD);
 
-        invoiceDao.save(invoice);
+        invoiceDao.create(invoice);
 
         UUID invoiceId = invoice.getId();
         DateTime startDate = new DateTime(2011, 3, 1, 0, 0, 0, 0);
@@ -108,7 +108,7 @@ public class InvoiceItemDaoTests extends InvoiceDaoTestBase {
 
         UUID subscriptionId = UUID.randomUUID();
         DefaultInvoiceItem item = new DefaultInvoiceItem(invoiceId, subscriptionId, startDate, startDate.plusMonths(1), "test", rate, rate, Currency.USD);
-        invoiceItemDao.save(item);
+        invoiceItemDao.create(item);
 
         List<InvoiceItem> items = invoiceItemDao.getInvoiceItemsByAccount(accountId.toString());
         assertEquals(items.size(), 1);
diff --git a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
index d01c470..5d36d55 100644
--- a/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
+++ b/invoice/src/test/java/com/ning/billing/invoice/tests/DefaultInvoiceGeneratorTests.java
@@ -16,26 +16,33 @@
 
 package com.ning.billing.invoice.tests;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.testng.annotations.Test;
+
+import com.ning.billing.catalog.MockCatalog;
 import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.Plan;
+import com.ning.billing.catalog.api.PlanPhase;
 import com.ning.billing.entitlement.api.billing.BillingEvent;
 import com.ning.billing.entitlement.api.billing.BillingModeType;
+import com.ning.billing.entitlement.api.billing.DefaultBillingEvent;
+import com.ning.billing.entitlement.api.user.Subscription;
+import com.ning.billing.entitlement.api.user.SubscriptionData;
+import com.ning.billing.entitlement.api.user.SubscriptionFactory.SubscriptionBuilder;
 import com.ning.billing.invoice.api.BillingEventSet;
-import com.ning.billing.invoice.api.DefaultBillingEvent;
 import com.ning.billing.invoice.api.Invoice;
 import com.ning.billing.invoice.api.InvoiceItem;
 import com.ning.billing.invoice.model.DefaultInvoiceGenerator;
 import com.ning.billing.invoice.model.DefaultInvoiceItem;
 import com.ning.billing.invoice.model.InvoiceGenerator;
 import com.ning.billing.invoice.model.InvoiceItemList;
-import org.joda.time.DateTime;
-import org.testng.annotations.Test;
-
-import java.math.BigDecimal;
-import java.util.UUID;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
 
 @Test(groups = {"invoicing", "invoiceGenerator"})
 public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
@@ -68,14 +75,15 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     public void testWithSingleMonthlyEvent() {
         BillingEventSet events = new BillingEventSet();
 
-        UUID subscriptionId = UUID.randomUUID();
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
         DateTime startDate = buildDateTime(2011, 9, 1);
-        String planName = "World Domination";
-        String phaseName = "Build Space Laser";
-        BillingEvent event = new DefaultBillingEvent(subscriptionId, startDate, planName, phaseName,
-                                               new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE);
-
+        MockCatalog catalog = new MockCatalog();
+        Plan plan = catalog.getCurrentPlans()[0];
+        PlanPhase phase = plan.getAllPhases()[0];
+        
+        BillingEvent event = new DefaultBillingEvent(sub, startDate, plan, phase,
+                new InternationalPriceMock(ZERO),new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
+                                               1, BillingModeType.IN_ADVANCE, "Test");
         events.add(event);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
@@ -93,15 +101,15 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     public void testWithSingleMonthlyEventWithLeadingProRation() {
         BillingEventSet events = new BillingEventSet();
 
-        UUID subscriptionId = UUID.randomUUID();
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
         DateTime startDate = buildDateTime(2011, 9, 1);
-        String planName = "World Domination";
-        String phaseName = "Build Space Laser";
+        MockCatalog catalog = new MockCatalog();
+        Plan plan = catalog.getCurrentPlans()[0];
+        PlanPhase phase = plan.getAllPhases()[0];
         BigDecimal rate = TEN;
-        BillingEvent event = new DefaultBillingEvent(subscriptionId, startDate, planName, phaseName,
-                                               new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
-                                               15, BillingModeType.IN_ADVANCE);
-
+        BillingEvent event = new DefaultBillingEvent(sub, startDate, plan, phase,
+                new InternationalPriceMock(ZERO), new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
+                                               15, BillingModeType.IN_ADVANCE,"Test");
         events.add(event);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
@@ -123,16 +131,24 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     public void testTwoMonthlySubscriptionsWithAlignedBillingDates() {
         BillingEventSet events = new BillingEventSet();
 
-        BillingEvent event1 = new DefaultBillingEvent(UUID.randomUUID(), buildDateTime(2011, 9, 1),
-                                               "World Domination", "Build Space Laser",
-                                               new InternationalPriceMock(FIVE), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE);
+        MockCatalog catalog = new MockCatalog();
+        Plan plan1 = catalog.getCurrentPlans()[0];
+        PlanPhase phase1 = plan1.getAllPhases()[0];
+        Plan plan2 = catalog.getCurrentPlans()[1];
+        PlanPhase phase2 = plan2.getAllPhases()[0];
+        
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+
+        BillingEvent event1 = new DefaultBillingEvent(sub, buildDateTime(2011, 9, 1),
+                                               plan1,phase1,
+                                               new InternationalPriceMock(ZERO), new InternationalPriceMock(FIVE), BillingPeriod.MONTHLY,
+                                               1, BillingModeType.IN_ADVANCE, "Test");
         events.add(event1);
 
-        BillingEvent event2 = new DefaultBillingEvent(UUID.randomUUID(), buildDateTime(2011, 10, 1),
-                                               "Groceries", "Pick Up Milk",
-                                               new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE);
+        BillingEvent event2 = new DefaultBillingEvent(sub, buildDateTime(2011, 10, 1),
+                                               plan2,phase2,
+                                               new InternationalPriceMock(ZERO), new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
+                                               1, BillingModeType.IN_ADVANCE,"Test");
         events.add(event2);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
@@ -149,17 +165,22 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     public void testOnePlan_TwoMonthlyPhases_ChangeImmediate() {
         BillingEventSet events = new BillingEventSet();
 
-        UUID subscriptionId = UUID.randomUUID();
-        BillingEvent event1 = new DefaultBillingEvent(subscriptionId, buildDateTime(2011, 9, 1),
-                                               "World Domination", "Build Space Laser",
-                                               new InternationalPriceMock(FIVE), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE);
+        MockCatalog catalog = new MockCatalog();
+        Plan plan1 = catalog.getCurrentPlans()[0];
+        PlanPhase phase1 = plan1.getAllPhases()[0];
+
+        
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+        BillingEvent event1 = new DefaultBillingEvent(sub, buildDateTime(2011, 9, 1),
+                                               plan1,phase1,
+                                               new InternationalPriceMock(ZERO),new InternationalPriceMock(FIVE), BillingPeriod.MONTHLY,
+                                               1, BillingModeType.IN_ADVANCE,"Test");
         events.add(event1);
 
-        BillingEvent event2 = new DefaultBillingEvent(subscriptionId, buildDateTime(2011, 10, 15),
-                                               "World Domination", "Incinerate James Bond",
-                                               new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
-                                               15, BillingModeType.IN_ADVANCE);
+        BillingEvent event2 = new DefaultBillingEvent(sub, buildDateTime(2011, 10, 15),
+                                               plan1,phase1, //technically should be a different phase but it doesn't impact the test
+                                               new InternationalPriceMock(ZERO),new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
+                                               15, BillingModeType.IN_ADVANCE,"Test");
         events.add(event2);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
@@ -187,23 +208,28 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     public void testOnePlan_ThreeMonthlyPhases_ChangeEOT() {
         BillingEventSet events = new BillingEventSet();
 
-        UUID subscriptionId = UUID.randomUUID();
-        BillingEvent event1 = new DefaultBillingEvent(subscriptionId, buildDateTime(2011, 9, 1),
-                                               "World Domination", "Build Space Laser",
-                                               new InternationalPriceMock(FIVE), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE);
+        MockCatalog catalog = new MockCatalog();
+        Plan plan1 = catalog.getCurrentPlans()[0];
+        PlanPhase phase1 = plan1.getAllPhases()[0];
+
+
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+        BillingEvent event1 = new DefaultBillingEvent(sub, buildDateTime(2011, 9, 1),
+        										plan1,phase1,
+                                               new InternationalPriceMock(ZERO),new InternationalPriceMock(FIVE), BillingPeriod.MONTHLY,
+                                               1, BillingModeType.IN_ADVANCE,"Test");
         events.add(event1);
 
-        BillingEvent event2 = new DefaultBillingEvent(subscriptionId, buildDateTime(2011, 10, 1),
-                                               "World Domination", "Incinerate James Bond",
-                                               new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE);
+        BillingEvent event2 = new DefaultBillingEvent(sub, buildDateTime(2011, 10, 1),
+												plan1,phase1, //technically should be a different phase but it doesn't impact the test
+                                               new InternationalPriceMock(ZERO),new InternationalPriceMock(TEN), BillingPeriod.MONTHLY,
+                                               1, BillingModeType.IN_ADVANCE,"Test");
         events.add(event2);
 
-        BillingEvent event3 = new DefaultBillingEvent(subscriptionId, buildDateTime(2011, 11, 1),
-                                               "World Domination", "Cackle Gleefully",
-                                               new InternationalPriceMock(THIRTY), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE);
+        BillingEvent event3 = new DefaultBillingEvent(sub, buildDateTime(2011, 11, 1),
+												plan1,phase1, //technically should be a different phase but it doesn't impact the test
+                                               new InternationalPriceMock(ZERO),new InternationalPriceMock(THIRTY), BillingPeriod.MONTHLY,
+                                               1, BillingModeType.IN_ADVANCE,"Test");
         events.add(event3);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
@@ -220,18 +246,22 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
     public void testSingleEventWithExistingInvoice() {
         BillingEventSet events = new BillingEventSet();
 
-        UUID subscriptionId = UUID.randomUUID();
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
         DateTime startDate = buildDateTime(2011, 9, 1);
-
+        
+        MockCatalog catalog = new MockCatalog();
+        Plan plan1 = catalog.getCurrentPlans()[0];
+        PlanPhase phase1 = plan1.getAllPhases()[0];
+        
         BigDecimal rate = FIVE;
-        BillingEvent event1 = new DefaultBillingEvent(subscriptionId, startDate,
-                                               "World Domination", "Build Space Laser",
-                                               new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
-                                               1, BillingModeType.IN_ADVANCE);
+        BillingEvent event1 = new DefaultBillingEvent(sub, startDate,
+        										plan1,phase1,
+                                               new InternationalPriceMock(ZERO),new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
+                                               1, BillingModeType.IN_ADVANCE,"Test");
         events.add(event1);
 
         InvoiceItemList existingInvoiceItems = new InvoiceItemList();
-        InvoiceItem invoiceItem = new DefaultInvoiceItem(UUID.randomUUID(), subscriptionId, startDate, buildDateTime(2012, 1, 1), "",
+        InvoiceItem invoiceItem = new DefaultInvoiceItem(UUID.randomUUID(), sub.getId(), startDate, buildDateTime(2012, 1, 1), "",
                                                  rate.multiply(FOUR), rate, Currency.USD);
         existingInvoiceItems.add(invoiceItem);
 
@@ -398,18 +428,24 @@ public class DefaultInvoiceGeneratorTests extends InvoicingTestBase {
 
     private DefaultBillingEvent createBillingEvent(UUID subscriptionId, DateTime startDate, String planName, String planPhaseName,
                                             BigDecimal rate, int billCycleDay) {
-        return new DefaultBillingEvent(subscriptionId, startDate, planName, planPhaseName,
-                                new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
-                                billCycleDay, BillingModeType.IN_ADVANCE);
-
+        MockCatalog catalog = new MockCatalog();
+        Plan plan = catalog.getCurrentPlans()[0];
+        PlanPhase phase = plan.getAllPhases()[0];
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+        return new DefaultBillingEvent(sub, startDate, plan, phase,
+                new InternationalPriceMock(ZERO),new InternationalPriceMock(rate), BillingPeriod.MONTHLY,
+                                billCycleDay, BillingModeType.IN_ADVANCE,"Test");
     }
 
     private DefaultBillingEvent createAnnualBillingEvent(UUID subscriptionId, DateTime startDate, String planName, String planPhaseName,
                                                   BigDecimal rate, int billCycleDay) {
-        return new DefaultBillingEvent(subscriptionId, startDate, planName, planPhaseName,
-                                new InternationalPriceMock(rate), BillingPeriod.ANNUAL,
-                                billCycleDay, BillingModeType.IN_ADVANCE);
-
+        MockCatalog catalog = new MockCatalog();
+        Plan plan = catalog.getCurrentPlans()[0];
+        PlanPhase phase = plan.getAllPhases()[0];
+        Subscription sub = new SubscriptionData(new SubscriptionBuilder().setId(UUID.randomUUID()));
+        return new DefaultBillingEvent(sub, startDate, plan, phase,
+                new InternationalPriceMock(ZERO),new InternationalPriceMock(rate), BillingPeriod.ANNUAL,
+                                billCycleDay, BillingModeType.IN_ADVANCE,"Test");
     }
 
     private void testInvoiceGeneration(BillingEventSet events, InvoiceItemList existingInvoiceItems, DateTime targetDate, int expectedNumberOfItems, BigDecimal expectedAmount) {
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 9198ba0..8577544 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
@@ -16,14 +16,13 @@
 
 package com.ning.billing.invoice.tests;
 
-import com.ning.billing.catalog.api.Currency;
-import com.ning.billing.catalog.api.InternationalPrice;
-import com.ning.billing.catalog.api.Price;
+import static org.testng.Assert.fail;
 
 import java.math.BigDecimal;
-import java.util.Date;
 
-import static org.testng.Assert.fail;
+import com.ning.billing.catalog.api.Currency;
+import com.ning.billing.catalog.api.InternationalPrice;
+import com.ning.billing.catalog.api.Price;
 
 public class InternationalPriceMock implements InternationalPrice {
     private final BigDecimal rate;
@@ -44,8 +43,4 @@ public class InternationalPriceMock implements InternationalPrice {
         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 aca06a2..3d6356e 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.21-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-payment</artifactId>

pom.xml 16(+15 -1)

diff --git a/pom.xml b/pom.xml
index f64b324..8c8fbb3 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.21-SNAPSHOT</version>
+    <version>0.1.2-SNAPSHOT</version>
     <name>killbill</name>
     <description>Library for managing recurring subscriptions and the associated billing</description>
     <url>http://github.com/ning/killbill</url>
@@ -61,6 +61,13 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-account</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-entitlement</artifactId>
                 <version>${project.version}</version>
             </dependency>
@@ -71,6 +78,13 @@
             </dependency>
             <dependency>
                 <groupId>com.ning.billing</groupId>
+                <artifactId>killbill-catalog</artifactId>
+                <version>${project.version}</version>
+                <type>test-jar</type>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>com.ning.billing</groupId>
                 <artifactId>killbill-util</artifactId>
                 <version>${project.version}</version>
                 <type>test-jar</type>

util/pom.xml 17(+15 -2)

diff --git a/util/pom.xml b/util/pom.xml
index 5152ab7..74d243c 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.21-SNAPSHOT</version>
+        <version>0.1.2-SNAPSHOT</version>
         <relativePath>../pom.xml</relativePath>
     </parent>
     <artifactId>killbill-util</artifactId>
@@ -33,6 +33,10 @@
             <artifactId>jdbi</artifactId>
         </dependency>
         <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
             <groupId>com.google.inject</groupId>
             <artifactId>guice</artifactId>
             <version>3.0</version>
@@ -69,7 +73,16 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
-        
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>stringtemplate</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>management-dbfiles</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java b/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
index 05d0137..ac184be 100644
--- a/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
+++ b/util/src/main/java/com/ning/billing/util/customfield/CustomizableEntityBase.java
@@ -23,18 +23,18 @@ import com.ning.billing.util.entity.EntityBase;
 public abstract class CustomizableEntityBase extends EntityBase implements CustomizableEntity {
     protected final FieldStore fields;
 
-    public CustomizableEntityBase(UUID id) {
+    public CustomizableEntityBase(final UUID id) {
         super(id);
         fields = DefaultFieldStore.create(getId(), getObjectName());
     }
 
     @Override
-    public String getFieldValue(String fieldName) {
+    public String getFieldValue(final String fieldName) {
         return fields.getValue(fieldName);
     }
 
     @Override
-    public void setFieldValue(String fieldName, String fieldValue) {
+    public void setFieldValue(final String fieldName, final String fieldValue) {
         fields.setValue(fieldName, fieldValue);
     }
 
@@ -44,6 +44,13 @@ public abstract class CustomizableEntityBase extends EntityBase implements Custo
     }
 
     @Override
+    public void addFields(final List<CustomField> fields) {
+        if (fields != null) {
+            this.fields.add(fields);
+        }
+    }
+
+    @Override
     public void clearFields() {
         fields.clear();
     }
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
index 8a7a8c5..8134203 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityCollectionDao.java
@@ -31,6 +31,10 @@ public interface EntityCollectionDao<T extends Entity> {
                      @Bind("objectType") final String objectType,
                      @BindBean final List<T> entities);
 
+    @SqlUpdate
+    public void clear(@Bind("objectId") final String objectId,
+                      @Bind("objectType") final String objectType);
+
     @SqlQuery
     public List<T> load(@Bind("objectId") final String objectId,
                         @Bind("objectType") final String objectType);
diff --git a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
index 1ee4fc1..3b5dd46 100644
--- a/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
+++ b/util/src/main/java/com/ning/billing/util/entity/EntityDao.java
@@ -26,7 +26,10 @@ import java.util.List;
 
 public interface EntityDao<T extends Entity> {
     @SqlUpdate
-    public void save(@BindBean T entity);
+    public void create(@BindBean T entity);
+
+    @SqlUpdate
+    public void update(@BindBean T entity);
 
     @SqlQuery
     public T getById(@Bind("id") final String id);
diff --git a/util/src/main/java/com/ning/billing/util/glue/NotificationQueueModule.java b/util/src/main/java/com/ning/billing/util/glue/NotificationQueueModule.java
new file mode 100644
index 0000000..f0babf9
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/glue/NotificationQueueModule.java
@@ -0,0 +1,29 @@
+/*
+ * 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.notificationq.DefaultNotificationQueueService;
+import com.ning.billing.util.notificationq.NotificationQueueService;
+
+public class NotificationQueueModule extends AbstractModule {
+
+    @Override
+    protected void configure() {
+        bind(NotificationQueueService.class).to(DefaultNotificationQueueService.class).asEagerSingleton();
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java b/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.java
new file mode 100644
index 0000000..818d831
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/notificationq/dao/NotificationSqlDao.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.notificationq.dao;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
+import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+import com.ning.billing.util.notificationq.DefaultNotification;
+import com.ning.billing.util.notificationq.Notification;
+import com.ning.billing.util.notificationq.NotificationLifecycle.NotificationLifecycleState;
+
+@ExternalizedSqlViaStringTemplate3()
+public interface NotificationSqlDao extends Transactional<NotificationSqlDao>, CloseMe {
+
+    //
+    // APIs for event notifications
+    //
+    @SqlQuery
+    @Mapper(NotificationSqlMapper.class)
+    public List<Notification> getReadyNotifications(@Bind("now") Date now, @Bind("max") int max);
+
+    @SqlUpdate
+    public int claimNotification(@Bind("owner") String owner, @Bind("next_available") Date nextAvailable, @Bind("notification_id") String eventId, @Bind("now") Date now);
+
+    @SqlUpdate
+    public void clearNotification(@Bind("notification_id") String eventId, @Bind("owner") String owner);
+
+    @SqlUpdate
+    public void insertNotification(@Bind(binder = NotificationSqlDaoBinder.class) Notification evt);
+
+    @SqlUpdate
+    public void insertClaimedHistory(@Bind("sequence_id") int sequenceId, @Bind("owner") String owner, @Bind("claimed_dt") Date clainedDate, @Bind("notification_id") String notificationId);
+
+    public static class NotificationSqlDaoBinder implements Binder<Bind, Notification> {
+
+        private Date getDate(DateTime dateTime) {
+            return dateTime == null ? null : dateTime.toDate();
+        }
+
+        @Override
+        public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, Notification evt) {
+            stmt.bind("notification_id", evt.getId().toString());
+            stmt.bind("created_dt", getDate(new DateTime()));
+            stmt.bind("notification_key", evt.getNotificationKey());
+            stmt.bind("effective_dt", getDate(evt.getEffectiveDate()));
+            stmt.bind("processing_available_dt", getDate(evt.getNextAvailableDate()));
+            stmt.bind("processing_owner", (String) null);
+            stmt.bind("processing_state", NotificationLifecycleState.AVAILABLE.toString());
+        }
+    }
+
+
+    public static class NotificationSqlMapper implements ResultSetMapper<Notification> {
+
+        private DateTime getDate(ResultSet r, String fieldName) throws SQLException {
+            final Timestamp resultStamp = r.getTimestamp(fieldName);
+            return r.wasNull() ? null : new DateTime(resultStamp).toDateTime(DateTimeZone.UTC);
+        }
+
+        @Override
+        public Notification map(int index, ResultSet r, StatementContext ctx)
+        throws SQLException {
+
+            final UUID id = UUID.fromString(r.getString("notification_id"));
+            final String notificationKey = r.getString("notification_key");
+            final DateTime effectiveDate = getDate(r, "effective_dt");
+            final DateTime nextAvailableDate = getDate(r, "processing_available_dt");
+            final String processingOwner = r.getString("processing_owner");
+            final NotificationLifecycleState processingState = NotificationLifecycleState.valueOf(r.getString("processing_state"));
+
+            return new DefaultNotification(id, processingOwner, nextAvailableDate,
+                    processingState, notificationKey, effectiveDate);
+
+        }
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotification.java b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotification.java
new file mode 100644
index 0000000..2946e13
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotification.java
@@ -0,0 +1,97 @@
+/*
+ * 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.notificationq;
+
+import java.util.UUID;
+
+import org.joda.time.DateTime;
+
+public class DefaultNotification implements Notification {
+
+    private final UUID id;
+    private final String owner;
+    private final DateTime nextAvailableDate;
+    private final NotificationLifecycleState lifecycleState;
+    private final String notificationKey;
+    private final DateTime effectiveDate;
+
+
+    public DefaultNotification(UUID id, String owner, DateTime nextAvailableDate,
+            NotificationLifecycleState lifecycleState,
+            String notificationKey, DateTime effectiveDate) {
+        super();
+        this.id = id;
+        this.owner = owner;
+        this.nextAvailableDate = nextAvailableDate;
+        this.lifecycleState = lifecycleState;
+        this.notificationKey = notificationKey;
+        this.effectiveDate = effectiveDate;
+    }
+
+    public DefaultNotification(String notificationKey, DateTime effectiveDate) {
+        this(UUID.randomUUID(), null, null, NotificationLifecycleState.AVAILABLE, notificationKey, effectiveDate);
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public String getOwner() {
+        return owner;
+    }
+
+    @Override
+    public DateTime getNextAvailableDate() {
+        return nextAvailableDate;
+    }
+
+    @Override
+    public NotificationLifecycleState getProcessingState() {
+        return lifecycleState;
+    }
+
+    @Override
+    public boolean isAvailableForProcessing(DateTime now) {
+        switch(lifecycleState) {
+        case AVAILABLE:
+            break;
+        case IN_PROCESSING:
+            // Somebody already got the event, not available yet
+            if (nextAvailableDate.isAfter(now)) {
+                return false;
+            }
+            break;
+        case PROCESSED:
+            return false;
+        default:
+            throw new RuntimeException(String.format("Unkwnon IEvent processing state %s", lifecycleState));
+        }
+        return effectiveDate.isBefore(now);
+    }
+
+    @Override
+    public String getNotificationKey() {
+        return notificationKey;
+    }
+
+    @Override
+    public DateTime getEffectiveDate() {
+        return effectiveDate;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
new file mode 100644
index 0000000..80f7385
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueue.java
@@ -0,0 +1,119 @@
+/*
+ * 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.notificationq;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
+import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
+
+public class DefaultNotificationQueue extends NotificationQueueBase {
+
+    protected final NotificationSqlDao dao;
+
+    public DefaultNotificationQueue(final DBI dbi, final Clock clock,  final String svcName, final String queueName, final NotificationQueueHandler handler, final NotificationConfig config) {
+        super(clock, svcName, queueName, handler, config);
+        this.dao = dbi.onDemand(NotificationSqlDao.class);
+    }
+
+    @Override
+    protected void doProcessEvents(int sequenceId) {
+        List<Notification> notifications = getReadyNotifications(sequenceId);
+        for (Notification cur : notifications) {
+            nbProcessedEvents.incrementAndGet();
+            handler.handleReadyNotification(cur.getNotificationKey());
+        }
+        // If anything happens before we get to clear those notifications, somebody else will pick them up
+        clearNotifications(notifications);
+    }
+
+    @Override
+    public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao,
+            final DateTime futureNotificationTime, final NotificationKey notificationKey) {
+        NotificationSqlDao transactionalNotificationDao =  transactionalDao.become(NotificationSqlDao.class);
+        Notification notification = new DefaultNotification(notificationKey.toString(), futureNotificationTime);
+        transactionalNotificationDao.insertNotification(notification);
+    }
+
+
+    private void clearNotifications(final Collection<Notification> cleared) {
+
+        log.debug(String.format("NotificationQueue %s clearEventsReady START cleared size = %d",
+                getFullQName(),
+                cleared.size()));
+
+        dao.inTransaction(new Transaction<Void, NotificationSqlDao>() {
+
+            @Override
+            public Void inTransaction(NotificationSqlDao transactional,
+                    TransactionStatus status) throws Exception {
+                for (Notification cur : cleared) {
+                    transactional.clearNotification(cur.getId().toString(), hostname);
+                    log.debug(String.format("NotificationQueue %s cleared events %s", getFullQName(), cur.getId()));
+                }
+                return null;
+            }
+        });
+    }
+
+    private List<Notification> getReadyNotifications(final int seqId) {
+
+        final Date now = clock.getUTCNow().toDate();
+        final Date nextAvailable = clock.getUTCNow().plus(config.getDaoClaimTimeMs()).toDate();
+
+        log.debug(String.format("NotificationQueue %s getEventsReady START effectiveNow =  %s",  getFullQName(), now));
+
+        List<Notification> result = dao.inTransaction(new Transaction<List<Notification>, NotificationSqlDao>() {
+
+            @Override
+            public List<Notification> inTransaction(NotificationSqlDao transactionalDao,
+                    TransactionStatus status) throws Exception {
+
+                List<Notification> claimedNotifications = new ArrayList<Notification>();
+                List<Notification> input = transactionalDao.getReadyNotifications(now, config.getDaoMaxReadyEvents());
+                for (Notification cur : input) {
+                    final boolean claimed = (transactionalDao.claimNotification(hostname, nextAvailable, cur.getId().toString(), now) == 1);
+                    if (claimed) {
+                        claimedNotifications.add(cur);
+                        transactionalDao.insertClaimedHistory(seqId, hostname, now, cur.getId().toString());
+                    }
+                }
+                return claimedNotifications;
+            }
+        });
+
+        for (Notification cur : result) {
+            log.debug(String.format("NotificationQueue %sclaimed events %s",
+                    getFullQName(), cur.getId()));
+            if (cur.getOwner() != null && !cur.getOwner().equals(hostname)) {
+                log.warn(String.format("NotificationQueue %s stealing notification %s from %s",
+                        getFullQName(), cur, cur.getOwner()));
+            }
+        }
+        return result;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueueService.java b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueueService.java
new file mode 100644
index 0000000..5181113
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/notificationq/DefaultNotificationQueueService.java
@@ -0,0 +1,41 @@
+/*
+ * 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.notificationq;
+
+import org.skife.jdbi.v2.DBI;
+
+import com.google.inject.Inject;
+import com.ning.billing.util.clock.Clock;
+
+public class DefaultNotificationQueueService extends NotificationQueueServiceBase {
+
+    private final DBI dbi;
+
+    @Inject
+    public DefaultNotificationQueueService(final DBI dbi, final Clock clock) {
+        super(clock);
+        this.dbi = dbi;
+    }
+
+    @Override
+    protected NotificationQueue createNotificationQueueInternal(String svcName,
+            String queueName, NotificationQueueHandler handler,
+            NotificationConfig config) {
+        return new DefaultNotificationQueue(dbi, clock, svcName, queueName, handler, config);
+    }
+
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationKey.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationKey.java
new file mode 100644
index 0000000..bfe4aa9
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationKey.java
@@ -0,0 +1,27 @@
+/*
+ * 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.notificationq;
+
+/**
+ *
+ * The notification key associated with a given notification
+ */
+public interface NotificationKey {
+
+    @Override
+    public String toString();
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
new file mode 100644
index 0000000..23f0de0
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueue.java
@@ -0,0 +1,59 @@
+/*
+ * 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.notificationq;
+
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
+
+public interface NotificationQueue {
+
+    /**
+    *
+    *  Record from within a transaction the need to be called back when the notification is ready
+    *
+    * @param transactionalDao the transactionalDao
+    * @param futureNotificationTime the time at which the notification is ready
+    * @param notificationKey the key for that notification
+    */
+   public void recordFutureNotificationFromTransaction(final Transmogrifier transactionalDao,
+           final DateTime futureNotificationTime, final NotificationKey notificationKey);
+
+   /**
+    * This is only valid when the queue has been configured with isNotificationProcessingOff is true
+    * In which case, it will callback users for all the ready notifications.
+    *
+    */
+   public void processReadyNotification();
+
+   /**
+    * Stops the queue.
+    *
+    * @see NotificationQueueHandler.completedQueueStop to be notified when the notification thread exited
+    */
+   public void stopQueue();
+
+   /**
+    * Starts the queue.
+    *
+    * @see NotificationQueueHandler.completedQueueStart to be notified when the notification thread started
+    */
+   public void startQueue();
+
+
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueBase.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueBase.java
new file mode 100644
index 0000000..cefd1eb
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueBase.java
@@ -0,0 +1,193 @@
+/*
+ * 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.notificationq;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.ning.billing.util.Hostname;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
+import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
+
+
+public abstract class NotificationQueueBase implements NotificationQueue {
+
+    protected final static Logger log = LoggerFactory.getLogger(NotificationQueueBase.class);
+
+    protected static final String NOTIFICATION_THREAD_PREFIX = "Notification-";
+    protected final long STOP_WAIT_TIMEOUT_MS = 60000;
+
+    protected final String svcName;
+    protected final String queueName;
+    protected final NotificationQueueHandler handler;
+    protected final NotificationConfig config;
+
+    protected final Executor executor;
+    protected final Clock clock;
+    protected final String hostname;
+
+    protected static final AtomicInteger sequenceId = new AtomicInteger();
+
+    protected AtomicLong nbProcessedEvents;
+
+    // Use this object's monitor for synchronization (no need for volatile)
+    protected boolean isProcessingEvents;
+
+    // Package visibility on purpose
+    NotificationQueueBase(final Clock clock,  final String svcName, final String queueName, final NotificationQueueHandler handler, final NotificationConfig config) {
+        this.clock = clock;
+        this.svcName = svcName;
+        this.queueName = queueName;
+        this.handler = handler;
+        this.config = config;
+        this.hostname = Hostname.get();
+
+        this.executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                Thread th = new Thread(r);
+                th.setName(NOTIFICATION_THREAD_PREFIX + svcName + "-" + queueName);
+                th.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+                    @Override
+                    public void uncaughtException(Thread t, Throwable e) {
+                        log.error("Uncaught exception for thread " + t.getName(), e);
+                    }
+                });
+                return th;
+            }
+        });
+    }
+
+
+    @Override
+    public void processReadyNotification() {
+        doProcessEvents(sequenceId.incrementAndGet());
+    }
+
+
+    @Override
+    public void stopQueue() {
+        if (config.isNotificationProcessingOff()) {
+            handler.completedQueueStop();
+            return;
+        }
+
+        synchronized(this) {
+            isProcessingEvents = false;
+            try {
+                log.info("NotificationQueue requested to stop");
+                wait(STOP_WAIT_TIMEOUT_MS);
+                log.info("NotificationQueue requested should have exited");
+            } catch (InterruptedException e) {
+                log.warn("NotificationQueue got interrupted exception when stopping notifications", e);
+            }
+        }
+
+    }
+
+    @Override
+    public void startQueue() {
+
+        this.isProcessingEvents = true;
+        this.nbProcessedEvents = new AtomicLong();
+
+
+        if (config.isNotificationProcessingOff()) {
+            log.warn(String.format("KILLBILL NOTIFICATION PROCESSING FOR SVC %s IS OFF !!!", getFullQName()));
+            handler.completedQueueStart();
+            return;
+        }
+        final NotificationQueueBase notificationQueue = this;
+
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+
+                log.info(String.format("NotificationQueue thread %s [%d] started",
+                        Thread.currentThread().getName(),
+                        Thread.currentThread().getId()));
+
+                // Thread is now started, notify the listener
+                handler.completedQueueStart();
+
+                try {
+                    while (true) {
+
+                        synchronized (notificationQueue) {
+                            if (!isProcessingEvents) {
+                                log.info(String.format("NotificationQueue has been requested to stop, thread  %s  [%d] stopping...",
+                                        Thread.currentThread().getName(),
+                                        Thread.currentThread().getId()));
+                                notificationQueue.notify();
+                                break;
+                            }
+                        }
+
+                        // Callback may trigger exceptions in user code so catch anything here and live with it.
+                        try {
+                            doProcessEvents(sequenceId.getAndIncrement());
+                        } catch (Exception e) {
+                            log.error(String.format("NotificationQueue thread  %s  [%d] got an exception..",
+                                    Thread.currentThread().getName(),
+                                    Thread.currentThread().getId()), e);
+                        }
+                        sleepALittle();
+                    }
+                } catch (InterruptedException e) {
+                    log.warn(Thread.currentThread().getName() + " got interrupted ", e);
+                } catch (Throwable e) {
+                    log.error(Thread.currentThread().getName() + " got an exception exiting...", e);
+                    // Just to make it really obvious in the log
+                    e.printStackTrace();
+                } finally {
+                    handler.completedQueueStop();
+                    log.info(String.format("NotificationQueue thread  %s  [%d] exited...",
+                            Thread.currentThread().getName(),
+                            Thread.currentThread().getId()));
+                }
+            }
+
+            private void sleepALittle() throws InterruptedException {
+                Thread.sleep(config.getNotificationSleepTimeMs());
+            }
+        });
+    }
+
+
+    protected String getFullQName() {
+        return svcName + ":" +  queueName;
+    }
+
+    protected abstract void doProcessEvents(int sequenceId);
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueService.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueService.java
new file mode 100644
index 0000000..a18906b
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueService.java
@@ -0,0 +1,86 @@
+/*
+ * 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.notificationq;
+
+import java.util.NoSuchElementException;
+
+
+public interface NotificationQueueService {
+
+    public interface NotificationQueueHandler {
+        /**
+         * Called when the Notification thread has been started
+         */
+        public void completedQueueStart();
+
+        /**
+         * Called for each notification ready
+         *
+         * @param key the notification key associated to that notification entry
+         */
+        public void handleReadyNotification(String notificationKey);
+        /**
+         * Called right before the Notification thread is about to exit
+         */
+        public void completedQueueStop();
+    }
+
+    public static final class NotficationQueueAlreadyExists extends Exception {
+        private static final long serialVersionUID = 1541281L;
+
+        public NotficationQueueAlreadyExists(String msg) {
+            super(msg);
+        }
+    }
+
+    public static final class NoSuchNotificationQueue extends Exception {
+        private static final long serialVersionUID = 1541281L;
+
+        public NoSuchNotificationQueue(String msg) {
+            super(msg);
+        }
+    }
+
+    /**
+     * Creates a new NotificationQueue for a given associated with the given service and queueName
+     *
+     * @param svcName the name of the service using that queue
+     * @param queueName a name for that queue (unique per service)
+     * @param handler the handler required for notifying the caller of state change
+     * @param config the notification queue configuration
+     *
+     * @return a new NotificationQueue
+     *
+     * @throws NotficationQueueAlreadyExists is the queue associated with that service and name already exits
+     *
+     */
+    NotificationQueue createNotificationQueue(final String svcName, final String queueName, final NotificationQueueHandler handler, final NotificationConfig config)
+        throws NotficationQueueAlreadyExists;
+
+    /**
+     * Retrieves an already created NotificationQueue by service and name if it exists
+     *
+     * @param svcName
+     * @param queueName
+     * @return
+     *
+     * @throws NoSuchNotificationQueue if queue does not exist
+     */
+    NotificationQueue getNotificationQueue(final String svcName, final String queueName)
+        throws NoSuchNotificationQueue;
+
+}
diff --git a/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueServiceBase.java b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueServiceBase.java
new file mode 100644
index 0000000..a4dc64e
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/notificationq/NotificationQueueServiceBase.java
@@ -0,0 +1,90 @@
+/*
+ * 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.notificationq;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.inject.Inject;
+import com.ning.billing.util.clock.Clock;
+
+public abstract class NotificationQueueServiceBase implements NotificationQueueService {
+
+    protected final Logger log = LoggerFactory.getLogger(DefaultNotificationQueueService.class);
+
+    protected final Clock clock;
+
+    private final Map<String, NotificationQueue> queues;
+
+    @Inject
+    public NotificationQueueServiceBase(final Clock clock) {
+
+        this.clock = clock;
+        this.queues = new TreeMap<String, NotificationQueue>();
+    }
+
+    @Override
+    public NotificationQueue createNotificationQueue(String svcName,
+            String queueName, NotificationQueueHandler handler,
+            NotificationConfig config) throws NotficationQueueAlreadyExists {
+        if (svcName == null || queueName == null || handler == null || config == null) {
+            throw new RuntimeException("Need to specify all parameters");
+        }
+
+        String compositeName = getCompositeName(svcName, queueName);
+        NotificationQueue result = null;
+        synchronized(queues) {
+            result = queues.get(compositeName);
+            if (result != null) {
+                throw new NotficationQueueAlreadyExists(String.format("Queue for svc %s and name %s already exist",
+                        svcName, queueName));
+            }
+            result = createNotificationQueueInternal(svcName, queueName, handler, config);
+            queues.put(compositeName, result);
+        }
+        return result;
+    }
+
+    @Override
+    public NotificationQueue getNotificationQueue(String svcName,
+            String queueName) throws NoSuchNotificationQueue {
+
+        NotificationQueue result = null;
+        String compositeName = getCompositeName(svcName, queueName);
+        synchronized(queues) {
+            result = queues.get(compositeName);
+            if (result == null) {
+                throw new NoSuchNotificationQueue(String.format("Queue for svc %s and name %s does not exist",
+                        svcName, queueName));
+            }
+        }
+        return result;
+    }
+
+
+    protected abstract NotificationQueue createNotificationQueueInternal(String svcName,
+            String queueName, NotificationQueueHandler handler,
+            NotificationConfig config);
+
+
+    private String getCompositeName(String svcName, String queueName) {
+        return svcName + ":" + queueName;
+    }
+}
diff --git a/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java b/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java
index 4938d49..6bc029f 100644
--- a/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java
+++ b/util/src/main/java/com/ning/billing/util/tag/dao/TagDescriptionDao.java
@@ -43,7 +43,11 @@ import org.skife.jdbi.v2.tweak.ResultSetMapper;
 public interface TagDescriptionDao extends EntityDao<TagDescription> {
     @Override
     @SqlUpdate
-    public void save(@TagDescriptionBinder TagDescription entity);
+    public void create(@TagDescriptionBinder TagDescription entity);
+
+    @Override
+    @SqlUpdate
+    public void update(@TagDescriptionBinder TagDescription entity);
 
     public class TagDescriptionMapper implements ResultSetMapper<TagDescription> {
         @Override
diff --git a/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java b/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java
index 6a3062e..5d49fb2 100644
--- a/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java
+++ b/util/src/main/java/com/ning/billing/util/tag/DefaultTagStore.java
@@ -20,12 +20,12 @@ import java.util.UUID;
 import com.ning.billing.util.entity.EntityCollectionBase;
 
 public class DefaultTagStore extends EntityCollectionBase<Tag> implements TagStore {
-    public DefaultTagStore(UUID objectId, String objectType) {
+    public DefaultTagStore(final UUID objectId, final String objectType) {
         super(objectId, objectType);
     }
 
     @Override
-    public String getEntityKey(Tag entity) {
+    public String getEntityKey(final Tag entity) {
         return entity.getName();
     }
 
@@ -58,14 +58,14 @@ public class DefaultTagStore extends EntityCollectionBase<Tag> implements TagSto
     }
 
     @Override
-    public void remove(String tagName) {
+    public void remove(final String tagName) {
         entities.remove(entities.get(tagName));
     }
 
     @Override
-    public boolean containsTag(String tagName) {
+    public boolean containsTag(final String tagName) {
         for (Tag tag : entities.values()) {
-            if (tag.getName() == tagName) {
+            if (tag.getName().equals(tagName)) {
                 return true;
             }
         }
diff --git a/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg b/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg
index c995694..57163a9 100644
--- a/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/customfield/dao/FieldStoreDao.sql.stg
@@ -13,6 +13,11 @@ load() ::= <<
     WHERE object_id = :objectId AND object_type = :objectType;
 >>
 
+clear() ::= <<
+  DELETE FROM custom_fields
+  WHERE object_id = :objectId AND object_type = :objectType;
+>>
+
 test() ::= <<
   SELECT 1 FROM custom_fields;
 >>
diff --git a/util/src/main/resources/com/ning/billing/util/ddl.sql b/util/src/main/resources/com/ning/billing/util/ddl.sql
new file mode 100644
index 0000000..30471c7
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/ddl.sql
@@ -0,0 +1,64 @@
+DROP TABLE IF EXISTS custom_fields;
+CREATE TABLE custom_fields (
+  id char(36) NOT NULL,
+  object_id char(36) NOT NULL,
+  object_type varchar(30) NOT NULL,
+  field_name varchar(30) NOT NULL,
+  field_value varchar(255) NOT NULL,
+  PRIMARY KEY(id)
+) ENGINE=innodb;
+CREATE INDEX custom_fields_object_id_object_type ON custom_fields(object_id, object_type);
+CREATE UNIQUE INDEX custom_fields_unique ON custom_fields(object_id, object_type, field_name);
+
+DROP TABLE IF EXISTS tag_descriptions;
+CREATE TABLE tag_descriptions (
+  id char(36) NOT NULL,
+  name varchar(20) NOT NULL,
+  created_by varchar(50) NOT NULL,
+  creation_date datetime NOT NULL,
+  description varchar(200) NOT NULL,
+  generate_invoice boolean DEFAULT false,
+  process_payment boolean DEFAULT false,
+  PRIMARY KEY(id)
+) ENGINE=innodb;
+CREATE UNIQUE INDEX tag_descriptions_name ON tag_descriptions(name);
+
+DROP TABLE IF EXISTS tags;
+CREATE TABLE tags (
+  id char(36) NOT NULL,
+  tag_description_id char(36) NOT NULL,
+  object_id char(36) NOT NULL,
+  object_type varchar(30) NOT NULL,
+  date_added datetime NOT NULL,
+  added_by varchar(50) NOT NULL,
+  PRIMARY KEY(id)
+) ENGINE = innodb;
+CREATE INDEX tags_by_object ON tags(object_id);
+CREATE UNIQUE INDEX tags_unique ON tags(tag_description_id, object_id);
+
+DROP TABLE IF EXISTS notifications;
+CREATE TABLE notifications (
+    id int(11) unsigned NOT NULL AUTO_INCREMENT,
+    notification_id char(36) NOT NULL,
+    created_dt datetime NOT NULL,
+	notification_key varchar(256) NOT NULL,
+    effective_dt datetime NOT NULL,
+    processing_owner char(36) DEFAULT NULL,
+    processing_available_dt datetime DEFAULT NULL,
+    processing_state varchar(14) DEFAULT 'AVAILABLE',
+    PRIMARY KEY(id)
+) ENGINE=innodb;
+CREATE INDEX  `idx_comp_where` ON notifications (`effective_dt`,`processing_state`,`processing_owner`,`processing_available_dt`);
+CREATE INDEX  `idx_update` ON notifications (`notification_id`,`processing_state`,`processing_owner`,`processing_available_dt`);
+CREATE INDEX  `idx_update1` ON notifications (`notification_id`,`processing_owner`);
+CREATE INDEX  `idx_get_ready` ON notifications (`effective_dt`,`created_dt`,`id`);
+
+DROP TABLE IF EXISTS claimed_notifications;
+CREATE TABLE claimed_notifications (
+    id int(11) unsigned NOT NULL AUTO_INCREMENT,    
+    sequence_id int(11) unsigned NOT NULL,    
+    owner_id varchar(64) NOT NULL,
+    claimed_dt datetime NOT NULL,
+    notification_id char(36) NOT NULL,
+    PRIMARY KEY(id)
+) ENGINE=innodb;
diff --git a/util/src/main/resources/com/ning/billing/util/notificationq/dao/NotificationSqlDao.sql.stg b/util/src/main/resources/com/ning/billing/util/notificationq/dao/NotificationSqlDao.sql.stg
new file mode 100644
index 0000000..5a44431
--- /dev/null
+++ b/util/src/main/resources/com/ning/billing/util/notificationq/dao/NotificationSqlDao.sql.stg
@@ -0,0 +1,83 @@
+group NotificationSqlDao;
+
+getReadyNotifications(now, max) ::= <<
+    select
+      notification_id
+    , notification_key
+      , created_dt
+      , effective_dt
+      , processing_owner
+      , processing_available_dt
+      , processing_state
+    from notifications
+    where
+      effective_dt \<= :now
+      and processing_state != 'PROCESSED'
+      and (processing_owner IS NULL OR processing_available_dt \<= :now)
+    order by
+      effective_dt asc
+      , created_dt asc
+      , id asc
+    limit :max
+    ;
+>>
+
+
+claimNotification(owner, next_available, notification_id, now) ::= <<
+    update notifications
+    set
+      processing_owner = :owner
+      , processing_available_dt = :next_available
+      , processing_state = 'IN_PROCESSING'
+    where
+      notification_id = :notification_id
+      and processing_state != 'PROCESSED'
+      and (processing_owner IS NULL OR processing_available_dt \<= :now)
+    ;
+>>
+
+clearNotification(notification_id, owner) ::= <<
+    update notifications
+    set
+      processing_owner = NULL
+      , processing_state = 'PROCESSED'
+    where
+      notification_id = :notification_id
+      and processing_owner = :owner
+    ;
+>>
+
+insertNotification() ::= <<
+    insert into notifications (
+      notification_id
+    , notification_key
+      , created_dt
+      , effective_dt
+      , processing_owner
+      , processing_available_dt
+      , processing_state
+    ) values (
+      :notification_id
+      , :notification_key
+      , :created_dt
+      , :effective_dt
+      , :processing_owner
+      , :processing_available_dt
+      , :processing_state
+    );   
+>>
+
+
+insertClaimedHistory(sequence_id, owner, hostname, claimed_dt, notification_id) ::= <<
+    insert into claimed_notifications (
+        sequence_id
+        , owner_id
+        , claimed_dt
+        , notification_id
+      ) values (
+        :sequence_id
+        , :owner
+        , :claimed_dt
+        , :notification_id
+      );
+>>
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg
index bdde261..c71d063 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagDescriptionDao.sql.stg
@@ -1,12 +1,15 @@
 group TagDescriptionDao;
 
-save() ::= <<
+create() ::= <<
   INSERT INTO tag_descriptions(id, name, created_by, creation_date, description, generate_invoice, process_payment)
-  VALUES(:id, :name, :createdBy, :creationDate, :description, :generateInvoice, :processPayment)
-  ON DUPLICATE KEY UPDATE
-    name = :name, created_by = :createdBy, creation_date = :creationDate,
-    description := description, generate_invoice = :generateInvoice,
-    process_payment = :processPayment
+  VALUES(:id, :name, :createdBy, :creationDate, :description, :generateInvoice, :processPayment);
+>>
+
+update() ::= <<
+  UPDATE tag_descriptions
+  SET name = :name, created_by = :createdBy, creation_date = :creationDate,
+      description = :description, generate_invoice = :generateInvoice, process_payment = :processPayment)
+  WHERE id = :id;
 >>
 
 load() ::= <<
diff --git a/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg b/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg
index 3b53b2e..e72555d 100644
--- a/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg
+++ b/util/src/main/resources/com/ning/billing/util/tag/dao/TagStoreDao.sql.stg
@@ -17,6 +17,11 @@ load() ::= <<
     WHERE t.object_id = :objectId AND t.object_type = :objectType;
 >>
 
+clear() ::= <<
+  DELETE FROM tags
+  WHERE object_id = :objectId AND object_type = :objectType;
+>>
+
 test() ::= <<
   SELECT 1 FROM tags;
 >>
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 0fb473d..7698697 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
@@ -76,6 +76,15 @@ public class ClockMock extends DefaultClock {
         deltaFromRealityMs = delta;
     }
 
+    public synchronized void addDeltaFromReality(long delta) {
+        if (deltaType != DeltaType.DELTA_ABS) {
+            throw new RuntimeException("ClockMock should be set with type DELTA_ABS");
+        }
+        deltaFromRealityDuration = null;
+        deltaFromRealitDurationEpsilon = 0;
+        deltaFromRealityMs += delta;
+    }
+
     public synchronized void resetDeltaFromReality() {
         deltaType = DeltaType.DELTA_NONE;
         deltaFromRealityDuration = null;
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
index 47691be..4b0f4a2 100644
--- a/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java
+++ b/util/src/test/java/com/ning/billing/util/eventbus/TestEventBus.java
@@ -71,7 +71,7 @@ public class TestEventBus {
         @Subscribe
         public synchronized void processEvent(MyEvent event) {
             gotEvents++;
-            log.info("Got event {} {}", event.name, event.value);
+            //log.debug("Got event {} {}", event.name, event.value);
         }
 
         public synchronized boolean waitForCompletion(long timeoutMs) {
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java b/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
new file mode 100644
index 0000000..89dfd76
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/notificationq/dao/TestNotificationSqlDao.java
@@ -0,0 +1,212 @@
+/*
+ * 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.notificationq.dao;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.commons.io.IOUtils;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.skife.config.ConfigurationObjectFactory;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.tweak.HandleCallback;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterSuite;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.ning.billing.dbi.DBIProvider;
+import com.ning.billing.dbi.DbiConfig;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.util.notificationq.DefaultNotification;
+import com.ning.billing.util.notificationq.Notification;
+import com.ning.billing.util.notificationq.NotificationLifecycle.NotificationLifecycleState;
+import com.ning.billing.util.notificationq.dao.NotificationSqlDao.NotificationSqlMapper;
+
+@Guice(modules = TestNotificationSqlDao.TestNotificationSqlDaoModule.class)
+public class TestNotificationSqlDao {
+
+    private static AtomicInteger sequenceId = new AtomicInteger();
+
+    @Inject
+    private DBI dbi;
+
+    @Inject
+    MysqlTestingHelper helper;
+
+    private NotificationSqlDao dao;
+
+    private void startMysql() throws IOException, ClassNotFoundException, SQLException {
+
+
+        final String ddl = IOUtils.toString(NotificationSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+        helper.startMysql();
+        helper.initDb(ddl);
+    }
+
+    @BeforeSuite(alwaysRun = true)
+    public void setup()  {
+        try {
+            startMysql();
+            dao = dbi.onDemand(NotificationSqlDao.class);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @AfterSuite(alwaysRun = true)
+    public void stopMysql()
+    {
+        helper.stopMysql();
+    }
+
+
+    @BeforeTest
+    public void cleanupDb() {
+        dbi.withHandle(new HandleCallback<Void>() {
+
+            @Override
+            public Void withHandle(Handle handle) throws Exception {
+                handle.execute("delete from notifications");
+                handle.execute("delete from claimed_notifications");
+                return null;
+            }
+        });
+    }
+
+    @Test
+    public void testBasic() throws InterruptedException {
+
+        final String ownerId = UUID.randomUUID().toString();
+
+        String notificationKey = UUID.randomUUID().toString();
+        DateTime effDt = new DateTime();
+        Notification notif = new DefaultNotification(notificationKey, effDt);
+        dao.insertNotification(notif);
+
+        Thread.sleep(1000);
+        DateTime now = new DateTime();
+        List<Notification> notifications = dao.getReadyNotifications(now.toDate(), 3);
+        assertNotNull(notifications);
+        assertEquals(notifications.size(), 1);
+
+        Notification notification = notifications.get(0);
+        assertEquals(notification.getNotificationKey(), notificationKey);
+        validateDate(notification.getEffectiveDate(), effDt);
+        assertEquals(notification.getOwner(), null);
+        assertEquals(notification.getProcessingState(), NotificationLifecycleState.AVAILABLE);
+        assertEquals(notification.getNextAvailableDate(), null);
+
+        DateTime nextAvailable = now.plusMinutes(5);
+        int res = dao.claimNotification(ownerId, nextAvailable.toDate(), notification.getId().toString(), now.toDate());
+        assertEquals(res, 1);
+        dao.insertClaimedHistory(sequenceId.incrementAndGet(), ownerId, now.toDate(), notification.getId().toString());
+
+        notification = fetchNotification(notification.getId().toString());
+        assertEquals(notification.getNotificationKey(), notificationKey);
+        validateDate(notification.getEffectiveDate(), effDt);
+        assertEquals(notification.getOwner().toString(), ownerId);
+        assertEquals(notification.getProcessingState(), NotificationLifecycleState.IN_PROCESSING);
+        validateDate(notification.getNextAvailableDate(), nextAvailable);
+
+        dao.clearNotification(notification.getId().toString(), ownerId);
+
+        notification = fetchNotification(notification.getId().toString());
+        assertEquals(notification.getNotificationKey(), notificationKey);
+        validateDate(notification.getEffectiveDate(), effDt);
+        assertEquals(notification.getOwner(), null);
+        assertEquals(notification.getProcessingState(), NotificationLifecycleState.PROCESSED);
+        validateDate(notification.getNextAvailableDate(), nextAvailable);
+
+    }
+
+    private Notification fetchNotification(final String notificationId) {
+        Notification res =  dbi.withHandle(new HandleCallback<Notification>() {
+
+            @Override
+            public Notification withHandle(Handle handle) throws Exception {
+                Notification res = handle.createQuery("   select" +
+                		" notification_id" +
+                		", notification_key" +
+                		", created_dt" +
+                		", effective_dt" +
+                		", processing_owner" +
+                		", processing_available_dt" +
+                		", processing_state" +
+                		"    from notifications " +
+                		" where " +
+                		" notification_id = '" + notificationId + "';")
+                		.map(new NotificationSqlMapper())
+                		.first();
+                return res;
+            }
+        });
+        return res;
+    }
+
+    private void validateDate(DateTime input, DateTime expected) {
+        if (input == null && expected != null) {
+            Assert.fail("Got input date null");
+        }
+        if (input != null && expected == null) {
+            Assert.fail("Was expecting null date");
+        }
+        expected = truncateAndUTC(expected);
+        input = truncateAndUTC(input);
+        Assert.assertEquals(input, expected);
+    }
+
+    private DateTime truncateAndUTC(DateTime input) {
+        if (input == null) {
+            return null;
+        }
+        DateTime result = input.minus(input.getMillisOfSecond());
+        return result.toDateTime(DateTimeZone.UTC);
+    }
+
+    public static class TestNotificationSqlDaoModule extends AbstractModule {
+        @Override
+        protected void configure() {
+
+            final MysqlTestingHelper helper = new MysqlTestingHelper();
+            bind(MysqlTestingHelper.class).toInstance(helper);
+            DBI dbi = helper.getDBI();
+            bind(DBI.class).toInstance(dbi);
+
+            /*
+            bind(DBI.class).toProvider(DBIProvider.class).asEagerSingleton();
+            final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
+            bind(DbiConfig.class).toInstance(config);
+            */
+        }
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/DummyObject.java b/util/src/test/java/com/ning/billing/util/notificationq/DummyObject.java
new file mode 100644
index 0000000..9495001
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/notificationq/DummyObject.java
@@ -0,0 +1,38 @@
+/*
+ * 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.notificationq;
+
+import java.util.UUID;
+
+public class DummyObject {
+    private final String value;
+    private final UUID key;
+
+    public DummyObject(String value, UUID key) {
+        super();
+        this.value = value;
+        this.key = key;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public UUID getKey() {
+        return key;
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/DummySqlTest.java b/util/src/test/java/com/ning/billing/util/notificationq/DummySqlTest.java
new file mode 100644
index 0000000..83b1b20
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/notificationq/DummySqlTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.notificationq;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.UUID;
+
+import org.skife.jdbi.v2.SQLStatement;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.sqlobject.Bind;
+import org.skife.jdbi.v2.sqlobject.Binder;
+import org.skife.jdbi.v2.sqlobject.SqlQuery;
+import org.skife.jdbi.v2.sqlobject.SqlUpdate;
+import org.skife.jdbi.v2.sqlobject.customizers.Mapper;
+import org.skife.jdbi.v2.sqlobject.mixins.CloseMe;
+import org.skife.jdbi.v2.sqlobject.mixins.Transactional;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTemplate3;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+
+@ExternalizedSqlViaStringTemplate3()
+public interface DummySqlTest extends Transactional<DummySqlTest>, Transmogrifier, CloseMe {
+
+    @SqlUpdate
+    public void insertDummy(@Bind(binder = DummySqlTestBinder.class) DummyObject dummy);
+
+    @SqlQuery
+    @Mapper(DummySqlTestMapper.class)
+    public DummyObject getDummyFromId(@Bind("dummy_id") String dummyId);
+
+    public static class DummySqlTestBinder implements Binder<Bind, DummyObject> {
+        @Override
+        public void bind(@SuppressWarnings("rawtypes") SQLStatement stmt, Bind bind, DummyObject dummy) {
+            stmt.bind("dummy_id", dummy.getKey().toString());
+            stmt.bind("value", dummy.getValue());
+        }
+    }
+
+    public static class DummySqlTestMapper  implements ResultSetMapper<DummyObject> {
+        @Override
+        public DummyObject map(int index, ResultSet r, StatementContext ctx)
+                throws SQLException {
+            final UUID key = UUID.fromString(r.getString("dummy_id"));
+            final String value = r.getString("value");
+            return new DummyObject(value, key);
+        }
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
new file mode 100644
index 0000000..7ee2e10
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/notificationq/MockNotificationQueue.java
@@ -0,0 +1,91 @@
+/*
+ * 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.notificationq;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+import org.joda.time.DateTime;
+import org.skife.jdbi.v2.sqlobject.mixins.Transmogrifier;
+
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.notificationq.NotificationLifecycle.NotificationLifecycleState;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
+
+public class MockNotificationQueue extends NotificationQueueBase implements NotificationQueue {
+
+
+    private final TreeSet<Notification> notifications;
+
+    public MockNotificationQueue(final Clock clock,  final String svcName, final String queueName, final NotificationQueueHandler handler, final NotificationConfig config) {
+        super(clock, svcName, queueName, handler, config);
+        notifications = new TreeSet<Notification>(new Comparator<Notification>() {
+            @Override
+            public int compare(Notification o1, Notification o2) {
+                if (o1.getEffectiveDate().equals(o2.getEffectiveDate())) {
+                    return o1.getNotificationKey().compareTo(o2.getNotificationKey());
+                } else {
+                    return o1.getEffectiveDate().compareTo(o2.getEffectiveDate());
+                }
+            }
+        });
+    }
+
+    @Override
+    public void recordFutureNotificationFromTransaction(
+            Transmogrifier transactionalDao, DateTime futureNotificationTime,
+            NotificationKey notificationKey) {
+        Notification notification = new DefaultNotification(notificationKey.toString(), futureNotificationTime);
+        synchronized(notifications) {
+            notifications.add(notification);
+        }
+    }
+
+    @Override
+    protected void doProcessEvents(int sequenceId) {
+
+        List<Notification> processedNotifications = new ArrayList<Notification>();
+        List<Notification> oldNotifications = new ArrayList<Notification>();
+
+        List<Notification> readyNotifications = new ArrayList<Notification>();
+        synchronized(notifications) {
+            Iterator<Notification> it = notifications.iterator();
+            while (it.hasNext()) {
+                Notification cur = it.next();
+                if (cur.isAvailableForProcessing(clock.getUTCNow())) {
+                    readyNotifications.add(cur);
+                }
+            }
+            for (Notification cur : readyNotifications) {
+                handler.handleReadyNotification(cur.getNotificationKey());
+                DefaultNotification processedNotification = new DefaultNotification(cur.getId(), hostname, clock.getUTCNow().plus(config.getDaoClaimTimeMs()), NotificationLifecycleState.PROCESSED, cur.getNotificationKey(), cur.getEffectiveDate());
+                oldNotifications.add(cur);
+                processedNotifications.add(processedNotification);
+
+            }
+            if (oldNotifications.size() > 0) {
+                notifications.removeAll(oldNotifications);
+            }
+            if (processedNotifications.size() > 0) {
+                notifications.addAll(processedNotifications);
+            }
+        }
+    }
+}
diff --git a/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
new file mode 100644
index 0000000..2e3bb3c
--- /dev/null
+++ b/util/src/test/java/com/ning/billing/util/notificationq/TestNotificationQueue.java
@@ -0,0 +1,428 @@
+/*
+ * 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.notificationq;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.UUID;
+
+import org.apache.commons.io.IOUtils;
+import org.joda.time.DateTime;
+import org.skife.config.ConfigurationObjectFactory;
+import org.skife.jdbi.v2.DBI;
+import org.skife.jdbi.v2.Handle;
+import org.skife.jdbi.v2.Transaction;
+import org.skife.jdbi.v2.TransactionStatus;
+import org.skife.jdbi.v2.tweak.HandleCallback;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.testng.annotations.AfterSuite;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeSuite;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Guice;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.inject.AbstractModule;
+import com.google.inject.Inject;
+import com.ning.billing.dbi.DBIProvider;
+import com.ning.billing.dbi.DbiConfig;
+import com.ning.billing.dbi.MysqlTestingHelper;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+import com.ning.billing.util.notificationq.NotificationQueueService.NotificationQueueHandler;
+import com.ning.billing.util.notificationq.dao.NotificationSqlDao;
+
+@Guice(modules = TestNotificationQueue.TestNotificationQueueModule.class)
+public class TestNotificationQueue {
+
+    private final static Logger log = LoggerFactory.getLogger(TestNotificationQueue.class);
+
+    @Inject
+    private DBI dbi;
+
+    @Inject
+    MysqlTestingHelper helper;
+
+    @Inject
+    private Clock clock;
+
+    private DummySqlTest dao;
+
+   // private NotificationQueue queue;
+
+    private void startMysql() throws IOException, ClassNotFoundException, SQLException {
+        final String ddl = IOUtils.toString(NotificationSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl.sql"));
+        final String testDdl = IOUtils.toString(NotificationSqlDao.class.getResourceAsStream("/com/ning/billing/util/ddl_test.sql"));
+        helper.startMysql();
+        helper.initDb(ddl);
+        helper.initDb(testDdl);
+    }
+
+    @BeforeSuite(alwaysRun = true)
+    public void setup() throws Exception {
+        startMysql();
+        dao = dbi.onDemand(DummySqlTest.class);
+    }
+
+    @BeforeTest
+    public void beforeTest() {
+        dbi.withHandle(new HandleCallback<Void>() {
+
+            @Override
+            public Void withHandle(Handle handle) throws Exception {
+                handle.execute("delete from notifications");
+                handle.execute("delete from claimed_notifications");
+                handle.execute("delete from dummy");
+                return null;
+            }
+        });
+        // Reset time to real value
+        ((ClockMock) clock).resetDeltaFromReality();
+    }
+
+
+
+    /**
+     * Verify that we can call start/stop on a disabled queue and that both start/stop callbacks are called
+     *
+     * @throws InterruptedException
+     */
+    @Test
+    public void testSimpleQueueDisabled() throws InterruptedException {
+
+        final TestStartStop testStartStop = new TestStartStop(false, false);
+        DefaultNotificationQueue queue = new DefaultNotificationQueue(dbi, clock, "test-svc", "dead",
+                new NotificationQueueHandler() {
+                    @Override
+                    public void handleReadyNotification(String notificationKey) {
+                    }
+                    @Override
+                    public void completedQueueStop() {
+                        testStartStop.stopped();
+                    }
+                    @Override
+                    public void completedQueueStart() {
+                        testStartStop.started();
+                    }
+                },
+                getNotificationConfig(true, 100, 1, 10000));
+
+        executeTest(testStartStop, queue, new WithTest() {
+            @Override
+            public void test(final DefaultNotificationQueue readyQueue) throws InterruptedException {
+                // Do nothing
+            }
+        });
+        assertTrue(true);
+    }
+
+    /**
+     * Test that we can post a notification in the future from a transaction and get the notification
+     * callback with the correct key when the time is ready
+     *
+     * @throws InterruptedException
+     */
+    @Test
+    public void testSimpleNotification() throws InterruptedException {
+
+        final Map<String, Boolean> expectedNotifications = new TreeMap<String, Boolean>();
+
+        final TestStartStop testStartStop = new TestStartStop(false, false);
+        DefaultNotificationQueue queue = new DefaultNotificationQueue(dbi, clock, "test-svc", "foo",
+                new NotificationQueueHandler() {
+                    @Override
+                    public void handleReadyNotification(String notificationKey) {
+                        synchronized (expectedNotifications) {
+                            expectedNotifications.put(notificationKey, Boolean.TRUE);
+                            expectedNotifications.notify();
+                        }
+                    }
+                    @Override
+                    public void completedQueueStop() {
+                        testStartStop.stopped();
+                    }
+                    @Override
+                    public void completedQueueStart() {
+                        testStartStop.started();
+                    }
+                },
+                getNotificationConfig(false, 100, 1, 10000));
+
+
+        executeTest(testStartStop, queue, new WithTest() {
+            @Override
+            public void test(final DefaultNotificationQueue readyQueue) throws InterruptedException {
+
+                final UUID key = UUID.randomUUID();
+                final DummyObject obj = new DummyObject("foo", key);
+                final DateTime now = new DateTime();
+                final DateTime readyTime = now.plusMillis(2000);
+                final NotificationKey notificationKey = new NotificationKey() {
+                    @Override
+                    public String toString() {
+                        return key.toString();
+                    }
+                };
+                expectedNotifications.put(notificationKey.toString(), Boolean.FALSE);
+
+
+                // Insert dummy to be processed in 2 sec'
+                dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+                    @Override
+                    public Void inTransaction(DummySqlTest transactional,
+                            TransactionStatus status) throws Exception {
+
+                        transactional.insertDummy(obj);
+                        readyQueue.recordFutureNotificationFromTransaction(transactional,
+                                readyTime, notificationKey);
+                        return null;
+                    }
+                });
+
+                // Move time in the future after the notification effectiveDate
+                ((ClockMock) clock).setDeltaFromReality(3000);
+
+                // Notification should have kicked but give it at least a sec' for thread scheduling
+                int nbTry = 1;
+                boolean success = false;
+                do {
+                    synchronized(expectedNotifications) {
+                        if (expectedNotifications.get(notificationKey.toString())) {
+                            success = true;
+                            break;
+                        }
+                        expectedNotifications.wait(1000);
+                    }
+                } while (nbTry-- > 0);
+                assertEquals(success, true);
+            }
+        });
+    }
+
+    @Test
+    public void testManyNotifications() throws InterruptedException {
+        final Map<String, Boolean> expectedNotifications = new TreeMap<String, Boolean>();
+
+        final TestStartStop testStartStop = new TestStartStop(false, false);
+        DefaultNotificationQueue queue = new DefaultNotificationQueue(dbi, clock, "test-svc", "many",
+                new NotificationQueueHandler() {
+                    @Override
+                    public void handleReadyNotification(String notificationKey) {
+                        synchronized (expectedNotifications) {
+                            expectedNotifications.put(notificationKey, Boolean.TRUE);
+                            expectedNotifications.notify();
+                        }
+                    }
+                    @Override
+                    public void completedQueueStop() {
+                        testStartStop.stopped();
+                    }
+                    @Override
+                    public void completedQueueStart() {
+                        testStartStop.started();
+                    }
+                },
+                getNotificationConfig(false, 100, 10, 10000));
+
+
+        executeTest(testStartStop, queue, new WithTest() {
+            @Override
+            public void test(final DefaultNotificationQueue readyQueue) throws InterruptedException {
+
+                final DateTime now = clock.getUTCNow();
+                final int MAX_NOTIFICATIONS = 100;
+                for (int i = 0; i < MAX_NOTIFICATIONS; i++) {
+
+                    final int nextReadyTimeIncrementMs = 1000;
+
+                    final UUID key = UUID.randomUUID();
+                    final DummyObject obj = new DummyObject("foo", key);
+                    final int currentIteration = i;
+
+                    final NotificationKey notificationKey = new NotificationKey() {
+                        @Override
+                        public String toString() {
+                            return key.toString();
+                        }
+                    };
+                    expectedNotifications.put(notificationKey.toString(), Boolean.FALSE);
+
+                    dao.inTransaction(new Transaction<Void, DummySqlTest>() {
+                        @Override
+                        public Void inTransaction(DummySqlTest transactional,
+                                TransactionStatus status) throws Exception {
+
+                            transactional.insertDummy(obj);
+                            readyQueue.recordFutureNotificationFromTransaction(transactional,
+                                    now.plus((currentIteration + 1) * nextReadyTimeIncrementMs), notificationKey);
+                            return null;
+                        }
+                    });
+
+                    // Move time in the future after the notification effectiveDate
+                    if (i == 0) {
+                        ((ClockMock) clock).setDeltaFromReality(nextReadyTimeIncrementMs);
+                    } else {
+                        ((ClockMock) clock).addDeltaFromReality(nextReadyTimeIncrementMs);
+                    }
+                }
+
+                // Wait a little longer since there are a lot of callback that need to happen
+                int nbTry = MAX_NOTIFICATIONS + 1;
+                boolean success = false;
+                do {
+                    synchronized(expectedNotifications) {
+
+                        Collection<Boolean> completed =  Collections2.filter(expectedNotifications.values(), new Predicate<Boolean>() {
+                            @Override
+                            public boolean apply(Boolean input) {
+                                return input;
+                            }
+                        });
+
+                        if (completed.size() == MAX_NOTIFICATIONS) {
+                            success = true;
+                            break;
+                        }
+                        //log.debug(String.format("BEFORE WAIT : Got %d notifications at time %s (real time %s)", completed.size(), clock.getUTCNow(), new DateTime()));
+                        expectedNotifications.wait(1000);
+                    }
+                } while (nbTry-- > 0);
+                assertEquals(success, true);
+            }
+        });
+    }
+
+
+    NotificationConfig getNotificationConfig(final boolean off,
+            final long sleepTime, final int maxReadyEvents, final long claimTimeMs) {
+        return new NotificationConfig() {
+            @Override
+            public boolean isNotificationProcessingOff() {
+                return off;
+            }
+            @Override
+            public long getNotificationSleepTimeMs() {
+                return sleepTime;
+            }
+            @Override
+            public int getDaoMaxReadyEvents() {
+                return maxReadyEvents;
+            }
+            @Override
+            public long getDaoClaimTimeMs() {
+                return claimTimeMs;
+            }
+        };
+    }
+
+    private static class TestStartStop {
+        private boolean started;
+        private boolean stopped;
+
+        public TestStartStop(boolean started, boolean stopped) {
+            super();
+            this.started = started;
+            this.stopped = stopped;
+        }
+
+        public void started() {
+            synchronized(this) {
+                started = true;
+                notify();
+            }
+        }
+
+        public void stopped() {
+            synchronized(this) {
+                stopped = true;
+                notify();
+            }
+        }
+
+        public boolean waitForStartComplete(int timeoutMs) throws InterruptedException {
+            return waitForEventCompletion(timeoutMs, true);
+        }
+
+        public boolean waitForStopComplete(int timeoutMs) throws InterruptedException {
+            return waitForEventCompletion(timeoutMs, false);
+        }
+
+        private boolean waitForEventCompletion(int timeoutMs, boolean start) throws InterruptedException {
+            DateTime init = new DateTime();
+            synchronized(this) {
+                while (! ((start ? started : stopped))) {
+                    wait(timeoutMs);
+                    if (init.plusMillis(timeoutMs).isAfterNow()) {
+                        break;
+                    }
+                }
+            }
+            return (start ? started : stopped);
+        }
+    }
+
+    private interface WithTest {
+        public void test(DefaultNotificationQueue readyQueue) throws InterruptedException;
+    }
+
+    private void executeTest(final TestStartStop testStartStop,
+            DefaultNotificationQueue queue, WithTest test) throws InterruptedException{
+
+        queue.startQueue();
+        boolean started = testStartStop.waitForStartComplete(3000);
+        assertEquals(started, true);
+
+        test.test(queue);
+
+        queue.stopQueue();
+        boolean stopped = testStartStop.waitForStopComplete(3000);
+        assertEquals(stopped, true);
+    }
+
+
+    public static class TestNotificationQueueModule extends AbstractModule {
+        @Override
+        protected void configure() {
+
+            bind(Clock.class).to(ClockMock.class);
+
+            final MysqlTestingHelper helper = new MysqlTestingHelper();
+            bind(MysqlTestingHelper.class).toInstance(helper);
+            DBI dbi = helper.getDBI();
+            bind(DBI.class).toInstance(dbi);
+            /*
+            bind(DBI.class).toProvider(DBIProvider.class).asEagerSingleton();
+            final DbiConfig config = new ConfigurationObjectFactory(System.getProperties()).build(DbiConfig.class);
+            bind(DbiConfig.class).toInstance(config);
+            */
+        }
+    }
+
+
+}
diff --git a/util/src/test/resources/com/ning/billing/util/ddl_test.sql b/util/src/test/resources/com/ning/billing/util/ddl_test.sql
new file mode 100644
index 0000000..50de498
--- /dev/null
+++ b/util/src/test/resources/com/ning/billing/util/ddl_test.sql
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS dummy;
+CREATE TABLE dummy (
+    dummy_id char(36) NOT NULL,
+    value varchar(256) NOT NULL,
+    PRIMARY KEY(dummy_id)
+) ENGINE = innodb;
diff --git a/util/src/test/resources/com/ning/billing/util/notificationq/DummySqlTest.sql.stg b/util/src/test/resources/com/ning/billing/util/notificationq/DummySqlTest.sql.stg
new file mode 100644
index 0000000..9a2e6e2
--- /dev/null
+++ b/util/src/test/resources/com/ning/billing/util/notificationq/DummySqlTest.sql.stg
@@ -0,0 +1,21 @@
+group DummySqlTest;
+
+insertDummy() ::= <<
+  insert into dummy (
+    dummy_id
+    , value
+  ) values (
+    :dummy_id
+    , :value
+  );
+>>
+
+getDummyFromId(dummy_id)  ::= <<
+  select 
+    dummy_id
+    , value
+  from dummy
+  where
+    dummy_id = :dummy_id
+  ;
+>> 
\ No newline at end of file