Merge pull request 133 from jpraet/BATCH-1975
* BATCH-1975: BATCH-1975: Minor cleanup BATCH-1975: StaxEventItemWriter namespace added to elements after restart
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2006-2012 the original author or authors.
|
||||
* Copyright 2006-2013 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.
|
||||
@@ -70,7 +70,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
*/
|
||||
public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> implements
|
||||
ResourceAwareItemWriterItemStream<T>, InitializingBean {
|
||||
ResourceAwareItemWriterItemStream<T>, InitializingBean {
|
||||
|
||||
private static final Log log = LogFactory.getLog(StaxEventItemWriter.class);
|
||||
|
||||
@@ -151,7 +151,7 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
*
|
||||
* @param resource the output file
|
||||
*/
|
||||
@Override
|
||||
@Override
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
@@ -323,7 +323,7 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
* @throws Exception
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(marshaller);
|
||||
if (rootTagName.contains("{")) {
|
||||
@@ -341,10 +341,10 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
*
|
||||
* @see org.springframework.batch.item.ItemStream#open(ExecutionContext)
|
||||
*/
|
||||
@Override
|
||||
@Override
|
||||
public void open(ExecutionContext executionContext) {
|
||||
super.open(executionContext);
|
||||
|
||||
super.open(executionContext);
|
||||
|
||||
Assert.notNull(resource, "The resource must be set");
|
||||
|
||||
long startAtPosition = 0;
|
||||
@@ -375,6 +375,7 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
/**
|
||||
* Helper method for opening output source at given file position
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
private void open(long position, boolean restarted) {
|
||||
|
||||
File file;
|
||||
@@ -415,12 +416,12 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
final FileChannel channel = fileChannel;
|
||||
if (transactional) {
|
||||
TransactionAwareBufferedWriter writer = new TransactionAwareBufferedWriter(channel, new Runnable() {
|
||||
@Override
|
||||
@Override
|
||||
public void run() {
|
||||
closeStream();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
writer.setEncoding(encoding);
|
||||
bufferedWriter = writer;
|
||||
}
|
||||
@@ -438,6 +439,7 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
}
|
||||
delegateEventWriter = createXmlEventWriter(outputFactory, bufferedWriter);
|
||||
eventWriter = new NoStartEndDocumentStreamWriter(delegateEventWriter);
|
||||
initNamespaceContext(delegateEventWriter);
|
||||
if (!restarted) {
|
||||
startDocument(delegateEventWriter);
|
||||
}
|
||||
@@ -492,6 +494,39 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
return StaxUtils.getResult(eventWriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inits the namespace context of the XMLEventWriter:
|
||||
* <ul>
|
||||
* <li>rootTagNamespacePrefix for rootTagName</li>
|
||||
* <li>any other xmlns namespace prefix declarations in the root element attributes</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param writer XML event writer
|
||||
* @throws XMLStreamException
|
||||
*/
|
||||
protected void initNamespaceContext(XMLEventWriter writer) throws XMLStreamException {
|
||||
if (StringUtils.hasText(getRootTagNamespace())) {
|
||||
if(StringUtils.hasText(getRootTagNamespacePrefix())) {
|
||||
writer.setPrefix(getRootTagNamespacePrefix(), getRootTagNamespace());
|
||||
} else {
|
||||
writer.setDefaultNamespace(getRootTagNamespace());
|
||||
}
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(getRootElementAttributes())) {
|
||||
for (Map.Entry<String, String> entry : getRootElementAttributes().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (key.startsWith("xmlns")) {
|
||||
String prefix = "";
|
||||
if (key.contains(":")) {
|
||||
prefix = key.substring(key.indexOf(":") + 1);
|
||||
}
|
||||
log.debug("registering prefix: " +prefix + "=" + entry.getValue());
|
||||
writer.setPrefix(prefix, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes simple XML header containing:
|
||||
* <ul>
|
||||
@@ -575,10 +610,10 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
*
|
||||
* @see org.springframework.batch.item.ItemStream#close()
|
||||
*/
|
||||
@Override
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
|
||||
super.close();
|
||||
|
||||
XMLEventFactory factory = createXmlEventFactory();
|
||||
try {
|
||||
delegateEventWriter.add(factory.createCharacters(""));
|
||||
@@ -640,7 +675,7 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
* @throws IOException
|
||||
* @throws XmlMappingException
|
||||
*/
|
||||
@Override
|
||||
@Override
|
||||
public void write(List<? extends T> items) throws XmlMappingException, Exception {
|
||||
|
||||
currentRecordCount += items.size();
|
||||
@@ -665,9 +700,9 @@ public class StaxEventItemWriter<T> extends AbstractItemStreamItemWriter<T> impl
|
||||
*
|
||||
* @see org.springframework.batch.item.ItemStream#update(ExecutionContext)
|
||||
*/
|
||||
@Override
|
||||
@Override
|
||||
public void update(ExecutionContext executionContext) {
|
||||
super.update(executionContext);
|
||||
super.update(executionContext);
|
||||
if (saveState) {
|
||||
Assert.notNull(executionContext, "ExecutionContext must not be null");
|
||||
executionContext.putLong(getExecutionContextKey(RESTART_DATA_NAME), getPosition());
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package org.springframework.batch.item.xml;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.stream.XMLEventFactory;
|
||||
import javax.xml.stream.XMLEventWriter;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
@@ -27,6 +28,7 @@ import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.oxm.Marshaller;
|
||||
import org.springframework.oxm.XmlMappingException;
|
||||
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
@@ -50,14 +52,18 @@ public class StaxEventItemWriterTests {
|
||||
|
||||
// test item for writing to output
|
||||
private Object item = new Object() {
|
||||
@Override
|
||||
@Override
|
||||
public String toString() {
|
||||
return ClassUtils.getShortName(StaxEventItemWriter.class) + "-testString";
|
||||
}
|
||||
};
|
||||
|
||||
private JAXBItem jaxbItem = new JAXBItem();
|
||||
|
||||
private List<? extends Object> items = Collections.singletonList(item);
|
||||
|
||||
private List<? extends Object> jaxbItems = Collections.singletonList(jaxbItem);
|
||||
|
||||
private static final String TEST_STRING = "<" + ClassUtils.getShortName(StaxEventItemWriter.class)
|
||||
+ "-testString/>";
|
||||
|
||||
@@ -69,6 +75,8 @@ public class StaxEventItemWriterTests {
|
||||
|
||||
private SimpleMarshaller marshaller;
|
||||
|
||||
private Jaxb2Marshaller jaxbMarshaller;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
File directory = new File("target/data");
|
||||
@@ -76,6 +84,8 @@ public class StaxEventItemWriterTests {
|
||||
resource = new FileSystemResource(File.createTempFile("StaxEventWriterOutputSourceTests", ".xml", directory));
|
||||
writer = createItemWriter();
|
||||
executionContext = new ExecutionContext();
|
||||
jaxbMarshaller = new Jaxb2Marshaller();
|
||||
jaxbMarshaller.setClassesToBeBound(JAXBItem.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,13 +136,14 @@ public class StaxEventItemWriterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void testTransactionalRestart() throws Exception {
|
||||
writer.open(executionContext);
|
||||
|
||||
PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
|
||||
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
|
||||
@Override
|
||||
@Override
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
try {
|
||||
// write item
|
||||
@@ -152,7 +163,7 @@ public class StaxEventItemWriterTests {
|
||||
writer = createItemWriter();
|
||||
writer.open(executionContext);
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
|
||||
@Override
|
||||
@Override
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
try {
|
||||
writer.write(items);
|
||||
@@ -175,6 +186,7 @@ public class StaxEventItemWriterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public void testTransactionalRestartFailOnFirstWrite() throws Exception {
|
||||
|
||||
PlatformTransactionManager transactionManager = new ResourcelessTransactionManager();
|
||||
@@ -182,7 +194,7 @@ public class StaxEventItemWriterTests {
|
||||
writer.open(executionContext);
|
||||
try {
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
|
||||
@Override
|
||||
@Override
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
try {
|
||||
writer.write(items);
|
||||
@@ -198,14 +210,13 @@ public class StaxEventItemWriterTests {
|
||||
// expected
|
||||
}
|
||||
writer.close();
|
||||
System.err.println(getOutputFileContent());
|
||||
String outputFile = getOutputFileContent();
|
||||
assertEquals("<root></root>", outputFile);
|
||||
|
||||
// create new writer from saved restart data and continue writing
|
||||
writer = createItemWriter();
|
||||
new TransactionTemplate(transactionManager).execute(new TransactionCallback() {
|
||||
@Override
|
||||
@Override
|
||||
public Object doInTransaction(TransactionStatus status) {
|
||||
writer.open(executionContext);
|
||||
try {
|
||||
@@ -224,7 +235,6 @@ public class StaxEventItemWriterTests {
|
||||
// check the output is concatenation of 'before restart' and 'after
|
||||
// restart' writes.
|
||||
outputFile = getOutputFileContent();
|
||||
System.err.println(getOutputFileContent());
|
||||
assertEquals(1, StringUtils.countOccurrencesOf(outputFile, TEST_STRING));
|
||||
assertTrue(outputFile.contains("<root>" + TEST_STRING + "</root>"));
|
||||
assertEquals("<root><StaxEventItemWriter-testString/></root>", outputFile);
|
||||
@@ -238,7 +248,7 @@ public class StaxEventItemWriterTests {
|
||||
|
||||
writer.setHeaderCallback(new StaxWriterCallback() {
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void write(XMLEventWriter writer) throws IOException {
|
||||
XMLEventFactory factory = XMLEventFactory.newInstance();
|
||||
try {
|
||||
@@ -284,7 +294,7 @@ public class StaxEventItemWriterTests {
|
||||
public void testOpenAndClose() throws Exception {
|
||||
writer.setHeaderCallback(new StaxWriterCallback() {
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void write(XMLEventWriter writer) throws IOException {
|
||||
XMLEventFactory factory = XMLEventFactory.newInstance();
|
||||
try {
|
||||
@@ -300,7 +310,7 @@ public class StaxEventItemWriterTests {
|
||||
});
|
||||
writer.setFooterCallback(new StaxWriterCallback() {
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void write(XMLEventWriter writer) throws IOException {
|
||||
XMLEventFactory factory = XMLEventFactory.newInstance();
|
||||
try {
|
||||
@@ -402,6 +412,89 @@ public class StaxEventItemWriterTests {
|
||||
assertTrue("Wrong content: " + content, content.contains(("<ns:root")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace prefixes are properly initialized on restart.
|
||||
*/
|
||||
@Test
|
||||
public void testRootTagWithNamespaceRestart() throws Exception {
|
||||
writer.setMarshaller(jaxbMarshaller);
|
||||
writer.setRootTagName("{http://www.springframework.org/test}root");
|
||||
writer.afterPropertiesSet();
|
||||
writer.open(executionContext);
|
||||
writer.write(jaxbItems);
|
||||
writer.update(executionContext);
|
||||
writer.close();
|
||||
|
||||
writer = createItemWriter();
|
||||
writer.setMarshaller(jaxbMarshaller);
|
||||
writer.setRootTagName("{http://www.springframework.org/test}root");
|
||||
writer.afterPropertiesSet();
|
||||
writer.open(executionContext);
|
||||
writer.write(jaxbItems);
|
||||
writer.update(executionContext);
|
||||
writer.close();
|
||||
|
||||
String content = getOutputFileContent();
|
||||
assertEquals("Wrong content: " + content,
|
||||
"<root xmlns=\"http://www.springframework.org/test\"><item/><item/></root>", content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace prefixes are properly initialized on restart.
|
||||
*/
|
||||
@Test
|
||||
public void testRootTagWithNamespaceAndPrefixRestart() throws Exception {
|
||||
writer.setMarshaller(jaxbMarshaller);
|
||||
writer.setRootTagName("{http://www.springframework.org/test}ns:root");
|
||||
writer.afterPropertiesSet();
|
||||
writer.open(executionContext);
|
||||
writer.write(jaxbItems);
|
||||
writer.update(executionContext);
|
||||
writer.close();
|
||||
|
||||
writer = createItemWriter();
|
||||
writer.setMarshaller(jaxbMarshaller);
|
||||
writer.setRootTagName("{http://www.springframework.org/test}ns:root");
|
||||
writer.afterPropertiesSet();
|
||||
writer.open(executionContext);
|
||||
writer.write(jaxbItems);
|
||||
writer.update(executionContext);
|
||||
writer.close();
|
||||
|
||||
String content = getOutputFileContent();
|
||||
assertEquals("Wrong content: " + content,
|
||||
"<ns:root xmlns:ns=\"http://www.springframework.org/test\"><ns:item/><ns:item/></ns:root>", content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace prefixes are properly initialized on restart.
|
||||
*/
|
||||
@Test
|
||||
public void testRootTagWithAdditionalNamespaceRestart() throws Exception {
|
||||
writer.setMarshaller(jaxbMarshaller);
|
||||
writer.setRootTagName("{urn:org.test.foo}foo:root");
|
||||
writer.setRootElementAttributes(Collections.singletonMap("xmlns:ns", "http://www.springframework.org/test"));
|
||||
writer.afterPropertiesSet();
|
||||
writer.open(executionContext);
|
||||
writer.write(jaxbItems);
|
||||
writer.update(executionContext);
|
||||
writer.close();
|
||||
|
||||
writer = createItemWriter();
|
||||
writer.setMarshaller(jaxbMarshaller);
|
||||
writer.setRootTagName("{urn:org.test.foo}foo:root");
|
||||
writer.setRootElementAttributes(Collections.singletonMap("xmlns:ns", "http://www.springframework.org/test"));
|
||||
writer.afterPropertiesSet();
|
||||
writer.open(executionContext);
|
||||
writer.write(jaxbItems);
|
||||
writer.update(executionContext);
|
||||
writer.close();
|
||||
|
||||
String content = getOutputFileContent();
|
||||
assertEquals("Wrong content: " + content,
|
||||
"<foo:root xmlns:foo=\"urn:org.test.foo\" xmlns:ns=\"http://www.springframework.org/test\"><ns:item/><ns:item/></foo:root>", content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes object's toString representation as XML comment.
|
||||
*/
|
||||
@@ -419,9 +512,9 @@ public class StaxEventItemWriterTests {
|
||||
this.namespacePrefix = namespacePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void marshal(Object graph, Result result) throws XmlMappingException, IOException {
|
||||
Assert.isInstanceOf( Result.class, result);
|
||||
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()));
|
||||
@@ -431,7 +524,7 @@ public class StaxEventItemWriterTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public boolean supports(Class clazz) {
|
||||
return true;
|
||||
@@ -468,4 +561,8 @@ public class StaxEventItemWriterTests {
|
||||
return source;
|
||||
}
|
||||
|
||||
@XmlRootElement(name="item", namespace="http://www.springframework.org/test")
|
||||
private static class JAXBItem {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user