BATCH-1532: introduced StaxUtils to provide indirection on javax.xml.transform.Result/Source creation so that the code cant work in Spring 2.5.x and Spring WS 1.5x as well as Spring 3.0x

This commit is contained in:
Dave Syer
2011-03-21 13:31:41 +00:00
parent 0a77a5b4ed
commit aef9f97f53
10 changed files with 201 additions and 65 deletions

View File

@@ -38,7 +38,6 @@ import org.springframework.core.io.Resource;
import org.springframework.oxm.Unmarshaller;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.xml.transform.StaxSource;
/**
* Item reader for reading XML input based on StAX.
@@ -52,8 +51,7 @@ import org.springframework.xml.transform.StaxSource;
*
* @author Robert Kasanicky
*/
public class StaxEventItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements
ResourceAwareItemReaderItemStream<T>, InitializingBean {
public class StaxEventItemReader<T> extends AbstractItemCountingItemStreamItemReader<T> implements ResourceAwareItemReaderItemStream<T>, InitializingBean {
private static final Log logger = LogFactory.getLog(StaxEventItemReader.class);
@@ -232,7 +230,7 @@ public class StaxEventItemReader<T> extends AbstractItemCountingItemStreamItemRe
fragmentReader.markStartFragment();
@SuppressWarnings("unchecked")
T mappedFragment = (T) unmarshaller.unmarshal(new StaxSource(fragmentReader));
T mappedFragment = (T) unmarshaller.unmarshal(StaxUtils.getSource(fragmentReader));
item = mappedFragment;
fragmentReader.markFragmentProcessed();

View File

@@ -31,6 +31,8 @@ import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -52,7 +54,6 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.xml.transform.StaxResult;
/**
* An implementation of {@link ItemWriter} which uses StAX and
@@ -563,14 +564,15 @@ public class StaxEventItemWriter<T> extends ExecutionContextUserSupport implemen
* @throws IOException
* @throws XmlMappingException
*/
public void write(List<? extends T> items) throws XmlMappingException, IOException {
public void write(List<? extends T> items) throws XmlMappingException, Exception {
currentRecordCount += items.size();
for (Object object : items) {
Assert.state(marshaller.supports(object.getClass()),
"Marshaller must support the class of the marshalled object");
marshaller.marshal(object, new StaxResult(eventWriter));
Result result = StaxUtils.getResult(eventWriter);
marshaller.marshal(object, result );
}
try {
eventWriter.flush();

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2006-2007 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* 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.
* <p/>
* 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.
* <p/>
* As the only class state maintained is to cache java reflection metadata, which is thread safe, this class is thread-safe.
*
* @author Josh Long
*
* @see org.springframework.util.xml.StaxUtils
* @see org.springframework.util.xml.StaxSource
* @see org.springframework.xml.transform.StaxSource
*/
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", new Class[]{ XMLEventReader.class});
// javax.xml.transform.Result
staxUtilsResultMethodOnSpring30 = ClassUtils.getStaticMethod(clzz, "createStaxResult", new Class[]{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 {
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 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) 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 = ClassUtils.getMethodIfAvailable(r.getClass(), "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 = ClassUtils.getMethodIfAvailable(s.getClass(), "getXMLEventReader", new Class[]{});
boolean accessible=m.isAccessible();
m.setAccessible(true);
Object result = m.invoke(s);
m.setAccessible(accessible);
return (XMLEventReader) result;
}
}