killbill-aplcache
Changes
beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java 733(+733 -0)
catalog/pom.xml 4(+4 -0)
catalog/src/main/java/org/killbill/billing/catalog/glue/DefaultCatalogProviderPluginRegistryProvider.java 38(+38 -0)
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
new file mode 100644
index 0000000..5fe80e5
--- /dev/null
+++ b/beatrix/src/test/java/org/killbill/billing/beatrix/integration/TestWithCatalogPlugin.java
@@ -0,0 +1,733 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.beatrix.integration;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.killbill.billing.account.api.Account;
+import org.killbill.billing.account.api.AccountData;
+import org.killbill.billing.api.TestApiListener.NextEvent;
+import org.killbill.billing.beatrix.util.InvoiceChecker.ExpectedInvoiceItemCheck;
+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.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.PhaseType;
+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.PriceList;
+import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.ProductCategory;
+import org.killbill.billing.catalog.api.Unit;
+import org.killbill.billing.catalog.api.rules.Case;
+import org.killbill.billing.catalog.api.rules.CaseBillingAlignment;
+import org.killbill.billing.catalog.api.rules.CaseCancelPolicy;
+import org.killbill.billing.catalog.api.rules.CaseChange;
+import org.killbill.billing.catalog.api.rules.CaseChangePlanAlignment;
+import org.killbill.billing.catalog.api.rules.CaseChangePlanPolicy;
+import org.killbill.billing.catalog.api.rules.CaseCreateAlignment;
+import org.killbill.billing.catalog.api.rules.CasePhase;
+import org.killbill.billing.catalog.api.rules.CasePriceList;
+import org.killbill.billing.catalog.api.rules.PlanRules;
+import org.killbill.billing.catalog.override.PriceOverride;
+import org.killbill.billing.catalog.plugin.api.StandalonePluginCatalog;
+import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
+import org.killbill.billing.entitlement.api.DefaultEntitlement;
+import org.killbill.billing.invoice.api.InvoiceItemType;
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.PluginProperty;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.xmlloader.XMLLoader;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Resources;
+
+public class TestWithCatalogPlugin extends TestIntegrationBase {
+
+ @Inject
+ private OSGIServiceRegistration<CatalogPluginApi> pluginRegistry;
+
+ @Inject
+ private PriceOverride priceOverride;
+
+ @Inject
+ private InternalCallContextFactory internalCallContextFactory;
+
+ private TestCatalogPluginApi testCatalogPluginApi;
+
+ @BeforeClass(groups = "slow")
+ public void beforeClass() throws Exception {
+ super.beforeClass();
+
+ this.testCatalogPluginApi = new TestCatalogPluginApi(priceOverride, internalCallContext, internalCallContextFactory);
+ pluginRegistry.registerService(new OSGIServiceDescriptor() {
+ @Override
+ public String getPluginSymbolicName() {
+ return "TestCatalogPluginApi";
+ }
+
+ @Override
+ public String getRegistrationName() {
+ return "TestCatalogPluginApi";
+ }
+ }, testCatalogPluginApi);
+ }
+
+ @Test(groups = "slow")
+ public void testCreateSubscriptionWithCatalogPlugin() throws Exception {
+
+ final AccountData accountData = getAccountData(1);
+ final Account account = createAccountWithNonOsgiPaymentMethod(accountData);
+ accountChecker.checkAccount(account.getId(), accountData, callContext);
+
+ // We take april as it has 30 days (easier to play with BCD)
+ // Set clock to the initial start date - we implicitly assume here that the account timezone is UTC
+ clock.setDay(new LocalDate(2012, 4, 1));
+
+ //
+ // Create original subscription (Trial PHASE) -> $0 invoice.
+ final DefaultEntitlement bpSubscription = createBaseEntitlementAndCheckForCompletion(account.getId(), "bundleKey", "Pistol", ProductCategory.BASE, BillingPeriod.MONTHLY, NextEvent.CREATE, NextEvent.INVOICE);
+ invoiceChecker.checkInvoice(account.getId(), 1, callContext, new ExpectedInvoiceItemCheck(new LocalDate(2012, 4, 1), null, InvoiceItemType.FIXED, new BigDecimal("0")));
+ subscriptionChecker.checkSubscriptionCreated(bpSubscription.getId(), internalCallContext);
+ }
+
+ public static class TestCatalogPluginApi implements CatalogPluginApi {
+
+ private final VersionedCatalog versionedCatalog;
+
+ 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 StandaloneCatalogWithPriceOverride standaloneCatalogWithPriceOverride = new StandaloneCatalogWithPriceOverride(inputCatalog, priceOverride, internalTenantContext.getTenantRecordId(), internalCallContextFactory);
+ versions.add(standaloneCatalogWithPriceOverride);
+ versionedCatalog = new VersionedCatalog(getClock(), inputCatalog.getCatalogName(), inputCatalog.getRecurringBillingMode(), versions, internalTenantContext);
+ }
+
+ @Override
+ public VersionedPluginCatalog getVersionedPluginCatalog(final Iterable<PluginProperty> properties, final TenantContext tenantContext) {
+ return new TestVersionedPluginCatalog(versionedCatalog.getCatalogName(), versionedCatalog.getRecurringBillingMode(), toStandalonePluginCatalogs(versionedCatalog.getVersions()));
+ }
+
+ private Iterable<StandalonePluginCatalog> toStandalonePluginCatalogs(final List<StandaloneCatalogWithPriceOverride> input) {
+ return Iterables.transform(input, new Function<StandaloneCatalogWithPriceOverride, StandalonePluginCatalog>() {
+ @Override
+ public StandalonePluginCatalog apply(final StandaloneCatalogWithPriceOverride input) {
+ try {
+ return new TestStandalonePluginCatalog(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);
+ }
+ }
+
+ private <I, C extends I> List<I> listOf(@Nullable C [] input) {
+ return (input != null) ? ImmutableList.<I>copyOf(input) : ImmutableList.<I>of();
+ }
+ });
+ }
+ }
+
+ private static final class TestVersionedPluginCatalog implements VersionedPluginCatalog {
+
+ private final String catalogName;
+
+ private final BillingMode billingMode;
+
+ private final Iterable<StandalonePluginCatalog> standalonePluginCatalogs;
+
+ public TestVersionedPluginCatalog(final String catalogName,
+ final BillingMode billingMode,
+ final Iterable<StandalonePluginCatalog> standalonePluginCatalogs) {
+ this.catalogName = catalogName;
+ this.billingMode = billingMode;
+ this.standalonePluginCatalogs = standalonePluginCatalogs;
+ }
+
+ @Override
+ public String getCatalogName() {
+ return catalogName;
+ }
+
+ @Override
+ public BillingMode getRecurringBillingMode() {
+ return billingMode;
+ }
+
+ @Override
+ public Iterable<StandalonePluginCatalog> getStandalonePluginCatalogs() {
+ return standalonePluginCatalogs;
+ }
+ }
+
+ private static class TestStandalonePluginCatalog implements StandalonePluginCatalog {
+
+ private final DateTime effectiveDate;
+
+ private final Iterable<Currency> currency;
+
+ private final Iterable<Product> products;
+
+ private final Iterable<Plan> plans;
+
+ private final PriceList defaultPriceList;
+
+ private final Iterable<PriceList> childrenPriceLists;
+
+ private final PlanRules planRules;
+
+ private final Iterable<Unit> units;
+
+ public TestStandalonePluginCatalog(final DateTime effectiveDate,
+ final Iterable<Currency> currency,
+ final Iterable<Product> products,
+ final Iterable<Plan> plans,
+ final PriceList defaultPriceList,
+ final Iterable<PriceList> childrenPriceLists,
+ final PlanRules planRules,
+ final Iterable<Unit> units) {
+ this.effectiveDate = effectiveDate;
+ this.currency = currency;
+ this.products = products;
+ this.plans = plans;
+ this.defaultPriceList = defaultPriceList;
+ this.childrenPriceLists = childrenPriceLists;
+ this.planRules = planRules;
+ this.units = units;
+ }
+
+ @Override
+ public DateTime getEffectiveDate() {
+ return effectiveDate;
+ }
+
+ @Override
+ public Iterable<Currency> getCurrencies() {
+ return currency;
+ }
+
+ @Override
+ public Iterable<Unit> getUnits() {
+ return units;
+ }
+
+ @Override
+ public Iterable<Product> getProducts() {
+ return products;
+ }
+
+ @Override
+ public Iterable<Plan> getPlans() {
+ return plans;
+ }
+
+ @Override
+ public PriceList getDefaultPriceList() {
+ return defaultPriceList;
+ }
+
+ @Override
+ public Iterable<PriceList> getChildrenPriceList() {
+ return childrenPriceLists;
+ }
+
+ @Override
+ public PlanRules getPlanRules() {
+ return planRules;
+ }
+ }
+
+ private static final class TestPlanRules implements PlanRules {
+
+ private final Iterable<Product> products;
+ private final Iterable<Plan> plans;
+ private final Iterable<PriceList> priceLists;
+
+ private final List<CaseChangePlanPolicy> caseChangePlanPolicy;
+ private final List<CaseChangePlanAlignment> caseChangePlanAlignment;
+ private final List<CaseCancelPolicy> caseCancelPolicy;
+ private final List<CaseCreateAlignment> caseCreateAlignment;
+ private final List<CaseBillingAlignment> caseBillingAlignments;
+ private final List<CasePriceList> casePriceList;
+
+ public TestPlanRules(final Iterable<Product> products, Iterable<Plan> plans, Iterable<PriceList> priceLists) {
+ this.products = products;
+ this.plans = plans;
+ this.priceLists = priceLists;
+ this.caseChangePlanPolicy = new ArrayList<CaseChangePlanPolicy>();
+ this.caseChangePlanAlignment = new ArrayList<CaseChangePlanAlignment>();
+ this.caseCancelPolicy = new ArrayList<CaseCancelPolicy>();
+ this.caseCreateAlignment = new ArrayList<CaseCreateAlignment>();
+ this.caseBillingAlignments = new ArrayList<CaseBillingAlignment>();
+ this.casePriceList = new ArrayList<CasePriceList>();
+ }
+
+ public void addCaseBillingAlignmentRule(final CaseBillingAlignment input) {
+ caseBillingAlignments.add(input);
+ }
+
+ public void addCaseBillingAlignmentRule(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList,
+ final PhaseType phaseType,
+ final BillingAlignment billingAlignment) {
+ caseBillingAlignments.add(new TestCaseBillingAlignment(product, productCategory, billingPeriod, priceList, phaseType, billingAlignment));
+ }
+
+ public void addCaseCancelRule(final CaseCancelPolicy input) {
+ caseCancelPolicy.add(input);
+ }
+
+ public void addCaseCancelRule(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList,
+ final PhaseType phaseType,
+ final BillingActionPolicy billingActionPolicy) {
+ caseCancelPolicy.add(new TestCaseCancelPolicy(product, productCategory, billingPeriod, priceList, phaseType, billingActionPolicy));
+
+ }
+
+ public void addCaseChangeAlignmentRule(final CaseChangePlanAlignment input) {
+ caseChangePlanAlignment.add(input);
+ }
+
+ public void addCaseChangeAlignmentRule(final PhaseType phaseType,
+ final String fromProduct,
+ final ProductCategory fromProductCategory,
+ final BillingPeriod fromBillingPeriod,
+ final String fromPriceList,
+ final String toProduct,
+ final ProductCategory toProductCategory,
+ final BillingPeriod toBillingPeriod,
+ final String toPriceList,
+ final PlanAlignmentChange planAlignmentChange) {
+ caseChangePlanAlignment.add(new TestCaseChangePlanAlignment(phaseType, findProduct(fromProduct), fromProductCategory, fromBillingPeriod, findPriceList(fromPriceList), findProduct(toProduct), toProductCategory, toBillingPeriod, findPriceList(toPriceList), planAlignmentChange));
+ }
+
+ public void addCaseChangePlanPolicyRule(final CaseChangePlanPolicy input) {
+ caseChangePlanPolicy.add(input);
+ }
+
+ public void addCaseChangePlanPolicyRule(final PhaseType phaseType,
+ final String fromProduct,
+ final ProductCategory fromProductCategory,
+ final BillingPeriod fromBillingPeriod,
+ final String fromPriceList,
+ final String toProduct,
+ final ProductCategory toProductCategory,
+ final BillingPeriod toBillingPeriod,
+ final String toPriceList,
+ final BillingActionPolicy policy) {
+ caseChangePlanPolicy.add(new TestCaseChangePlanPolicy(phaseType, findProduct(fromProduct), fromProductCategory, fromBillingPeriod, findPriceList(fromPriceList), findProduct(toProduct), toProductCategory, toBillingPeriod, findPriceList(toPriceList), policy));
+ }
+
+ public void addCaseCreateAlignmentRule(final CaseCreateAlignment input) {
+ caseCreateAlignment.add(input);
+ }
+
+ public void addCaseCreateAlignmentRule(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList,
+ final PlanAlignmentCreate planAlignmentCreate) {
+ caseCreateAlignment.add(new TestCaseCreateAlignment(product, productCategory, billingPeriod, priceList, planAlignmentCreate));
+ }
+
+ public void addPriceListRule(final CasePriceList input) {
+ casePriceList.add(input);
+ }
+
+ public void addPriceListRule(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList,
+ final PriceList destPriceList) {
+ casePriceList.add(new TestCasePriceList(product, productCategory, billingPeriod, priceList, destPriceList));
+ }
+
+ @Override
+ public Iterable<CaseChangePlanPolicy> getCaseChangePlanPolicy() {
+ return caseChangePlanPolicy;
+ }
+
+ @Override
+ public Iterable<CaseChangePlanAlignment> getCaseChangePlanAlignment() {
+ return caseChangePlanAlignment;
+ }
+
+ @Override
+ public Iterable<CaseCancelPolicy> getCaseCancelPolicy() {
+ return caseCancelPolicy;
+ }
+
+ @Override
+ public Iterable<CaseCreateAlignment> getCaseCreateAlignment() {
+ return caseCreateAlignment;
+ }
+
+ @Override
+ public Iterable<CaseBillingAlignment> getCaseBillingAlignment() {
+ return caseBillingAlignments;
+ }
+
+ @Override
+ public Iterable<CasePriceList> getCasePriceList() {
+ return casePriceList;
+ }
+
+ private Product findProduct(@Nullable final String productName) {
+ return find(products, productName, "products", new Predicate<Product>() {
+ @Override
+ public boolean apply(final Product input) {
+ return input.getName().equals(productName);
+ }
+ });
+ }
+
+ private Plan findPlan(@Nullable final String planName) {
+ return find(plans, planName, "plans", new Predicate<Plan>() {
+ @Override
+ public boolean apply(final Plan input) {
+ return input.getName().equals(planName);
+ }
+ });
+ }
+
+ private PriceList findPriceList(@Nullable final String priceListName) {
+ return find(priceLists, priceListName, "pricelists", new Predicate<PriceList>() {
+ @Override
+ public boolean apply(final PriceList input) {
+ return input.getName().equals(priceListName);
+ }
+ });
+ }
+
+ private <T> T find(final Iterable<T> all, @Nullable final String name, final String what, final Predicate<T> predicate) {
+ if (name == null) {
+ return null;
+ }
+ final T result = Iterables.tryFind(all, predicate).orNull();
+ if (result == null) {
+ throw new IllegalStateException(String.format("%s : cannot find entry %s", what, name));
+ }
+ return result;
+ }
+ }
+
+ public static class TestCasePriceList extends TestCase implements CasePriceList {
+
+ private final PriceList destPriceList;
+
+ public TestCasePriceList(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList,
+ final PriceList destPriceList) {
+ super(product, productCategory, billingPeriod, priceList);
+ this.destPriceList = destPriceList;
+ }
+
+ @Override
+ public PriceList getDestinationPriceList() {
+ return destPriceList;
+ }
+ }
+
+ public static class TestCaseCreateAlignment extends TestCase implements CaseCreateAlignment {
+
+ private final PlanAlignmentCreate planAlignmentCreate;
+
+ public TestCaseCreateAlignment(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList,
+ final PlanAlignmentCreate planAlignmentCreate) {
+ super(product, productCategory, billingPeriod, priceList);
+ this.planAlignmentCreate = planAlignmentCreate;
+ }
+
+ @Override
+ public PlanAlignmentCreate getPlanAlignmentCreate() {
+ return planAlignmentCreate;
+ }
+ }
+
+ public static class TestCaseCancelPolicy extends TestCasePhase implements CaseCancelPolicy {
+
+ private final BillingActionPolicy billingActionPolicy;
+
+ public TestCaseCancelPolicy(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList,
+ final PhaseType phaseType,
+ final BillingActionPolicy billingActionPolicy) {
+ super(product, productCategory, billingPeriod, priceList, phaseType);
+ this.billingActionPolicy = billingActionPolicy;
+ }
+
+ @Override
+ public BillingActionPolicy getBillingActionPolicy() {
+ return billingActionPolicy;
+ }
+ }
+
+ public static class TestCaseBillingAlignment extends TestCasePhase implements CaseBillingAlignment {
+
+ private final BillingAlignment billingAlignment;
+
+ public TestCaseBillingAlignment(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList,
+ final PhaseType phaseType,
+ final BillingAlignment billingAlignment) {
+ super(product, productCategory, billingPeriod, priceList, phaseType);
+ this.billingAlignment = billingAlignment;
+ }
+
+ @Override
+ public BillingAlignment getBillingAlignment() {
+ return billingAlignment;
+ }
+ }
+
+ public static class TestCasePhase extends TestCase implements CasePhase {
+
+ private final PhaseType phaseType;
+
+ public TestCasePhase(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList,
+ final PhaseType phaseType) {
+ super(product, productCategory, billingPeriod, priceList);
+ this.phaseType = phaseType;
+ }
+
+ @Override
+ public PhaseType getPhaseType() {
+ return phaseType;
+ }
+ }
+
+ public static class TestCase implements Case {
+
+ private final Product product;
+
+ private final ProductCategory productCategory;
+
+ private final BillingPeriod billingPeriod;
+
+ private final PriceList priceList;
+
+ public TestCase(final Product product,
+ final ProductCategory productCategory,
+ final BillingPeriod billingPeriod,
+ final PriceList priceList) {
+ this.product = product;
+ this.productCategory = productCategory;
+ this.billingPeriod = billingPeriod;
+ this.priceList = priceList;
+ }
+
+ @Override
+ public Product getProduct() {
+ return product;
+ }
+
+ @Override
+ public ProductCategory getProductCategory() {
+ return productCategory;
+ }
+
+ @Override
+ public BillingPeriod getBillingPeriod() {
+ return billingPeriod;
+ }
+
+ @Override
+ public PriceList getPriceList() {
+ return priceList;
+ }
+ }
+
+ public static class TestCaseChangePlanAlignment extends TestCaseChange implements CaseChangePlanAlignment {
+
+ private final PlanAlignmentChange planAlignmentChange;
+
+ public TestCaseChangePlanAlignment(final PhaseType phaseType,
+ final Product fromProduct,
+ final ProductCategory fromProductCategory,
+ final BillingPeriod fromBillingPeriod,
+ final PriceList fromPriceList,
+ final Product toProduct,
+ final ProductCategory toProductCategory,
+ final BillingPeriod toBillingPeriod,
+ final PriceList toPriceList,
+ final PlanAlignmentChange planAlignmentChange) {
+ super(phaseType, fromProduct, fromProductCategory, fromBillingPeriod, fromPriceList, toProduct, toProductCategory, toBillingPeriod, toPriceList);
+ this.planAlignmentChange = planAlignmentChange;
+ }
+
+ @Override
+ public PlanAlignmentChange getAlignment() {
+ return planAlignmentChange;
+ }
+ }
+
+ public static class TestCaseChangePlanPolicy extends TestCaseChange implements CaseChangePlanPolicy {
+
+ private final BillingActionPolicy billingPolicy;
+
+ public TestCaseChangePlanPolicy(final PhaseType phaseType,
+ final Product fromProduct,
+ final ProductCategory fromProductCategory,
+ final BillingPeriod fromBillingPeriod,
+ final PriceList fromPriceList,
+ final Product toProduct,
+ final ProductCategory toProductCategory,
+ final BillingPeriod toBillingPeriod,
+ final PriceList toPriceList,
+ final BillingActionPolicy billingPolicy) {
+ super(phaseType, fromProduct, fromProductCategory, fromBillingPeriod, fromPriceList, toProduct, toProductCategory, toBillingPeriod, toPriceList);
+ this.billingPolicy = billingPolicy;
+ }
+
+ @Override
+ public BillingActionPolicy getBillingActionPolicy() {
+ return billingPolicy;
+ }
+ }
+
+ public static class TestCaseChange implements CaseChange {
+
+ private final PhaseType phaseType;
+
+ private final Product fromProduct;
+
+ private final ProductCategory fromProductCategory;
+
+ private final BillingPeriod fromBillingPeriod;
+
+ private final PriceList fromPriceList;
+
+ private final Product toProduct;
+
+ private final ProductCategory toProductCategory;
+
+ private final BillingPeriod toBillingPeriod;
+
+ private final PriceList toPriceList;
+
+ public TestCaseChange(final PhaseType phaseType,
+ final Product fromProduct,
+ final ProductCategory fromProductCategory,
+ final BillingPeriod fromBillingPeriod,
+ final PriceList fromPriceList,
+ final Product toProduct,
+ final ProductCategory toProductCategory,
+ final BillingPeriod toBillingPeriod,
+ final PriceList toPriceList) {
+ this.phaseType = phaseType;
+ this.fromProduct = fromProduct;
+ this.fromProductCategory = fromProductCategory;
+ this.fromBillingPeriod = fromBillingPeriod;
+ this.fromPriceList = fromPriceList;
+ this.toProduct = toProduct;
+ this.toProductCategory = toProductCategory;
+ this.toBillingPeriod = toBillingPeriod;
+ this.toPriceList = toPriceList;
+ }
+
+ @Override
+ public PhaseType getPhaseType() {
+ return phaseType;
+ }
+
+ @Override
+ public Product getFromProduct() {
+ return fromProduct;
+ }
+
+ @Override
+ public ProductCategory getFromProductCategory() {
+ return fromProductCategory;
+ }
+
+ @Override
+ public BillingPeriod getFromBillingPeriod() {
+ return fromBillingPeriod;
+ }
+
+ @Override
+ public PriceList getFromPriceList() {
+ return fromPriceList;
+ }
+
+ @Override
+ public Product getToProduct() {
+ return toProduct;
+ }
+
+ @Override
+ public ProductCategory getToProductCategory() {
+ return toProductCategory;
+ }
+
+ @Override
+ public BillingPeriod getToBillingPeriod() {
+ return toBillingPeriod;
+ }
+
+ @Override
+ public PriceList getToPriceList() {
+ return toPriceList;
+ }
+ }
+
+}
catalog/pom.xml 4(+4 -0)
diff --git a/catalog/pom.xml b/catalog/pom.xml
index 0978f4a..f6dd499 100644
--- a/catalog/pom.xml
+++ b/catalog/pom.xml
@@ -68,6 +68,10 @@
</dependency>
<dependency>
<groupId>org.kill-bill.billing</groupId>
+ <artifactId>killbill-platform-osgi-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.kill-bill.billing</groupId>
<artifactId>killbill-platform-test</artifactId>
<scope>test</scope>
</dependency>
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 ef3806d..8c8ccdb 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
@@ -27,16 +27,23 @@ import org.killbill.billing.callcontext.InternalTenantContext;
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.plugin.VersionedCatalogMapper;
+import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.killbill.billing.payment.api.PluginProperty;
import org.killbill.billing.util.cache.Cachable.CacheType;
import org.killbill.billing.util.cache.CacheController;
import org.killbill.billing.util.cache.CacheControllerDispatcher;
import org.killbill.billing.util.cache.CacheLoaderArgument;
import org.killbill.billing.util.cache.TenantCatalogCacheLoader.LoaderCallback;
import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.billing.util.callcontext.TenantContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableList;
public class EhCacheCatalogCache implements CatalogCache {
@@ -45,13 +52,23 @@ public class EhCacheCatalogCache implements CatalogCache {
private final CacheController cacheController;
private final VersionedCatalogLoader loader;
private final CacheLoaderArgument cacheLoaderArgument;
+ private final OSGIServiceRegistration<CatalogPluginApi> pluginRegistry;
+ private final VersionedCatalogMapper versionedCatalogMapper;
+ private final InternalCallContextFactory internalCallContextFactory;
private VersionedCatalog defaultCatalog;
@Inject
- public EhCacheCatalogCache(final CacheControllerDispatcher cacheControllerDispatcher, final VersionedCatalogLoader loader) {
+ public EhCacheCatalogCache(final OSGIServiceRegistration<CatalogPluginApi> pluginRegistry,
+ final VersionedCatalogMapper versionedCatalogMapper,
+ final CacheControllerDispatcher cacheControllerDispatcher,
+ final VersionedCatalogLoader loader,
+ final InternalCallContextFactory internalCallContextFactory) {
+ this.pluginRegistry = pluginRegistry;
+ this.versionedCatalogMapper = versionedCatalogMapper;
this.cacheController = cacheControllerDispatcher.getCacheController(CacheType.TENANT_CATALOG);
this.loader = loader;
+ this.internalCallContextFactory = internalCallContextFactory;
this.cacheLoaderArgument = initializeCacheLoaderArgument(this);
setDefaultCatalog();
}
@@ -65,6 +82,13 @@ public class EhCacheCatalogCache implements CatalogCache {
@Override
public VersionedCatalog getCatalog(final InternalTenantContext tenantContext) throws CatalogApiException {
+
+ // STEPH TODO what are the possibilities for caching here ?
+ final VersionedCatalog pluginVersionedCatalog = getCatalogFromPlugins(tenantContext);
+ if (pluginVersionedCatalog != null) {
+ return pluginVersionedCatalog;
+ }
+
if (tenantContext.getTenantRecordId() == InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID) {
return defaultCatalog;
}
@@ -75,7 +99,7 @@ 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 (tenantCatalog == null) {
- tenantCatalog = new VersionedCatalog(defaultCatalog, tenantContext);
+ tenantCatalog = new VersionedCatalog(defaultCatalog.getClock(), defaultCatalog.getCatalogName(), defaultCatalog.getRecurringBillingMode(), defaultCatalog.getVersions(), tenantContext);
cacheController.add(tenantContext.getTenantRecordId(), tenantCatalog);
}
return tenantCatalog;
@@ -91,6 +115,20 @@ public class EhCacheCatalogCache implements CatalogCache {
}
}
+ private VersionedCatalog getCatalogFromPlugins(final InternalTenantContext internalTenantContext) {
+ final TenantContext tenantContext = internalCallContextFactory.createTenantContext(internalTenantContext);
+ for (final String service : pluginRegistry.getAllServices()) {
+ final CatalogPluginApi plugin = pluginRegistry.getServiceForName(service);
+ final VersionedPluginCatalog pluginCatalog = plugin.getVersionedPluginCatalog(ImmutableList.<PluginProperty>of(), tenantContext);
+ // First plugin that gets something (for that tenant) returns it
+ if (pluginCatalog != null) {
+ logger.info("Returning catalog from plugin {} on tenant {} ", service, internalTenantContext.getTenantRecordId());
+ return versionedCatalogMapper.toVersionedCatalog(pluginCatalog, internalTenantContext);
+ }
+ }
+ return null;
+ }
+
//
// Build the LoaderCallback that is required to build the catalog from the xml from a module that knows
// nothing about catalog.
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
index 4e93f86..f6100e9 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/glue/CatalogModule.java
@@ -33,12 +33,18 @@ import org.killbill.billing.catalog.io.CatalogLoader;
import org.killbill.billing.catalog.io.VersionedCatalogLoader;
import org.killbill.billing.catalog.override.DefaultPriceOverride;
import org.killbill.billing.catalog.override.PriceOverride;
+import org.killbill.billing.catalog.plugin.StandaloneCatalogMapper;
+import org.killbill.billing.catalog.plugin.VersionedCatalogMapper;
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.invoice.plugin.api.InvoicePluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
import org.killbill.billing.platform.api.KillbillConfigSource;
import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
import org.killbill.billing.util.config.CatalogConfig;
import org.killbill.billing.util.glue.KillBillModule;
import org.skife.config.ConfigurationObjectFactory;
+import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
public class CatalogModule extends KillBillModule {
@@ -75,6 +81,10 @@ public class CatalogModule extends KillBillModule {
bind(OverriddenPlanCache.class).to(EhCacheOverriddenPlanCache.class).asEagerSingleton();
}
+ protected void installCatalogPluginApi() {
+ bind(new TypeLiteral<OSGIServiceRegistration<CatalogPluginApi>>() {}).toProvider(DefaultCatalogProviderPluginRegistryProvider.class).asEagerSingleton();
+ bind(VersionedCatalogMapper.class).asEagerSingleton();
+ }
@Override
@@ -84,5 +94,6 @@ public class CatalogModule extends KillBillModule {
installCatalog();
installCatalogUserApi();
installCatalogConfigCache();
+ installCatalogPluginApi();
}
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/glue/DefaultCatalogProviderPluginRegistryProvider.java b/catalog/src/main/java/org/killbill/billing/catalog/glue/DefaultCatalogProviderPluginRegistryProvider.java
new file mode 100644
index 0000000..640ed2e
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/glue/DefaultCatalogProviderPluginRegistryProvider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.catalog.glue;
+
+import org.killbill.billing.catalog.provider.DefaultCatalogProviderPluginRegistry;
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class DefaultCatalogProviderPluginRegistryProvider implements Provider<OSGIServiceRegistration<CatalogPluginApi>> {
+
+ @Inject
+ public DefaultCatalogProviderPluginRegistryProvider() {
+ }
+
+ @Override
+ public OSGIServiceRegistration<CatalogPluginApi> get() {
+ final DefaultCatalogProviderPluginRegistry pluginRegistry = new DefaultCatalogProviderPluginRegistry();
+ return pluginRegistry;
+ }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java b/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
index b046eb6..e70f9d1 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/plugin/StandaloneCatalogMapper.java
@@ -494,7 +494,7 @@ public class StandaloneCatalogMapper {
}
private <I, C extends I> C[] toArrayWithTransform(final Iterable<I> input, final Function<I, C> transformer, boolean returnNullIfNothing) {
- if (returnNullIfNothing && !input.iterator().hasNext()) {
+ if (returnNullIfNothing && (input == null || !input.iterator().hasNext())) {
return null;
}
final Iterable<C> tmp = Iterables.transform(input, transformer);
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
new file mode 100644
index 0000000..39ec3fa
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/plugin/VersionedCatalogMapper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.catalog.plugin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+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.override.PriceOverride;
+import org.killbill.billing.catalog.plugin.api.StandalonePluginCatalog;
+import org.killbill.billing.catalog.plugin.api.VersionedPluginCatalog;
+import org.killbill.billing.util.callcontext.InternalCallContextFactory;
+import org.killbill.clock.Clock;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class VersionedCatalogMapper {
+
+ private final Clock clock;
+
+ private final PriceOverride priceOverride;
+ private final InternalCallContextFactory internalCallContextFactory;
+
+ @Inject
+ public VersionedCatalogMapper(final Clock clock, final PriceOverride priceOverride, final InternalCallContextFactory internalCallContextFactory) {
+ this.clock = clock;
+ this.priceOverride = priceOverride;
+ 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);
+ 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);
+ return result;
+ }
+}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java b/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java
new file mode 100644
index 0000000..017c0d4
--- /dev/null
+++ b/catalog/src/main/java/org/killbill/billing/catalog/provider/DefaultCatalogProviderPluginRegistry.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project 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 org.killbill.billing.catalog.provider;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.invoice.plugin.api.CatalogPluginApi;
+import org.killbill.billing.osgi.api.OSGIServiceDescriptor;
+import org.killbill.billing.osgi.api.OSGIServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultCatalogProviderPluginRegistry implements OSGIServiceRegistration<CatalogPluginApi> {
+
+ private final static Logger log = LoggerFactory.getLogger(DefaultCatalogProviderPluginRegistry.class);
+
+ private final Map<String, CatalogPluginApi> pluginsByName = new ConcurrentHashMap<String, CatalogPluginApi>();
+
+ @Inject
+ public DefaultCatalogProviderPluginRegistry() {
+ }
+
+ @Override
+ public void registerService(final OSGIServiceDescriptor desc, final CatalogPluginApi service) {
+ log.info("DefaultInvoiceProviderPluginRegistry registering service " + desc.getRegistrationName());
+ pluginsByName.put(desc.getRegistrationName(), service);
+ }
+
+ @Override
+ public void unregisterService(final String serviceName) {
+ log.info("DefaultInvoiceProviderPluginRegistry unregistering service " + serviceName);
+ pluginsByName.remove(serviceName);
+ }
+
+ @Override
+ public CatalogPluginApi getServiceForName(final String serviceName) {
+ if (serviceName == null) {
+ throw new IllegalArgumentException("Null catalog plugin API name");
+ }
+ final CatalogPluginApi plugin = pluginsByName.get(serviceName);
+ return plugin;
+ }
+
+ @Override
+ public Set<String> getAllServices() {
+ return pluginsByName.keySet();
+ }
+
+ @Override
+ public Class<CatalogPluginApi> getServiceType() {
+ return CatalogPluginApi.class;
+ }
+}
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 38e9181..5c4a565 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -69,26 +69,42 @@ public class VersionedCatalog extends ValidatingConfig<StandaloneCatalogWithPric
private BillingMode recurringBillingMode;
@XmlElement(name = "catalogVersion", required = true)
- private final List<StandaloneCatalogWithPriceOverride> versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
+ private List<StandaloneCatalogWithPriceOverride> versions;
// Required for JAXB deserialization
public VersionedCatalog() {
this.clock = null;
+ versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
}
public VersionedCatalog(final Clock clock) {
this.clock = clock;
+ 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);
+ }
+ }
+
+ /*
public VersionedCatalog(final VersionedCatalog inputCatalog, final InternalTenantContext tenantContext) {
this.clock = inputCatalog.getClock();
this.catalogName = inputCatalog.getCatalogName();
this.recurringBillingMode = inputCatalog.getRecurringBillingMode();
+ versions = new ArrayList<StandaloneCatalogWithPriceOverride>();
for (final StandaloneCatalogWithPriceOverride cur : inputCatalog.getVersions()) {
final StandaloneCatalogWithPriceOverride catalogWithTenantInfo = new StandaloneCatalogWithPriceOverride(cur, tenantContext);
versions.add(catalogWithTenantInfo);
}
}
+ */
//
// Private methods
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 d2e4e19..4e771ce 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
@@ -86,7 +86,7 @@ public class TestEhCacheCatalogCache extends CatalogTestSuiteNoDB {
Assert.assertEquals(products.length, 3);
// Verify the lookup with other contexts
- final VersionedCatalog resultForMultiTenantContext = new VersionedCatalog(result, multiTenantContext);
+ final VersionedCatalog resultForMultiTenantContext = new VersionedCatalog(result.getClock(), result.getCatalogName(), result.getRecurringBillingMode(), result.getVersions(), multiTenantContext);
Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getCatalogName(), resultForMultiTenantContext.getCatalogName());
Assert.assertEquals(catalogCache.getCatalog(multiTenantContext).getVersions().size(), resultForMultiTenantContext.getVersions().size());
for (int i = 0; i < catalogCache.getCatalog(multiTenantContext).getVersions().size(); i++) {