killbill-aplcache

catalog: Remove completely the StandaloneCatalogWithPriceOverride

8/10/2016 7:10:34 PM

Details

diff --git a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
index 254ea4f..b2f1779 100644
--- a/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
@@ -35,7 +35,6 @@ import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.BillingPeriod;
-import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
@@ -120,10 +119,11 @@ public class TestWithCatalogPlugin extends TestIntegrationBase {
 
         public TestCatalogPluginApi(final PriceOverride priceOverride, final InternalTenantContext internalTenantContext, final InternalCallContextFactory internalCallContextFactory) throws Exception {
             final StandaloneCatalog inputCatalog = XMLLoader.getObjectFromString(Resources.getResource("WeaponsHire.xml").toExternalForm(), StandaloneCatalog.class);
-            final List<StandaloneCatalogWithPriceOverride> versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+            final List<StandaloneCatalog> versions = new ArrayList<StandaloneCatalog>();
             final StandaloneCatalogWithPriceOverride standaloneCatalogWithPriceOverride = new StandaloneCatalogWithPriceOverride(inputCatalog, priceOverride, internalTenantContext.getTenantRecordId(), internalCallContextFactory);
             versions.add(standaloneCatalogWithPriceOverride);
-            versionedCatalog = new VersionedCatalog(getClock(), inputCatalog.getCatalogName(), inputCatalog.getRecurringBillingMode(), versions, internalTenantContext);
+            versionedCatalog = new VersionedCatalog(getClock());
+            versionedCatalog.addAll(versions);
         }
 
         @Override
@@ -131,23 +131,19 @@ public class TestWithCatalogPlugin extends TestIntegrationBase {
             return new TestModelVersionedPluginCatalog(versionedCatalog.getCatalogName(), versionedCatalog.getRecurringBillingMode(), toStandalonePluginCatalogs(versionedCatalog.getVersions()));
         }
 
-        private Iterable<StandalonePluginCatalog> toStandalonePluginCatalogs(final List<StandaloneCatalogWithPriceOverride> input) {
-            return Iterables.transform(input, new Function<StandaloneCatalogWithPriceOverride, StandalonePluginCatalog>() {
+        private Iterable<StandalonePluginCatalog> toStandalonePluginCatalogs(final List<StandaloneCatalog> input) {
+            return Iterables.transform(input, new Function<StandaloneCatalog, StandalonePluginCatalog>() {
                 @Override
-                public StandalonePluginCatalog apply(final StandaloneCatalogWithPriceOverride input) {
-                    try {
-
-                        return new TestModelStandalonePluginCatalog(new DateTime(input.getEffectiveDate()),
-                                                                    ImmutableList.copyOf(input.getCurrentSupportedCurrencies()),
-                                                                    ImmutableList.<Product>copyOf(input.getCurrentProducts()),
-                                                                    ImmutableList.<Plan>copyOf(input.getCurrentPlans()),
-                                                                    input.getStandaloneCatalog().getPriceLists().getDefaultPricelist(),
-                                                                    ImmutableList.<PriceList>copyOf(input.getStandaloneCatalog().getPriceLists().getChildPriceLists()),
-                                                                    input.getStandaloneCatalog().getPlanRules(),
-                                                                    null /*ImmutableList.<Unit>copyOf(input.getStandaloneCatalog().getCurrentUnits()) */);
-                    } catch (CatalogApiException e) {
-                        throw new IllegalStateException(e);
-                    }
+                public StandalonePluginCatalog apply(final StandaloneCatalog input) {
+
+                    return new TestModelStandalonePluginCatalog(new DateTime(input.getEffectiveDate()),
+                                                                ImmutableList.copyOf(input.getCurrentSupportedCurrencies()),
+                                                                ImmutableList.<Product>copyOf(input.getCurrentProducts()),
+                                                                ImmutableList.<Plan>copyOf(input.getCurrentPlans()),
+                                                                input.getPriceLists().getDefaultPricelist(),
+                                                                ImmutableList.<PriceList>copyOf(input.getPriceLists().getChildPriceLists()),
+                                                                input.getPlanRules(),
+                                                                null /* ImmutableList.<Unit>copyOf(input.getCurrentUnits()) */);
                 }
 
                 private <I, C extends I> List<I> listOf(@Nullable C[] input) {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
index 338d4aa..2adf38c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/api/user/DefaultCatalogUserApi.java
@@ -133,8 +133,8 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
     private StandaloneCatalog getCurrentStandaloneCatalogForTenant(final InternalTenantContext internalTenantContext) throws CatalogApiException {
         final VersionedCatalog versionedCatalog = (VersionedCatalog) catalogService.getCurrentCatalog(false, false, internalTenantContext);
         if (versionedCatalog != null && !versionedCatalog.getVersions().isEmpty()) {
-            final StandaloneCatalogWithPriceOverride standaloneCatalogWithPriceOverride = versionedCatalog.getVersions().get(versionedCatalog.getVersions().size() - 1);
-            return standaloneCatalogWithPriceOverride.getStandaloneCatalog();
+            final StandaloneCatalog standaloneCatalogWithPriceOverride = versionedCatalog.getVersions().get(versionedCatalog.getVersions().size() - 1);
+            return standaloneCatalogWithPriceOverride;
         } else {
             return null;
         }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
index 2da4c42..ee21422 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheCatalogCache.java
@@ -24,9 +24,12 @@ import javax.inject.Inject;
 import org.killbill.billing.ErrorCode;
 import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.io.VersionedCatalogLoader;
+import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.catalog.plugin.VersionedCatalogMapper;
 import org.killbill.billing.catalog.plugin.api.CatalogPluginApi;
 import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
@@ -55,6 +58,7 @@ public class EhCacheCatalogCache implements CatalogCache {
     private final CacheLoaderArgument cacheLoaderArgument;
     private final OSGIServiceRegistration<CatalogPluginApi> pluginRegistry;
     private final VersionedCatalogMapper versionedCatalogMapper;
+    private final PriceOverride priceOverride;
     private final InternalCallContextFactory internalCallContextFactory;
 
     private VersionedCatalog defaultCatalog;
@@ -64,11 +68,13 @@ public class EhCacheCatalogCache implements CatalogCache {
                                final VersionedCatalogMapper versionedCatalogMapper,
                                final CacheControllerDispatcher cacheControllerDispatcher,
                                final VersionedCatalogLoader loader,
+                               final PriceOverride priceOverride,
                                final InternalCallContextFactory internalCallContextFactory) {
         this.pluginRegistry = pluginRegistry;
         this.versionedCatalogMapper = versionedCatalogMapper;
         this.cacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_CATALOG);
         this.loader = loader;
+        this.priceOverride = priceOverride;
         this.internalCallContextFactory = internalCallContextFactory;
         this.cacheLoaderArgumentWithTemplateFiltering = initializeCacheLoaderArgument(true);
         this.cacheLoaderArgument = initializeCacheLoaderArgument(false);
@@ -102,7 +108,11 @@ public class EhCacheCatalogCache implements CatalogCache {
             // It means we are using a default catalog in a multi-tenant deployment, that does not really match a real use case, but we want to support it
             // for test purpose.
             if (useDefaultCatalog && tenantCatalog == null) {
-                tenantCatalog = new VersionedCatalog(defaultCatalog.getClock(), defaultCatalog.getCatalogName(), defaultCatalog.getRecurringBillingMode(), defaultCatalog.getVersions(), tenantContext);
+                tenantCatalog = new VersionedCatalog(defaultCatalog.getClock());
+                for (final StandaloneCatalog cur : defaultCatalog.getVersions()) {
+                    final StandaloneCatalogWithPriceOverride curWithOverride = new StandaloneCatalogWithPriceOverride(cur, priceOverride, tenantContext.getTenantRecordId(), internalCallContextFactory);
+                    tenantCatalog.add(curWithOverride);
+                }
                 cacheController.add(tenantContext.getTenantRecordId(), tenantCatalog);
             }
             return tenantCatalog;
@@ -118,7 +128,7 @@ public class EhCacheCatalogCache implements CatalogCache {
         }
     }
 
-    private VersionedCatalog getCatalogFromPlugins(final InternalTenantContext internalTenantContext) {
+    private VersionedCatalog getCatalogFromPlugins(final InternalTenantContext internalTenantContext) throws CatalogApiException {
         final TenantContext tenantContext = internalCallContextFactory.createTenantContext(internalTenantContext);
         for (final String service : pluginRegistry.getAllServices()) {
             final CatalogPluginApi plugin = pluginRegistry.getServiceForName(service);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java b/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
index 1c6ba8c..1ee8470 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
@@ -25,6 +25,7 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
+import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.catalog.plugin.api.StandalonePluginCatalog;
 import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
@@ -49,24 +50,18 @@ public class VersionedCatalogMapper {
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
-    public VersionedCatalog toVersionedCatalog(final VersionedPluginCatalog pluginCatalog, final InternalTenantContext internalTenantContext) {
-        final VersionedCatalog result = new VersionedCatalog(clock, pluginCatalog.getCatalogName(), pluginCatalog.getRecurringBillingMode(), toStandaloneCatalogWithPriceOverrideList(pluginCatalog, internalTenantContext), internalTenantContext);
+    public VersionedCatalog toVersionedCatalog(final VersionedPluginCatalog pluginCatalog, final InternalTenantContext internalTenantContext) throws CatalogApiException {
+        final VersionedCatalog result = new VersionedCatalog(clock);
+        for (final StandalonePluginCatalog cur : pluginCatalog.getStandalonePluginCatalogs()) {
+            result.add(toStandaloneCatalogWithPriceOverride(pluginCatalog, cur, internalTenantContext));
+        }
         return result;
     }
 
-    private List<StandaloneCatalogWithPriceOverride> toStandaloneCatalogWithPriceOverrideList(final VersionedPluginCatalog pluginCatalog, final InternalTenantContext internalTenantContext) {
-        return ImmutableList.copyOf(Iterables.transform(pluginCatalog.getStandalonePluginCatalogs(), new Function<StandalonePluginCatalog, StandaloneCatalogWithPriceOverride>() {
-            @Override
-            public StandaloneCatalogWithPriceOverride apply(final StandalonePluginCatalog input) {
-                return toStandaloneCatalogWithPriceOverride(pluginCatalog, input, internalTenantContext);
-            }
-        }));
-    }
-
     private StandaloneCatalogWithPriceOverride toStandaloneCatalogWithPriceOverride(final VersionedPluginCatalog pluginCatalog, final StandalonePluginCatalog input, final InternalTenantContext internalTenantContext) {
         final StandaloneCatalogMapper mapper = new StandaloneCatalogMapper(pluginCatalog.getCatalogName(), pluginCatalog.getRecurringBillingMode());
-        final StandaloneCatalog standaloneCatalog = mapper.toStandaloneCatalog(input, null);
-        final StandaloneCatalogWithPriceOverride result = new StandaloneCatalogWithPriceOverride(standaloneCatalog, priceOverride, internalTenantContext.getTenantRecordId(), internalCallContextFactory);
+        final StandaloneCatalog catalog = mapper.toStandaloneCatalog(input, null);
+        final StandaloneCatalogWithPriceOverride result = new StandaloneCatalogWithPriceOverride(catalog, priceOverride, internalTenantContext.getTenantRecordId(), internalCallContextFactory);
         return result;
     }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
index 76e2c2d..8a458c4 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
@@ -17,45 +17,25 @@
 
 package org.killbill.billing.catalog;
 
-import java.net.URI;
-import java.util.Date;
-import java.util.List;
 import java.util.regex.Matcher;
 
-import javax.annotation.Nullable;
-
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
-import org.killbill.billing.catalog.api.BillingActionPolicy;
-import org.killbill.billing.catalog.api.BillingAlignment;
-import org.killbill.billing.catalog.api.BillingMode;
-import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.CatalogApiException;
-import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.catalog.api.Listing;
 import org.killbill.billing.catalog.api.Plan;
-import org.killbill.billing.catalog.api.PlanAlignmentChange;
-import org.killbill.billing.catalog.api.PlanAlignmentCreate;
-import org.killbill.billing.catalog.api.PlanChangeResult;
 import org.killbill.billing.catalog.api.PlanPhase;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverridesWithCallContext;
-import org.killbill.billing.catalog.api.PlanPhaseSpecifier;
 import org.killbill.billing.catalog.api.PlanSpecifier;
-import org.killbill.billing.catalog.api.PriceList;
 import org.killbill.billing.catalog.api.Product;
 import org.killbill.billing.catalog.api.StaticCatalog;
-import org.killbill.billing.catalog.api.Unit;
 import org.killbill.billing.catalog.override.DefaultPriceOverride;
 import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
-import org.killbill.xmlloader.ValidatingConfig;
-import org.killbill.xmlloader.ValidationErrors;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
-public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<StandaloneCatalogWithPriceOverride> implements StaticCatalog {
+public class StandaloneCatalogWithPriceOverride extends StandaloneCatalog implements StaticCatalog {
 
-    private final StandaloneCatalog standaloneCatalog;
     private final Long tenantRecordId;
 
     /* Since we offer endpoints that attempt to serialize catalog objects, we need to explicitly tell Jackson to ignore those fields */
@@ -64,28 +44,22 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
     @JsonIgnore
     private final PriceOverride priceOverride;
 
-    public StandaloneCatalogWithPriceOverride(final StandaloneCatalog staticCatalog, final PriceOverride priceOverride, final Long tenantRecordId, final InternalCallContextFactory internalCallContextFactory) {
+    public StandaloneCatalogWithPriceOverride(final StandaloneCatalog catalog, final PriceOverride priceOverride, final Long tenantRecordId, final InternalCallContextFactory internalCallContextFactory) {
+        // Initialize from input catalog
+        setCatalogName(catalog.getCatalogName());
+        setEffectiveDate(catalog.getEffectiveDate());
+        setRecurringBillingMode(catalog.getRecurringBillingMode());
+        setProducts(catalog.getCurrentProducts());
+        setPlans(catalog.getCurrentPlans());
+        setPriceLists(catalog.getPriceLists());
+        setPlanRules(catalog.getPlanRules());
+        setSupportedCurrencies(catalog.getCurrentSupportedCurrencies());
+        setUnits(catalog.getCurrentUnits());
         this.tenantRecordId = tenantRecordId;
-        this.standaloneCatalog = staticCatalog;
         this.priceOverride = priceOverride;
         this.internalCallContextFactory = internalCallContextFactory;
     }
 
-    public StandaloneCatalogWithPriceOverride(final StandaloneCatalogWithPriceOverride cur, final InternalTenantContext tenantContext) {
-        this.tenantRecordId = tenantContext.getTenantRecordId();
-        this.priceOverride = cur.getPriceOverride();
-        this.standaloneCatalog = cur.getStandaloneCatalog();
-        this.internalCallContextFactory = cur.getInternalCallContextFactory();
-    }
-
-    public StandaloneCatalog getStandaloneCatalog() {
-        return standaloneCatalog;
-    }
-
-    public PriceOverride getPriceOverride() {
-        return priceOverride;
-    }
-
     public Long getTenantRecordId() {
         return tenantRecordId;
     }
@@ -94,45 +68,10 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
         return internalCallContextFactory;
     }
 
-    @Override
-    public String getCatalogName() {
-        return standaloneCatalog.getCatalogName();
-    }
-
-    @Override
-    public BillingMode getRecurringBillingMode() {
-        return standaloneCatalog.getRecurringBillingMode();
-    }
-
-    @Override
-    public Date getEffectiveDate() {
-        return standaloneCatalog.getEffectiveDate();
-    }
-
-    @Override
-    public Currency[] getCurrentSupportedCurrencies() throws CatalogApiException {
-        return standaloneCatalog.getCurrentSupportedCurrencies();
-    }
 
     @Override
-    public DefaultProduct[] getCurrentProducts() throws CatalogApiException {
-        return standaloneCatalog.getCurrentProducts();
-    }
-
-    @Override
-    public Unit[] getCurrentUnits() throws CatalogApiException {
-        return standaloneCatalog.getCurrentUnits();
-    }
-
-    @Override
-    public DefaultPlan[] getCurrentPlans() throws CatalogApiException {
-        return standaloneCatalog.getCurrentPlans();
-    }
-
-    @Override
-    public Plan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
-        final Plan defaultPlan = standaloneCatalog.createOrFindCurrentPlan(spec, null);
-
+    public DefaultPlan createOrFindCurrentPlan(final PlanSpecifier spec, final PlanPhasePriceOverridesWithCallContext overrides) throws CatalogApiException {
+        final DefaultPlan defaultPlan = super.createOrFindCurrentPlan(spec, null);
         if (overrides == null ||
             overrides.getOverrides() == null ||
             overrides.getOverrides().isEmpty()) {
@@ -144,18 +83,18 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
     }
 
     @Override
-    public Plan findCurrentPlan(final String planName) throws CatalogApiException {
+    public DefaultPlan findCurrentPlan(final String planName) throws CatalogApiException {
         final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
         if (m.matches()) {
             final InternalTenantContext internalTenantContext = createInternalTenantContext();
-            return priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
+            return priceOverride.getOverriddenPlan(planName, this, internalTenantContext);
         }
-        return standaloneCatalog.findCurrentPlan(planName);
+        return super.findCurrentPlan(planName);
     }
 
     @Override
     public Product findCurrentProduct(final String productName) throws CatalogApiException {
-        return standaloneCatalog.findCurrentProduct(productName);
+        return super.findCurrentProduct(productName);
     }
 
     @Override
@@ -164,79 +103,10 @@ public class StandaloneCatalogWithPriceOverride extends ValidatingConfig<Standal
         final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(planName);
         if (m.matches()) {
             final InternalTenantContext internalTenantContext = createInternalTenantContext();
-            final Plan plan = priceOverride.getOverriddenPlan(planName, standaloneCatalog, internalTenantContext);
+            final Plan plan = priceOverride.getOverriddenPlan(planName, this, internalTenantContext);
             return plan.findPhase(phaseName);
         }
-        return standaloneCatalog.findCurrentPhase(phaseName);
-    }
-
-    @Override
-    public PriceList findCurrentPricelist(final String priceListName) throws CatalogApiException {
-        return standaloneCatalog.findCurrentPricelist(priceListName);
-    }
-
-    @Override
-    public BillingActionPolicy planChangePolicy(final PlanPhaseSpecifier planPhaseSpecifier, final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planChangePolicy(planPhaseSpecifier, planSpecifier);
-    }
-
-    @Override
-    public PlanChangeResult planChange(final PlanPhaseSpecifier planPhaseSpecifier, final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planChange(planPhaseSpecifier, planSpecifier);
-    }
-
-    @Override
-    public BillingActionPolicy planCancelPolicy(final PlanPhaseSpecifier planPhaseSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planCancelPolicy(planPhaseSpecifier);
-    }
-
-    @Override
-    public PlanAlignmentCreate planCreateAlignment(final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planCreateAlignment(planSpecifier);
-    }
-
-    @Override
-    public BillingAlignment billingAlignment(final PlanPhaseSpecifier planPhaseSpecifier) throws CatalogApiException {
-        return standaloneCatalog.billingAlignment(planPhaseSpecifier);
-    }
-
-    @Override
-    public PlanAlignmentChange planChangeAlignment(final PlanPhaseSpecifier planPhaseSpecifier, final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.planChangeAlignment(planPhaseSpecifier, planSpecifier);
-    }
-
-    @Override
-    public boolean canCreatePlan(final PlanSpecifier planSpecifier) throws CatalogApiException {
-        return standaloneCatalog.canCreatePlan(planSpecifier);
-    }
-
-    @Override
-    public List<Listing> getAvailableBasePlanListings() throws CatalogApiException {
-        return standaloneCatalog.getAvailableBasePlanListings();
-    }
-
-    @Override
-    public List<Listing> getAvailableAddOnListings(final String baseProductName, @Nullable final String priceListName) throws CatalogApiException {
-        return standaloneCatalog.getAvailableAddOnListings(baseProductName, priceListName);
-    }
-
-    @Override
-    public boolean compliesWithLimits(final String phaseName, final String unit, final double value) throws CatalogApiException {
-        return standaloneCatalog.compliesWithLimits(phaseName, unit, value);
-    }
-
-    @Override
-    public ValidationErrors validate(final StandaloneCatalogWithPriceOverride root, final ValidationErrors errors) {
-        return standaloneCatalog.validate(root.standaloneCatalog, errors);
-    }
-
-    @Override
-    public void initialize(final StandaloneCatalogWithPriceOverride root, final URI sourceURI) {
-        standaloneCatalog.initialize(root.standaloneCatalog, sourceURI);
-    }
-
-    public DefaultPriceList findCurrentPriceList(final String priceListName) throws CatalogApiException {
-        return standaloneCatalog.findCurrentPriceList(priceListName);
+        return super.findCurrentPhase(phaseName);
     }
 
     private InternalTenantContext createInternalTenantContext() {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
index cc00006..90e499e 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -30,6 +30,7 @@ import javax.annotation.Nullable;
 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 org.joda.time.DateTime;
@@ -60,47 +61,38 @@ import org.killbill.clock.Clock;
 import org.killbill.xmlloader.ValidatingConfig;
 import org.killbill.xmlloader.ValidationErrors;
 
-@XmlRootElement(name = "catalog")
+@XmlRootElement(name = "catalogs")
 @XmlAccessorType(XmlAccessType.NONE)
-public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPriceOverride> implements Catalog, StaticCatalog {
+public class VersionedCatalog extends ValidatingConfig<VersionedCatalog> implements Catalog, StaticCatalog {
 
     private final Clock clock;
-    @XmlElement(name = "catalogVersion", required = true)
-    private final List<StandaloneCatalogWithPriceOverride> versions;
+
+    @XmlElementWrapper(name = "versions", required = true)
+    @XmlElement(name = "version", required = true)
+    private final List<StandaloneCatalog> versions;
     private String catalogName;
     private BillingMode recurringBillingMode;
 
     // Required for JAXB deserialization
     public VersionedCatalog() {
         this.clock = null;
-        this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+        this.versions = new ArrayList<StandaloneCatalog>();
     }
 
     public VersionedCatalog(final Clock clock) {
         this.clock = clock;
-        this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
-    }
-
-    public VersionedCatalog(final Clock clock, final String catalogName, final BillingMode recurringBillingMode, final List<StandaloneCatalogWithPriceOverride> versions, final InternalTenantContext tenantContext) {
-        this.clock = clock;
-        this.catalogName = catalogName;
-        this.recurringBillingMode = recurringBillingMode;
-        this.versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
-        for (final StandaloneCatalogWithPriceOverride cur : versions) {
-            final StandaloneCatalogWithPriceOverride catalogWithTenantInfo = new StandaloneCatalogWithPriceOverride(cur, tenantContext);
-            this.versions.add(catalogWithTenantInfo);
-        }
+        this.versions = new ArrayList<StandaloneCatalog>();
     }
 
     //
     // Private methods
     //
-    private StandaloneCatalogWithPriceOverride versionForDate(final DateTime date) throws CatalogApiException {
+    private StandaloneCatalog versionForDate(final DateTime date) throws CatalogApiException {
         return versions.get(indexOfVersionForDate(date.toDate()));
     }
 
-    private List<StandaloneCatalogWithPriceOverride> versionsBeforeDate(final Date date) throws CatalogApiException {
-        final List<StandaloneCatalogWithPriceOverride> result = new ArrayList<StandaloneCatalogWithPriceOverride>();
+    private List<StandaloneCatalog> versionsBeforeDate(final Date date) throws CatalogApiException {
+        final List<StandaloneCatalog> result = new ArrayList<StandaloneCatalog>();
         final int index = indexOfVersionForDate(date);
         for (int i = 0; i <= index; i++) {
             result.add(versions.get(i));
@@ -110,7 +102,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
 
     private int indexOfVersionForDate(final Date date) throws CatalogApiException {
         for (int i = versions.size() - 1; i >= 0; i--) {
-            final StandaloneCatalogWithPriceOverride c = versions.get(i);
+            final StandaloneCatalog c = versions.get(i);
             if (c.getEffectiveDate().getTime() <= date.getTime()) {
                 return i;
             }
@@ -135,7 +127,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
         }
 
 
-        public Plan findPlan(final StandaloneCatalogWithPriceOverride catalog) throws CatalogApiException {
+        public Plan findPlan(final StandaloneCatalog catalog) throws CatalogApiException {
             if (spec.getPlanName() != null) {
                 return catalog.findCurrentPlan(spec.getPlanName());
             } else {
@@ -148,13 +140,13 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
                                                   final DateTime requestedDate,
                                                   final DateTime subscriptionStartDate)
             throws CatalogApiException {
-        final List<StandaloneCatalogWithPriceOverride> catalogs = versionsBeforeDate(requestedDate.toDate());
+        final List<StandaloneCatalog> catalogs = versionsBeforeDate(requestedDate.toDate());
         if (catalogs.isEmpty()) {
             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
-            final StandaloneCatalogWithPriceOverride c = catalogs.get(i);
+            final StandaloneCatalog c = catalogs.get(i);
             final Plan plan;
             try {
                 plan = wrapper.findPlan(c);
@@ -207,14 +199,20 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
         return clock;
     }
 
-    public List<StandaloneCatalogWithPriceOverride> getVersions() {
+    public List<StandaloneCatalog> getVersions() {
         return versions;
     }
 
     //
     // Public methods not exposed in interface
     //
-    public void add(final StandaloneCatalogWithPriceOverride e) throws CatalogApiException {
+    public void addAll(final List<StandaloneCatalog> inputVersions) throws CatalogApiException {
+        for (final StandaloneCatalog cur : inputVersions) {
+            add(cur);
+        }
+    }
+
+    public void add(final StandaloneCatalog e) throws CatalogApiException {
         if (catalogName == null) {
             catalogName = e.getCatalogName();
         } else {
@@ -230,15 +228,15 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
             }
         }
         versions.add(e);
-        Collections.sort(versions, new Comparator<StandaloneCatalogWithPriceOverride>() {
+        Collections.sort(versions, new Comparator<StandaloneCatalog>() {
             @Override
-            public int compare(final StandaloneCatalogWithPriceOverride c1, final StandaloneCatalogWithPriceOverride c2) {
+            public int compare(final StandaloneCatalog c1, final StandaloneCatalog c2) {
                 return c1.getEffectiveDate().compareTo(c2.getEffectiveDate());
             }
         });
     }
 
-    public Iterator<StandaloneCatalogWithPriceOverride> iterator() {
+    public Iterator<StandaloneCatalog> iterator() {
         return versions.iterator();
     }
 
@@ -271,7 +269,7 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
 
     @Override
     public PriceListSet getPriceLists(final DateTime requestedDate) throws CatalogApiException {
-        return versionForDate(requestedDate).getStandaloneCatalog().getPriceLists();
+        return versionForDate(requestedDate).getPriceLists();
     }
 
     //
@@ -397,15 +395,15 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
     // VerifiableConfig API
     //
     @Override
-    public void initialize(final StandaloneCatalogWithPriceOverride catalog, final URI sourceURI) {
-        for (final StandaloneCatalogWithPriceOverride c : versions) {
-            c.initialize(catalog, sourceURI);
+    public void initialize(final VersionedCatalog catalog, final URI sourceURI) {
+        for (final StandaloneCatalog c : versions) {
+            c.initialize(c, sourceURI);
         }
     }
 
     @Override
-    public ValidationErrors validate(final StandaloneCatalogWithPriceOverride catalog, final ValidationErrors errors) {
-        for (final StandaloneCatalogWithPriceOverride c : versions) {
+    public ValidationErrors validate(final VersionedCatalog catalog, final ValidationErrors errors) {
+        for (final StandaloneCatalog c : versions) {
             errors.addAll(c.validate(c, errors));
         }
         //TODO MDW validation - ensure all catalog versions have a single name
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
index 3c59954..a5e675b 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/caching/TestEhCacheCatalogCache.java
@@ -29,6 +29,8 @@ import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.CatalogTestSuiteNoDB;
 import org.killbill.billing.catalog.DefaultProduct;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.xmlloader.UriAccessor;
@@ -85,11 +87,15 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
         Assert.assertEquals(products.length, 3);
 
         // Verify the lookup with other contexts
-        final VersionedCatalog resultForMultiTenantContext = new VersionedCatalog(result.getClock(), result.getCatalogName(), result.getRecurringBillingMode(), result.getVersions(), multiTenantContext);
+        final VersionedCatalog resultForMultiTenantContext = new VersionedCatalog(result.getClock());
+        for (final StandaloneCatalog cur : result.getVersions()) {
+            resultForMultiTenantContext.add(new StandaloneCatalogWithPriceOverride(cur, priceOverride, multiTenantContext.getTenantRecordId(), internalCallContextFactory));
+        }
+
         Assert.assertEquals(catalogCache.getCatalog(true, true, multiTenantContext).getCatalogName(), resultForMultiTenantContext.getCatalogName());
         Assert.assertEquals(catalogCache.getCatalog(true, true, multiTenantContext).getVersions().size(), resultForMultiTenantContext.getVersions().size());
         for (int i = 0; i < catalogCache.getCatalog(true, true, multiTenantContext).getVersions().size(); i++) {
-            Assert.assertEquals(catalogCache.getCatalog(true, true, multiTenantContext).getVersions().get(i).getTenantRecordId(), resultForMultiTenantContext.getVersions().get(i).getTenantRecordId());
+           Assert.assertEquals(((StandaloneCatalogWithPriceOverride) catalogCache.getCatalog(true, true, multiTenantContext).getVersions().get(i)).getTenantRecordId(), ((StandaloneCatalogWithPriceOverride) resultForMultiTenantContext.getVersions().get(i)).getTenantRecordId());
         }
     }
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
index 9b84483..3109de4 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/CatalogTestSuiteNoDB.java
@@ -21,6 +21,7 @@ import org.killbill.billing.catalog.caching.CatalogCache;
 import org.killbill.billing.catalog.caching.CatalogCacheInvalidationCallback;
 import org.killbill.billing.catalog.glue.TestCatalogModuleNoDB;
 import org.killbill.billing.catalog.io.VersionedCatalogLoader;
+import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.tenant.api.TenantInternalApi;
 import org.killbill.billing.util.cache.CacheControllerDispatcher;
 import org.testng.annotations.BeforeClass;
@@ -46,6 +47,9 @@ public abstract class CatalogTestSuiteNoDB extends GuicyKillbillTestSuiteNoDB {
     @Inject
     protected CatalogCacheInvalidationCallback cacheInvalidationCallback;
 
+    @Inject
+    protected PriceOverride priceOverride;
+
     @BeforeClass(groups = "fast")
     protected void beforeClass() throws Exception {
         final Injector injector = Guice.createInjector(new TestCatalogModuleNoDB(configSource));
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java b/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
index ef0d645..f5513d6 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/io/TestVersionedCatalogLoader.java
@@ -32,6 +32,7 @@ import javax.xml.transform.TransformerException;
 
 import org.joda.time.DateTime;
 import org.killbill.billing.catalog.CatalogTestSuiteNoDB;
+import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
@@ -120,7 +121,7 @@ public class TestVersionedCatalogLoader extends CatalogTestSuiteNoDB {
     public void testLoad() throws IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, CatalogApiException {
         final VersionedCatalog c = loader.loadDefaultCatalog(Resources.getResource("versionedCatalog").toString());
         Assert.assertEquals(c.size(), 3);
-        final Iterator<StandaloneCatalogWithPriceOverride> it = c.iterator();
+        final Iterator<StandaloneCatalog> it = c.iterator();
         DateTime dt = new DateTime("2011-01-01T00:00:00+00:00");
         Assert.assertEquals(it.next().getEffectiveDate(), dt.toDate());
         dt = new DateTime("2011-02-02T00:00:00+00:00");
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java b/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java
index 5d89b63..5d7966f 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/io/TestXMLWriter.java
@@ -34,6 +34,8 @@ import org.killbill.billing.catalog.DefaultPriceListSet;
 import org.killbill.billing.catalog.DefaultProduct;
 import org.killbill.billing.catalog.DefaultRecurring;
 import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
+import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.BillingPeriod;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.MutableStaticCatalog;
@@ -41,22 +43,35 @@ import org.killbill.billing.catalog.api.PhaseType;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.ProductCategory;
 import org.killbill.billing.catalog.api.TimeUnit;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.xmlloader.XMLLoader;
 import org.killbill.xmlloader.XMLWriter;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.io.Resources;
 
 import static org.testng.Assert.assertEquals;
 
 public class TestXMLWriter extends CatalogTestSuiteNoDB {
 
+
+    // Verifies we can generate the XML associated with a VersionedCatalog
+    @Test(groups = "fast")
+    public void testVersionedCatalog() throws Exception {
+        final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        final VersionedCatalog versionedCatalog = new VersionedCatalog(clock);
+        versionedCatalog.add(catalog);
+        final String newCatalogStr = XMLWriter.writeXML(versionedCatalog, VersionedCatalog.class);
+        //System.err.println(newCatalogStr);
+    }
+
+
     // Verify we can marshall/unmarshall a (fairly complex catalog) catalog and get back the same result (Required to support catalog update)
     @Test(groups = "fast")
     public void testMarshallUnmarshall() throws Exception {
         final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
         final String oldCatalogStr = XMLWriter.writeXML(catalog, StandaloneCatalog.class);
-
         //System.err.println(oldCatalogStr);
 
         final StandaloneCatalog oldCatalog = XMLLoader.getObjectFromStream(new URI("dummy"), new ByteArrayInputStream(oldCatalogStr.getBytes(Charset.forName("UTF-8"))), StandaloneCatalog.class);
@@ -65,6 +80,8 @@ public class TestXMLWriter extends CatalogTestSuiteNoDB {
     }
 
 
+
+
     @Test(groups = "fast")
     public void testAddPlan() throws Exception {
         final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarBasic.xml").toExternalForm(), StandaloneCatalog.class);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
index 5aa65bd..c4bd0b9 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestVersionedCatalog.java
@@ -21,7 +21,6 @@ package org.killbill.billing.catalog;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.net.URISyntaxException;
-import java.util.Date;
 
 import javax.xml.bind.JAXBException;
 import javax.xml.transform.TransformerException;
@@ -38,8 +37,6 @@ import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 import org.xml.sax.SAXException;
 
-import com.google.common.io.Resources;
-
 public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
 
     private VersionedCatalog vc;
@@ -50,11 +47,6 @@ public class TestVersionedCatalog extends CatalogTestSuiteNoDB {
         vc = loader.loadDefaultCatalog("versionedCatalog");
     }
 
-    @Test(groups = "fast")
-    public void testAddCatalog() throws IOException, SAXException, InvalidConfigException, JAXBException, TransformerException, URISyntaxException, ServiceException, CatalogApiException {
-        vc.add(new StandaloneCatalogWithPriceOverride(new StandaloneCatalog(new Date()).setCatalogName(vc.getCatalogName()).setRecurringBillingMode(vc.getRecurringBillingMode()), null, 0L, null));
-        Assert.assertEquals(vc.size(), 4);
-    }
 
     @Test(groups = "fast")
     public void testFindPlanWithDates() throws Exception {
diff --git a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
index 9fc2098..22f7568 100644
--- a/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
+++ b/jaxrs/src/main/java/org/killbill/billing/jaxrs/resources/CatalogResource.java
@@ -35,6 +35,7 @@ import javax.ws.rs.core.UriInfo;
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.killbill.billing.account.api.AccountUserApi;
+import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.VersionedCatalog;
 import org.killbill.billing.catalog.api.Catalog;
@@ -135,7 +136,7 @@ public class CatalogResource extends JaxRsResourceBase {
         if (catalogDateVersion != null) {
             result.add(new CatalogJson(catalog, catalogDateVersion));
         } else {
-            for (final StandaloneCatalogWithPriceOverride v : catalog.getVersions()) {
+            for (final StandaloneCatalog v : catalog.getVersions()) {
                 result.add(new CatalogJson(catalog, new DateTime(v.getEffectiveDate())));
             }
         }