diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java index 782191cbf..f5b659fe8 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java @@ -438,6 +438,7 @@ public class StaxEventItemWriter extends AbstractItemStreamItemWriter impl } delegateEventWriter = createXmlEventWriter(outputFactory, bufferedWriter); eventWriter = new NoStartEndDocumentStreamWriter(delegateEventWriter); + initNamespaceContext(delegateEventWriter); if (!restarted) { startDocument(delegateEventWriter); } @@ -491,6 +492,39 @@ public class StaxEventItemWriter extends AbstractItemStreamItemWriter impl protected Result createStaxResult() throws Exception { return StaxUtils.getResult(eventWriter); } + + /** + * Inits the namespace context of the XMLEventWriter: + *
    + *
  • rootTagNamespacePrefix for rootTagName
  • + *
  • any other xmlns namespace prefix declarations in the root element attributes
  • + *
+ * + * @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 entry : getRootElementAttributes().entrySet()) { + String key = entry.getKey(); + if (key.startsWith("xmlns")) { + String prefix = ""; + if (key.contains(":")) { + prefix = key.substring(key.indexOf(":") + 1); + } + System.err.println("registering prefix: " +prefix + "=" + entry.getValue()); + writer.setPrefix(prefix, entry.getValue()); + } + } + } + } /** * Writes simple XML header containing: diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java index 9e505eed6..4194cdc30 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java @@ -12,6 +12,7 @@ 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; @@ -55,8 +57,12 @@ public class StaxEventItemWriterTests { return ClassUtils.getShortName(StaxEventItemWriter.class) + "-testString"; } }; + + private JAXBItem jaxbItem = new JAXBItem(); private List items = Collections.singletonList(item); + + private List jaxbItems = Collections.singletonList(jaxbItem); private static final String TEST_STRING = "<" + ClassUtils.getShortName(StaxEventItemWriter.class) + "-testString/>"; @@ -68,6 +74,8 @@ public class StaxEventItemWriterTests { + "-testString/>"; private SimpleMarshaller marshaller; + + private Jaxb2Marshaller jaxbMarshaller; @Before public void setUp() throws Exception { @@ -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); } /** @@ -198,7 +208,6 @@ public class StaxEventItemWriterTests { // expected } writer.close(); - System.err.println(getOutputFileContent()); String outputFile = getOutputFileContent(); assertEquals("", outputFile); @@ -224,7 +233,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("" + TEST_STRING + "")); assertEquals("", outputFile); @@ -401,6 +409,89 @@ public class StaxEventItemWriterTests { assertTrue("Wrong content: " + content, content.contains((""))); assertTrue("Wrong content: " + content, content.contains(("", 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, + "", 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, + "", content); + } /** * Writes object's toString representation as XML comment. @@ -467,5 +558,9 @@ public class StaxEventItemWriterTests { return source; } + + @XmlRootElement(name="item", namespace="http://www.springframework.org/test") + private static class JAXBItem { + } }