Java Xml Transformer无法关闭打开的文件,导致Linux操作系统上出现“打开的文件过多错误”

baubqpgj  于 2023-06-21  发布在  Linux
关注(0)|答案(2)|浏览(140)

我们正在开发Java 8遗留软件。
我们使用XSLT转换以. html格式呈现. xml文件。
我们的问题是. xsl styleSheet文件导入了其他的. xsl文件。
(All下面的代码是dumbed下来,因为我不能给真正的代码...)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fn="http://www.w3.org/2005/xpath-functions">

    <xsl:import href="someTemplate.xsl"/>
    <xsl:output method="html" encoding="UTF-8" indent="yes"/>

[...]
</xsl:stylesheet>

然后我们使用这个. xsl样式表转换我们的. xml数据源,并将net.sf.saxon.jaxp.SaxonTransformerFactory作为我们的javax.xml.transform.Transformer源。
Saxon pom依赖性:

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

net.sf.saxon.jaxp.SaxonTransformerFactory示例化如下:

TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();

    Path targetFolderPath = <some Path>;
    transformerFactory.setURIResolver((arg0, arg1) -> {

      try
      {
        InputStream xslStream = Files.newInputStream(targetFolderPath.resolve(arg0));
        return new StreamSource(xslStream);
      }
      catch (IOException e)
      {
        LOGGER.error(e.getLocalizedMessage(), e);
        return null;
      }
    });

如果在try-with-resource中打开. xsl样式表。

try (InputStream xslStream = Files.newInputStream(Paths.get(pathToTheXslFile));)
    {

      Transformer transformer = transformerFactory.newTransformer(new StreamSource(xslStream));
      
      transformer.transform(XmlData, new StreamResult(destFile));
       
    }
    catch (IOException | TransformerException e)
    {
      [some logs...]
    }

我们的问题是,转换似乎打开了由. xsl样式表导入的文件,而没有关闭它。
这导致在Linux操作系统上工作时出现“打开文件过多错误”。
基本上我们完全被这个问题难住了,因为javax.xml.transform.Transformer不提供任何自动关闭选项,我们似乎没有任何访问打开的文件。
我们想到的是做一些自定义代码来通过Linux命令行强制关闭文件。

    • 编辑**:

阅读代码后,我的最新更新的描述,我现在看到它...
在为TransformerFactory创建URIResolver时,我们没有实现Try-with-resource。
我会做出改变,测试它,然后关闭问题,如果它确实工作(它应该...;)).
Thx的评论。

    • EDIT2**:

将try-with-resource添加到URIResolver lambda表达式中会导致net.sf.saxon.s9api.SaxonApiException: I/O error reported by XML parser processing null: null这是因为一旦返回StreamSource,用于创建StreamSourceInputStream就会关闭,从而使其变得无用。

u5i3ibmn

u5i3ibmn1#

看起来像bug https://saxonica.plan.io/issues/3401
这在Saxon 9.7.0.21和更高版本中得到了修复。
保持最新是一个好主意,如果不是主要版本,至少保持维护版本的最新。Saxon 9.7可以追溯到2017年。
在bug条目中还有一个链接,指向我用于诊断的工具--你可能会发现这很有帮助。

enxuqcxy

enxuqcxy2#

感谢您的所有输入,我们发现了问题并提出了更正(确实接近@MichaelKay评论的建议)。
基本上,我们实现了自己的URIResolver,它实现了一个close()方法,然后我们将其调用到finally子句中。
CustomUriResolver:

ublic class CustomUriResolver implements URIResolver
{

  private Path folderPath;

  private List<InputStream> inputStreamList = new ArrayList<>();

  public CustomUriResolver(Path folderPath)
  {
    super();
    this.folderPath = folderPath;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Source resolve(String href, String base) throws TransformerException
  {
    try
    {
      InputStream xslStream = Files.newInputStream(folderPath.resolve(href));
      inputStreamList.add(xslStream);
      return new StreamSource(xslStream);
    }
    catch (IOException e)
    {
      LOGGER.error(e.getLocalizedMessage(), e);
      return null;
    }
  }

  public void closeStreams() throws IOException
  {
    for (InputStream is : inputStreamList)
    {
      is.close();
    }
  }

}

XSLT转换的用法:

TransformerFactoryImpl transformerFactory = new TransformerFactoryImpl();

    ReportUriResolver uriResolver = new ReportUriResolver(getReportXslFolderPath());

    transformerFactory.setURIResolver(uriResolver);

    try
    {
      xsltTransformation(transformerFactory, xmlDocument);
    }
    finally
    {
      try
      {
        // close streams opened during XSL transform with imports
        uriResolver.closeStreams();
      }
      catch (IOException e)
      {
        LOGGER.error(e.getLocalizedMessage(), e);
      }
    }

相关问题