From d0f94cb0eec47c8f36d351dc9d0e6db305049c60 Mon Sep 17 00:00:00 2001 From: dsyer Date: Tue, 5 Jan 2010 09:40:08 +0000 Subject: [PATCH] RESOLVED - issue BATCH-1377: StaxEventItemWriter: Handle namespace for the root tag --- .../batch/item/xml/StaxEventItemReader.java | 81 ++++++++++--------- .../item/xml/StaxEventItemReaderTests.java | 33 ++++++++ 2 files changed, 77 insertions(+), 37 deletions(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemReader.java index 23cef8cdd..6e7c1b618 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemReader.java @@ -56,7 +56,7 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe ResourceAwareItemReaderItemStream, InitializingBean { private static final Log logger = LogFactory.getLog(StaxEventItemReader.class); - + private FragmentEventReader fragmentReader; private XMLEventReader eventReader; @@ -72,7 +72,9 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe private boolean noInput; private boolean strict = true; - + + private String fragmentRootElementNameSpace; + public StaxEventItemReader() { setName(ClassUtils.getShortName(StaxEventItemReader.class)); } @@ -92,8 +94,8 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe } /** - * @param unmarshaller maps xml fragments corresponding to - * records to objects + * @param unmarshaller maps xml fragments corresponding to records to + * objects */ public void setUnmarshaller(Unmarshaller unmarshaller) { this.unmarshaller = unmarshaller; @@ -118,6 +120,10 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe public void afterPropertiesSet() throws Exception { Assert.notNull(unmarshaller, "The Unmarshaller must not be null."); Assert.hasLength(fragmentRootElementName, "The FragmentRootElementName must not be null"); + if (fragmentRootElementName.contains("{")) { + fragmentRootElementNameSpace = fragmentRootElementName.replaceAll("\\{(.*)\\}.*", "$1"); + fragmentRootElementName = fragmentRootElementName.replaceAll("\\{.*\\}(.*)", "$1"); + } } /** @@ -128,8 +134,8 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe * does not care about element nesting. You will need to override this * method to correctly handle composite fragments. * - * @return true if next fragment was found, - * false otherwise. + * @return true if next fragment was found, false + * otherwise. */ protected boolean moveCursorToNextFragment(XMLEventReader reader) { try { @@ -142,11 +148,12 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe } QName startElementName = ((StartElement) reader.peek()).getName(); if (startElementName.getLocalPart().equals(fragmentRootElementName)) { - return true; - } - else { - reader.nextEvent(); + if (fragmentRootElementNameSpace==null || startElementName.getNamespaceURI().equals(fragmentRootElementNameSpace)) { + return true; + } } + reader.nextEvent(); + } } catch (XMLStreamException e) { @@ -172,7 +179,7 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe protected void doOpen() throws Exception { Assert.notNull(resource, "The Resource must not be null."); - + noInput = false; if (!resource.exists()) { if (strict) { @@ -201,30 +208,30 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe * Move to next fragment and map it to item. */ protected T doRead() throws Exception { - + if (noInput) { return null; } - + T item = null; if (moveCursorToNextFragment(fragmentReader)) { fragmentReader.markStartFragment(); - + @SuppressWarnings("unchecked") T mappedFragment = (T) unmarshaller.unmarshal(new StaxSource(fragmentReader)); - + item = mappedFragment; fragmentReader.markFragmentProcessed(); } return item; } - + /* - * jumpToItem is overridden because reading in and attempting to bind an entire fragment - * is unacceptable in a restart scenario, and may cause exceptions to be thrown that - * were already skipped in previous runs. + * jumpToItem is overridden because reading in and attempting to bind an + * entire fragment is unacceptable in a restart scenario, and may cause + * exceptions to be thrown that were already skipped in previous runs. */ @Override protected void jumpToItem(int itemIndex) throws Exception { @@ -233,34 +240,34 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe readToEndFragment(); } } - + /* - * Read until the first StartElement tag that matches the provided - * fragmentRootElementName. Because there may be any number of tags in between where the reader - * is now and the fragment start, this is done in a loop until the element type and name - * match. + * Read until the first StartElement tag that matches the provided + * fragmentRootElementName. Because there may be any number of tags in + * between where the reader is now and the fragment start, this is done in a + * loop until the element type and name match. */ - private void readToStartFragment() throws XMLStreamException{ - while(true){ + private void readToStartFragment() throws XMLStreamException { + while (true) { XMLEvent nextEvent = eventReader.nextEvent(); - if( nextEvent.isStartElement() && - ((StartElement)nextEvent).getName().getLocalPart().equals(fragmentRootElementName)){ + if (nextEvent.isStartElement() + && ((StartElement) nextEvent).getName().getLocalPart().equals(fragmentRootElementName)) { return; } } } - + /* - * Read until the first EndElement tag that matches the provided - * fragmentRootElementName. Because there may be any number of tags in between where the reader - * is now and the fragment end tag, this is done in a loop until the element type and name - * match + * Read until the first EndElement tag that matches the provided + * fragmentRootElementName. Because there may be any number of tags in + * between where the reader is now and the fragment end tag, this is done in + * a loop until the element type and name match */ - private void readToEndFragment() throws XMLStreamException{ - while(true){ + private void readToEndFragment() throws XMLStreamException { + while (true) { XMLEvent nextEvent = eventReader.nextEvent(); - if( nextEvent.isEndElement() && - ((EndElement)nextEvent).getName().getLocalPart().equals(fragmentRootElementName)){ + if (nextEvent.isEndElement() + && ((EndElement) nextEvent).getName().getLocalPart().equals(fragmentRootElementName)) { return; } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemReaderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemReaderTests.java index 0a69df2ce..1350e5984 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemReaderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemReaderTests.java @@ -41,6 +41,10 @@ public class StaxEventItemReaderTests { // test xml input private String xml = " testString "; + private String fooXml = " testString "; + + private String mixedXml = " testString "; + private Unmarshaller unmarshaller = new MockFragmentUnmarshaller(); private static final String FRAGMENT_ROOT_ELEMENT = "fragment"; @@ -98,6 +102,35 @@ public class StaxEventItemReaderTests { source.close(); } + @Test + public void testFragmentNamespace() throws Exception { + + source.setResource(new ByteArrayResource(fooXml.getBytes())); + source.afterPropertiesSet(); + source.open(executionContext); + // see asserts in the mock unmarshaller + assertNotNull(source.read()); + assertNotNull(source.read()); + assertNull(source.read()); // there are only two fragments + + source.close(); + } + + @Test + public void testFragmentMixedNamespace() throws Exception { + + source.setResource(new ByteArrayResource(mixedXml.getBytes())); + source.setFragmentRootElementName("{urn:org.test.bar}"+FRAGMENT_ROOT_ELEMENT); + source.afterPropertiesSet(); + source.open(executionContext); + // see asserts in the mock unmarshaller + assertNotNull(source.read()); + assertNotNull(source.read()); + assertNull(source.read()); // there are only two fragments + + source.close(); + } + /** * Cursor is moved before beginning of next fragment. */