From 21bc4d582bfe9f3e828a31a6988b21c1762dae87 Mon Sep 17 00:00:00 2001 From: Mahmoud Ben Hassine Date: Thu, 27 Apr 2023 20:52:37 +0200 Subject: [PATCH] Allow StaxEventItemReader to auto-detect the input file encoding Before this commit, it was not possible to pass a null encoding to the StaxEventItemReader, which prevents the XML event reader to auto-detect the file encoding. This commits makes the encoding setter more lenient by accepting a null value. Resolves #4101 --- .../batch/item/xml/StaxEventItemReader.java | 11 +++--- .../builder/StaxEventItemReaderBuilder.java | 8 ++-- .../item/xml/StaxEventItemReaderTests.java | 39 ++++++++++++++++++- 3 files changed, 49 insertions(+), 9 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 17bd9f151..827c1fa2e 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 @@ -1,5 +1,5 @@ /* - * Copyright 2006-2022 the original author or authors. + * Copyright 2006-2023 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. @@ -142,10 +142,10 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe /** * Set encoding to be used for the input file. Defaults to {@link #DEFAULT_ENCODING}. - * @param encoding the encoding to be used + * @param encoding the encoding to be used. Can be {@code null}, in which case, the + * XML event reader will attempt to auto-detect the encoding from tht input file. */ - public void setEncoding(String encoding) { - Assert.notNull(encoding, "The encoding must not be null"); + public void setEncoding(@Nullable String encoding) { this.encoding = encoding; } @@ -239,7 +239,8 @@ public class StaxEventItemReader extends AbstractItemCountingItemStreamItemRe } inputStream = resource.getInputStream(); - eventReader = xmlInputFactory.createXMLEventReader(inputStream, this.encoding); + eventReader = this.encoding != null ? xmlInputFactory.createXMLEventReader(inputStream, this.encoding) + : xmlInputFactory.createXMLEventReader(inputStream); fragmentReader = new DefaultFragmentEventReader(eventReader); noInput = false; diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/builder/StaxEventItemReaderBuilder.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/builder/StaxEventItemReaderBuilder.java index ea508a3ec..71682bfff 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/builder/StaxEventItemReaderBuilder.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/builder/StaxEventItemReaderBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2020 the original author or authors. + * Copyright 2017-2023 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. @@ -26,6 +26,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.batch.item.xml.StaxEventItemReader; import org.springframework.core.io.Resource; +import org.springframework.lang.Nullable; import org.springframework.oxm.Unmarshaller; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -194,12 +195,13 @@ public class StaxEventItemReaderBuilder { /** * Encoding for the input file. Defaults to - * {@link StaxEventItemReader#DEFAULT_ENCODING}. + * {@link StaxEventItemReader#DEFAULT_ENCODING}. Can be {@code null}, in which case + * the XML event reader will attempt to auto-detect the encoding from tht input file. * @param encoding String encoding algorithm * @return the current instance of the builder * @see StaxEventItemReader#setEncoding(String) */ - public StaxEventItemReaderBuilder encoding(String encoding) { + public StaxEventItemReaderBuilder encoding(@Nullable String encoding) { this.encoding = encoding; return this; 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 c46b9321e..570e880a9 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 @@ -1,5 +1,5 @@ /* - * Copyright 2008-2022 the original author or authors. + * Copyright 2008-2023 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. @@ -19,6 +19,7 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemCountAware; import org.springframework.batch.item.ItemStreamException; @@ -36,8 +37,10 @@ import org.springframework.util.xml.StaxUtils; import javax.xml.namespace.QName; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartDocument; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import javax.xml.transform.Source; @@ -58,6 +61,9 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * Tests for {@link StaxEventItemReader}. @@ -159,6 +165,37 @@ class StaxEventItemReaderTests { source.close(); } + @Test + void testNullEncoding() throws Exception { + // given + XMLEventReader eventReader = mock(XMLEventReader.class); + when(eventReader.peek()).thenReturn(mock(StartDocument.class)); + + Resource resource = mock(Resource.class); + InputStream inputStream = mock(InputStream.class); + when(resource.getInputStream()).thenReturn(inputStream); + when(resource.isReadable()).thenReturn(true); + when(resource.exists()).thenReturn(true); + XMLInputFactory xmlInputFactory = mock(XMLInputFactory.class); + when(xmlInputFactory.createXMLEventReader(inputStream)).thenReturn(eventReader); + + StaxEventItemReader reader = new StaxEventItemReader<>(); + reader.setUnmarshaller(new MockFragmentUnmarshaller()); + reader.setFragmentRootElementName(FRAGMENT_ROOT_ELEMENT); + reader.setResource(resource); + reader.setEncoding(null); + reader.setStrict(false); + reader.setXmlInputFactory(xmlInputFactory); + reader.afterPropertiesSet(); + + // when + reader.open(new ExecutionContext()); + + // then + verify(xmlInputFactory).createXMLEventReader(inputStream); + reader.close(); + } + @Test void testItemCountAwareFragment() throws Exception { StaxEventItemReader source = createNewItemCountAwareInputSource();