考虑这段代码(完全基于飞碟的“入门”代码,保留他们的权利):
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 加载时,它会失败并出现以下错误:
原因:org.w3c.dom.DOMException:NAMESPACE_ERR:试图以不正确的命名空间方式创建或更改对象。 在 org.apache.xerces.dom.AttrNSImpl.setName(未知来源) 在 org.apache.xerces.dom.AttrNSImpl.(未知来源) 在 org.apache.xerces.dom.CoreDocumentImpl.createAttributeNS(未知来源) 在 org.apache.xerces.dom.ElementImpl.setAttributeNS(未知来源) 在 org.apache.xml.utils.DOMBuilder.startElement(DOMBuilder.java:307) ... 19 更多
在错误的地方找了一会儿(例如,我创建了一个 child-first / parent-last 类加载器怀疑 xalan / xerces jar,但它仍然失败),我终于缩小了根本原因:
似乎加载我的代码的 Web 应用程序有一个旧的xalan.jar,规范版本 1.2
我做了一个小测试,我将上面的代码作为独立的代码运行(之前运行良好),但是这次我将 web 应用程序中的xalan.jar添加到它的类路径和宾果游戏中,与 web 应用程序场景中的错误相同
所以我检查了那个旧的 xalan.jar 并想知道,什么会导致 JVM 加载它的旧 xalan 实现而不是 JDK 的?毕竟我的 child-first 类加载器也是 parent-last eg system in the middle,说:在父级之前搜索系统类加载器(以避免加载父级覆盖的 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 的实现?(我无法从要加载我的代码的 Web 应用程序中删除该 jar)