0

I have to process an xml against an xslt with result-document that create many xml. As suggested here: Catch output stream of xsl result-document

I wrote my personal URI Resolver:

public class CustomOutputURIResolver implements OutputURIResolver{

    private File directoryOut;

    public CustomOutputURIResolver(File directoryOut) {
        super();
        this.directoryOut = directoryOut;
    }

    public void close(Result arg0) throws TransformerException {

    }

    public Result resolve(String href, String base) throws TransformerException {
       FileOutputStream fout = null;
       try {
            File f = new File(directoryOut.getAbsolutePath() + File.separator + href + File.separator + href + ".xml");
            f.getParentFile().mkdirs();
            fout = new FileOutputStream(f);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return new StreamResult(fout);
    }

}

that get the output directory and then saves here the files.

But then when I tested it in a junit I had some problems in the clean-up phase, when trying to delete the created files and noticed that the FileOutputStream fout is not well handled. Trying to solve the problem gave me some thoughts:

First I came out with this idea:

public class CustomOutputURIResolver implements OutputURIResolver{

    private File directoryOut;
    private FileOutputStream fout

    public CustomOutputURIResolver(File directoryOut) {
        super();
        this.directoryOut = directoryOut;
        this.fout = null;
    }

    public void close(Result arg0) throws TransformerException {
        try {
            if (null != fout) {
                fout.flush();
                fout.close();
                fout = null;
            }
        } catch (Exception e) {}
    }

    public Result resolve(String href, String base) throws TransformerException {

        try {
            if (null != fout) {
                fout.flush();
                fout.close();
            }
        } catch (Exception e) {}

        fout = null;
        try {
            File f = new File(directoryOut.getAbsolutePath() + File.separator + href + File.separator + href + ".xml");
        f.getParentFile().mkdirs();
            fout = new FileOutputStream(f);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return new StreamResult(fout);
    }

}

So the fileOutputStream is closed anytime another one is opened. But:

1) I don't like this solution very much

2) what if this function is called in a multithread process? (I'm not very skilled about Saxon parsing, so i really don't know..)

3) Is there a chance to create and handle one FileOutputStream for each resolve ?

Community
  • 1
  • 1
Stefano Vercellino
  • 353
  • 1
  • 6
  • 17

1 Answers1

1

The reason close() takes a Result argument is so that you can identify which stream to close. Why not:

public void close(Result arg0) throws TransformerException {
    try {
        if (arg0 instanceof StreamResult) {
            OutputStream os = ((StreamResult)arg0).getOutputStream();
            os.flush();
            os.close();
        }
    } catch (Exception e) {}
}

From Saxon-EE 9.5, xsl:result-document executes in a new thread, so it's very important that the OutputURIResolver should be thread-safe. Because of this change, from 9.5 an OutputURIResolver must implement an additional method getInstance() which makes it easier to manage state: if your newInstance() method actually creates a new instance, then there will be one instance of the OutputURIResolver for each result document being processed, and it can hold the output stream and close it when requested.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • that's right.. for some reason I was thinking that the close function were called only at the end of the entire process and I didn't pay attention to its argument.. now I've arranged the code and it worked nice, Thanks! – Stefano Vercellino Jan 09 '14 at 09:27