Search code examples
javaxmlxsdunmarshalling

Unmarshal XML from XSD failed with JAR Execution with "java.io.IOException: Buffer already closed"


I have a java code that extract an Xml section from a PDF (Apache PDFBox) and unmarshall it to a java Class.

On local execution with IntelliJ, all is OK, but when I deploy jar on an openshift cluster, unmarshal is ko with "java.io.IOException: Buffer already closed"

From my PDF, I extract a List

Then, I create an unmarshal like this :

try {
      JAXBContext context = JAXBContext.newInstance(typeClasse.getClass());
      Unmarshaller unmarshaller = context.createUnmarshaller();
      SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
      String xsdFilePath = getXSD();
      Schema schema = factory.newSchema(this.getClass().getResource("/" + xsdFilePath));
      unmarshaller.setSchema(schema);
      unmarshaller.setEventHandler(event -> false);
      return unmarshaller;
    } catch (JAXBException | SAXException e) {
      throw new UnmarshallException(e);
    }

typeClasse is my output unmarshall class, getXSD() give me the path on my root XSD definition, all my XSD are on the same directory "xsd" on resource java folder and the one return by getXSD() include all the other. Path is like "xsd/myparentXSD.xsd"

Then, with my List (myList) I do this code that return a List where T is the typeClasse.

myList.stream()
        .map(myObject -> unmarshallFichier(myObject, unmarshaller))
        .collect(Collectors.toList());

unmarshaller is the one detail below and unmarshallFichier is :

try {
  StreamSource stream = new StreamSource(myObject);
  JAXBElement<?> jaxbElement = unmarshaller.unmarshal(stream, typeClasse.getClass());
  return (T) jaxbElement.getValue();
} catch (JAXBException e) {
  throw new UnmarshallException(e);
}

But on openshift, this code is wrong and produce java.io.IOException: Buffer already closed (but not inc local)

What is wrong whith this ?


Solution

  • StreamSource stream = new StreamSource(myObject);
    

    What is myObject in this instance? Is it a InputStream or a Reader?

    Judging by the exception I get the feeling you are trying to read from a Reader or InputStream more than once.

    Please note that you can only read from an InputStream / Reader once.

    Perhaps you want to do something like the following so you create a new InputStream each time you iterate the list?

    public interface InputStreamSource {
       InputStream get() throws IOException;
    }
    
    public class MyClass {
       // myList is a long-lived object and can be iterated multiple times
       // since we create a new InputStream each time we call InputStreamSource.get() 
       private final List<InputStreamSource> myList = List.of(
          () -> new FileInputStream("path/to/file1.xml"),
          () -> new FileInputStream("path/to/file2.xml")
       );
    
       public List<Object> doIt() {
          return myList.stream()
             .map(myObject -> unmarshallFichier(myObject, unmarshaller))
             .collect(Collectors.toList());
       }
    
       private Object unmarshallFichier(InputStreamSource streamSource, Unmarshaller unmarshaller) {
          try (InputStream in = streamSource.get()) {
             StreamSource stream = new StreamSource(in);
             JAXBElement<?> jaxbElement = unmarshaller.unmarshal(stream, typeClasse.getClass());
             return (T) jaxbElement.getValue();
          } catch (JAXBException e) {
             throw new UnmarshallException(e);
          }
       }
    }