提交 157a87e9 编写于 作者: A Arjen Poutsma

SPR-6003 - Improve CastorMarshaller support for loading class descriptors

上级 52b51888
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/> <import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
<import file="${basedir}/../spring-build/standard/default.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.castor.dir" value="${target.dir}/castor/test"/>
<property name="test.jaxb.dir" value="${target.dir}/jaxb/test"/> <property name="test.jaxb.dir" value="${target.dir}/jaxb/test"/>
...@@ -26,10 +28,17 @@ ...@@ -26,10 +28,17 @@
<delete quiet="true" dir="${test.castor.dir}" /> <delete quiet="true" dir="${test.castor.dir}" />
<mkdir dir="${test.castor.dir}" /> <mkdir dir="${test.castor.dir}" />
<castor types="j2" warnings="false" file="${schema}" todir="${test.castor.dir}" <castor types="j2" warnings="false" file="${flightSchema}" todir="${test.castor.dir}"
package="org.springframework.oxm.castor"/> 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}" <do-compile classpath.id="@{classpath.id}" input.dir="${test.castor.dir}" output.dir="@{output.dir}"
resources.dir="${test.castor.dir}"/> resources.dir="${test.castor.dir}"/>
<copy todir="@{output.dir}">
<fileset dir="${test.castor.dir}">
<exclude name="**/*.java"/>
</fileset>
</copy>
<!-- JAXB2 --> <!-- JAXB2 -->
<ivy:cachepath resolveId="jaxb.classpath" pathid="jaxb.classpath" organisation="com.sun.xml" <ivy:cachepath resolveId="jaxb.classpath" pathid="jaxb.classpath" organisation="com.sun.xml"
...@@ -40,7 +49,7 @@ ...@@ -40,7 +49,7 @@
<delete quiet="true" dir="${test.jaxb.dir}" /> <delete quiet="true" dir="${test.jaxb.dir}" />
<mkdir 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"/> <produces dir="${test.jaxb.dir}" includes="**/*.java"/>
</xjc> </xjc>
<do-compile classpath.id="@{classpath.id}" input.dir="${test.jaxb.dir}" output.dir="@{output.dir}" <do-compile classpath.id="@{classpath.id}" input.dir="${test.jaxb.dir}" output.dir="@{output.dir}"
...@@ -67,7 +76,7 @@ ...@@ -67,7 +76,7 @@
module="com.springsource.org.apache.xmlbeans" revision="2.4.0" module="com.springsource.org.apache.xmlbeans" revision="2.4.0"
conf="runtime" type="jar" inline="true" log="download-only"/> conf="runtime" type="jar" inline="true" log="download-only"/>
<taskdef name="xmlbeans" classname="org.apache.xmlbeans.impl.tool.XMLBean" classpathref="xmlbeans.classpath"/> <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"/> verbose="false"/>
</sequential> </sequential>
......
...@@ -61,7 +61,7 @@ import org.springframework.util.xml.StaxUtils; ...@@ -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 * 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. * behavior of Castor.
* *
* <p>If a target class is specified using <code>setTargetClass</code>, the <code>CastorMarshaller</code> can only be * <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; ...@@ -74,6 +74,7 @@ import org.springframework.util.xml.StaxUtils;
* @author Arjen Poutsma * @author Arjen Poutsma
* @see #setEncoding(String) * @see #setEncoding(String)
* @see #setTargetClass(Class) * @see #setTargetClass(Class)
* @see #setTargetPackages(String[])
* @see #setMappingLocation(Resource) * @see #setMappingLocation(Resource)
* @see #setMappingLocations(Resource[]) * @see #setMappingLocations(Resource[])
* @since 3.0 * @since 3.0
...@@ -90,7 +91,9 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing ...@@ -90,7 +91,9 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing
private String encoding = DEFAULT_ENCODING; private String encoding = DEFAULT_ENCODING;
private Class targetClass; private Class[] targetClasses;
private String[] targetPackages;
private boolean validating = false; private boolean validating = false;
...@@ -132,12 +135,28 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing ...@@ -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> * Set the Castor target class. Alternative means of configuring
* is tied to this one specific class. Use a mapping file for unmarshalling multiple classes. * <code>CastorMarshaller<code> for unmarshalling multiple classes include
* <p>You cannot set both this property and the mapping (location). * use of mapping files, and specifying packages with Castor descriptor classes.
*/ */
public void setTargetClass(Class targetClass) { 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 ...@@ -214,21 +233,28 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing
this.suppressXsiType = suppressXsiType; this.suppressXsiType = suppressXsiType;
} }
public final void afterPropertiesSet() throws CastorMappingException, IOException { public final void afterPropertiesSet() throws CastorMappingException, IOException {
if (logger.isInfoEnabled()) { if (logger.isInfoEnabled()) {
if (this.mappingLocations != null) { if (!ObjectUtils.isEmpty(this.mappingLocations)) {
logger.info("Configured using " + StringUtils.arrayToCommaDelimitedString(this.mappingLocations)); logger.info(
"Configured using [" + StringUtils.arrayToCommaDelimitedString(this.mappingLocations) + "]");
}
if (!ObjectUtils.isEmpty(this.targetClasses)) {
logger.info("Configured for target classes " + StringUtils.arrayToCommaDelimitedString(targetClasses) +
"]");
} }
if (this.targetClass != null) { if (!ObjectUtils.isEmpty(this.targetPackages)) {
logger.info("Configured for target class [" + this.targetClass.getName() + "]"); logger.info(
"Configured for target packages [" + StringUtils.arrayToCommaDelimitedString(targetPackages) +
"]");
} }
if (this.mappingLocations == null && this.targetClass == null) { if (ObjectUtils.isEmpty(this.mappingLocations) && ObjectUtils.isEmpty(this.targetClasses) &&
ObjectUtils.isEmpty(this.targetPackages)) {
logger.info("Using default configuration"); logger.info("Using default configuration");
} }
} }
try { try {
this.xmlContext = createXMLContext(this.mappingLocations, this.targetClass); this.xmlContext = createXMLContext(this.mappingLocations, this.targetClasses, this.targetPackages);
} }
catch (MappingException ex) { catch (MappingException ex) {
throw new CastorMappingException("Could not load Castor mapping", ex); throw new CastorMappingException("Could not load Castor mapping", ex);
...@@ -240,14 +266,16 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing ...@@ -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. * 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 * @return the created resolver
* @throws MappingException when the mapping file cannot be loaded * @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#addMapping(org.exolab.castor.mapping.Mapping)
* @see XMLContext#addClass(Class) * @see XMLContext#addClass(Class)
*/ */
protected XMLContext createXMLContext(Resource[] mappingLocations, Class targetClass) protected XMLContext createXMLContext(Resource[] mappingLocations, Class[] targetClasses, String[] targetPackages)
throws MappingException, ResolverException, IOException { throws MappingException, ResolverException, IOException {
XMLContext context = new XMLContext(); XMLContext context = new XMLContext();
...@@ -258,13 +286,15 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing ...@@ -258,13 +286,15 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing
} }
context.addMapping(mapping); context.addMapping(mapping);
} }
if (targetClass != null) { if (!ObjectUtils.isEmpty(targetClasses)) {
context.addClass(targetClass); context.addClasses(targetClasses);
}
if (!ObjectUtils.isEmpty(targetPackages)) {
context.addPackages(targetPackages);
} }
return context; return context;
} }
/** /**
* Returns <code>true</code> for all classes, i.e. Castor supports arbitrary classes. * Returns <code>true</code> for all classes, i.e. Castor supports arbitrary classes.
*/ */
...@@ -272,7 +302,6 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing ...@@ -272,7 +302,6 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing
return true; return true;
} }
// Marshalling // Marshalling
@Override @Override
...@@ -378,7 +407,7 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing ...@@ -378,7 +407,7 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing
return unmarshalSaxReader(reader, new InputSource()); return unmarshalSaxReader(reader, new InputSource());
} }
catch (IOException ex) { 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 ...@@ -411,19 +440,20 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing
private Unmarshaller createUnmarshaller() { private Unmarshaller createUnmarshaller() {
Unmarshaller unmarshaller = this.xmlContext.createUnmarshaller(); Unmarshaller unmarshaller = this.xmlContext.createUnmarshaller();
if (this.targetClass != null) {
unmarshaller.setClass(this.targetClass);
unmarshaller.setClassLoader(this.targetClass.getClassLoader());
}
customizeUnmarshaller(unmarshaller); customizeUnmarshaller(unmarshaller);
return unmarshaller; return unmarshaller;
} }
/** /**
* Template method that allows for customizing of the given Castor {@link Unmarshaller}. * Template method that allows for customizing of the given Castor
* <p>The default implementation invokes {@link Unmarshaller#setValidation(boolean)}, * {@link Unmarshaller}.
* {@link Unmarshaller#setWhitespacePreserve(boolean)}, {@link Unmarshaller#setIgnoreExtraAttributes(boolean)}, * <p>
* and {@link Unmarshaller#setIgnoreExtraElements(boolean)} with the properties set on this marshaller. * 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) { protected void customizeUnmarshaller(Unmarshaller unmarshaller) {
unmarshaller.setValidation(this.validating); unmarshaller.setValidation(this.validating);
...@@ -433,13 +463,16 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing ...@@ -433,13 +463,16 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing
} }
/** /**
* Convert the given <code>XMLException</code> to an appropriate exception from the * Convert the given <code>XMLException</code> to an appropriate exception
* <code>org.springframework.oxm</code> hierarchy. * from the <code>org.springframework.oxm</code> hierarchy.
* <p>A boolean flag is used to indicate whether this exception occurs during marshalling or * <p>
* unmarshalling, since Castor itself does not make this distinction in its exception hierarchy. * A boolean flag is used to indicate whether this exception occurs during
* @param ex Castor <code>XMLException</code> that occured * marshalling or unmarshalling, since Castor itself does not make this
* @param marshalling indicates whether the exception occurs during marshalling (<code>true</code>), * distinction in its exception hierarchy.
* or unmarshalling (<code>false</code>) *
* @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> * @return the corresponding <code>XmlMappingException</code>
*/ */
protected XmlMappingException convertCastorException(XMLException ex, boolean marshalling) { protected XmlMappingException convertCastorException(XMLException ex, boolean marshalling) {
...@@ -448,7 +481,7 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing ...@@ -448,7 +481,7 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing
} }
else if (ex instanceof MarshalException) { else if (ex instanceof MarshalException) {
if (marshalling) { if (marshalling) {
return new MarshallingFailureException("Castor marshalling exception", ex); return new MarshallingFailureException("Castor marshalling exception", ex);
} }
else { else {
return new UnmarshallingFailureException("Castor unmarshalling exception", ex); return new UnmarshallingFailureException("Castor unmarshalling exception", ex);
......
...@@ -19,7 +19,6 @@ package org.springframework.oxm.castor; ...@@ -19,7 +19,6 @@ package org.springframework.oxm.castor;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import static org.junit.Assert.*; import static org.junit.Assert.*;
...@@ -44,7 +43,7 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests { ...@@ -44,7 +43,7 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests {
protected void testFlight(Object o) { protected void testFlight(Object o) {
Flight flight = (Flight) o; Flight flight = (Flight) o;
assertNotNull("Flight is null", flight); assertNotNull("Flight is null", flight);
assertEquals("Number is invalid", 42L, flight.getNumber()); assertEquals("Number is invalid", Long.valueOf(42L), flight.getNumber());
} }
@Override @Override
...@@ -59,7 +58,7 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests { ...@@ -59,7 +58,7 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests {
@Test @Test
public void unmarshalTargetClass() throws Exception { public void unmarshalTargetClass() throws Exception {
CastorMarshaller unmarshaller = new CastorMarshaller(); CastorMarshaller unmarshaller = new CastorMarshaller();
unmarshaller.setTargetClass(Flights.class); unmarshaller.setTargetClasses(new Class[] { Flights.class } );
unmarshaller.afterPropertiesSet(); unmarshaller.afterPropertiesSet();
StreamSource source = new StreamSource(new ByteArrayInputStream(INPUT_STRING.getBytes("UTF-8"))); StreamSource source = new StreamSource(new ByteArrayInputStream(INPUT_STRING.getBytes("UTF-8")));
Object flights = unmarshaller.unmarshal(source); Object flights = unmarshaller.unmarshal(source);
...@@ -67,10 +66,10 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests { ...@@ -67,10 +66,10 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests {
} }
@Test @Test
public void testSetBothTargetClassAndMapping() throws IOException { public void testSetBothTargetClassesAndMapping() throws IOException {
CastorMarshaller unmarshaller = new CastorMarshaller(); CastorMarshaller unmarshaller = new CastorMarshaller();
unmarshaller.setMappingLocation(new ClassPathResource("order-mapping.xml", CastorMarshaller.class)); unmarshaller.setMappingLocation(new ClassPathResource("order-mapping.xml", CastorMarshaller.class));
unmarshaller.setTargetClass(ArrayList.class); unmarshaller.setTargetClasses(new Class[] { Order.class } );
unmarshaller.afterPropertiesSet(); unmarshaller.afterPropertiesSet();
String xml = "<order>" + String xml = "<order>" +
...@@ -78,12 +77,12 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests { ...@@ -78,12 +77,12 @@ public class CastorUnmarshallerTests extends AbstractUnmarshallerTests {
"<order-item id=\"3\" quantity=\"20\"/>" + "<order-item id=\"3\" quantity=\"20\"/>" +
"</order>"; "</order>";
ArrayList result = (ArrayList) unmarshaller.unmarshal(new StreamSource(new StringReader(xml))); Order order = (Order) unmarshaller.unmarshal(new StreamSource(new StringReader(xml)));
assertEquals("Invalid amount of items", 2, result.size()); assertEquals("Invalid amount of items", 2, order.getOrderItemCount());
OrderItem item = (OrderItem) result.get(0); OrderItem item = order.getOrderItem(0);
assertEquals("Invalid items", "1", item.getId()); assertEquals("Invalid items", "1", item.getId());
assertEquals("Invalid items", new Integer(15), item.getQuantity()); 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", "3", item.getId());
assertEquals("Invalid items", new Integer(20), item.getQuantity()); assertEquals("Invalid items", new Integer(20), item.getQuantity());
} }
......
<?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>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册