From 157a87e9103642ec49bc07dff2bcc15df4049009 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Thu, 27 Aug 2009 09:05:36 +0000 Subject: [PATCH] SPR-6003 - Improve CastorMarshaller support for loading class descriptors --- org.springframework.oxm/build.xml | 19 +++- .../oxm/castor/CastorMarshaller.java | 107 ++++++++++++------ .../oxm/castor/CastorUnmarshallerTests.java | 17 ++- .../org/springframework/oxm/order.xsd | 18 +++ 4 files changed, 110 insertions(+), 51 deletions(-) create mode 100644 org.springframework.oxm/src/test/resources/org/springframework/oxm/order.xsd diff --git a/org.springframework.oxm/build.xml b/org.springframework.oxm/build.xml index 1130b801326..9623453fb1f 100644 --- a/org.springframework.oxm/build.xml +++ b/org.springframework.oxm/build.xml @@ -4,7 +4,9 @@ - + + + @@ -26,10 +28,17 @@ - + + + + + + + - + - diff --git a/org.springframework.oxm/src/main/java/org/springframework/oxm/castor/CastorMarshaller.java b/org.springframework.oxm/src/main/java/org/springframework/oxm/castor/CastorMarshaller.java index abe2be26759..871075f2362 100644 --- a/org.springframework.oxm/src/main/java/org/springframework/oxm/castor/CastorMarshaller.java +++ b/org.springframework.oxm/src/main/java/org/springframework/oxm/castor/CastorMarshaller.java @@ -61,7 +61,7 @@ import org.springframework.util.xml.StaxUtils; /** * Implementation of the Marshaller interface for Castor. By default, Castor does not require any further - * configuration, though setting a target class or providing a mapping file can be used to have more control over the + * configuration, though setting target classes, target packages or providing a mapping file can be used to have more control over the * behavior of Castor. * *

If a target class is specified using setTargetClass, the CastorMarshaller can only be @@ -74,6 +74,7 @@ import org.springframework.util.xml.StaxUtils; * @author Arjen Poutsma * @see #setEncoding(String) * @see #setTargetClass(Class) + * @see #setTargetPackages(String[]) * @see #setMappingLocation(Resource) * @see #setMappingLocations(Resource[]) * @since 3.0 @@ -90,7 +91,9 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing private String encoding = DEFAULT_ENCODING; - private Class targetClass; + private Class[] targetClasses; + + private String[] targetPackages; private boolean validating = false; @@ -132,12 +135,28 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing } /** - * Set the Castor target class. If this property is set, this CastorMarshaller - * is tied to this one specific class. Use a mapping file for unmarshalling multiple classes. - *

You cannot set both this property and the mapping (location). + * Set the Castor target class. Alternative means of configuring + * CastorMarshaller for unmarshalling multiple classes include + * use of mapping files, and specifying packages with Castor descriptor classes. */ public void setTargetClass(Class targetClass) { - this.targetClass = targetClass; + this.targetClasses = new Class[]{targetClass}; + } + + /** + * Set the Castor target classes. Alternative means of configuring + * CastorMarshaller for unmarshalling multiple classes include + * use of mapping files, and specifying packages with Castor descriptor classes. + */ + public void setTargetClasses(Class[] targetClasses) { + this.targetClasses = targetClasses; + } + + /** + * Set the package names of packages with the Castor descriptor classes. + */ + public void setTargetPackages(String[] targetPackages) { + this.targetPackages = targetPackages; } /** @@ -214,21 +233,28 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing this.suppressXsiType = suppressXsiType; } - public final void afterPropertiesSet() throws CastorMappingException, IOException { if (logger.isInfoEnabled()) { - if (this.mappingLocations != null) { - logger.info("Configured using " + StringUtils.arrayToCommaDelimitedString(this.mappingLocations)); + if (!ObjectUtils.isEmpty(this.mappingLocations)) { + logger.info( + "Configured using [" + StringUtils.arrayToCommaDelimitedString(this.mappingLocations) + "]"); } - if (this.targetClass != null) { - logger.info("Configured for target class [" + this.targetClass.getName() + "]"); + if (!ObjectUtils.isEmpty(this.targetClasses)) { + logger.info("Configured for target classes " + StringUtils.arrayToCommaDelimitedString(targetClasses) + + "]"); } - if (this.mappingLocations == null && this.targetClass == null) { + if (!ObjectUtils.isEmpty(this.targetPackages)) { + logger.info( + "Configured for target packages [" + StringUtils.arrayToCommaDelimitedString(targetPackages) + + "]"); + } + if (ObjectUtils.isEmpty(this.mappingLocations) && ObjectUtils.isEmpty(this.targetClasses) && + ObjectUtils.isEmpty(this.targetPackages)) { logger.info("Using default configuration"); } } try { - this.xmlContext = createXMLContext(this.mappingLocations, this.targetClass); + this.xmlContext = createXMLContext(this.mappingLocations, this.targetClasses, this.targetPackages); } catch (MappingException ex) { throw new CastorMappingException("Could not load Castor mapping", ex); @@ -240,14 +266,16 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing /** * Create the Castor XMLContext. Subclasses can override this to create a custom context. - *

The default implementation loads mapping files if defined, and the target class if not defined. + *

+ * The default implementation loads mapping files if defined, or the target class or packages if defined. + * * @return the created resolver * @throws MappingException when the mapping file cannot be loaded - * @throws IOException in case of I/O errors + * @throws IOException in case of I/O errors * @see XMLContext#addMapping(org.exolab.castor.mapping.Mapping) * @see XMLContext#addClass(Class) */ - protected XMLContext createXMLContext(Resource[] mappingLocations, Class targetClass) + protected XMLContext createXMLContext(Resource[] mappingLocations, Class[] targetClasses, String[] targetPackages) throws MappingException, ResolverException, IOException { XMLContext context = new XMLContext(); @@ -258,13 +286,15 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing } context.addMapping(mapping); } - if (targetClass != null) { - context.addClass(targetClass); + if (!ObjectUtils.isEmpty(targetClasses)) { + context.addClasses(targetClasses); + } + if (!ObjectUtils.isEmpty(targetPackages)) { + context.addPackages(targetPackages); } return context; } - /** * Returns true for all classes, i.e. Castor supports arbitrary classes. */ @@ -272,7 +302,6 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing return true; } - // Marshalling @Override @@ -378,7 +407,7 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing return unmarshalSaxReader(reader, new InputSource()); } catch (IOException ex) { - throw new UnmarshallingFailureException("Failed to read XML stream", ex); + throw new UnmarshallingFailureException("Failed to read XML stream", ex); } } @@ -411,19 +440,20 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing private Unmarshaller createUnmarshaller() { Unmarshaller unmarshaller = this.xmlContext.createUnmarshaller(); - if (this.targetClass != null) { - unmarshaller.setClass(this.targetClass); - unmarshaller.setClassLoader(this.targetClass.getClassLoader()); - } customizeUnmarshaller(unmarshaller); return unmarshaller; } /** - * Template method that allows for customizing of the given Castor {@link Unmarshaller}. - *

The default implementation invokes {@link Unmarshaller#setValidation(boolean)}, - * {@link Unmarshaller#setWhitespacePreserve(boolean)}, {@link Unmarshaller#setIgnoreExtraAttributes(boolean)}, - * and {@link Unmarshaller#setIgnoreExtraElements(boolean)} with the properties set on this marshaller. + * Template method that allows for customizing of the given Castor + * {@link Unmarshaller}. + *

+ * The default implementation invokes + * {@link Unmarshaller#setValidation(boolean)}, + * {@link Unmarshaller#setWhitespacePreserve(boolean)}, + * {@link Unmarshaller#setIgnoreExtraAttributes(boolean)}, and + * {@link Unmarshaller#setIgnoreExtraElements(boolean)} with the properties + * set on this marshaller. */ protected void customizeUnmarshaller(Unmarshaller unmarshaller) { unmarshaller.setValidation(this.validating); @@ -433,13 +463,16 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing } /** - * Convert the given XMLException to an appropriate exception from the - * org.springframework.oxm hierarchy. - *

A boolean flag is used to indicate whether this exception occurs during marshalling or - * unmarshalling, since Castor itself does not make this distinction in its exception hierarchy. - * @param ex Castor XMLException that occured - * @param marshalling indicates whether the exception occurs during marshalling (true), - * or unmarshalling (false) + * Convert the given XMLException to an appropriate exception + * from the org.springframework.oxm hierarchy. + *

+ * A boolean flag is used to indicate whether this exception occurs during + * marshalling or unmarshalling, since Castor itself does not make this + * distinction in its exception hierarchy. + * + * @param ex Castor XMLException that occured + * @param marshalling indicates whether the exception occurs during + * marshalling (true), or unmarshalling (false) * @return the corresponding XmlMappingException */ protected XmlMappingException convertCastorException(XMLException ex, boolean marshalling) { @@ -448,7 +481,7 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing } else if (ex instanceof MarshalException) { if (marshalling) { - return new MarshallingFailureException("Castor marshalling exception", ex); + return new MarshallingFailureException("Castor marshalling exception", ex); } else { return new UnmarshallingFailureException("Castor unmarshalling exception", ex); diff --git a/org.springframework.oxm/src/test/java/org/springframework/oxm/castor/CastorUnmarshallerTests.java b/org.springframework.oxm/src/test/java/org/springframework/oxm/castor/CastorUnmarshallerTests.java index 5171f33bccd..d79ab94c39a 100644 --- a/org.springframework.oxm/src/test/java/org/springframework/oxm/castor/CastorUnmarshallerTests.java +++ b/org.springframework.oxm/src/test/java/org/springframework/oxm/castor/CastorUnmarshallerTests.java @@ -19,7 +19,6 @@ package org.springframework.oxm.castor; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.StringReader; -import java.util.ArrayList; import javax.xml.transform.stream.StreamSource; import static org.junit.Assert.*; @@ -44,7 +43,7 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests { protected void testFlight(Object o) { Flight flight = (Flight) o; assertNotNull("Flight is null", flight); - assertEquals("Number is invalid", 42L, flight.getNumber()); + assertEquals("Number is invalid", Long.valueOf(42L), flight.getNumber()); } @Override @@ -59,7 +58,7 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests { @Test public void unmarshalTargetClass() throws Exception { CastorMarshaller unmarshaller = new CastorMarshaller(); - unmarshaller.setTargetClass(Flights.class); + unmarshaller.setTargetClasses(new Class[] { Flights.class } ); unmarshaller.afterPropertiesSet(); StreamSource source = new StreamSource(new ByteArrayInputStream(INPUT_STRING.getBytes("UTF-8"))); Object flights = unmarshaller.unmarshal(source); @@ -67,10 +66,10 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests { } @Test - public void testSetBothTargetClassAndMapping() throws IOException { + public void testSetBothTargetClassesAndMapping() throws IOException { CastorMarshaller unmarshaller = new CastorMarshaller(); unmarshaller.setMappingLocation(new ClassPathResource("order-mapping.xml", CastorMarshaller.class)); - unmarshaller.setTargetClass(ArrayList.class); + unmarshaller.setTargetClasses(new Class[] { Order.class } ); unmarshaller.afterPropertiesSet(); String xml = "" + @@ -78,12 +77,12 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests { "" + ""; - ArrayList result = (ArrayList) unmarshaller.unmarshal(new StreamSource(new StringReader(xml))); - assertEquals("Invalid amount of items", 2, result.size()); - OrderItem item = (OrderItem) result.get(0); + Order order = (Order) unmarshaller.unmarshal(new StreamSource(new StringReader(xml))); + assertEquals("Invalid amount of items", 2, order.getOrderItemCount()); + OrderItem item = order.getOrderItem(0); assertEquals("Invalid items", "1", item.getId()); assertEquals("Invalid items", new Integer(15), item.getQuantity()); - item = (OrderItem) result.get(1); + item = order.getOrderItem(1); assertEquals("Invalid items", "3", item.getId()); assertEquals("Invalid items", new Integer(20), item.getQuantity()); } diff --git a/org.springframework.oxm/src/test/resources/org/springframework/oxm/order.xsd b/org.springframework.oxm/src/test/resources/org/springframework/oxm/order.xsd new file mode 100644 index 00000000000..92eee80e41a --- /dev/null +++ b/org.springframework.oxm/src/test/resources/org/springframework/oxm/order.xsd @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file