BATCH-2417 Drop JAXP 1.3 support

Spring 4 requires JDK 6 which includes JAXP 1.4. In Spring 4
`org.springframework.util.xml.StaxUtils` is public and directly
delegates to `javax.xml.transform.stax.StAXSource`. This means that
`org.springframework.batch.item.xml.StaxUtils#hasSpring30StaxSupport`
will always be true. Therefore a lot of code an be simplified by
calling the constructors directly instead of through reflection.

 - remove reflection no longer needed in Spring 4
 - move methods only used in tests to custom test class

Issue: BATCH-2417
This commit is contained in:
Philippe Marschall
2015-08-10 20:11:13 +02:00
committed by Michael Minella
parent 7a3a72f5ca
commit 73ca73688d
10 changed files with 71 additions and 125 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.batch.item.xml;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@@ -217,7 +218,7 @@ ResourceAwareItemReaderItemStream<T>, InitializingBean {
* Move to next fragment and map it to item.
*/
@Override
protected T doRead() throws Exception {
protected T doRead() throws IOException, XMLStreamException {
if (noInput) {
return null;

View File

@@ -546,10 +546,8 @@ ResourceAwareItemWriterItemStream<T>, InitializingBean {
* Subclasses can override to customize the STAX result.
*
* @return a result for writing to
*
* @throws Exception thrown if an error occurs curing the creation of the result.
*/
protected Result createStaxResult() throws Exception {
protected Result createStaxResult() {
return StaxUtils.getResult(eventWriter);
}
@@ -757,7 +755,7 @@ ResourceAwareItemWriterItemStream<T>, InitializingBean {
* @throws XmlMappingException thrown if error occurs during XML Mapping.
*/
@Override
public void write(List<? extends T> items) throws XmlMappingException, Exception {
public void write(List<? extends T> items) throws XmlMappingException, IOException {
if(!this.initialized) {
throw new WriterNotOpenException("Writer must be open before it can be written to");

View File

@@ -16,131 +16,30 @@
package org.springframework.batch.item.xml;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.stax.StAXResult;
import javax.xml.transform.stax.StAXSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* This class provides a little bit of indirection to avoid ugly conditional object creation. It is unfortunately
* a bit redundant assuming a Spring 3.0 environment, but is necessary to work with Spring WS 1.5.x.
* StAX utility methods.
* <br>
* The returned object determines whether the environment has Spring OXM as included in the Spring 3.x series of relies
* or whether it has Spring OXM from Spring WS 1.5x and factories a StaxSource instance appropriately.
* <br>
* As the only class state maintained is to cache java reflection metadata, which is thread-safe, this class is thread-safe.
* This class is thread-safe.
*
* @author Josh Long
*
*/
public abstract class StaxUtils {
private static final Log logger = LogFactory.getLog(StaxUtils.class);
private static ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
// regular object.
private static String staxSourceClassNameOnSpringWs15 = "org.springframework.xml.transform.StaxSource";
private static String staxResultClassNameOnSpringOxm15 = "org.springframework.xml.transform.StaxResult";
// in Spring 3, StaxUtils is package private, so use static utility StaxUtils#createStaxSource / StaxUtils#createStaxResult
private static String staxSourceClassNameOnSpringOxm30 = "org.springframework.util.xml.StaxUtils";
private static boolean hasSpringWs15StaxSupport = ClassUtils.isPresent(staxSourceClassNameOnSpringWs15, defaultClassLoader);
private static boolean hasSpring30StaxSupport = ClassUtils.isPresent(staxSourceClassNameOnSpringOxm30, defaultClassLoader);
private static Method staxUtilsSourceMethodOnSpring30, staxUtilsResultMethodOnSpring30;
private static Constructor<?> staxSourceClassCtorOnSpringWs15, staxResultClassCtorOnSpringWs15;
static {
try {
// cache the factory method / constructor so that we spend as little time in reflection as possible
if (hasSpring30StaxSupport) {
Class<?> clzz = ClassUtils.forName(staxSourceClassNameOnSpringOxm30, defaultClassLoader);
// javax.xml.transform.Source
staxUtilsSourceMethodOnSpring30 = ClassUtils.getStaticMethod(clzz, "createStaxSource", XMLEventReader.class);
// javax.xml.transform.Result
staxUtilsResultMethodOnSpring30 = ClassUtils.getStaticMethod(clzz, "createStaxResult", XMLEventWriter.class);
} else if (hasSpringWs15StaxSupport) {
// javax.xml.transform.Source
Class<?> staxSourceClassOnSpringWs15 = ClassUtils.forName(staxSourceClassNameOnSpringWs15, defaultClassLoader);
staxSourceClassCtorOnSpringWs15 = staxSourceClassOnSpringWs15.getConstructor(XMLEventReader.class);
// javax.xml.transform.Result
Class<?> staxResultClassOnSpringWs15 = ClassUtils.forName(staxResultClassNameOnSpringOxm15, defaultClassLoader);
staxResultClassCtorOnSpringWs15 = staxResultClassOnSpringWs15.getConstructor(XMLEventWriter.class);
} else {
if (logger.isDebugEnabled()) {
logger.debug("'StaxSource' was not detected in Spring 3.0's OXM support or Spring WS 1.5's OXM support. " +
"This is a problem if you intend to use the " +StaxEventItemWriter.class.getName() + " or " +
StaxEventItemReader.class.getName()+". Please add the appropriate dependencies.");
}
}
} catch (Exception ex) {
logger.error("Could not precache required class and method metadata in " + StaxUtils.class.getName());
}
public static Source getSource(XMLEventReader r) throws XMLStreamException {
return new StAXSource(r);
}
public static Source getSource(XMLEventReader r) throws Exception {
if (hasSpring30StaxSupport) {
// org.springframework.util.xml.StaxUtils.createStaxSource(r)
Object result = staxUtilsSourceMethodOnSpring30.invoke(null,r);
Assert.isInstanceOf(Source.class, result, "the result should be assignable to " + Source.class.getName());
return (Source) result;
} else if (hasSpringWs15StaxSupport) {
Object result = staxSourceClassCtorOnSpringWs15.newInstance(r);
Assert.isInstanceOf(Source.class, result, "the result should be assignable to " + Source.class.getName());
return (Source) result;
}
// maybe you don't have either environment?
return null;
public static Result getResult(XMLEventWriter w) {
return new StAXResult(w);
}
public static Result getResult(XMLEventWriter w) throws Exception {
if (hasSpring30StaxSupport) {
Object result = staxUtilsResultMethodOnSpring30.invoke(null,w);
Assert.isInstanceOf(Result.class, result, "the result should be assignable to " + Result.class.getName());
return (Result) result;
} else if (hasSpringWs15StaxSupport) {
Object result = staxResultClassCtorOnSpringWs15.newInstance(w);
Assert.isInstanceOf(Result.class, result, "the result should be assignable to " + Result.class.getName());
return (Result) result;
}
// maybe you don't have either environment?
return null;
}
public static XMLEventWriter getXmlEventWriter(Result r) throws Exception {
Method m = r.getClass().getDeclaredMethod("getXMLEventWriter");
boolean accessible = m.isAccessible();
m.setAccessible(true);
Object result = m.invoke(r);
m.setAccessible(accessible);
return (XMLEventWriter) result;
}
public static XMLEventReader getXmlEventReader(Source s) throws Exception {
Method m = s.getClass().getDeclaredMethod("getXMLEventReader");
boolean accessible = m.isAccessible();
m.setAccessible(true);
Object result = m.invoke(s);
m.setAccessible(accessible);
return (XMLEventReader) result;
}
}

View File

@@ -32,7 +32,7 @@ import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.sample.Foo;
import org.springframework.batch.item.xml.StaxEventItemReader;
import org.springframework.batch.item.xml.StaxUtils;
import org.springframework.batch.item.xml.StaxTestUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.oxm.Unmarshaller;
@@ -55,7 +55,7 @@ public class MultiResourceItemReaderXmlTests extends AbstractItemStreamItemReade
Attribute attr;
try {
XMLEventReader eventReader = StaxUtils.getXmlEventReader(source );
XMLEventReader eventReader = StaxTestUtils.getXmlEventReader(source );
assertTrue(eventReader.nextEvent().isStartDocument());
StartElement event = eventReader.nextEvent().asStartElement();
attr = (Attribute) event.getAttributes().next();

View File

@@ -28,7 +28,7 @@ import javax.xml.transform.Result;
import org.junit.Before;
import org.junit.Test;
import org.springframework.batch.item.xml.StaxEventItemWriter;
import org.springframework.batch.item.xml.StaxUtils;
import org.springframework.batch.item.xml.StaxTestUtils;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.XmlMappingException;
import org.springframework.util.Assert;
@@ -63,7 +63,7 @@ public class MultiResourceItemWriterXmlTests extends AbstractMultiResourceItemWr
try {
XMLEventFactory factory = XMLEventFactory.newInstance();
XMLEventWriter writer = StaxUtils.getXmlEventWriter(result);
XMLEventWriter writer = StaxTestUtils.getXmlEventWriter(result);
writer.add(factory.createStartDocument("UTF-8"));
writer.add(factory.createStartElement("prefix", "namespace", graph.toString()));
writer.add(factory.createEndElement("prefix", "namespace", graph.toString()));

View File

@@ -45,7 +45,7 @@ public class StaxEventItemReaderCommonTests extends AbstractItemStreamItemReader
public Object unmarshal(Source source) throws XmlMappingException, IOException {
Attribute attr = null ;
try {
XMLEventReader eventReader = StaxUtils.getXmlEventReader( source);
XMLEventReader eventReader = StaxTestUtils.getXmlEventReader( source);
assertTrue(eventReader.nextEvent().isStartDocument());
StartElement event = eventReader.nextEvent().asStartElement();
attr = (Attribute) event.getAttributes().next();

View File

@@ -662,7 +662,7 @@ public class StaxEventItemReaderTests {
List<XMLEvent> fragmentContent;
try {
XMLEventReader eventReader = StaxUtils.getXmlEventReader(source);
XMLEventReader eventReader = StaxTestUtils.getXmlEventReader(source);
// first event should be StartDocument
XMLEvent event1 = eventReader.nextEvent();

View File

@@ -772,7 +772,7 @@ public class StaxEventItemWriterTests {
public void marshal(Object graph, Result result) throws XmlMappingException, IOException {
super.marshal(graph, result);
try {
StaxUtils.getXmlEventWriter(result).close();
StaxTestUtils.getXmlEventWriter(result).close();
} catch (Exception e) {
throw new RuntimeException("Exception while writing to output file", e);
}
@@ -955,8 +955,8 @@ public class StaxEventItemWriterTests {
public void marshal(Object graph, Result result) throws XmlMappingException, IOException {
Assert.isInstanceOf( Result.class, result);
try {
StaxUtils.getXmlEventWriter( result ).add( XMLEventFactory.newInstance().createStartElement(namespacePrefix, namespace, graph.toString()));
StaxUtils.getXmlEventWriter( result ).add( XMLEventFactory.newInstance().createEndElement(namespacePrefix, namespace, graph.toString()));
StaxTestUtils.getXmlEventWriter( result ).add( XMLEventFactory.newInstance().createStartElement(namespacePrefix, namespace, graph.toString()));
StaxTestUtils.getXmlEventWriter( result ).add( XMLEventFactory.newInstance().createEndElement(namespacePrefix, namespace, graph.toString()));
}
catch ( Exception e) {
throw new RuntimeException("Exception while writing to output file", e);

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.batch.item.xml;
import java.lang.reflect.Method;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
/**
* Utility methods for StAX related tests.
*/
public final class StaxTestUtils {
public static XMLEventWriter getXmlEventWriter(Result r) throws Exception {
Method m = r.getClass().getDeclaredMethod("getXMLEventWriter", new Class[]{});
boolean accessible = m.isAccessible();
m.setAccessible(true);
Object result = m.invoke(r);
m.setAccessible(accessible);
return (XMLEventWriter) result;
}
public static XMLEventReader getXmlEventReader(Source s) throws Exception {
Method m = s.getClass().getDeclaredMethod("getXMLEventReader", new Class[]{});
boolean accessible = m.isAccessible();
m.setAccessible(true);
Object result = m.invoke(s);
m.setAccessible(accessible);
return (XMLEventReader) result;
}
}

View File

@@ -238,7 +238,7 @@ public class TransactionalStaxEventItemWriterTests {
@Override
public void marshal(Object graph, Result result) throws XmlMappingException, IOException {
try {
StaxUtils.getXmlEventWriter(result).add(XMLEventFactory.newInstance().createComment(graph.toString()));
StaxTestUtils.getXmlEventWriter(result).add(XMLEventFactory.newInstance().createComment(graph.toString()));
}
catch ( Exception e) {
throw new RuntimeException("Exception while writing to output file", e);