2016-08-30 1 views
0

Я использую API спецификации JAXP в сочетании с API Saxon-HE, основной целью которого было разработать приложение, которое преобразует файлы XML с помощью настраиваемых таблиц стилей XSLT, возможность переопределить сгенерированные выходные документы .. я пропущу подробности, потому что я создал пример проекта, чтобы проиллюстрировать возникшую проблему:JAXP saxon-he: XML-файл StreamSource не освобождает доступ к файлам после ошибки синтаксического анализа

использования: в случае ошибок преобразования, перемещение файла XML в другой каталог (может быть каталог ошибок) вызывает исключение доступа.

Когда я создаю экземпляр StreamSource на основе экземпляра файла (указывая на файл XML), если возникает некоторая ошибка синтаксического анализа, перемещение файла приводит к «Процесс не может получить доступ к файлу, потому что он используется другим процессом». Исключение.

Вот основные одноклассны приложение, которое я написал, чтобы проиллюстрировать этот вопрос:

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()); 
    } 
    } 
} 

Teh настроен XSLT содержимого файл преобразование должны быть действительными для воспроизведения. Если входной файл xml пуст, он создаст ошибку преобразования/разбора, но ошибка файла доступа не произойдет.

Пример входного файла для воспроизведения:

<root> 
    <elem> 
</root> 

Пример STDOUT:

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. 

используемую командную строку (я использую 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 Parser следует переопределить на другой API, рекомендованный Saxon? Я думаю, что синтаксический анализатор Xerces используется в соответствии с протоколом отладки, но полностью ли он совместим с реализацией трансформатора, предоставленной Saxon? Я немного запутался в этом ...

Спасибо за помощь!

+0

Я проведу дополнительные проверки, но я уверен, что в этой ситуации Saxon передает исходный код в XML-парсер в качестве URI, поэтому поток открывается парсером XML и поэтому должен быть закрыт синтаксическим анализатором XML ; Саксон никогда не видит поток и поэтому не имеет возможности закрыть его. Попробуйте использовать Apache Xerces вместо JDK-версии Xerces; версия Apache почти всегда более надежна. Кроме того, передайте FileInputStream, а затем закройте его в блоке finally {}. –

+0

Я добавил зависимость xercesImpl версии 2.11.0 в pom.xml, и ошибка с ней не возникает. Итак, из версии, поставляемой с JDK, я использую: java-версию «1.8.0_92»/Java (TM) SE Runtime Environment (build 1.8.0_92-b14)/Java HotSpot (TM) 64-разрядная серверная VM (сборка 25.92) -b14, смешанный режим) – PacDroid

ответ

1

Из строки с комментариями, которые задают этот вопрос, представляется ошибкой/дефектом в парсере XML, поставляемом вместе с JDK. Ваши варианты:

(а) сообщить об ошибке и ждать терпеливо для того, чтобы быть фиксированными

(б) использовать анализатору Apache Xerces вместо

(с) вместо того, чтобы поставлять файл, снабжение FileInputStream, и закройте его самостоятельно.

Моя рекомендация будет (b), поскольку парсер Apache Xerces намного надежнее, чем версия в JDK.

+0

Как и вы, я выбрал решение (b), но также сообщаю об ошибке – PacDroid

Смежные вопросы