Details
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/CatalogSafetyInitializer.java b/catalog/src/main/java/org/killbill/billing/catalog/CatalogSafetyInitializer.java
index 68d90d8..c04bb98 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/CatalogSafetyInitializer.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/CatalogSafetyInitializer.java
@@ -33,6 +33,7 @@ public class CatalogSafetyInitializer {
public static final Integer DEFAULT_NON_REQUIRED_INTEGER_FIELD_VALUE = -1;
+ public static final Double DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE = new Double(-1);
private static final Map<Class, LinkedList<Field>> perCatalogClassNonRequiredFields = new HashMap<Class, LinkedList<Field>>();
@@ -60,6 +61,8 @@ public class CatalogSafetyInitializer {
}
} else if (Integer.class.equals(f.getType())) {
initializeFieldWithValue(obj, f, DEFAULT_NON_REQUIRED_INTEGER_FIELD_VALUE);
+ } else if (Double.class.equals(f.getType())) {
+ initializeFieldWithValue(obj, f, DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE);
}
}
}
@@ -97,6 +100,8 @@ public class CatalogSafetyInitializer {
}
} else if (Integer.class.equals(f.getType())) {
result.add(f);
+ } else if (Double.class.equals(f.getType())) {
+ result.add(f);
}
}
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultBlock.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultBlock.java
index 6604384..a7dd99b 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultBlock.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultBlock.java
@@ -17,6 +17,8 @@
package org.killbill.billing.catalog;
+import java.net.URI;
+
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
@@ -78,7 +80,7 @@ public class DefaultBlock extends ValidatingConfig<StandaloneCatalog> implements
@Override
public Double getMinTopUpCredit() throws CatalogApiException {
- if (minTopUpCredit != null && type != BlockType.TOP_UP) {
+ if (minTopUpCredit != CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE && type != BlockType.TOP_UP) {
throw new CatalogApiException(ErrorCode.CAT_NOT_TOP_UP_BLOCK, phase.getName());
}
return minTopUpCredit;
@@ -91,13 +93,20 @@ public class DefaultBlock extends ValidatingConfig<StandaloneCatalog> implements
throw new IllegalStateException("type should have been automatically been initialized with VANILLA ");
}
- if (type == BlockType.TOP_UP && minTopUpCredit == null) {
+ if (type == BlockType.TOP_UP && minTopUpCredit == CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE) {
errors.add(new ValidationError(String.format("TOP_UP block needs to define minTopUpCredit for phase %s",
phase.getName()), catalog.getCatalogURI(), DefaultUsage.class, ""));
}
return errors;
}
+ @Override
+ public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
+ super.initialize(catalog, sourceURI);
+ CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
+ }
+
+
public DefaultBlock setType(final BlockType type) {
this.type = type;
return this;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultLimit.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultLimit.java
index 30176b5..b594c03 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultLimit.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultLimit.java
@@ -69,12 +69,13 @@ public class DefaultLimit extends ValidatingConfig<StandaloneCatalog> implements
@Override
public ValidationErrors validate(StandaloneCatalog root, ValidationErrors errors) {
- if (max == null && min == null) {
+ if (max == CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE && min == CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE) {
errors.add(new ValidationError("max and min cannot both be ommitted", root.getCatalogURI(), Limit.class, ""));
- } else if (max != null && min != null && max.doubleValue() < min.doubleValue()) {
+ } else if (max != CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE &&
+ min != CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE &&
+ max.doubleValue() < min.doubleValue()) {
errors.add(new ValidationError("max must be greater than min", root.getCatalogURI(), Limit.class, ""));
}
-
return errors;
}
@@ -87,12 +88,12 @@ public class DefaultLimit extends ValidatingConfig<StandaloneCatalog> implements
@Override
public boolean compliesWith(double value) {
- if (max != null) {
+ if (max != CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE) {
if (value > max.doubleValue()) {
return false;
}
}
- if (min != null) {
+ if (min != CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_DOUBLE_FIELD_VALUE) {
if (value < min.doubleValue()) {
return false;
}
@@ -126,10 +127,10 @@ public class DefaultLimit extends ValidatingConfig<StandaloneCatalog> implements
final DefaultLimit that = (DefaultLimit) o;
- if (max != null ? !max.equals(that.max) : that.max != null) {
+ if (!max.equals(that.max)) {
return false;
}
- if (min != null ? !min.equals(that.min) : that.min != null) {
+ if (!min.equals(that.min)) {
return false;
}
if (unit != null ? !unit.equals(that.unit) : that.unit != null) {
@@ -142,8 +143,8 @@ public class DefaultLimit extends ValidatingConfig<StandaloneCatalog> implements
@Override
public int hashCode() {
int result = unit != null ? unit.hashCode() : 0;
- result = 31 * result + (max != null ? max.hashCode() : 0);
- result = 31 * result + (min != null ? min.hashCode() : 0);
+ result = 31 * result + max.hashCode();
+ result = 31 * result + min.hashCode();
return result;
}
}
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
index 21ad681..9b205c0 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
@@ -134,6 +134,12 @@ public class DefaultTier extends ValidatingConfig<StandaloneCatalog> implements
public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
super.initialize(catalog, sourceURI);
CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
+ for (DefaultLimit cur : limits) {
+ cur.initialize(catalog, sourceURI);
+ }
+ for (DefaultBlock cur : blocks) {
+ cur.initialize(catalog, sourceURI);
+ }
}
diff --git a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
index d7ab1b3..25ce0f3 100644
--- a/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
+++ b/profiles/killbill/src/test/java/org/killbill/billing/jaxrs/TestCatalog.java
@@ -61,6 +61,15 @@ public class TestCatalog extends TestJaxrsBase {
}
@Test(groups = "slow")
+ public void testUploadAndFetchUsageCatlog() throws Exception {
+ final String versionPath1 = Resources.getResource("UsageExperimental.xml").getPath();
+ killBillClient.uploadXMLCatalog(versionPath1, requestOptions);
+ String catalog = killBillClient.getXMLCatalog(requestOptions);
+ Assert.assertNotNull(catalog);
+ }
+
+
+ @Test(groups = "slow")
public void testUploadWithErrors() throws Exception {
final String versionPath1 = Resources.getResource("SpyCarBasic.xml").getPath();
killBillClient.uploadXMLCatalog(versionPath1, requestOptions);
diff --git a/profiles/killbill/src/test/resources/UsageExperimental.xml b/profiles/killbill/src/test/resources/UsageExperimental.xml
new file mode 100644
index 0000000..4d2e7e9
--- /dev/null
+++ b/profiles/killbill/src/test/resources/UsageExperimental.xml
@@ -0,0 +1,289 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ ~ Copyright 2014 The Billing Project, Inc.
+ ~
+ ~ Ning licenses this file to you under the Apache License, version 2.0
+ ~ (the "License"); you may not use this file except in compliance with the
+ ~ License. You may obtain a copy of the License at:
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ ~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ ~ License for the specific language governing permissions and limitations
+ ~ under the License.
+ -->
+
+<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
+
+ <effectiveDate>2013-02-08T00:00:00+00:00</effectiveDate>
+ <catalogName>Usage</catalogName>
+
+ <!-- TBD
+ Defines the billingMode for all recurring subscription in that catalog:
+ Goal is to avoid to end up in a situation where a user could switch plan and suddenly his
+ recurring billing goes from IN_ADVANCE to IN_ARREAR or the reverse.
+ -->
+ <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
+
+ <currencies>
+ <currency>BTC</currency>
+ </currencies>
+
+ <units>
+ <unit name="members"/>
+ <unit name="cell-phone-minutes"/>
+ <unit name="fastrack-tokens"/>
+ <unit name="bandwith-meg-sec"/>
+ <unit name="Mbytes"/>
+ </units>
+
+ <products>
+ <product name="Dummy">
+ <category>BASE</category>
+ </product>
+ <product name="CapacityInAdvance">
+ <category>BASE</category>
+ </product>
+ <product name="ConsumableInAdvancePrepayCredit">
+ <category>BASE</category>
+ </product>
+ <product name="ConsumableInAdvanceTopUp">
+ <category>BASE</category>
+ </product>
+ <product name="CapacityInArrear">
+ <category>BASE</category>
+ </product>
+ <product name="ConsumableInArrear">
+ <category>BASE</category>
+ </product>
+ </products>
+
+ <rules>
+ <changePolicy>
+ <changePolicyCase>
+ <policy>IMMEDIATE</policy>
+ </changePolicyCase>
+ </changePolicy>
+ <cancelPolicy>
+ <cancelPolicyCase>
+ <policy>IMMEDIATE</policy>
+ </cancelPolicyCase>
+ </cancelPolicy>
+ </rules>
+
+ <plans>
+ <plan name="capacity-in-advance-monthly">
+ <product>CapacityInAdvance</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+
+ <usages>
+ <usage name="capacity-in-advance-monthly-usage1" billingMode="IN_ADVANCE" usageType="CAPACITY">
+ <billingPeriod>MONTHLY</billingPeriod>
+ <limits>
+ <limit>
+ <unit>members</unit>
+ <max>100</max>
+ </limit>
+ </limits>
+ <!-- could accept a fixed price and/or a recurring price -->
+ <recurringPrice>
+ <price>
+ <currency>BTC</currency>
+ <value>100.00</value>
+ </price>
+ </recurringPrice>
+ </usage>
+ </usages>
+ </finalPhase>
+ </plan>
+
+
+ <plan name="consumable-in-advance-prepay-credit-monthly">
+ <product>ConsumableInAdvancePrepayCredit</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <usages>
+ <usage name="consumable-in-advance-prepay-credit-monthly-usage1" billingMode="IN_ADVANCE" usageType="CONSUMABLE">
+ <billingPeriod>MONTHLY</billingPeriod>
+ <blocks>
+ <block>
+ <unit>cell-phone-minutes</unit>
+ <size>1000</size>
+ <!-- could be either fixed (with NO_BILLING_PERIOD) )or recurring:
+ * In billing period is NO_BILLING_PERIOD, we buy one block of units
+ * In billing period ha s been specified, we buy one block of units for each period
+ -->
+ <prices>
+ <price>
+ <currency>BTC</currency>
+ <value>0.10</value>
+ </price>
+ </prices>
+ </block>
+ </blocks>
+ <!-- We could instead define the price here as we did for capacity-in-advance if we want to 'bundle' linit/units -->
+ </usage>
+ </usages>
+ </finalPhase>
+ </plan>
+
+
+ <plan name="consumable-in-advance-topup">
+ <product>ConsumableInAdvanceTopUp</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+
+ <usages>
+ <usage name="consumable-in-advance-topup-usage1" billingMode="IN_ADVANCE" usageType="CONSUMABLE">
+ <billingPeriod>NO_BILLING_PERIOD</billingPeriod>
+ <blocks>
+ <block type="TOP_UP">
+ <unit>fastrack-tokens</unit>
+ <size>10</size>
+ <prices>
+ <price>
+ <currency>BTC</currency>
+ <value>0.10</value>
+ </price>
+ </prices>
+ <minTopUpCredit>5</minTopUpCredit>
+ </block>
+ </blocks>
+ </usage>
+ </usages>
+ </finalPhase>
+ </plan>
+
+
+ <plan name="capacity-in-arrear">
+ <product>CapacityInArrear</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+
+ <usages>
+ <usage name="capacity-in-arrear-usage1" billingMode="IN_ARREAR" usageType="CAPACITY">
+ <billingPeriod>MONTHLY</billingPeriod>
+ <tiers>
+ <tier>
+ <limits>
+ <limit>
+ <unit>bandwith-meg-sec</unit>
+ <max>100</max>
+ </limit>
+ <limit>
+ <unit>members</unit>
+ <max>500</max>
+ </limit>
+ </limits>
+ <fixedPrice>
+ <price>
+ <currency>BTC</currency>
+ <value>0.007</value>
+ </price>
+ </fixedPrice>
+ <recurringPrice>
+ <price>
+ <currency>BTC</currency>
+ <value>0.8</value>
+ </price>
+ </recurringPrice>
+ </tier>
+ <tier>
+ <limits>
+ <limit>
+ <unit>bandwith-meg-sec</unit>
+ <max>100</max>
+ </limit>
+ <limit>
+ <unit>members</unit>
+ <max>1000</max>
+ </limit>
+ </limits>
+ <fixedPrice>
+ <price>
+ <currency>BTC</currency>
+ <value>0.4</value>
+ </price>
+ </fixedPrice>
+ <recurringPrice>
+ <price>
+ <currency>BTC</currency>
+ <value>1.2</value>
+ </price>
+ </recurringPrice>
+ </tier>
+ </tiers>
+ </usage>
+ </usages>
+ </finalPhase>
+ </plan>
+
+
+ <plan name="consumable-in-arrear">
+ <product>ConsumableInArrear</product>
+ <finalPhase type="EVERGREEN">
+ <duration>
+ <unit>UNLIMITED</unit>
+ </duration>
+ <usages>
+ <usage name="consumable-in-arrear-usage1" billingMode="IN_ARREAR" usageType="CONSUMABLE">
+ <billingPeriod>MONTHLY</billingPeriod>
+ <tiers>
+ <tier>
+ <blocks>
+ <tieredBlock>
+ <unit>cell-phone-minutes</unit>
+ <size>1000</size>
+ <prices>
+ <price>
+ <currency>BTC</currency>
+ <value>0.5</value>
+ </price>
+ </prices>
+ <max>10000</max>
+ </tieredBlock>
+ <tieredBlock>
+ <unit>Mbytes</unit>
+ <size>512</size>
+ <prices>
+ <price>
+ <currency>BTC</currency>
+ <value>0.3</value>
+ </price>
+ </prices>
+ <max>512000</max>
+ </tieredBlock>
+ </blocks>
+ </tier>
+ </tiers>
+ </usage>
+ </usages>
+ </finalPhase>
+ </plan>
+
+
+ </plans>
+ <priceLists>
+ <defaultPriceList name="DEFAULT">
+ <plans>
+ <plan>capacity-in-advance-monthly</plan>
+ <plan>consumable-in-advance-prepay-credit-monthly</plan>
+ <plan>consumable-in-advance-topup</plan>
+ <plan>capacity-in-arrear</plan>
+ <plan>consumable-in-arrear</plan>
+ </plans>
+ </defaultPriceList>
+ </priceLists>
+</catalog>