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..8ece25c 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,7 +48,9 @@ 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 {
@@ -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(ErrorCode.CAT_INVALID_FOR_TENANT, internalTenantContext.getTenantRecordId());
+ } catch (final JAXBException e) {
+ throw new CatalogApiException(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/io/VersionedCatalogLoader.java b/catalog/src/main/java/org/killbill/billing/catalog/io/VersionedCatalogLoader.java
index 80217c8..6014008 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(ErrorCode.CAT_INVALID_DEFAULT);
+ } catch (final JAXBException e) {
+ logger.warn("Failed to load default catalog", e);
+ throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, e);
+ } catch(IllegalArgumentException e) {
+ logger.warn("Failed to load default catalog", e);
+ throw new CatalogApiException(ErrorCode.CAT_INVALID_DEFAULT, e);
} 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) {
+ } catch (final ValidationException e) {
+ logger.warn("Failed to load catalog for tenant='{}'", tenantRecordId, e);
+ throw new CatalogApiException(ErrorCode.CAT_INVALID_FOR_TENANT, tenantRecordId);
+ } catch (final JAXBException e) {
+ logger.warn("Failed to load catalog for tenant='{}'", tenantRecordId, e);
throw new CatalogApiException(ErrorCode.CAT_INVALID_FOR_TENANT, tenantRecordId);
+ } catch (final IOException e) {
+ logger.warn("Failed to load catalog for tenant='{}'", tenantRecordId, e);
+ throw new IllegalStateException(e);
+ } catch (final TransformerException e) {
+ logger.warn("Failed to load catalog for tenant='{}'", tenantRecordId, e);
+ throw new IllegalStateException(e);
+ } catch (final URISyntaxException e) {
+ logger.warn("Failed to load catalog for tenant='{}'", tenantRecordId, e);
+ throw new IllegalStateException(e);
+ } catch (final SAXException e) {
+ logger.warn("Failed to load catalog for tenant='{}'", tenantRecordId, e);
+ throw new IllegalStateException(e);
+ } catch (final InvalidConfigException e) {
+ logger.warn("Failed to load catalog for tenant='{}'", tenantRecordId, e);
+ throw new IllegalStateException(e);
}
}
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..df49615 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/VersionedCatalog.java
@@ -407,11 +407,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);
- }
}
@Override