Skip to content

Commit 157a87e

Browse files
committedAug 27, 2009
SPR-6003 - Improve CastorMarshaller support for loading class descriptors
1 parent 52b5188 commit 157a87e

File tree

4 files changed

+110
-51
lines changed

4 files changed

+110
-51
lines changed
 

‎org.springframework.oxm/build.xml

+14-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
55
<import file="${basedir}/../spring-build/standard/default.xml"/>
66

7-
<property name="schema" value="${basedir}/src/test/resources/org/springframework/oxm/flight.xsd"/>
7+
<property name="flightSchema" value="${basedir}/src/test/resources/org/springframework/oxm/flight.xsd"/>
8+
<property name="orderSchema" value="${basedir}/src/test/resources/org/springframework/oxm/order.xsd"/>
9+
<property name="castorBuilderProperties" value="${basedir}/src/test/castor/castorbuilder.properties"/>
810
<property name="test.castor.dir" value="${target.dir}/castor/test"/>
911
<property name="test.jaxb.dir" value="${target.dir}/jaxb/test"/>
1012

@@ -26,10 +28,17 @@
2628
<delete quiet="true" dir="${test.castor.dir}" />
2729
<mkdir dir="${test.castor.dir}" />
2830

29-
<castor types="j2" warnings="false" file="${schema}" todir="${test.castor.dir}"
30-
package="org.springframework.oxm.castor"/>
31+
<castor types="j2" warnings="false" file="${flightSchema}" todir="${test.castor.dir}"
32+
package="org.springframework.oxm.castor" properties="${castorBuilderProperties}"/>
33+
<castor types="j2" warnings="false" file="${orderSchema}" todir="${test.castor.dir}"
34+
package="org.springframework.oxm.castor" properties="${castorBuilderProperties}"/>
3135
<do-compile classpath.id="@{classpath.id}" input.dir="${test.castor.dir}" output.dir="@{output.dir}"
3236
resources.dir="${test.castor.dir}"/>
37+
<copy todir="@{output.dir}">
38+
<fileset dir="${test.castor.dir}">
39+
<exclude name="**/*.java"/>
40+
</fileset>
41+
</copy>
3342

3443
<!-- JAXB2 -->
3544
<ivy:cachepath resolveId="jaxb.classpath" pathid="jaxb.classpath" organisation="com.sun.xml"
@@ -40,7 +49,7 @@
4049
<delete quiet="true" dir="${test.jaxb.dir}" />
4150
<mkdir dir="${test.jaxb.dir}" />
4251

43-
<xjc destdir="${test.jaxb.dir}" package="org.springframework.oxm.jaxb.test" schema="${schema}">
52+
<xjc destdir="${test.jaxb.dir}" package="org.springframework.oxm.jaxb.test" schema="${flightSchema}">
4453
<produces dir="${test.jaxb.dir}" includes="**/*.java"/>
4554
</xjc>
4655
<do-compile classpath.id="@{classpath.id}" input.dir="${test.jaxb.dir}" output.dir="@{output.dir}"
@@ -67,7 +76,7 @@
6776
module="com.springsource.org.apache.xmlbeans" revision="2.4.0"
6877
conf="runtime" type="jar" inline="true" log="download-only"/>
6978
<taskdef name="xmlbeans" classname="org.apache.xmlbeans.impl.tool.XMLBean" classpathref="xmlbeans.classpath"/>
70-
<xmlbeans schema="${schema}" classgendir="@{output.dir}" classpathref="xmlbeans.classpath" compiler="modern"
79+
<xmlbeans schema="${flightSchema}" classgendir="@{output.dir}" classpathref="xmlbeans.classpath" compiler="modern"
7180
verbose="false"/>
7281
</sequential>
7382

‎org.springframework.oxm/src/main/java/org/springframework/oxm/castor/CastorMarshaller.java

+70-37
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161

6262
/**
6363
* Implementation of the <code>Marshaller</code> interface for Castor. By default, Castor does not require any further
64-
* configuration, though setting a target class or providing a mapping file can be used to have more control over the
64+
* configuration, though setting target classes, target packages or providing a mapping file can be used to have more control over the
6565
* behavior of Castor.
6666
*
6767
* <p>If a target class is specified using <code>setTargetClass</code>, the <code>CastorMarshaller</code> can only be
@@ -74,6 +74,7 @@
7474
* @author Arjen Poutsma
7575
* @see #setEncoding(String)
7676
* @see #setTargetClass(Class)
77+
* @see #setTargetPackages(String[])
7778
* @see #setMappingLocation(Resource)
7879
* @see #setMappingLocations(Resource[])
7980
* @since 3.0
@@ -90,7 +91,9 @@ public class CastorMarshaller extends AbstractMarshaller implements Initializing
9091

9192
private String encoding = DEFAULT_ENCODING;
9293

93-
private Class targetClass;
94+
private Class[] targetClasses;
95+
96+
private String[] targetPackages;
9497

9598
private boolean validating = false;
9699

@@ -132,12 +135,28 @@ public void setMappingLocations(Resource[] mappingLocations) {
132135
}
133136

134137
/**
135-
* Set the Castor target class. If this property is set, this <code>CastorMarshaller</code>
136-
* is tied to this one specific class. Use a mapping file for unmarshalling multiple classes.
137-
* <p>You cannot set both this property and the mapping (location).
138+
* Set the Castor target class. Alternative means of configuring
139+
* <code>CastorMarshaller<code> for unmarshalling multiple classes include
140+
* use of mapping files, and specifying packages with Castor descriptor classes.
138141
*/
139142
public void setTargetClass(Class targetClass) {
140-
this.targetClass = targetClass;
143+
this.targetClasses = new Class[]{targetClass};
144+
}
145+
146+
/**
147+
* Set the Castor target classes. Alternative means of configuring
148+
* <code>CastorMarshaller<code> for unmarshalling multiple classes include
149+
* use of mapping files, and specifying packages with Castor descriptor classes.
150+
*/
151+
public void setTargetClasses(Class[] targetClasses) {
152+
this.targetClasses = targetClasses;
153+
}
154+
155+
/**
156+
* Set the package names of packages with the Castor descriptor classes.
157+
*/
158+
public void setTargetPackages(String[] targetPackages) {
159+
this.targetPackages = targetPackages;
141160
}
142161

143162
/**
@@ -214,21 +233,28 @@ public void setSuppressXsiType(boolean suppressXsiType) {
214233
this.suppressXsiType = suppressXsiType;
215234
}
216235

217-
218236
public final void afterPropertiesSet() throws CastorMappingException, IOException {
219237
if (logger.isInfoEnabled()) {
220-
if (this.mappingLocations != null) {
221-
logger.info("Configured using " + StringUtils.arrayToCommaDelimitedString(this.mappingLocations));
238+
if (!ObjectUtils.isEmpty(this.mappingLocations)) {
239+
logger.info(
240+
"Configured using [" + StringUtils.arrayToCommaDelimitedString(this.mappingLocations) + "]");
241+
}
242+
if (!ObjectUtils.isEmpty(this.targetClasses)) {
243+
logger.info("Configured for target classes " + StringUtils.arrayToCommaDelimitedString(targetClasses) +
244+
"]");
222245
}
223-
if (this.targetClass != null) {
224-
logger.info("Configured for target class [" + this.targetClass.getName() + "]");
246+
if (!ObjectUtils.isEmpty(this.targetPackages)) {
247+
logger.info(
248+
"Configured for target packages [" + StringUtils.arrayToCommaDelimitedString(targetPackages) +
249+
"]");
225250
}
226-
if (this.mappingLocations == null && this.targetClass == null) {
251+
if (ObjectUtils.isEmpty(this.mappingLocations) && ObjectUtils.isEmpty(this.targetClasses) &&
252+
ObjectUtils.isEmpty(this.targetPackages)) {
227253
logger.info("Using default configuration");
228254
}
229255
}
230256
try {
231-
this.xmlContext = createXMLContext(this.mappingLocations, this.targetClass);
257+
this.xmlContext = createXMLContext(this.mappingLocations, this.targetClasses, this.targetPackages);
232258
}
233259
catch (MappingException ex) {
234260
throw new CastorMappingException("Could not load Castor mapping", ex);
@@ -240,14 +266,16 @@ public final void afterPropertiesSet() throws CastorMappingException, IOExceptio
240266

241267
/**
242268
* Create the Castor <code>XMLContext</code>. Subclasses can override this to create a custom context.
243-
* <p>The default implementation loads mapping files if defined, and the target class if not defined.
269+
* <p>
270+
* The default implementation loads mapping files if defined, or the target class or packages if defined.
271+
*
244272
* @return the created resolver
245273
* @throws MappingException when the mapping file cannot be loaded
246-
* @throws IOException in case of I/O errors
274+
* @throws IOException in case of I/O errors
247275
* @see XMLContext#addMapping(org.exolab.castor.mapping.Mapping)
248276
* @see XMLContext#addClass(Class)
249277
*/
250-
protected XMLContext createXMLContext(Resource[] mappingLocations, Class targetClass)
278+
protected XMLContext createXMLContext(Resource[] mappingLocations, Class[] targetClasses, String[] targetPackages)
251279
throws MappingException, ResolverException, IOException {
252280

253281
XMLContext context = new XMLContext();
@@ -258,21 +286,22 @@ protected XMLContext createXMLContext(Resource[] mappingLocations, Class targetC
258286
}
259287
context.addMapping(mapping);
260288
}
261-
if (targetClass != null) {
262-
context.addClass(targetClass);
289+
if (!ObjectUtils.isEmpty(targetClasses)) {
290+
context.addClasses(targetClasses);
291+
}
292+
if (!ObjectUtils.isEmpty(targetPackages)) {
293+
context.addPackages(targetPackages);
263294
}
264295
return context;
265296
}
266297

267-
268298
/**
269299
* Returns <code>true</code> for all classes, i.e. Castor supports arbitrary classes.
270300
*/
271301
public boolean supports(Class<?> clazz) {
272302
return true;
273303
}
274304

275-
276305
// Marshalling
277306

278307
@Override
@@ -378,7 +407,7 @@ protected final Object unmarshalXmlEventReader(XMLEventReader eventReader) {
378407
return unmarshalSaxReader(reader, new InputSource());
379408
}
380409
catch (IOException ex) {
381-
throw new UnmarshallingFailureException("Failed to read XML stream", ex);
410+
throw new UnmarshallingFailureException("Failed to read XML stream", ex);
382411
}
383412
}
384413

@@ -411,19 +440,20 @@ protected final Object unmarshalXmlStreamReader(XMLStreamReader streamReader) {
411440

412441
private Unmarshaller createUnmarshaller() {
413442
Unmarshaller unmarshaller = this.xmlContext.createUnmarshaller();
414-
if (this.targetClass != null) {
415-
unmarshaller.setClass(this.targetClass);
416-
unmarshaller.setClassLoader(this.targetClass.getClassLoader());
417-
}
418443
customizeUnmarshaller(unmarshaller);
419444
return unmarshaller;
420445
}
421446

422447
/**
423-
* Template method that allows for customizing of the given Castor {@link Unmarshaller}.
424-
* <p>The default implementation invokes {@link Unmarshaller#setValidation(boolean)},
425-
* {@link Unmarshaller#setWhitespacePreserve(boolean)}, {@link Unmarshaller#setIgnoreExtraAttributes(boolean)},
426-
* and {@link Unmarshaller#setIgnoreExtraElements(boolean)} with the properties set on this marshaller.
448+
* Template method that allows for customizing of the given Castor
449+
* {@link Unmarshaller}.
450+
* <p>
451+
* The default implementation invokes
452+
* {@link Unmarshaller#setValidation(boolean)},
453+
* {@link Unmarshaller#setWhitespacePreserve(boolean)},
454+
* {@link Unmarshaller#setIgnoreExtraAttributes(boolean)}, and
455+
* {@link Unmarshaller#setIgnoreExtraElements(boolean)} with the properties
456+
* set on this marshaller.
427457
*/
428458
protected void customizeUnmarshaller(Unmarshaller unmarshaller) {
429459
unmarshaller.setValidation(this.validating);
@@ -433,13 +463,16 @@ protected void customizeUnmarshaller(Unmarshaller unmarshaller) {
433463
}
434464

435465
/**
436-
* Convert the given <code>XMLException</code> to an appropriate exception from the
437-
* <code>org.springframework.oxm</code> hierarchy.
438-
* <p>A boolean flag is used to indicate whether this exception occurs during marshalling or
439-
* unmarshalling, since Castor itself does not make this distinction in its exception hierarchy.
440-
* @param ex Castor <code>XMLException</code> that occured
441-
* @param marshalling indicates whether the exception occurs during marshalling (<code>true</code>),
442-
* or unmarshalling (<code>false</code>)
466+
* Convert the given <code>XMLException</code> to an appropriate exception
467+
* from the <code>org.springframework.oxm</code> hierarchy.
468+
* <p>
469+
* A boolean flag is used to indicate whether this exception occurs during
470+
* marshalling or unmarshalling, since Castor itself does not make this
471+
* distinction in its exception hierarchy.
472+
*
473+
* @param ex Castor <code>XMLException</code> that occured
474+
* @param marshalling indicates whether the exception occurs during
475+
* marshalling (<code>true</code>), or unmarshalling (<code>false</code>)
443476
* @return the corresponding <code>XmlMappingException</code>
444477
*/
445478
protected XmlMappingException convertCastorException(XMLException ex, boolean marshalling) {
@@ -448,7 +481,7 @@ protected XmlMappingException convertCastorException(XMLException ex, boolean ma
448481
}
449482
else if (ex instanceof MarshalException) {
450483
if (marshalling) {
451-
return new MarshallingFailureException("Castor marshalling exception", ex);
484+
return new MarshallingFailureException("Castor marshalling exception", ex);
452485
}
453486
else {
454487
return new UnmarshallingFailureException("Castor unmarshalling exception", ex);

‎org.springframework.oxm/src/test/java/org/springframework/oxm/castor/CastorUnmarshallerTests.java

+8-9
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.io.ByteArrayInputStream;
2020
import java.io.IOException;
2121
import java.io.StringReader;
22-
import java.util.ArrayList;
2322
import javax.xml.transform.stream.StreamSource;
2423

2524
import static org.junit.Assert.*;
@@ -44,7 +43,7 @@ protected void testFlights(Object o) {
4443
protected void testFlight(Object o) {
4544
Flight flight = (Flight) o;
4645
assertNotNull("Flight is null", flight);
47-
assertEquals("Number is invalid", 42L, flight.getNumber());
46+
assertEquals("Number is invalid", Long.valueOf(42L), flight.getNumber());
4847
}
4948

5049
@Override
@@ -59,31 +58,31 @@ protected Unmarshaller createUnmarshaller() throws Exception {
5958
@Test
6059
public void unmarshalTargetClass() throws Exception {
6160
CastorMarshaller unmarshaller = new CastorMarshaller();
62-
unmarshaller.setTargetClass(Flights.class);
61+
unmarshaller.setTargetClasses(new Class[] { Flights.class } );
6362
unmarshaller.afterPropertiesSet();
6463
StreamSource source = new StreamSource(new ByteArrayInputStream(INPUT_STRING.getBytes("UTF-8")));
6564
Object flights = unmarshaller.unmarshal(source);
6665
testFlights(flights);
6766
}
6867

6968
@Test
70-
public void testSetBothTargetClassAndMapping() throws IOException {
69+
public void testSetBothTargetClassesAndMapping() throws IOException {
7170
CastorMarshaller unmarshaller = new CastorMarshaller();
7271
unmarshaller.setMappingLocation(new ClassPathResource("order-mapping.xml", CastorMarshaller.class));
73-
unmarshaller.setTargetClass(ArrayList.class);
72+
unmarshaller.setTargetClasses(new Class[] { Order.class } );
7473
unmarshaller.afterPropertiesSet();
7574

7675
String xml = "<order>" +
7776
"<order-item id=\"1\" quantity=\"15\"/>" +
7877
"<order-item id=\"3\" quantity=\"20\"/>" +
7978
"</order>";
8079

81-
ArrayList result = (ArrayList) unmarshaller.unmarshal(new StreamSource(new StringReader(xml)));
82-
assertEquals("Invalid amount of items", 2, result.size());
83-
OrderItem item = (OrderItem) result.get(0);
80+
Order order = (Order) unmarshaller.unmarshal(new StreamSource(new StringReader(xml)));
81+
assertEquals("Invalid amount of items", 2, order.getOrderItemCount());
82+
OrderItem item = order.getOrderItem(0);
8483
assertEquals("Invalid items", "1", item.getId());
8584
assertEquals("Invalid items", new Integer(15), item.getQuantity());
86-
item = (OrderItem) result.get(1);
85+
item = order.getOrderItem(1);
8786
assertEquals("Invalid items", "3", item.getId());
8887
assertEquals("Invalid items", new Integer(20), item.getQuantity());
8988
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
3+
targetNamespace="http://samples.springframework.org/order"
4+
xmlns:tns="http://samples.springframework.org/order">
5+
<element name="order">
6+
<complexType>
7+
<sequence>
8+
<element name="order-item" type="tns:orderItemType"
9+
maxOccurs="unbounded">
10+
</element>
11+
</sequence>
12+
</complexType>
13+
</element>
14+
<complexType name="orderItemType">
15+
<attribute name="id" type="string" />
16+
<attribute name="quantity" type="int" />
17+
</complexType>
18+
</schema>

0 commit comments

Comments
 (0)
Please sign in to comment.