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

SPR-6003 - Improve CastorMarshaller support for loading class descriptors

上级 52b51888
......@@ -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 (!ObjectUtils.isEmpty(this.targetClasses)) {
logger.info("Configured for target classes " + StringUtils.arrayToCommaDelimitedString(targetClasses) +
"]");
}
if (this.targetClass != null) {
logger.info("Configured for target class [" + this.targetClass.getName() + "]");
if (!ObjectUtils.isEmpty(this.targetPackages)) {
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");
}
}
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());
}
......
<?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.
先完成此消息的编辑!
想要评论请 注册