killbill-uncached

Adding validation that the pricelist contains at most one plan

12/5/2011 3:26:41 AM

Details

diff --git a/catalog/src/main/java/com/ning/billing/catalog/PriceList.java b/catalog/src/main/java/com/ning/billing/catalog/PriceList.java
index 2ce3ef9..0c9940d 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/PriceList.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/PriceList.java
@@ -28,6 +28,7 @@ import com.ning.billing.catalog.api.BillingPeriod;
 import com.ning.billing.catalog.api.IPriceList;
 import com.ning.billing.catalog.api.IProduct;
 import com.ning.billing.util.config.ValidatingConfig;
+import com.ning.billing.util.config.ValidationError;
 import com.ning.billing.util.config.ValidationErrors;
 
 @XmlAccessorType(XmlAccessType.NONE)
@@ -76,9 +77,29 @@ public class PriceList extends ValidatingConfig<Catalog> implements IPriceList  
     }
 
 	@Override
-	public ValidationErrors validate(Catalog root, ValidationErrors errors) {
+	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
+		 for (Plan cur : getPlans()) {
+			 int numPlans = findNumberOfPlans(cur.getProduct(), cur.getBillingPeriod());
+			 if ( numPlans > 1 ) {
+				 errors.add(new ValidationError(
+						 String.format("There are %d plans in pricelist %s and have the same product/billingPeriod (%s, %s)", 
+								 numPlans, getName(), cur.getProduct(), cur.getBillingPeriod()), catalog.getCatalogURI(),
+								 PriceListSet.class, getName()));
+			 }
+		 }
 		return errors;
 	}
+	
+	private int findNumberOfPlans(IProduct product, BillingPeriod period) {
+		int count = 0;
+        for (Plan cur : getPlans()) {
+            if (cur.getProduct().equals(product) && 
+            		(cur.getBillingPeriod() == null || cur.getBillingPeriod().equals(period))) {
+                count++;
+            }
+        }
+        return count;
+    }
 
 
 }
diff --git a/catalog/src/main/java/com/ning/billing/catalog/PriceListDefault.java b/catalog/src/main/java/com/ning/billing/catalog/PriceListDefault.java
index 781f44d..2bd9310 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/PriceListDefault.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/PriceListDefault.java
@@ -34,7 +34,8 @@ public class PriceListDefault extends PriceList {
 
 	@Override
 	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
-		if(getName().equals(IPriceListSet.DEFAULT_PRICELIST_NAME)) {
+		super.validate(catalog, errors);
+		if(!getName().equals(IPriceListSet.DEFAULT_PRICELIST_NAME)) {
 			errors.add(new ValidationError("The name of the default pricelist must be 'DEFAULT'", 
 					catalog.getCatalogURI(), PriceList.class, getName()));
 			
diff --git a/catalog/src/main/java/com/ning/billing/catalog/PriceListSet.java b/catalog/src/main/java/com/ning/billing/catalog/PriceListSet.java
index 10010a7..fbda299 100644
--- a/catalog/src/main/java/com/ning/billing/catalog/PriceListSet.java
+++ b/catalog/src/main/java/com/ning/billing/catalog/PriceListSet.java
@@ -73,12 +73,15 @@ public class PriceListSet extends ValidatingConfig<Catalog> {
 	}
 
 	@Override
-	public ValidationErrors validate(Catalog root, ValidationErrors errors) {
+	public ValidationErrors validate(Catalog catalog, ValidationErrors errors) {
+		defaultPricelist.validate(catalog, errors);
 		//Check that the default pricelist name is not in use in the children
 		for(PriceList pl : childPriceLists) {
 			if(pl.getName().equals(IPriceListSet.DEFAULT_PRICELIST_NAME)){
-				errors.add(new ValidationError("Pricelists cannot use the reserved name '" + IPriceListSet.DEFAULT_PRICELIST_NAME + "'", root.getCatalogURI(), PriceListSet.class, pl.getName()));
+				errors.add(new ValidationError("Pricelists cannot use the reserved name '" + IPriceListSet.DEFAULT_PRICELIST_NAME + "'",
+						catalog.getCatalogURI(), PriceListSet.class, pl.getName()));
 			}
+			pl.validate(catalog, errors); // and validate the individual pricelists
 		}
 		return errors;
 	}
diff --git a/util/src/main/java/com/ning/billing/util/config/ValidationError.java b/util/src/main/java/com/ning/billing/util/config/ValidationError.java
index 4a68dac..33af867 100644
--- a/util/src/main/java/com/ning/billing/util/config/ValidationError.java
+++ b/util/src/main/java/com/ning/billing/util/config/ValidationError.java
@@ -49,4 +49,8 @@ public class ValidationError {
 	public void log(Logger log) {
 		log.error(String.format("%s [%s] (%s:%s)", description, sourceURI, objectType, objectName));
 	}
+	
+	public String toString() {
+		return String.format("%s [%s] (%s:%s)\n", description, sourceURI, objectType, objectName);
+	}
 }
diff --git a/util/src/main/java/com/ning/billing/util/config/ValidationErrors.java b/util/src/main/java/com/ning/billing/util/config/ValidationErrors.java
index 901f636..19e4270 100644
--- a/util/src/main/java/com/ning/billing/util/config/ValidationErrors.java
+++ b/util/src/main/java/com/ning/billing/util/config/ValidationErrors.java
@@ -35,5 +35,13 @@ public class ValidationErrors extends ArrayList<ValidationError>{
 			error.log(log);
 		}	
 	}
+	
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		for(ValidationError error : this) {
+			builder.append(error.toString());
+		}	
+		return builder.toString();
+	}
 
 }
\ No newline at end of file
diff --git a/util/src/main/java/com/ning/billing/util/config/ValidationException.java b/util/src/main/java/com/ning/billing/util/config/ValidationException.java
new file mode 100644
index 0000000..cd5c9c1
--- /dev/null
+++ b/util/src/main/java/com/ning/billing/util/config/ValidationException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010-2011 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.util.config;
+
+import java.io.PrintStream;
+
+public class ValidationException extends Exception {
+	private final ValidationErrors errors;
+	
+	ValidationException(ValidationErrors errors) {
+		this.errors = errors;
+	}
+
+	public ValidationErrors getErrors() {
+		return errors;
+	}
+
+	@Override
+	public void printStackTrace(PrintStream arg0) {
+		arg0.print(errors.toString());
+		super.printStackTrace(arg0);
+	}
+
+	
+}
+
diff --git a/util/src/main/java/com/ning/billing/util/config/XMLLoader.java b/util/src/main/java/com/ning/billing/util/config/XMLLoader.java
index 481447c..9ed3bce 100644
--- a/util/src/main/java/com/ning/billing/util/config/XMLLoader.java
+++ b/util/src/main/java/com/ning/billing/util/config/XMLLoader.java
@@ -56,22 +56,31 @@ public class XMLLoader {
 		return getObjectFromStream(uri, UriAccessor.accessUri(uri), objectType);
 	}
 	
-	public static <T extends ValidatingConfig<T>> T getObjectFromStream(URI uri, InputStream stream, Class<T> clazz) throws SAXException, InvalidConfigException, JAXBException, IOException, TransformerException {
+	public static <T extends ValidatingConfig<T>> T getObjectFromStream(URI uri, InputStream stream, Class<T> clazz) throws SAXException, InvalidConfigException, JAXBException, IOException, TransformerException, ValidationException {
         Object o = unmarshaller(clazz).unmarshal(stream);
         if (clazz.isInstance(o)) {
         	@SuppressWarnings("unchecked")
 			T castObject = (T)o;
-            validate(uri,castObject);
+        	try {
+        		validate(uri,castObject);
+        	} catch (ValidationException e) {
+        		e.getErrors().log(log);
+        		System.err.println(e.getErrors().toString());
+        		throw e;
+        	}
             return castObject;
         } else {
             return null;
         }
     } 
 
-	public static <T extends ValidatingConfig<T>> void validate(URI uri, T c) {
+	public static <T extends ValidatingConfig<T>> void validate(URI uri, T c) throws ValidationException {
             c.initialize(c, uri);
             ValidationErrors errs = c.validate(c, new ValidationErrors());
-            log.info("Errors: " + errs.size() + " for " + uri);       
+            log.info("Errors: " + errs.size() + " for " + uri);  
+            if(errs.size() > 0) {
+            	throw new ValidationException(errs);
+            }
     }
     
     public static Unmarshaller unmarshaller(Class<?> clazz) throws JAXBException, SAXException, IOException, TransformerException {
diff --git a/util/src/test/java/com/ning/billing/util/config/TestXMLLoader.java b/util/src/test/java/com/ning/billing/util/config/TestXMLLoader.java
index 02fbcd2..30b8ee3 100644
--- a/util/src/test/java/com/ning/billing/util/config/TestXMLLoader.java
+++ b/util/src/test/java/com/ning/billing/util/config/TestXMLLoader.java
@@ -44,7 +44,7 @@ public class TestXMLLoader {
 			"</xmlTestClass>";
 	
 	@Test
-	public void test() throws SAXException, InvalidConfigException, JAXBException, IOException, TransformerException, URISyntaxException {
+	public void test() throws SAXException, InvalidConfigException, JAXBException, IOException, TransformerException, URISyntaxException, ValidationException {
 		InputStream is = new ByteArrayInputStream(TEST_XML.getBytes());
 		XmlTestClass test = XMLLoader.getObjectFromStream(new URI("internal:/"), is, XmlTestClass.class);
 		assertEquals(test.getFoo(), "foo");