SPR-6003 - Improve CastorMarshaller support for loading class descriptors
This commit is contained in:
parent
52b51888f9
commit
157a87e910
|
|
@ -4,7 +4,9 @@
|
|||
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
|
||||
<import file="${basedir}/../spring-build/standard/default.xml"/>
|
||||
|
||||
<property name="schema" value="${basedir}/src/test/resources/org/springframework/oxm/flight.xsd"/>
|
||||
<property name="flightSchema" value="${basedir}/src/test/resources/org/springframework/oxm/flight.xsd"/>
|
||||
<property name="orderSchema" value="${basedir}/src/test/resources/org/springframework/oxm/order.xsd"/>
|
||||
<property name="castorBuilderProperties" value="${basedir}/src/test/castor/castorbuilder.properties"/>
|
||||
<property name="test.castor.dir" value="${target.dir}/castor/test"/>
|
||||
<property name="test.jaxb.dir" value="${target.dir}/jaxb/test"/>
|
||||
|
||||
|
|
@ -26,10 +28,17 @@
|
|||
<delete quiet="true" dir="${test.castor.dir}" />
|
||||
<mkdir dir="${test.castor.dir}" />
|
||||
|
||||
<castor types="j2" warnings="false" file="${schema}" todir="${test.castor.dir}"
|
||||
package="org.springframework.oxm.castor"/>
|
||||
<castor types="j2" warnings="false" file="${flightSchema}" todir="${test.castor.dir}"
|
||||
package="org.springframework.oxm.castor" properties="${castorBuilderProperties}"/>
|
||||
<castor types="j2" warnings="false" file="${orderSchema}" todir="${test.castor.dir}"
|
||||
package="org.springframework.oxm.castor" properties="${castorBuilderProperties}"/>
|
||||
<do-compile classpath.id="@{classpath.id}" input.dir="${test.castor.dir}" output.dir="@{output.dir}"
|
||||
resources.dir="${test.castor.dir}"/>
|
||||
<copy todir="@{output.dir}">
|
||||
<fileset dir="${test.castor.dir}">
|
||||
<exclude name="**/*.java"/>
|
||||
</fileset>
|
||||
</copy>
|
||||
|
||||
<!-- JAXB2 -->
|
||||
<ivy:cachepath resolveId="jaxb.classpath" pathid="jaxb.classpath" organisation="com.sun.xml"
|
||||
|
|
@ -40,7 +49,7 @@
|
|||
<delete quiet="true" dir="${test.jaxb.dir}" />
|
||||
<mkdir dir="${test.jaxb.dir}" />
|
||||
|
||||
<xjc destdir="${test.jaxb.dir}" package="org.springframework.oxm.jaxb.test" schema="${schema}">
|
||||
<xjc destdir="${test.jaxb.dir}" package="org.springframework.oxm.jaxb.test" schema="${flightSchema}">
|
||||
<produces dir="${test.jaxb.dir}" includes="**/*.java"/>
|
||||
</xjc>
|
||||
<do-compile classpath.id="@{classpath.id}" input.dir="${test.jaxb.dir}" output.dir="@{output.dir}"
|
||||
|
|
@ -67,7 +76,7 @@
|
|||
module="com.springsource.org.apache.xmlbeans" revision="2.4.0"
|
||||
conf="runtime" type="jar" inline="true" log="download-only"/>
|
||||
<taskdef name="xmlbeans" classname="org.apache.xmlbeans.impl.tool.XMLBean" classpathref="xmlbeans.classpath"/>
|
||||
<xmlbeans schema="${schema}" classgendir="@{output.dir}" classpathref="xmlbeans.classpath" compiler="modern"
|
||||
<xmlbeans schema="${flightSchema}" classgendir="@{output.dir}" classpathref="xmlbeans.classpath" compiler="modern"
|
||||
verbose="false"/>
|
||||
</sequential>
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ import org.springframework.util.xml.StaxUtils;
|
|||
|
||||
/**
|
||||
* Implementation of the <code>Marshaller</code> 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.
|
||||
*
|
||||
* <p>If a target class is specified using <code>setTargetClass</code>, the <code>CastorMarshaller</code> 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 <code>CastorMarshaller</code>
|
||||
* is tied to this one specific class. Use a mapping file for unmarshalling multiple classes.
|
||||
* <p>You cannot set both this property and the mapping (location).
|
||||
* Set the Castor target class. Alternative means of configuring
|
||||
* <code>CastorMarshaller<code> 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
|
||||
* <code>CastorMarshaller<code> 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 <code>XMLContext</code>. Subclasses can override this to create a custom context.
|
||||
* <p>The default implementation loads mapping files if defined, and the target class if not defined.
|
||||
* <p>
|
||||
* 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 <code>true</code> 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}.
|
||||
* <p>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}.
|
||||
* <p>
|
||||
* 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 <code>XMLException</code> to an appropriate exception from the
|
||||
* <code>org.springframework.oxm</code> hierarchy.
|
||||
* <p>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 <code>XMLException</code> that occured
|
||||
* @param marshalling indicates whether the exception occurs during marshalling (<code>true</code>),
|
||||
* or unmarshalling (<code>false</code>)
|
||||
* Convert the given <code>XMLException</code> to an appropriate exception
|
||||
* from the <code>org.springframework.oxm</code> hierarchy.
|
||||
* <p>
|
||||
* 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 <code>XMLException</code> that occured
|
||||
* @param marshalling indicates whether the exception occurs during
|
||||
* marshalling (<code>true</code>), or unmarshalling (<code>false</code>)
|
||||
* @return the corresponding <code>XmlMappingException</code>
|
||||
*/
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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 = "<order>" +
|
||||
|
|
@ -78,12 +77,12 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests {
|
|||
"<order-item id=\"3\" quantity=\"20\"/>" +
|
||||
"</order>";
|
||||
|
||||
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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
|
||||
targetNamespace="http://samples.springframework.org/order"
|
||||
xmlns:tns="http://samples.springframework.org/order">
|
||||
<element name="order">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="order-item" type="tns:orderItemType"
|
||||
maxOccurs="unbounded">
|
||||
</element>
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
<complexType name="orderItemType">
|
||||
<attribute name="id" type="string" />
|
||||
<attribute name="quantity" type="int" />
|
||||
</complexType>
|
||||
</schema>
|
||||
Loading…
Reference in New Issue