title: JAXP saxon-he : XMLfile StreamSource doesn't release file access after parsing error
body: <p>I'm using JAXP specification API combined with Saxon-HE API, the main purpose was to develop an application which transform XML files using configurable XSLT stylesheets, able to override generated output documents.. I skip details because I created an example project to illustrate the encountered issue:</p>
  
  <p><strong>Use case:</strong> in case of transformation errors, moving xml file to another directory (could be error directory) raises access exception.</p>
  
  <p>When I instantiate the StreamSource based on a File instance (pointing to the XML file), if certain parsing error occurs, moving the file raises "The process cannot access the file because it is being used by another process." Exception.</p>
  
  <p>Here is a main-single-class app I wrote to illustrate the issue : </p>
  
  <pre><code>package com.sample.xslt.application;
  
  import net.sf.saxon.Configuration;
  import net.sf.saxon.lib.FeatureKeys;
  
  import java.io.File;
  import java.io.IOException;
  import java.nio.file.Files;
  import java.nio.file.Path;
  import java.nio.file.Paths;
  import java.nio.file.StandardCopyOption;
  
  import javax.xml.transform.Source;
  import javax.xml.transform.Transformer;
  import javax.xml.transform.TransformerException;
  import javax.xml.transform.TransformerFactory;
  import javax.xml.transform.dom.DOMResult;
  import javax.xml.transform.stream.StreamSource;
  
  public class XsltApplicationSample {
  
    public static void main(String[] args) throws Exception {
  
      if (args.length != 2) {
        throw new RuntimeException("Two arguments are expected : &lt;xslFilePath&gt; &lt;inputFilePath&gt;");
      }
      String xslFilePath = args[0];
      String xmlFilePath = args[1];
  
      TransformerFactory factory = TransformerFactory.newInstance();
      factory.setAttribute(FeatureKeys.ALLOW_MULTITHREADING, Boolean.TRUE);
      factory.setAttribute(FeatureKeys.RECOVERY_POLICY,
          new Integer(Configuration.RECOVER_WITH_WARNINGS));
  
      Source xslSource = new StreamSource(new File(xslFilePath));
      Source xmlSource = new StreamSource(new File(xmlFilePath));
      Transformer transformer = factory.newTransformer(xslSource);
  
      try {
        transformer.transform(xmlSource, new DOMResult());
  
      } catch (TransformerException e) {
        System.out.println(e.getMessage());
      }
  
      // move input file to tmp directory (for example, could be configured error dir)
  
      File srcFile = Paths.get(xmlFilePath).toFile();
      File tempDir = new File(System.getProperty("java.io.tmpdir"));
  
      Path destFilePath = new File(tempDir, srcFile.getName()).toPath();
  
      try {
        Files.move(srcFile.toPath(), destFilePath, StandardCopyOption.REPLACE_EXISTING);
      } catch (SecurityException | IOException e) {
        System.out.println(e.getMessage());
      }
    }
  }
  </code></pre>
  
  <p>Teh configured xslt transformation file content must be valid to reproduce.
  If the input xml file is empty, it will create a transformation/parsing error, but the access file error won't occur.</p>
  
  <p>Example of input file to reproduce : </p>
  
  <pre><code>&lt;root&gt;
      &lt;elem&gt;
  &lt;/root&gt;
  </code></pre>
  
  <p>Example of STDOUT:</p>
  
  <pre><code>JAXP: find factoryId =javax.xml.transform.TransformerFactory
  JAXP: find factoryId =javax.xml.parsers.SAXParserFactory
  JAXP: loaded from fallback value: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
  JAXP: created new instance of class com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl using ClassLoader: null
  JAXP: find factoryId =javax.xml.parsers.SAXParserFactory
  JAXP: loaded from fallback value: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
  JAXP: created new instance of class com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl using ClassLoader: null
  JAXP: find factoryId =javax.xml.parsers.DocumentBuilderFactory
  JAXP: loaded from fallback value: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
  JAXP: created new instance of class com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl using ClassLoader: null
  JAXP: find factoryId =javax.xml.parsers.SAXParserFactory
  JAXP: loaded from fallback value: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
  JAXP: created new instance of class com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl using ClassLoader: null
  Error on line 3 column 3 of input_err.xml:
    SXXP0003: Error reported by XML parser: The element type "elem" must be terminated by the
    matching end-tag "&lt;/elem&gt;".
  org.xml.sax.SAXParseException; systemId: file:/C:/&lt;path&gt;/input_err.xml; lineNumber: 3; columnNumber: 3; The element type "elem" must be terminated by the matching end-tag "&lt;/elem&gt;".
  C:\&lt;path&gt;\input_err.xml -&gt; C:\&lt;path&gt;\AppData\Local\Temp\input_err.xml: The process cannot access the file because it is being used by another process.
  </code></pre>
  
  <p>used command line (I use Eclipse) :</p>
  
  <pre><code>java ... -Djaxp.debug=1 -Dfile.encoding=UTF-8 -classpath &lt;...&gt; com.sample.xslt.application.XsltApplicationSample C:\&lt;path&gt;\transform.xsl C:\&lt;path&gt;\input_err.xml
  </code></pre>
  
  <p>used pom.xml:</p>
  
  <pre><code>&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
      &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
      &lt;groupId&gt;com.sample&lt;/groupId&gt;
      &lt;artifactId&gt;XsltExampleProject&lt;/artifactId&gt;
      &lt;version&gt;1.0.0-SNAPSHOT&lt;/version&gt;
  
      &lt;name&gt;XsltExampleProject&lt;/name&gt;
      &lt;description&gt;XSLT example project&lt;/description&gt;
  
      &lt;dependencies&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;net.sf.saxon&lt;/groupId&gt;
              &lt;artifactId&gt;Saxon-HE&lt;/artifactId&gt;
              &lt;version&gt;9.7.0-7&lt;/version&gt;
          &lt;/dependency&gt;
  
          &lt;dependency&gt;
              &lt;groupId&gt;commons-io&lt;/groupId&gt;
              &lt;artifactId&gt;commons-io&lt;/artifactId&gt;
              &lt;version&gt;2.5&lt;/version&gt;
          &lt;/dependency&gt;
  
          &lt;dependency&gt;
              &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
              &lt;artifactId&gt;commons-lang3&lt;/artifactId&gt;
              &lt;version&gt;3.2.1&lt;/version&gt;
          &lt;/dependency&gt;
      &lt;/dependencies&gt;
  
      &lt;build&gt;
          &lt;sourceDirectory&gt;src&lt;/sourceDirectory&gt;
          &lt;plugins&gt;
              &lt;plugin&gt;
                  &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                  &lt;version&gt;3.3&lt;/version&gt;
                  &lt;configuration&gt;
                      &lt;source&gt;1.8&lt;/source&gt;
                      &lt;target&gt;1.8&lt;/target&gt;
                      &lt;encoding&gt;UTF-8&lt;/encoding&gt;
                  &lt;/configuration&gt;
              &lt;/plugin&gt;
          &lt;/plugins&gt;
      &lt;/build&gt;
  &lt;/project&gt;
  </code></pre>
  
  <p>The workaround I used is to load xml input file's content in memory as String, see following :</p>
  
  <pre><code>String xmlContent = FileUtils.readFileToString(new File(xmlFilePath), StandardCharsets.UTF_8);
  
  Source xslSource = new StreamSource(new File(xslFilePath));
  Source xmlSource = new StreamSource(new StringReader(xmlContent));
  </code></pre>
  
  <p>Do I miss something while initializing the Transformer?
  Default resolved SAX Parser should be overrided to another API recommended by Saxon? I think that Xerces parser is used according to debug logging, but is it fully compatible with the transformer implementation provided by Saxon?
  I'm a bit confused on this one..</p>
  
  <p>Thanks for your help !</p>