killbill-memoizeit

Changes

pom.xml 2(+1 -1)

Details

diff --git a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml
index 4701c94..5a977b6 100644
--- a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml
+++ b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v1.xml
@@ -68,12 +68,20 @@
                 <phaseType>TRIAL</phaseType>
                 <policy>IMMEDIATE</policy>
             </changePolicyCase>
+            <changePolicyCase>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
         </changePolicy>
         <changeAlignment>
             <changeAlignmentCase>
                 <alignment>START_OF_SUBSCRIPTION</alignment>
             </changeAlignmentCase>
         </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>END_OF_TERM</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
         <createAlignment>
             <createAlignmentCase>
                 <product>Laser-Scope</product>
diff --git a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml
index d75891d..c575b09 100644
--- a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml
+++ b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v2.xml
@@ -68,12 +68,20 @@
                 <phaseType>TRIAL</phaseType>
                 <policy>IMMEDIATE</policy>
             </changePolicyCase>
+            <changePolicyCase>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
         </changePolicy>
         <changeAlignment>
             <changeAlignmentCase>
                 <alignment>START_OF_SUBSCRIPTION</alignment>
             </changeAlignmentCase>
         </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>END_OF_TERM</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
         <createAlignment>
             <createAlignmentCase>
                 <product>Laser-Scope</product>
diff --git a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml
index 8ad8ba4..63acb70 100644
--- a/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml
+++ b/beatrix/src/test/resources/retiredCatalogs/WeaponsHireSmall-v3.xml
@@ -66,12 +66,20 @@
                 <phaseType>TRIAL</phaseType>
                 <policy>IMMEDIATE</policy>
             </changePolicyCase>
+            <changePolicyCase>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
         </changePolicy>
         <changeAlignment>
             <changeAlignmentCase>
                 <alignment>START_OF_SUBSCRIPTION</alignment>
             </changeAlignmentCase>
         </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>END_OF_TERM</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
         <createAlignment>
             <createAlignmentCase>
                 <product>Laser-Scope</product>
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 2adf38c..5c6482d 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
@@ -17,12 +17,17 @@
 package org.killbill.billing.catalog.api.user;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
+import java.net.URISyntaxException;
 
 import javax.inject.Inject;
+import javax.xml.bind.JAXBException;
+import javax.xml.transform.TransformerException;
 
 import org.joda.time.DateTime;
+import org.killbill.billing.ErrorCode;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.CatalogUpdater;
 import org.killbill.billing.catalog.StandaloneCatalog;
@@ -33,6 +38,7 @@ import org.killbill.billing.catalog.api.Catalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.CatalogService;
 import org.killbill.billing.catalog.api.CatalogUserApi;
+import org.killbill.billing.catalog.api.InvalidConfigException;
 import org.killbill.billing.catalog.api.SimplePlanDescriptor;
 import org.killbill.billing.catalog.api.StaticCatalog;
 import org.killbill.billing.catalog.caching.CatalogCache;
@@ -42,9 +48,11 @@ import org.killbill.billing.tenant.api.TenantUserApi;
 import org.killbill.billing.util.callcontext.CallContext;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.billing.util.callcontext.TenantContext;
+import org.killbill.xmlloader.ValidationException;
 import org.killbill.xmlloader.XMLLoader;
+import org.xml.sax.SAXException;
 
-public class DefaultCatalogUserApi implements CatalogUserApi {
+public class  DefaultCatalogUserApi implements CatalogUserApi {
 
     private final CatalogService catalogService;
     private final InternalCallContextFactory internalCallContextFactory;
@@ -77,19 +85,34 @@ public class DefaultCatalogUserApi implements CatalogUserApi {
 
     @Override
     public void uploadCatalog(final String catalogXML, final CallContext callContext) throws CatalogApiException {
+
+
+        final InternalTenantContext internalTenantContext = createInternalTenantContext(callContext);
         try {
             // Validation purpose:  Will throw if bad XML or catalog validation fails
             final InputStream stream = new ByteArrayInputStream(catalogXML.getBytes());
             XMLLoader.getObjectFromStream(new URI("dummy"), stream, StandaloneCatalog.class);
 
-            final InternalTenantContext internalTenantContext = createInternalTenantContext(callContext);
             catalogCache.clearCatalog(internalTenantContext);
             tenantApi.addTenantKeyValue(TenantKey.CATALOG.toString(), catalogXML, callContext);
-        } catch (TenantApiException e) {
+        } catch (final TenantApiException e) {
             throw new CatalogApiException(e);
-        } catch (final Exception e) {
+        } catch (final ValidationException e) {
+            throw new CatalogApiException(e, ErrorCode.CAT_INVALID_FOR_TENANT, internalTenantContext.getTenantRecordId());
+        } catch (final JAXBException e) {
+            throw new CatalogApiException(e, ErrorCode.CAT_INVALID_FOR_TENANT, internalTenantContext.getTenantRecordId());
+        } catch (final IOException e) {
+            throw new IllegalStateException(e);
+        } catch (final TransformerException e) {
+            throw new IllegalStateException(e);
+        } catch (final URISyntaxException e) {
+            throw new IllegalStateException(e);
+        } catch (final SAXException e) {
+            throw new IllegalStateException(e);
+        } catch (final InvalidConfigException e) {
             throw new IllegalStateException(e);
         }
+
     }
 
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java
index 3b54252..f0b3f7c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/EhCacheOverriddenPlanCache.java
@@ -27,6 +27,7 @@ import org.killbill.billing.ObjectType;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.DefaultPlan;
 import org.killbill.billing.catalog.DefaultPlanPhasePriceOverride;
+import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Plan;
@@ -93,7 +94,9 @@ public class EhCacheOverriddenPlanCache implements OverriddenPlanCache {
         final DefaultPlan defaultPlan = (DefaultPlan) catalog.findCurrentPlan(parentPlanName);
 
         final PlanPhasePriceOverride[] overrides = createOverrides(defaultPlan, phaseDefs);
-        return new DefaultPlan(planName, defaultPlan, overrides);
+        final DefaultPlan result =  new DefaultPlan(planName, defaultPlan, overrides);
+        result.initialize((StandaloneCatalog) catalog, ((StandaloneCatalog) catalog).getCatalogURI());
+        return result;
     }
 
     private PlanPhasePriceOverride[] createOverrides(final Plan defaultPlan, final List<CatalogOverridePhaseDefinitionModelDao> phaseDefs) {
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 996d2f1..68d90d8 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/CatalogSafetyInitializer.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/CatalogSafetyInitializer.java
@@ -19,32 +19,47 @@ package org.killbill.billing.catalog;
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
 
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 
+import org.killbill.billing.catalog.api.BlockType;
+import org.killbill.billing.catalog.api.FixedType;
+
 public class CatalogSafetyInitializer {
 
 
+    public static final Integer DEFAULT_NON_REQUIRED_INTEGER_FIELD_VALUE = -1;
+
+    private static final Map<Class, LinkedList<Field>> perCatalogClassNonRequiredFields = new HashMap<Class, LinkedList<Field>>();
+
     //
     // Ensure that all uninitialized arrays for which there is neither a 'required' XmlElementWrapper or XmlElement annotation
     // end up initialized with a default zero length array (allowing to safely get the length and iterate over (0) element.
     //
-    public static void initializeNonRequiredArrayFields(final Object obj) {
+    public static void initializeNonRequiredNullFieldsWithDefaultValue(final Object obj) {
+
+        LinkedList<Field> fields = perCatalogClassNonRequiredFields.get(obj.getClass());
+        if (fields == null) {
+            fields = initializeNonRequiredFields(obj.getClass());
+            perCatalogClassNonRequiredFields.put(obj.getClass(), fields);
+        }
         try {
-            final Field[] fields = obj.getClass().getDeclaredFields();
             for (final Field f : fields) {
                 if (f.getType().isArray()) {
-                    final XmlElementWrapper xmlElementWrapper = f.getAnnotation(XmlElementWrapper.class);
-                    if (xmlElementWrapper != null) {
-                        if (!xmlElementWrapper.required()) {
-                            initializeArrayIfNull(obj, f);
-                        }
-                    } else {
-                        final XmlElement xmlElement = f.getAnnotation(XmlElement.class);
-                        if (xmlElement != null && !xmlElement.required()) {
-                            initializeArrayIfNull(obj, f);
+                    initializeArrayIfNull(obj, f);
+                } else if (!f.getType().isPrimitive()) {
+                    if (f.getType().isEnum()) {
+                        if (FixedType.class.equals(f.getType())) {
+                            initializeFieldWithValue(obj, f, FixedType.ONE_TIME);
+                        } else if (BlockType.class.equals(f.getType())) {
+                            initializeFieldWithValue(obj, f, BlockType.VANILLA);
                         }
+                    } else if (Integer.class.equals(f.getType())) {
+                        initializeFieldWithValue(obj, f, DEFAULT_NON_REQUIRED_INTEGER_FIELD_VALUE);
                     }
                 }
             }
@@ -55,6 +70,47 @@ public class CatalogSafetyInitializer {
         }
     }
 
+    // For each type of catalog object we keep the 'Field' associated to non required attribute fields
+    private static LinkedList<Field> initializeNonRequiredFields(final Class<?> aClass) {
+
+        final LinkedList<Field> result = new LinkedList();
+        final Field[] fields = aClass.getDeclaredFields();
+        for (final Field f : fields) {
+            if (f.getType().isArray()) {
+                final XmlElementWrapper xmlElementWrapper = f.getAnnotation(XmlElementWrapper.class);
+                if (xmlElementWrapper != null) {
+                    if (!xmlElementWrapper.required()) {
+                        result.add(f);
+                    }
+                } else {
+                    final XmlElement xmlElement = f.getAnnotation(XmlElement.class);
+                    if (xmlElement != null && !xmlElement.required()) {
+                        result.add(f);
+                    }
+                }
+            } else if (!f.getType().isPrimitive()) {
+                if (f.getType().isEnum()) {
+                    if (FixedType.class.equals(f.getType())) {
+                        result.add(f);
+                    } else if (BlockType.class.equals(f.getType())) {
+                        result.add(f);
+                    }
+                } else if (Integer.class.equals(f.getType())) {
+                    result.add(f);
+                }
+            }
+        }
+        return result;
+    }
+
+    private static void initializeFieldWithValue(final Object obj, final Field f, final Object value) throws IllegalAccessException, ClassNotFoundException {
+        f.setAccessible(true);
+        if (f.get(obj) == null) {
+            f.set(obj, value);
+        }
+        f.setAccessible(false);
+    }
+
     private static void initializeArrayIfNull(final Object obj, final Field f) throws IllegalAccessException, ClassNotFoundException {
         f.setAccessible(true);
         if (f.get(obj) == null) {
@@ -63,6 +119,7 @@ public class CatalogSafetyInitializer {
         f.setAccessible(false);
     }
 
+
     private static Object[] getZeroLengthArrayInitializer(final Field f) throws ClassNotFoundException {
         // Yack... type erasure, why?
         final String arrayClassName = f.getType().getCanonicalName();
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 784e577..6604384 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultBlock.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultBlock.java
@@ -86,6 +86,11 @@ public class DefaultBlock extends ValidatingConfig<StandaloneCatalog> implements
 
     @Override
     public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
+        // Safety check
+        if (type == null) {
+            throw new IllegalStateException("type should have been automatically been initialized with VANILLA ");
+        }
+
         if (type == BlockType.TOP_UP && minTopUpCredit == null) {
             errors.add(new ValidationError(String.format("TOP_UP block needs to define minTopUpCredit for phase %s",
                                                          phase.getName()), catalog.getCatalogURI(), DefaultUsage.class, ""));
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java
index 726ea77..b7e5fd4 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultDuration.java
@@ -16,6 +16,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.XmlElement;
@@ -35,7 +37,6 @@ import org.killbill.xmlloader.ValidationErrors;
 @XmlAccessorType(XmlAccessType.NONE)
 public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> implements Duration {
 
-    public static final int DEFAULT_DURATION_NUMBER  = -1;
     @XmlElement(required = true)
     private TimeUnit unit;
 
@@ -59,7 +60,6 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
     }
 
     public DefaultDuration() {
-        number = DEFAULT_DURATION_NUMBER;
     }
 
     @Override
@@ -102,17 +102,30 @@ public class DefaultDuration extends ValidatingConfig<StandaloneCatalog> impleme
 
     @Override
     public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
+
+        // Safety check
+        if (number == null) {
+            throw new IllegalStateException("number should have been automatically been initialized with DEFAULT_NON_REQUIRED_INTEGER_FIELD_VALUE ");
+        }
+
         //Validation: TimeUnit UNLIMITED if number == -1
-        if ((unit == TimeUnit.UNLIMITED && number != DEFAULT_DURATION_NUMBER)) {
+        if ((unit == TimeUnit.UNLIMITED && number != CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_INTEGER_FIELD_VALUE)) {
             errors.add(new ValidationError("Duration can only have 'UNLIMITED' unit if the number is omitted",
                                            catalog.getCatalogURI(), DefaultDuration.class, ""));
-        } else if ((unit != TimeUnit.UNLIMITED) && number == DEFAULT_DURATION_NUMBER) {
+        } else if ((unit != TimeUnit.UNLIMITED) && number == CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_INTEGER_FIELD_VALUE) {
             errors.add(new ValidationError("Finite Duration must have a well defined length",
                                            catalog.getCatalogURI(), DefaultDuration.class, ""));
         }
         return errors;
     }
 
+    @Override
+    public void initialize(final StandaloneCatalog root, final URI uri) {
+        super.initialize(root, uri);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
+    }
+
+
     public DefaultDuration setUnit(final TimeUnit unit) {
         this.unit = unit;
         return this;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java
index 1218f4d..924fbf5 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultFixed.java
@@ -16,7 +16,6 @@
 
 package org.killbill.billing.catalog;
 
-import java.math.BigDecimal;
 import java.net.URI;
 
 import javax.xml.bind.annotation.XmlAccessType;
@@ -24,7 +23,6 @@ import javax.xml.bind.annotation.XmlAccessorType;
 import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
 
-import org.killbill.billing.catalog.api.Currency;
 import org.killbill.billing.catalog.api.Fixed;
 import org.killbill.billing.catalog.api.FixedType;
 import org.killbill.billing.catalog.api.InternationalPrice;
@@ -52,7 +50,6 @@ public class DefaultFixed extends ValidatingConfig<StandaloneCatalog> implements
     }
 
     public DefaultFixed() {
-        type = FixedType.ONE_TIME;
     }
 
     public DefaultFixed(final DefaultFixed in, final PlanPhasePriceOverride override) {
@@ -63,8 +60,7 @@ public class DefaultFixed extends ValidatingConfig<StandaloneCatalog> implements
     @Override
     public void initialize(final StandaloneCatalog root, final URI uri) {
         super.initialize(root, uri);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
-
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
         if (fixedPrice != null) {
             fixedPrice.initialize(root, uri);
         }
@@ -72,6 +68,10 @@ public class DefaultFixed extends ValidatingConfig<StandaloneCatalog> implements
 
     @Override
     public ValidationErrors validate(final StandaloneCatalog root, final ValidationErrors errors) {
+        // Safety check
+        if (type == null) {
+            throw new IllegalStateException("fixedPrice should have been automatically been initialized with ONE_TIME ");
+        }
         return errors;
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java
index 5ff3622..1b391c8 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultInternationalPrice.java
@@ -125,7 +125,7 @@ public class DefaultInternationalPrice extends ValidatingConfig<StandaloneCatalo
     @Override
     public void initialize(final StandaloneCatalog root, final URI uri) {
         super.initialize(root, uri);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(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 103d209..30176b5 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultLimit.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultLimit.java
@@ -81,7 +81,7 @@ public class DefaultLimit extends ValidatingConfig<StandaloneCatalog> implements
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
index 25fa3c4..5f2e9ea 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlan.java
@@ -75,7 +75,7 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     //No other value is allowed for Tiered ADDONS
     //A value of -1 means unlimited
     @XmlElement(required = false)
-    private Integer plansAllowedInBundle = -1;
+    private Integer plansAllowedInBundle;
 
     private String priceListName;
 
@@ -174,7 +174,7 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
 
         if (finalPhase != null) {
             finalPhase.setPlan(this);
@@ -217,6 +217,10 @@ public class DefaultPlan extends ValidatingConfig<StandaloneCatalog> implements 
                                                          finalPhase.getName(), name, finalPhase.getPhaseType()),
                                            catalog.getCatalogURI(), DefaultPlan.class, ""));
         }
+        // Safety check
+        if (plansAllowedInBundle == null) {
+            throw new IllegalStateException("plansAllowedInBundle should have been automatically been initialized with DEFAULT_NON_REQUIRED_INTEGER_FIELD_VALUE (-1)");
+        }
         return errors;
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
index d49ba3e..4a2eade 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPlanPhase.java
@@ -163,7 +163,7 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
     public void initialize(final StandaloneCatalog root, final URI uri) {
 
         super.initialize(root, uri);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
 
         if (fixed != null) {
             fixed.initialize(root, uri);
@@ -177,6 +177,7 @@ public class DefaultPlanPhase extends ValidatingConfig<StandaloneCatalog> implem
             usage.initialize(root, uri);
             usage.setPhase(this);
         }
+        duration.initialize(root, uri);
     }
 
     public DefaultPlanPhase setFixed(final DefaultFixed fixed) {
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPrice.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPrice.java
index 6b5f6ec..069ccb6 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPrice.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPrice.java
@@ -83,7 +83,7 @@ public class DefaultPrice extends ValidatingConfig<StandaloneCatalog> implements
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
index 273f12c..a67783a 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceList.java
@@ -102,7 +102,7 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
     public DefaultPriceList setName(final String name) {
@@ -141,4 +141,10 @@ public class DefaultPriceList extends ValidatingConfig<StandaloneCatalog> implem
         result = 31 * result + (plans != null ? plans.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultPriceList{" +
+               "name='" + name + '}';
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
index 15a479c..4aaf3cb 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultPriceListSet.java
@@ -109,7 +109,7 @@ public class DefaultPriceListSet extends ValidatingConfig<StandaloneCatalog> imp
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
index c8d4999..af95ceb 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultProduct.java
@@ -152,7 +152,7 @@ public class DefaultProduct extends ValidatingConfig<StandaloneCatalog> implemen
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
         for (DefaultLimit cur : limits) {
             cur.initialize(catalog, sourceURI);
         }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
index 14f956a..d4c180b 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultRecurring.java
@@ -65,7 +65,7 @@ public class DefaultRecurring extends ValidatingConfig<StandaloneCatalog> implem
     @Override
     public void initialize(final StandaloneCatalog root, final URI uri) {
         super.initialize(root, uri);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
         if (recurringPrice != null) {
             recurringPrice.initialize(root, uri);
         }
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 1680036..21ad681 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultTier.java
@@ -133,7 +133,7 @@ public class DefaultTier extends ValidatingConfig<StandaloneCatalog> implements 
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUnit.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUnit.java
index c11f6b9..f82499d 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUnit.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUnit.java
@@ -51,7 +51,7 @@ public class DefaultUnit extends ValidatingConfig<StandaloneCatalog> implements 
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java
index cb34f94..59af193 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/DefaultUsage.java
@@ -165,7 +165,7 @@ public class DefaultUsage extends ValidatingConfig<StandaloneCatalog> implements
     @Override
     public void initialize(final StandaloneCatalog root, final URI uri) {
         super.initialize(root, uri);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
 
         for (DefaultLimit limit : limits) {
             limit.initialize(root, uri);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
index 80217c8..edfdf88 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
@@ -19,6 +19,7 @@
 package org.killbill.billing.catalog.io;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
 import java.net.URI;
@@ -27,22 +28,33 @@ import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.xml.bind.JAXBException;
+import javax.xml.transform.TransformerException;
+
 import org.killbill.billing.ErrorCode;
 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.api.InvalidConfigException;
 import org.killbill.billing.catalog.override.PriceOverride;
 import org.killbill.billing.util.callcontext.InternalCallContextFactory;
 import org.killbill.clock.Clock;
 import org.killbill.xmlloader.UriAccessor;
+import org.killbill.xmlloader.ValidationErrors;
+import org.killbill.xmlloader.ValidationException;
 import org.killbill.xmlloader.XMLLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
 
 import com.google.common.io.Resources;
 import com.google.inject.Inject;
 
 public class VersionedCatalogLoader implements CatalogLoader {
 
+    private static final Logger logger = LoggerFactory.getLogger(VersionedCatalogLoader.class);
+
     private static final Object PROTOCOL_FOR_FILE = "file";
     private static final String XML_EXTENSION = ".xml";
 
@@ -75,9 +87,21 @@ public class VersionedCatalogLoader implements CatalogLoader {
                 final StandaloneCatalog catalog = XMLLoader.getObjectFromUri(u, StandaloneCatalog.class);
                 result.add(new StandaloneCatalogWithPriceOverride(catalog, priceOverride, InternalCallContextFactory.INTERNAL_TENANT_RECORD_ID, internalCallContextFactory));
             }
+            // Perform initialization and validation for VersionedCatalog
+            XMLLoader.initializeAndValidate(new URI(uriString), result);
             return result;
+        } catch (final ValidationException e) {
+            logger.warn("Failed to load default catalog", e);
+            throw new CatalogApiException(e, ErrorCode.CAT_INVALID_DEFAULT, uriString);
+        } catch (final JAXBException e) {
+            logger.warn("Failed to load default catalog", e);
+            throw new CatalogApiException(e, ErrorCode.CAT_INVALID_DEFAULT, uriString);
+        } catch(IllegalArgumentException e) {
+            logger.warn("Failed to load default catalog", e);
+            throw new CatalogApiException(e, ErrorCode.CAT_INVALID_DEFAULT, uriString);
         } catch (Exception e) {
-            throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, "Problem encountered loading catalog: ", e.getMessage());
+            logger.warn("Failed to load default catalog", e);
+            throw new IllegalStateException(e);
         }
     }
 
@@ -103,11 +127,30 @@ public class VersionedCatalogLoader implements CatalogLoader {
                     result.add(new StandaloneCatalogWithPriceOverride(catalog, priceOverride, tenantRecordId, internalCallContextFactory));
                 }
             }
+            // Perform initialization and validation for VersionedCatalog
+            XMLLoader.initializeAndValidate(uri, result);
             return result;
-        } catch (final CatalogApiException e) {
-            throw e;
-        } catch (final Exception e) {
-            throw new CatalogApiException(ErrorCode.CAT_INVALID_FOR_TENANT, tenantRecordId);
+        } catch (final ValidationException e) {
+            logger.warn("Failed to load catalog for tenantRecordId='{}'",  tenantRecordId, e);
+            throw new CatalogApiException(e, ErrorCode.CAT_INVALID_FOR_TENANT, tenantRecordId);
+        } catch (final JAXBException e) {
+            logger.warn("Failed to load catalog for tenantRecordId='{}'",  tenantRecordId, e);
+            throw new CatalogApiException(e, ErrorCode.CAT_INVALID_FOR_TENANT, tenantRecordId);
+        } catch (final IOException e) {
+            logger.warn("Failed to load catalog for tenantRecordId='{}'",  tenantRecordId, e);
+            throw new IllegalStateException(e);
+        } catch (final TransformerException e) {
+            logger.warn("Failed to load catalog for tenantRecordId='{}'",  tenantRecordId, e);
+            throw new IllegalStateException(e);
+        } catch (final URISyntaxException e) {
+            logger.warn("Failed to load catalog for tenantRecordId='{}'",  tenantRecordId, e);
+            throw new IllegalStateException(e);
+        } catch (final SAXException e) {
+            logger.warn("Failed to load catalog for tenantRecordId='{}'",  tenantRecordId, e);
+            throw new IllegalStateException(e);
+        } catch (final InvalidConfigException e) {
+            logger.warn("Failed to load catalog for tenantRecordId='{}'",  tenantRecordId, e);
+            throw new IllegalStateException(e);
         }
     }
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
index 86b8739..822ef14 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/override/DefaultPriceOverride.java
@@ -30,6 +30,8 @@ import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.DefaultPlan;
 import org.killbill.billing.catalog.DefaultPlanPhase;
 import org.killbill.billing.catalog.DefaultPlanPhasePriceOverride;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhase;
@@ -60,7 +62,7 @@ public class DefaultPriceOverride implements PriceOverride {
     }
 
     @Override
-    public DefaultPlan getOrCreateOverriddenPlan(final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, @Nullable final InternalCallContext context) throws CatalogApiException {
+    public DefaultPlan getOrCreateOverriddenPlan(final StandaloneCatalog standaloneCatalog, final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, @Nullable final InternalCallContext context) throws CatalogApiException {
 
         final PlanPhasePriceOverride[] resolvedOverride = new PlanPhasePriceOverride[parentPlan.getAllPhases().length];
         int index = 0;
@@ -110,6 +112,7 @@ public class DefaultPriceOverride implements PriceOverride {
             planName = new StringBuffer(parentPlan.getName()).append("-dryrun-").append(DRY_RUN_PLAN_IDX.incrementAndGet()).toString();
         }
         final DefaultPlan result = new DefaultPlan(planName, (DefaultPlan) parentPlan, resolvedOverride);
+        result.initialize(standaloneCatalog, standaloneCatalog.getCatalogURI());
         if (context == null) {
             overriddenPlanCache.addDryRunPlan(planName, result);
         }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java b/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java
index 49cdbc8..cb81b5b 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/override/PriceOverride.java
@@ -23,6 +23,8 @@ import org.joda.time.DateTime;
 import org.killbill.billing.callcontext.InternalCallContext;
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.catalog.DefaultPlan;
+import org.killbill.billing.catalog.StandaloneCatalog;
+import org.killbill.billing.catalog.StandaloneCatalogWithPriceOverride;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
@@ -30,7 +32,7 @@ import org.killbill.billing.catalog.api.StaticCatalog;
 
 public interface PriceOverride {
 
-    DefaultPlan getOrCreateOverriddenPlan(final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, final InternalCallContext context) throws CatalogApiException;
+    DefaultPlan getOrCreateOverriddenPlan(final StandaloneCatalog catalog, final Plan parentPlan, final DateTime catalogEffectiveDate, final List<PlanPhasePriceOverride> overrides, final InternalCallContext context) throws CatalogApiException;
 
 
     DefaultPlan getOverriddenPlan(final String planName, final StaticCatalog catalog, final InternalTenantContext context) throws CatalogApiException;
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/PriceListDefault.java b/catalog/src/main/java/org/killbill/billing/catalog/PriceListDefault.java
index 0778ad4..08571ef 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/PriceListDefault.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/PriceListDefault.java
@@ -49,7 +49,7 @@ public class PriceListDefault extends DefaultPriceList {
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
     @Override
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java
index 319a486..99e23d4 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCase.java
@@ -20,7 +20,6 @@ package org.killbill.billing.catalog.rules;
 import java.net.URI;
 
 import org.killbill.billing.catalog.CatalogSafetyInitializer;
-import org.killbill.billing.catalog.DefaultPrice;
 import org.killbill.billing.catalog.DefaultPriceList;
 import org.killbill.billing.catalog.DefaultProduct;
 import org.killbill.billing.catalog.StandaloneCatalog;
@@ -98,7 +97,7 @@ public abstract class DefaultCase<T> extends ValidatingConfig<StandaloneCatalog>
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
     protected abstract DefaultCase<T> setProduct(Product product);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseBillingAlignment.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseBillingAlignment.java
index fc2b8f5..31e56fc 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseBillingAlignment.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseBillingAlignment.java
@@ -74,4 +74,17 @@ public class DefaultCaseBillingAlignment extends DefaultCasePhase<BillingAlignme
         result = 31 * result + (alignment != null ? alignment.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseBillingAlignment {" +
+               "alignment=" + alignment +
+               ", phaseType=" + getPhaseType() +
+               ", product=" + getProduct() +
+               ", productCategory=" + getProductCategory() +
+               ", billingPeriod=" + getBillingPeriod() +
+               ", priceList=" + getPriceList() +
+               '}';
+    }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCancelPolicy.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCancelPolicy.java
index b1a061c..11a3399 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCancelPolicy.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCancelPolicy.java
@@ -74,4 +74,17 @@ public class DefaultCaseCancelPolicy extends DefaultCasePhase<BillingActionPolic
         result = 31 * result + (policy != null ? policy.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseCancelPolicy{" +
+               "policy =" + policy +
+               ", phaseType =" + getPhaseType() +
+               ", product=" + getProduct() +
+               ", productCategory=" + getProductCategory() +
+               ", billingPeriod=" + getBillingPeriod() +
+               ", priceList=" + getPriceList() +
+               '}';
+    }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
index 62a83cb..d522bff 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChange.java
@@ -155,7 +155,7 @@ public abstract class DefaultCaseChange<T> extends ValidatingConfig<StandaloneCa
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
     public DefaultCaseChange<T> setPhaseType(final PhaseType phaseType) {
@@ -303,4 +303,19 @@ public abstract class DefaultCaseChange<T> extends ValidatingConfig<StandaloneCa
     public PriceList getToPriceList() {
         return toPriceList;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseChange{" +
+               "phaseType=" + phaseType +
+               ", fromProduct=" + fromProduct +
+               ", fromProductCategory=" + fromProductCategory +
+               ", fromBillingPeriod=" + fromBillingPeriod +
+               ", fromPriceList=" + fromPriceList +
+               ", toProduct=" + toProduct +
+               ", toProductCategory=" + toProductCategory +
+               ", toBillingPeriod=" + toBillingPeriod +
+               ", toPriceList=" + toPriceList +
+               '}';
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanAlignment.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanAlignment.java
index f01860c..d3a95b6 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanAlignment.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanAlignment.java
@@ -68,4 +68,21 @@ public class DefaultCaseChangePlanAlignment extends DefaultCaseChange<PlanAlignm
         result = 31 * result + (alignment != null ? alignment.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseChangePlanAlignment {" +
+               "alignment=" + alignment +
+               ", phaseType=" + getPhaseType() +
+               ", fromProduct=" + getFromProduct() +
+               ", fromProductCategory=" + getFromProductCategory() +
+               ", fromBillingPeriod=" + getFromBillingPeriod() +
+               ", fromPriceList=" + getFromPriceList() +
+               ", toProduct=" + getToProduct() +
+               ", toProductCategory=" + getToProductCategory() +
+               ", toBillingPeriod=" + getToBillingPeriod() +
+               ", toPriceList=" + getToPriceList() +
+               '}';
+    }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanPolicy.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanPolicy.java
index 1464b73..71f90b5 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanPolicy.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseChangePlanPolicy.java
@@ -16,16 +16,16 @@
 
 package org.killbill.billing.catalog.rules;
 
+import java.net.URI;
+
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlSeeAlso;
 
+import org.killbill.billing.catalog.CatalogSafetyInitializer;
+import org.killbill.billing.catalog.StandaloneCatalog;
 import org.killbill.billing.catalog.api.BillingActionPolicy;
-import org.killbill.billing.catalog.api.BillingPeriod;
-import org.killbill.billing.catalog.api.PhaseType;
-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.rules.CaseChangePlanPolicy;
+import org.killbill.xmlloader.ValidationErrors;
 
 @XmlSeeAlso(DefaultCaseChange.class)
 public class DefaultCaseChangePlanPolicy extends DefaultCaseChange<BillingActionPolicy> implements CaseChangePlanPolicy {
@@ -49,6 +49,17 @@ public class DefaultCaseChangePlanPolicy extends DefaultCaseChange<BillingAction
     }
 
     @Override
+    public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
+        return errors;
+    }
+
+    @Override
+    public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
+        super.initialize(catalog, sourceURI);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
+    }
+
+    @Override
     public boolean equals(final Object o) {
         if (this == o) {
             return true;
@@ -75,4 +86,20 @@ public class DefaultCaseChangePlanPolicy extends DefaultCaseChange<BillingAction
         result = 31 * result + (policy != null ? policy.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseChangePlanPolicy {" +
+               "policy=" + policy +
+               ", phaseType=" + phaseType +
+               ", fromProduct=" + getFromProduct() +
+               ", fromProductCategory=" + getFromProductCategory() +
+               ", fromBillingPeriod=" + getFromBillingPeriod() +
+               ", fromPriceList=" + getFromPriceList() +
+               ", toProduct=" + getToProduct() +
+               ", toProductCategory=" + getToProductCategory() +
+               ", toBillingPeriod=" + getToBillingPeriod() +
+               ", toPriceList=" + getToPriceList() +
+               '}';
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCreateAlignment.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCreateAlignment.java
index 5d7862d..5577db3 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCreateAlignment.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseCreateAlignment.java
@@ -68,4 +68,16 @@ public class DefaultCaseCreateAlignment extends DefaultCaseStandardNaming<PlanAl
         result = 31 * result + (alignment != null ? alignment.hashCode() : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DefaultCaseCreateAlignment {" +
+               "alignment =" + alignment +
+               ", product=" + getProduct() +
+               ", productCategory=" + getProductCategory() +
+               ", billingPeriod=" + getBillingPeriod() +
+               ", priceList=" + getPriceList() +
+               '}';
+    }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePhase.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePhase.java
index d36db6c..ef8773c 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePhase.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePhase.java
@@ -63,7 +63,7 @@ public abstract class DefaultCasePhase<T> extends DefaultCaseStandardNaming<T> {
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
 
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePriceList.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePriceList.java
index 7958365..4b9cf33 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePriceList.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCasePriceList.java
@@ -143,4 +143,16 @@ public class DefaultCasePriceList extends DefaultCaseStandardNaming<DefaultPrice
         result = 31 * result + (toPriceList != null ? toPriceList.hashCode() : 0);
         return result;
     }
+
+
+    @Override
+    public String toString() {
+        return "DefaultCasePriceList {" +
+               "fromProduct=" + fromProduct +
+               ", fromProductCategory=" + fromProductCategory +
+               ", fromBillingPeriod=" + fromBillingPeriod +
+               ", fromPriceList=" + fromPriceList +
+               ", toPriceList=" + toPriceList +
+               '}';
+    }
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java
index 79c73aa..d23c55f 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultCaseStandardNaming.java
@@ -115,4 +115,5 @@ public abstract class DefaultCaseStandardNaming<T> extends DefaultCase<T> implem
         result = 31 * result + (priceList != null ? priceList.hashCode() : 0);
         return result;
     }
+
 }
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
index 20c4e9c..bc7f4ae 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/rules/DefaultPlanRules.java
@@ -18,6 +18,7 @@ package org.killbill.billing.catalog.rules;
 
 import java.net.URI;
 import java.util.Arrays;
+import java.util.HashSet;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -45,6 +46,7 @@ import org.killbill.billing.catalog.api.rules.CaseCreateAlignment;
 import org.killbill.billing.catalog.api.rules.CasePriceList;
 import org.killbill.billing.catalog.api.rules.PlanRules;
 import org.killbill.xmlloader.ValidatingConfig;
+import org.killbill.xmlloader.ValidationError;
 import org.killbill.xmlloader.ValidationErrors;
 
 import com.google.common.collect.ImmutableList;
@@ -157,7 +159,7 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
     }
 
     private DefaultPriceList findPriceList(final PlanSpecifier specifier, final StaticCatalog catalog) throws CatalogApiException {
-        DefaultPriceList result = DefaultCase.getResult(priceListCase, specifier, catalog);
+        DefaultPriceList result = DefaultCasePriceList.getResult(priceListCase, specifier, catalog);
         if (result == null) {
             final String priceListName = specifier.getPlanName() != null ? catalog.findCurrentPlan(specifier.getPlanName()).getPriceListName() : specifier.getPriceListName();
             result = (DefaultPriceList) catalog.findCurrentPricelist(priceListName);
@@ -165,13 +167,92 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
         return result;
     }
 
+
     @Override
     public ValidationErrors validate(final StandaloneCatalog catalog, final ValidationErrors errors) {
-        //TODO: MDW - Validation: check that the plan change special case pairs are unique!
-        //TODO: MDW - Validation: check that the each product appears in at most one tier.
-        //TODO: MDW - Unit tests for rules
-        //TODO: MDW - validate that there is a default policy for change AND cancel
+        //
+        // Validate that there is a default policy for change AND cancel rules and check unicity of rules
+        //
+        final HashSet<DefaultCaseChangePlanPolicy> caseChangePlanPoliciesSet = new HashSet<DefaultCaseChangePlanPolicy>();
+        boolean foundDefaultCase = false;
+        for (final DefaultCaseChangePlanPolicy cur : changeCase) {
+            if (caseChangePlanPoliciesSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for change plan %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                caseChangePlanPoliciesSet.add(cur);
+            }
+            if (cur.getPhaseType() == null &&
+                cur.getFromProduct() == null &&
+                cur.getFromProductCategory() == null &&
+                cur.getFromBillingPeriod() == null &&
+                cur.getFromPriceList() == null &&
+                cur.getToProduct() == null &&
+                cur.getToProductCategory() == null &&
+                cur.getToBillingPeriod() == null &&
+                cur.getToPriceList() == null) {
+                foundDefaultCase = true;
+            }
+        }
+        if (!foundDefaultCase) {
+            errors.add(new ValidationError("Missing default rule case for plan change", catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+        }
+
+        final HashSet<DefaultCaseCancelPolicy> defaultCaseCancelPoliciesSet = new HashSet<DefaultCaseCancelPolicy>();
+        foundDefaultCase = false;
+        for (final DefaultCaseCancelPolicy cur : cancelCase) {
+            if (defaultCaseCancelPoliciesSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for plan cancellation %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                defaultCaseCancelPoliciesSet.add(cur);
+            }
+            if (cur.getPhaseType() == null &&
+                cur.getProduct() == null &&
+                cur.getProductCategory() == null &&
+                cur.getBillingPeriod() == null &&
+                cur.getPriceList() == null) {
+                foundDefaultCase = true;
+            }
+        }
+        if (!foundDefaultCase) {
+            errors.add(new ValidationError("Missing default rule case for plan cancellation", catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+        }
+
+
+        final HashSet<DefaultCaseChangePlanAlignment> caseChangePlanAlignmentsSet = new HashSet<DefaultCaseChangePlanAlignment>();
+        for (final DefaultCaseChangePlanAlignment cur : changeAlignmentCase) {
+            if (caseChangePlanAlignmentsSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for plan change alignment %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                caseChangePlanAlignmentsSet.add(cur);
+            }
+        }
+
+        final HashSet<DefaultCaseCreateAlignment> caseCreateAlignmentsSet = new HashSet<DefaultCaseCreateAlignment>();
+        for (final DefaultCaseCreateAlignment cur : createAlignmentCase) {
+            if (caseCreateAlignmentsSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for create plan alignment %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                caseCreateAlignmentsSet.add(cur);
+            }
+        }
 
+        final HashSet<DefaultCaseBillingAlignment> caseBillingAlignmentsSet = new HashSet<DefaultCaseBillingAlignment>();
+        for (final DefaultCaseBillingAlignment cur : billingAlignmentCase) {
+            if (caseBillingAlignmentsSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for billing alignment %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                caseBillingAlignmentsSet.add(cur);
+            }
+        }
+
+        final HashSet<DefaultCasePriceList> casePriceListsSet = new HashSet<DefaultCasePriceList>();
+        for (final DefaultCasePriceList cur : priceListCase) {
+            if (casePriceListsSet.contains(cur)) {
+                errors.add(new ValidationError(String.format("Duplicate rule for price list transition %s", cur.toString()), catalog.getCatalogURI(), DefaultPlanRules.class, ""));
+            } else {
+                casePriceListsSet.add(cur);
+            }
+        }
         return errors;
     }
 
@@ -179,7 +260,7 @@ public class DefaultPlanRules extends ValidatingConfig<StandaloneCatalog> implem
     @Override
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
 
         for (final DefaultCaseChangePlanPolicy cur : changeCase) {
             cur.initialize(catalog, sourceURI);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
index 81366e4..96869e4 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalog.java
@@ -300,7 +300,7 @@ public class StandaloneCatalog extends ValidatingConfig<StandaloneCatalog> imple
     public void initialize(final StandaloneCatalog catalog, final URI sourceURI) {
 
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
 
         catalogURI = sourceURI;
         planRules.initialize(catalog, sourceURI);
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 8a42e4b..7e6deb9 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/StandaloneCatalogWithPriceOverride.java
@@ -79,7 +79,7 @@ public class StandaloneCatalogWithPriceOverride extends StandaloneCatalog implem
         }
 
         final InternalCallContext internalCallContext = overrides.getCallContext() != null ? internalCallContextFactory.createInternalCallContextWithoutAccountRecordId(overrides.getCallContext()) : null;
-        return priceOverride.getOrCreateOverriddenPlan(defaultPlan, CatalogDateHelper.toUTCDateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
+        return priceOverride.getOrCreateOverriddenPlan(this, defaultPlan, CatalogDateHelper.toUTCDateTime(getEffectiveDate()), overrides.getOverrides(), internalCallContext);
     }
 
     @Override
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 aa53bda..b8f8fd0 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -226,17 +226,9 @@ public class VersionedCatalog extends ValidatingConfig<VersionedCatalog> impleme
     public void add(final StandaloneCatalog e) throws CatalogApiException {
         if (catalogName == null) {
             catalogName = e.getCatalogName();
-        } else {
-            if (!catalogName.equals(e.getCatalogName())) {
-                throw new CatalogApiException(ErrorCode.CAT_CATALOG_NAME_MISMATCH, catalogName, e.getCatalogName());
-            }
         }
         if (recurringBillingMode == null) {
             recurringBillingMode = e.getRecurringBillingMode();
-        } else {
-            if (!recurringBillingMode.equals(e.getRecurringBillingMode())) {
-                throw new CatalogApiException(ErrorCode.CAT_CATALOG_RECURRING_MODE_MISMATCH, recurringBillingMode, e.getRecurringBillingMode());
-            }
         }
         versions.add(e);
         Collections.sort(versions, new Comparator<StandaloneCatalog>() {
@@ -407,11 +399,13 @@ public class VersionedCatalog extends ValidatingConfig<VersionedCatalog> impleme
     //
     @Override
     public void initialize(final VersionedCatalog catalog, final URI sourceURI) {
+        //
+        // Initialization is performed first on each StandaloneCatalog (XMLLoader#initializeAndValidate)
+        // and then later on the VersionedCatalog, so we only initialize and validate VersionedCatalog
+        // *without** recursively through each StandaloneCatalog
+        //
         super.initialize(catalog, sourceURI);
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(this);
-        for (final StandaloneCatalog c : versions) {
-            c.initialize(c, sourceURI);
-        }
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(this);
     }
 
     @Override
@@ -428,7 +422,11 @@ public class VersionedCatalog extends ValidatingConfig<VersionedCatalog> impleme
             }
             if (!c.getCatalogName().equals(catalogName)) {
                 errors.add(new ValidationError(String.format("Catalog name '%s' is not consistent across versions ", c.getCatalogName()),
-                        c.getCatalogURI(), VersionedCatalog.class, ""));
+                                               c.getCatalogURI(), VersionedCatalog.class, ""));
+            }
+            if (!c.getRecurringBillingMode().equals(recurringBillingMode)) {
+                errors.add(new ValidationError(String.format("Catalog recurringBillingMode '%s' is not consistent across versions ", c.getCatalogName()),
+                                               c.getCatalogURI(), VersionedCatalog.class, ""));
             }
             errors.addAll(c.validate(c, errors));
         }
diff --git a/catalog/src/main/resources/EmptyCatalog.xml b/catalog/src/main/resources/EmptyCatalog.xml
index c3d9dff..679f928 100644
--- a/catalog/src/main/resources/EmptyCatalog.xml
+++ b/catalog/src/main/resources/EmptyCatalog.xml
@@ -35,6 +35,16 @@
     </products>
 
     <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
     </rules>
 
     <plans>
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/MockPlanPhase.java b/catalog/src/test/java/org/killbill/billing/catalog/MockPlanPhase.java
index 5ba79d3..b93df37 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/MockPlanPhase.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/MockPlanPhase.java
@@ -28,7 +28,7 @@ public class MockPlanPhase extends DefaultPlanPhase {
     public static MockPlanPhase create1USDMonthlyEvergreen() {
         return (MockPlanPhase) new MockPlanPhase(BillingPeriod.MONTHLY,
                                                  PhaseType.EVERGREEN,
-                                                 new DefaultDuration().setUnit(TimeUnit.UNLIMITED),
+                                                 new DefaultDuration().setUnit(TimeUnit.UNLIMITED).setNumber(-1),
                                                  MockInternationalPrice.create1USD(),
                                                  null).setPlan(MockPlan.createBicycleNoTrialEvergreen1USD());
     }
@@ -36,7 +36,7 @@ public class MockPlanPhase extends DefaultPlanPhase {
     public static MockPlanPhase createUSDMonthlyEvergreen(final String reccuringUSDPrice, final String fixedPrice) {
         return new MockPlanPhase(BillingPeriod.MONTHLY,
                                  PhaseType.EVERGREEN,
-                                 new DefaultDuration().setUnit(TimeUnit.UNLIMITED),
+                                 new DefaultDuration().setUnit(TimeUnit.UNLIMITED).setNumber(-1),
                                  (reccuringUSDPrice == null) ? null : MockInternationalPrice.createUSD(reccuringUSDPrice),
                                  (fixedPrice == null) ? null : MockInternationalPrice.createUSD(fixedPrice));
     }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogSafetyInitializer.java b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogSafetyInitializer.java
index 26da112..18989ea 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogSafetyInitializer.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestCatalogSafetyInitializer.java
@@ -17,11 +17,14 @@
 
 package org.killbill.billing.catalog;
 
+import javax.xml.bind.annotation.XmlAttribute;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlIDREF;
 
+import org.killbill.billing.catalog.api.FixedType;
 import org.killbill.billing.catalog.api.Product;
+import org.killbill.billing.catalog.api.TimeUnit;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -50,6 +53,20 @@ public class TestCatalogSafetyInitializer {
     @XmlElement(type = DefaultProduct.class, name = "addonProduct", required = false)
     private CatalogEntityCollection<Product> available;
 
+    @XmlElement(required = true)
+    private TimeUnit unit;
+
+    @XmlElement(required = false)
+    private Integer number;
+
+    @XmlElement(required = false)
+    private int smallNumber;
+
+    @XmlAttribute(required = false)
+    private FixedType type;
+
+
+
     @Test(groups = "fast")
     public void testNonRequiredArrayFields() {
 
@@ -60,7 +77,7 @@ public class TestCatalogSafetyInitializer {
         Assert.assertNull(test.getPricesNotRequired());
         Assert.assertNull(test.getPrices());
 
-        CatalogSafetyInitializer.initializeNonRequiredArrayFields(test);
+        CatalogSafetyInitializer.initializeNonRequiredNullFieldsWithDefaultValue(test);
 
         Assert.assertNull(test.getInitialPhasesWrapperAllRequired());
         Assert.assertNotNull(test.getInitialPhasesWrapperNotRequired());
@@ -70,6 +87,13 @@ public class TestCatalogSafetyInitializer {
         Assert.assertEquals(test.getPricesNotRequired().length, 0);
         Assert.assertNull(test.getPrices());
 
+        Assert.assertNotNull(test.getNumber());
+        Assert.assertEquals(test.getNumber(), CatalogSafetyInitializer.DEFAULT_NON_REQUIRED_INTEGER_FIELD_VALUE);
+
+        Assert.assertNotNull(test.getSmallNumber());
+
+        Assert.assertNotNull(test.getType());
+        Assert.assertEquals(test.getType(), FixedType.ONE_TIME);
     }
 
     public DefaultPlanPhase[] getInitialPhasesWrapperAllRequired() {
@@ -92,4 +116,23 @@ public class TestCatalogSafetyInitializer {
         return prices;
     }
 
+    public CatalogEntityCollection<Product> getAvailable() {
+        return available;
+    }
+
+    public TimeUnit getUnit() {
+        return unit;
+    }
+
+    public Integer getNumber() {
+        return number;
+    }
+
+    public int getSmallNumber() {
+        return smallNumber;
+    }
+
+    public FixedType getType() {
+        return type;
+    }
 }
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java b/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java
index 278c67b..fb05299 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestDefaultPriceOverride.java
@@ -25,7 +25,6 @@ import java.util.regex.Matcher;
 import org.joda.time.DateTime;
 import org.killbill.billing.catalog.api.CatalogApiException;
 import org.killbill.billing.catalog.api.Currency;
-import org.killbill.billing.catalog.api.CurrencyValueNull;
 import org.killbill.billing.catalog.api.InternationalPrice;
 import org.killbill.billing.catalog.api.Plan;
 import org.killbill.billing.catalog.api.PlanPhasePriceOverride;
@@ -48,6 +47,7 @@ public class TestDefaultPriceOverride extends CatalogTestSuiteWithEmbeddedDB {
     public void testBasic() throws Exception {
 
         final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        catalog.initialize(catalog, null);
         final Plan plan = catalog.findCurrentPlan("discount-standard-monthly");
 
         final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
@@ -56,7 +56,7 @@ public class TestDefaultPriceOverride extends CatalogTestSuiteWithEmbeddedDB {
         final PlanPhasePriceOverride phase3 = new DefaultPlanPhasePriceOverride(plan.getAllPhases()[2].getName(), Currency.USD, null, new BigDecimal("142.41"));
         overrides.add(phase3);
 
-        final DefaultPlan overriddenPlan = priceOverride.getOrCreateOverriddenPlan(plan, new DateTime(catalog.getEffectiveDate()), overrides, internalCallContext);
+        final DefaultPlan overriddenPlan = priceOverride.getOrCreateOverriddenPlan(catalog, plan, new DateTime(catalog.getEffectiveDate()), overrides, internalCallContext);
 
         final Matcher m = DefaultPriceOverride.CUSTOM_PLAN_NAME_PATTERN.matcher(overriddenPlan.getName());
         assertTrue(m.matches());
@@ -101,13 +101,15 @@ public class TestDefaultPriceOverride extends CatalogTestSuiteWithEmbeddedDB {
     public void testWithInvalidPriceOverride() throws Exception {
 
         final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        catalog.initialize(catalog, null);
+
         final Plan plan = catalog.findCurrentPlan("discount-standard-monthly");
 
         final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
         final PlanPhasePriceOverride phase1 = new DefaultPlanPhasePriceOverride(plan.getAllPhases()[0].getName(), Currency.USD, null, BigDecimal.ONE);
         overrides.add(phase1);
 
-        priceOverride.getOrCreateOverriddenPlan(plan, new DateTime(catalog.getEffectiveDate()), overrides, internalCallContext);
+        priceOverride.getOrCreateOverriddenPlan(catalog, plan, new DateTime(catalog.getEffectiveDate()), overrides, internalCallContext);
 
     }
 
@@ -115,6 +117,8 @@ public class TestDefaultPriceOverride extends CatalogTestSuiteWithEmbeddedDB {
     public void testGetOverriddenPlan() throws Exception {
 
         final StandaloneCatalog catalog = XMLLoader.getObjectFromString(Resources.getResource("SpyCarAdvanced.xml").toExternalForm(), StandaloneCatalog.class);
+        catalog.initialize(catalog, null);
+
         final Plan plan = catalog.findCurrentPlan("discount-standard-monthly");
 
         final List<PlanPhasePriceOverride> overrides = new ArrayList<PlanPhasePriceOverride>();
@@ -123,7 +127,7 @@ public class TestDefaultPriceOverride extends CatalogTestSuiteWithEmbeddedDB {
         final PlanPhasePriceOverride phase3 = new DefaultPlanPhasePriceOverride(plan.getAllPhases()[2].getName(), Currency.USD, null, new BigDecimal("142.41"));
         overrides.add(phase3);
 
-        final DefaultPlan overriddenPlanCreated = priceOverride.getOrCreateOverriddenPlan(plan, new DateTime(catalog.getEffectiveDate()), overrides, internalCallContext);
+        final DefaultPlan overriddenPlanCreated = priceOverride.getOrCreateOverriddenPlan(catalog, plan, new DateTime(catalog.getEffectiveDate()), overrides, internalCallContext);
 
         System.out.println("overriddenPlanCreated = " + overriddenPlanCreated.getName());
 
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestPlan.java b/catalog/src/test/java/org/killbill/billing/catalog/TestPlan.java
index 2f7cdaf..8e466cf 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestPlan.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestPlan.java
@@ -32,6 +32,7 @@ public class TestPlan extends CatalogTestSuiteNoDB {
         final StandaloneCatalog c = new MockCatalog();
         c.setSupportedCurrencies(new Currency[]{Currency.GBP, Currency.EUR, Currency.USD, Currency.BRL, Currency.MXN});
         final DefaultPlan p1 = MockPlan.createBicycleTrialEvergreen1USD();
+        p1.setPlansAllowedInBundle(-1);
         p1.setEffectiveDateForExistingSubscriptions(new Date((new Date().getTime()) - (1000 * 60 * 60 * 24)));
         final ValidationErrors errors = p1.validate(c, new ValidationErrors());
         Assert.assertEquals(errors.size(), 3);
diff --git a/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java b/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java
index 32d839a..c7c411a 100644
--- a/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java
+++ b/catalog/src/test/java/org/killbill/billing/catalog/TestStandaloneCatalog.java
@@ -30,17 +30,26 @@ import com.google.common.io.Resources;
 public class TestStandaloneCatalog extends CatalogTestSuiteNoDB {
 
     @Test(groups = "fast")
-    public void testLoadCatalogWithPlanInvalidProduct() throws Exception {
+    public void testLoadCatalogWithValidationIssues() throws Exception {
         try {
             XMLLoader.getObjectFromString(Resources.getResource("CatalogWithValidationErrors.xml").toExternalForm(), StandaloneCatalog.class);
             Assert.fail();
         } catch (final ValidationException e) {
-            Assert.assertEquals(e.getErrors().size(), 5);
+            Assert.assertEquals(e.getErrors().size(), 13);
             Assert.assertEquals(e.getErrors().get(0).getDescription(), "Invalid product for plan 'standard'");
             Assert.assertEquals(e.getErrors().get(1).getDescription(), "Duration can only have 'UNLIMITED' unit if the number is omitted");
             Assert.assertEquals(e.getErrors().get(2).getDescription(), "Finite Duration must have a well defined length");
             Assert.assertEquals(e.getErrors().get(3).getDescription(), "Initial Phase standard-trial-evergreen of plan standard-trial cannot be of type EVERGREEN");
             Assert.assertEquals(e.getErrors().get(4).getDescription(), "Final Phase standard-trial-trial of plan standard-trial cannot be of type TRIAL");
+            Assert.assertEquals(e.getErrors().get(5).getDescription(), "Duplicate rule for change plan DefaultCaseChangePlanPolicy {policy=IMMEDIATE, phaseType=null, fromProduct=DefaultProduct{name='Standard', category=BASE, included=org.killbill.billing.catalog.CatalogEntityCollection@0, available=org.killbill.billing.catalog.CatalogEntityCollection@0, limits=[], catalogName='CatalogWithValidationErrors'}, fromProductCategory=null, fromBillingPeriod=null, fromPriceList=null, toProduct=null, toProductCategory=null, toBillingPeriod=null, toPriceList=null}");
+            Assert.assertEquals(e.getErrors().get(6).getDescription(), "Missing default rule case for plan change");
+            Assert.assertEquals(e.getErrors().get(7).getDescription(), "Duplicate rule for plan cancellation DefaultCaseCancelPolicy{policy =IMMEDIATE, phaseType =null, product=DefaultProduct{name='Standard', category=BASE, included=org.killbill.billing.catalog.CatalogEntityCollection@0, available=org.killbill.billing.catalog.CatalogEntityCollection@0, limits=[], catalogName='CatalogWithValidationErrors'}, productCategory=null, billingPeriod=null, priceList=null}");
+            Assert.assertEquals(e.getErrors().get(8).getDescription(), "Missing default rule case for plan cancellation");
+            Assert.assertEquals(e.getErrors().get(9).getDescription(), "Duplicate rule for plan change alignment DefaultCaseChangePlanAlignment {alignment=START_OF_BUNDLE, phaseType=null, fromProduct=null, fromProductCategory=null, fromBillingPeriod=null, fromPriceList=null, toProduct=null, toProductCategory=null, toBillingPeriod=null, toPriceList=null}");
+            Assert.assertEquals(e.getErrors().get(10).getDescription(), "Duplicate rule for create plan alignment DefaultCaseCreateAlignment {alignment =START_OF_BUNDLE, product=null, productCategory=null, billingPeriod=null, priceList=null}");
+            Assert.assertEquals(e.getErrors().get(11).getDescription(), "Duplicate rule for billing alignment DefaultCaseBillingAlignment {alignment=ACCOUNT, phaseType=null, product=null, productCategory=null, billingPeriod=null, priceList=null}");
+            Assert.assertEquals(e.getErrors().get(12).getDescription(), "Duplicate rule for price list transition DefaultCasePriceList {fromProduct=null, fromProductCategory=null, fromBillingPeriod=null, fromPriceList=null, toPriceList=DefaultPriceList{name='DEFAULT}}");
+
         }
     }
 
diff --git a/catalog/src/test/resources/catalogTest.xml b/catalog/src/test/resources/catalogTest.xml
index 670abb5..8f28d1c 100644
--- a/catalog/src/test/resources/catalogTest.xml
+++ b/catalog/src/test/resources/catalogTest.xml
@@ -119,10 +119,6 @@
                 <policy>IMMEDIATE</policy>
             </changePolicyCase>
             <changePolicyCase>
-                <toPriceList>rescue</toPriceList>
-                <policy>END_OF_TERM</policy>
-            </changePolicyCase>
-            <changePolicyCase>
                 <fromBillingPeriod>MONTHLY</fromBillingPeriod>
                 <toBillingPeriod>ANNUAL</toBillingPeriod>
                 <policy>IMMEDIATE</policy>
diff --git a/catalog/src/test/resources/CatalogWithValidationErrors.xml b/catalog/src/test/resources/CatalogWithValidationErrors.xml
index 97cfb4d..26758f8 100644
--- a/catalog/src/test/resources/CatalogWithValidationErrors.xml
+++ b/catalog/src/test/resources/CatalogWithValidationErrors.xml
@@ -20,7 +20,7 @@
          xsi:noNamespaceSchemaLocation="CatalogSchema.xsd ">
 
     <effectiveDate>2013-02-08T00:00:00+00:00</effectiveDate>
-    <catalogName>CatalogWithPlanInvalidProduct</catalogName>
+    <catalogName>CatalogWithValidationErrors</catalogName>
 
     <recurringBillingMode>IN_ADVANCE</recurringBillingMode>
 
@@ -34,7 +34,64 @@
         </product>
     </products>
 
-    <rules></rules>
+
+
+    <!-- Note we defined twice the same rule for each case and also we miss default rules for plan cancellation and change of plan -->
+    <rules>
+        <changePolicy>
+            <changePolicyCase>
+                <fromProduct>Standard</fromProduct>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+            <changePolicyCase>
+                <fromProduct>Standard</fromProduct>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
+        </changePolicy>
+        <changeAlignment>
+            <changeAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </changeAlignmentCase>
+            <changeAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </changeAlignmentCase>
+        </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <product>Standard</product>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+            <cancelPolicyCase>
+                <product>Standard</product>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
+        <createAlignment>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+            <createAlignmentCase>
+                <alignment>START_OF_BUNDLE</alignment>
+            </createAlignmentCase>
+        </createAlignment>
+        <billingAlignment>
+            <billingAlignmentCase>
+                <alignment>ACCOUNT</alignment>
+            </billingAlignmentCase>
+            <billingAlignmentCase>
+                <alignment>ACCOUNT</alignment>
+            </billingAlignmentCase>
+        </billingAlignment>
+        <priceList>
+            <priceListCase>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+            <priceListCase>
+                <toPriceList>DEFAULT</toPriceList>
+            </priceListCase>
+        </priceList>
+    </rules>
+
 
     <plans>
         <plan name="standard">
diff --git a/catalog/src/test/resources/UsageExperimental.xml b/catalog/src/test/resources/UsageExperimental.xml
index 11f3d17..4d2e7e9 100644
--- a/catalog/src/test/resources/UsageExperimental.xml
+++ b/catalog/src/test/resources/UsageExperimental.xml
@@ -62,10 +62,19 @@
     </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">
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
index b2b9276..f333572 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-1.xml
@@ -68,12 +68,20 @@
                 <phaseType>TRIAL</phaseType>
                 <policy>IMMEDIATE</policy>
             </changePolicyCase>
+            <changePolicyCase>
+                <policy>END_OF_TERM</policy>
+            </changePolicyCase>
         </changePolicy>
         <changeAlignment>
             <changeAlignmentCase>
                 <alignment>START_OF_SUBSCRIPTION</alignment>
             </changeAlignmentCase>
         </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
         <createAlignment>
             <createAlignmentCase>
                 <product>Laser-Scope</product>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
index f04ff0e..df1c09d 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-2.xml
@@ -68,12 +68,20 @@
                 <phaseType>TRIAL</phaseType>
                 <policy>IMMEDIATE</policy>
             </changePolicyCase>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
         </changePolicy>
         <changeAlignment>
             <changeAlignmentCase>
                 <alignment>START_OF_SUBSCRIPTION</alignment>
             </changeAlignmentCase>
         </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
         <createAlignment>
             <createAlignmentCase>
                 <product>Laser-Scope</product>
diff --git a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
index dc9c0b1..eb21229 100644
--- a/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
+++ b/catalog/src/test/resources/versionedCatalog/WeaponsHireSmall-3.xml
@@ -68,12 +68,20 @@
                 <phaseType>TRIAL</phaseType>
                 <policy>IMMEDIATE</policy>
             </changePolicyCase>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
         </changePolicy>
         <changeAlignment>
             <changeAlignmentCase>
                 <alignment>START_OF_SUBSCRIPTION</alignment>
             </changeAlignmentCase>
         </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
         <createAlignment>
             <createAlignmentCase>
                 <product>Laser-Scope</product>
diff --git a/catalog/src/test/resources/WeaponsHireSmall.xml b/catalog/src/test/resources/WeaponsHireSmall.xml
index 5adfb99..f64b5c6 100644
--- a/catalog/src/test/resources/WeaponsHireSmall.xml
+++ b/catalog/src/test/resources/WeaponsHireSmall.xml
@@ -68,12 +68,20 @@
                 <phaseType>TRIAL</phaseType>
                 <policy>IMMEDIATE</policy>
             </changePolicyCase>
+            <changePolicyCase>
+                <policy>IMMEDIATE</policy>
+            </changePolicyCase>
         </changePolicy>
         <changeAlignment>
             <changeAlignmentCase>
                 <alignment>START_OF_SUBSCRIPTION</alignment>
             </changeAlignmentCase>
         </changeAlignment>
+        <cancelPolicy>
+            <cancelPolicyCase>
+                <policy>IMMEDIATE</policy>
+            </cancelPolicyCase>
+        </cancelPolicy>
         <createAlignment>
             <createAlignmentCase>
                 <product>Laser-Scope</product>

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index d0a6be0..7e6042e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <artifactId>killbill-oss-parent</artifactId>
         <groupId>org.kill-bill.billing</groupId>
-        <version>0.140.5</version>
+        <version>0.140.6</version>
     </parent>
     <artifactId>killbill</artifactId>
     <version>0.18.2-SNAPSHOT</version>