2016-08-30 37 views
0

我使用JAXP規範API和Saxon-HE API,主要目的是開發一個應用程序,它使用可配置的XSLT樣式表來轉換XML文件,能夠覆蓋生成的輸出文件。我跳過的細節,因爲我創建了一個示例項目說明遇到的問題:JAXP saxon-he:XMLfile StreamSource在解析錯誤後不釋放文件訪問

使用案例:在轉換錯誤的情況下,XML文件移動到另一個目錄(可能是錯誤的目錄)引發訪問異常。

當我基於File實例(指向XML文件)實例化StreamSource時,如果發生某些分析錯誤,則移動該文件引發「進程無法訪問該文件,因爲它正在被另一個進程使用」。例外。

這裏是一個主要的單級的應用程序,我寫來說明這個問題:

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 : <xslFilePath> <inputFilePath>"); 
    } 
    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()); 
    } 
    } 
} 

德配置XSLT轉換文件的內容必須是有效的重現。 如果輸入xml文件爲空,它將創建轉換/解析錯誤,但不會發生訪問文件錯誤。輸入文件的

實施例重現:STDOUT的

<root> 
    <elem> 
</root> 

實施例:

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 "</elem>". 
org.xml.sax.SAXParseException; systemId: file:/C:/<path>/input_err.xml; lineNumber: 3; columnNumber: 3; The element type "elem" must be terminated by the matching end-tag "</elem>". 
C:\<path>\input_err.xml -> C:\<path>\AppData\Local\Temp\input_err.xml: The process cannot access the file because it is being used by another process. 

使用命令行(I使用Eclipse):

java ... -Djaxp.debug=1 -Dfile.encoding=UTF-8 -classpath <...> com.sample.xslt.application.XsltApplicationSample C:\<path>\transform.xsl C:\<path>\input_err.xml 

使用的pom.xml:

<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"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>com.sample</groupId> 
    <artifactId>XsltExampleProject</artifactId> 
    <version>1.0.0-SNAPSHOT</version> 

    <name>XsltExampleProject</name> 
    <description>XSLT example project</description> 

    <dependencies> 
     <dependency> 
      <groupId>net.sf.saxon</groupId> 
      <artifactId>Saxon-HE</artifactId> 
      <version>9.7.0-7</version> 
     </dependency> 

     <dependency> 
      <groupId>commons-io</groupId> 
      <artifactId>commons-io</artifactId> 
      <version>2.5</version> 
     </dependency> 

     <dependency> 
      <groupId>org.apache.commons</groupId> 
      <artifactId>commons-lang3</artifactId> 
      <version>3.2.1</version> 
     </dependency> 
    </dependencies> 

    <build> 
     <sourceDirectory>src</sourceDirectory> 
     <plugins> 
      <plugin> 
       <artifactId>maven-compiler-plugin</artifactId> 
       <version>3.3</version> 
       <configuration> 
        <source>1.8</source> 
        <target>1.8</target> 
        <encoding>UTF-8</encoding> 
       </configuration> 
      </plugin> 
     </plugins> 
    </build> 
</project> 

我使用的解決方法是在內存中加載XML輸入文件的內容作爲字符串,請參閱以下內容:

String xmlContent = FileUtils.readFileToString(new File(xmlFilePath), StandardCharsets.UTF_8); 

Source xslSource = new StreamSource(new File(xslFilePath)); 
Source xmlSource = new StreamSource(new StringReader(xmlContent)); 

我懷念的東西,而初始化變壓器? 默認解析的SAX解析器應該優先於Saxon推薦的另一個API?我認爲根據調試日誌記錄使用Xerces解析器,但它是否與Saxon提供的變壓器實現完全兼容? 我對這個有點困惑..

感謝您的幫助!

+0

我會做進一步的檢查,但我非常確定,在這種情況下,Saxon將源代碼作爲URI傳遞給XML解析器,所以流由XML解析器打開,因此應該由XML解析器關閉;撒克遜人從來沒有看到這條河,所以沒有機會關閉它。嘗試使用Apache Xerces而不是JDK版本的Xerces; Apache版本幾乎總是更可靠。或者,傳遞FileInputStream,然後在finally {}塊中自行關閉它。 –

+0

我在pom.xml中添加了依賴xercesImpl版本2.11.0,並且該錯誤不會發生。所以它來自JDK附帶的版本我使用:java版本「1.8.0_92」/ Java(TM)SE運行時環境(版本1.8.0_92-b14)/ Java HotSpot™64位服務器VM(版本25.92 -b14,混合模式) – PacDroid

回答

1

從問題後面的註釋線程看來,它似乎是隨JDK提供的XML解析器中的缺陷/缺陷。您的選項是:

(一)報告錯誤並等待很耐心地爲它被固定

(二)使用Apache Xerces解析器,而不是

(C),而不是提供文件,供一個FileInputStream,並自己關閉它。

我的建議是(b),因爲Apache Xerces解析器比JDK中的版本更可靠。

+0

正如你所做的那樣,我選擇瞭解決方案(b),但我也會報告錯誤 – PacDroid