Pergunta

i have a webservice which get an inputstream from a xml- file. Now, i want validate AND read it with the same inputstream. I use mark and reset for it.

On Glassfish and Websphere it works fine. But when i run my integration tests with openEJB, the stream will be closed after validation. I can reproduce it in a simple example.

How can i realize it better? The Validator Implementation is always the same. But every environment use another implementation of inputstream.

public class XMLReader {
public static void main(String[] args) {
    try {
        XMLReader reader = new XMLReader();
        InputStream stream = new BufferedInputStream(new FileInputStream(
                new File("myXML.xml")));
        reader.read(stream);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void read(InputStream xmlInputStream) throws SAXException,
        IOException {
    if (xmlInputStream.markSupported()) {
        xmlInputStream.mark(0);
        validateXML(xmlInputStream);
        xmlInputStream.reset();
        readXML(xmlInputStream);
    }
}

private void readXML(InputStream xmlInputStream) {
    // READ xmInputStream with STAX, JAXB, etc. whatever

}

private void validateXML(InputStream xmlInputStream) throws SAXException,
        IOException {
    Source schemaFile = new StreamSource(new File("myXSD.xsd"));
    Source xmlFile = new StreamSource(xmlInputStream);
    SchemaFactory schemaFactory = SchemaFactory
            .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    Schema schema = schemaFactory.newSchema(schemaFile);
    Validator validator = schema.newValidator();
    try {
        validator.validate(xmlFile);
        System.out.println("is valid");
    } catch (SAXException e) {
        System.out.println("is NOT valid");
        System.out.println("Reason: " + e.getLocalizedMessage());
    }
}
}

Exception:

  java.io.IOException: Stream closed
at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:145)
at java.io.BufferedInputStream.reset(BufferedInputStream.java:414)
at xmltest.XMLReader.read(XMLReader.java:36)
at xmltest.XMLReader.main(XMLReader.java:27)
Foi útil?

Solução

Unfortunately, the XML parser closes the stream upon finishing reading. I don't understand why it does that really and I wouldn't recommend anyone to closing streams that they don't own. There might be a reason that I couldn't grasp that quickly.

Anyways, what you could do is having a BufferedInputStream, that does not close the wrapped InputSteam:

public static void main(String[] args) {
    try (FileInputStream in = new FileInputStream(new File("myXML.xml"))){
        XMLReader reader = new XMLReader();
        InputStream stream = new BufferedInputStream(in) {
            @Override
            public void close() throws IOException {
               // don't close
            }
        };
        reader.read(stream);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Outras dicas

It makes me think of something like an overriden SAX implementation which close your inputstream at the end of the validation.

Does everything work fine if you comment the validateXML line:

if (xmlInputStream.markSupported()) {
    xmlInputStream.mark(0);
    // validateXML(xmlInputStream);
    xmlInputStream.reset();
    readXML(xmlInputStream);
}

If yes, do you have a xerces jar somewhere in openEJB classpath which wasn't in Glassfish?

I am pretty sure i had a problem like this in the past, but can't remember the exact issue.

One small modification to Marcel Steinbach's solution: the BufferedInputStream will maintain a buffer, which is unnecessary simply to prevent closing of the input stream. Instead, you can create an anonymous InputStream class that simply delegates the read method to the markable input stream that contains the XML. Since the default implementation of the close method in InputStream is to do nothing, the stream will remain open after validation:

validator.validate(new StreamSource(new InputStream() {
    @Override
    public int read() throws IOException {
        return xmlInputStream.read();
    }
}));

If you are not certain that your input stream is mark supported, you can use the following to efficiently copy it to a buffer from which the XML may be read after validation:

final StringWriter sw = new StringWriter(is.available());
validator.validate(new StreamSource(new InputStream() {
    @Override public int read() throws IOException {
        int ch = is.read();
        sw.write(ch);
        return ch;
    }
}));
readXML(new ByteArrayInputStream(sw.toString().getBytes()));
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top