如何防止具有 META-INF\services\javax.xml.transform.TransformerFactory 的 xalan.jar 接管 Xalan 实现中内置的 JDK 1.6?
Posted
技术标签:
【中文标题】如何防止具有 META-INF\\services\\javax.xml.transform.TransformerFactory 的 xalan.jar 接管 Xalan 实现中内置的 JDK 1.6?【英文标题】:How to prevent xalan.jar that has META-INF\services\javax.xml.transform.TransformerFactory from taking over JDK 1.6 built in Xalan implementation?如何防止具有 META-INF\services\javax.xml.transform.TransformerFactory 的 xalan.jar 接管 Xalan 实现中内置的 JDK 1.6? 【发布时间】:2011-07-23 18:55:36 【问题描述】:考虑这个代码(完全基于飞碟的“入门”代码,保留他们的权利):
package flyingsaucerpdf;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import org.xhtmlrenderer.pdf.ITextRenderer;
public class PDFMaker
public static void main(String[] args) throws Exception
new PDFMaker().go();
public void go() throws Exception
String inputFile = "sample.html";
String url = new File(inputFile).toURI().toURL().toString();
String outputFile = "firstdoc.pdf";
OutputStream os = new FileOutputStream(outputFile);
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(url);
renderer.layout();
renderer.createPDF(os);
os.close();
几个事实:
-
使用 JDK 1.6 或 1.5 独立运行(调用 main)可以完美运行(生成 PDF)
但是,当从现有 Web 应用程序通过 URLClassLoader 加载时,它会失败并出现以下错误:
在错误的地方找了一阵子(比如我创建了一个child-first/parent-last类加载器怀疑xalan/xerces jars,但还是失败了),我终于缩小了根本原因:
似乎加载我的代码的网络应用程序有一个旧的 xalan.jar,规范版本 1.2
我做了一个小测试,我将上面的代码作为独立的代码运行(之前运行良好),但这次我将 xalan.jar 从网络应用程序添加到它的类路径中,宾果游戏,与 web 应用场景相同的错误
所以我检查了那个旧的 xalan.jar 并想知道,什么会导致 JVM 加载它的旧 xalan 实现而不是 JDK 的?毕竟我的孩子第一类加载器也是父母最后的,例如中间的系统,也就是说:在父级之前搜索系统类加载器(避免加载父级覆盖的JDK jar,就像父级的xalan.jar覆盖JDK的xalan实现的这种情况)
然后有些东西让我眼前一亮 - xalan.jar/META-INF/services/ 中的一个名为 javax.xml.transform.TransformerFactory 的文件包含以下内容:
org.apache.xalan.processor.TransformerFactoryImpl
所以我立即在 eclipse 中按下 Ctrl+T 并寻找完整的限定名...仅在 xalan.jar 中!
然后我只搜索“TransformerFactoryImpl”,这就是JDK所具有的:
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
很容易看出区别
所以,如果你读到这里,我的底线问题是:如何让我的 TransformerFactory 使用 JDK 的实现而不是旧的 Xalan 的实现?(我无法从将加载我的代码的网络应用程序)
【问题讨论】:
我之前的相关问题:***.com/questions/5440395/…***.com/questions/5445511/…***.com/questions/5444246/… 我认为这也是相关的download.oracle.com/javase/6/docs/technotes/guides/standards 所以没有办法以编程方式做到这一点? 【参考方案1】:似乎答案比我想象的要简单。
在您的类加载器中,将此文件夹添加到类路径(不需要 jar):/META-INF/services/
在其中创建一个名为javax.xml.transform.TransformerFactory
的文件
编辑它并将其设置为:com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
就是这样!
为什么有效?请参阅 Java 用于加载 Xalan 实现的此类。
请注意,对于特定的“META-INF”条目,它似乎实际上是父级最后(或子级优先)加载器(与常规 Java 类加载器的工作方式相反,例如父级优先/子级最后)但如果我错了,请随时纠正我
来自 javax.xml.datatype.FactoryFinder
的片段
/*
* Try to find provider using Jar Service Provider Mechanism
*
* @return instance of provider class if found or null
*/
private static Object findJarServiceProvider(String factoryId)
throws ConfigurationError
String serviceId = "META-INF/services/" + factoryId;
InputStream is = null;
// First try the Context ClassLoader
ClassLoader cl = ss.getContextClassLoader();
if (cl != null)
is = ss.getResourceAsStream(cl, serviceId);
// If no provider found then try the current ClassLoader
if (is == null)
cl = FactoryFinder.class.getClassLoader();
is = ss.getResourceAsStream(cl, serviceId);
else
// No Context ClassLoader, try the current
// ClassLoader
cl = FactoryFinder.class.getClassLoader();
is = ss.getResourceAsStream(cl, serviceId);
if (is == null)
// No provider found
return null;
...
【讨论】:
即使我也面临同样的问题。我照你说的做了。我打开 xalan.jar 并将 meta-inf/service/javax.xml.transform.TransformerFactory 文件的内容更改为 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl 。现在 jboss 运行时出现很多错误。 Web 应用程序现在可以正常工作,但是 jboss 中有很多错误。我也无法使用数据库连接池。我怎样才能使两者都工作(正确运行jboss以及使变压器工厂工作) @Ashwin 您绝对不应该更改 xalan.jar,而是将名为 META-INF/service/javax.xml.transform.TransformerFactory 的文件添加到您的应用程序。不确定这是否有帮助,很难。 Java 小程序上的类加载器是什么? (我也有同样的问题)【参考方案2】:您还应该注意,您根本不需要使用 SPI 机制。
知道相关类的名称后,您可以使用http://download.oracle.com/javase/6/docs/api/javax/xml/xpath/XPathFactory.html#newInstance(java.lang.String、java.lang.String、java.lang.ClassLoader) 在您自己的隔离类加载器中使用 xalan 的副本。
【讨论】:
这很有趣!我去看看,这可能就是我要找的答案 但是,如果他们没有提供委托,我如何强制第 3 方代码使用我的类加载器? :) 哦,哎呀,我的错。如果你必须重定向其他人的代码,你就会被 SPI 困住。【参考方案3】:如果您正在开发 Web 应用程序,因此无法设置系统属性,那么最直接的方法是显式请求 JDK 转换器。 这是内部 XSLTC 转换器的示例(具有 StAXSupport)。
TransformerFactory tf = TransformerFactory.newInstance(
"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl", null);
【讨论】:
【参考方案4】:我正在使用 wildfly 应用服务器和使用 TransformerFactory 的 3rd jar。
使用文件 resources\META-INF\services\javax.xml.transform.TransformerFactory 覆盖 TransformerFactory。不适合我。
当我查看 FactoryFinder 实现 (JDK8u201) 时。我找到了以下代码片段
String systemProp = ss.getSystemProperty(factoryId);
if (systemProp != null)
dPrint("found system property, value=" + systemProp);
return newInstance(type, systemProp, null, true);
因此,解决方案是将系统属性 javax.xml.transform.TransformerFactory 设置为 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl。
干杯
【讨论】:
以上是关于如何防止具有 META-INF\services\javax.xml.transform.TransformerFactory 的 xalan.jar 接管 Xalan 实现中内置的 JDK 1.6?的主要内容,如果未能解决你的问题,请参考以下文章