考虑这段代码(完全基于飞碟的“入门”代码,保留其权利):
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();
}
}
很少有事实:
Caused by: org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces. at org.apache.xerces.dom.AttrNSImpl.setName(Unknown Source) at org.apache.xerces.dom.AttrNSImpl.(Unknown Source) at org.apache.xerces.dom.CoreDocumentImpl.createAttributeNS(Unknown Source) at org.apache.xerces.dom.ElementImpl.setAttributeNS(Unknown Source) at org.apache.xml.utils.DOMBuilder.startElement(DOMBuilder.java:307) ... 19 more
在错误的地方寻找一段时间后(例如,我创建了一个怀疑xalan / xerces jars的子级优先/父级最后一个类加载器,但它仍然失败),我最终缩小了根本原因:
似乎加载我的代码的Web应用程序有一个旧的 xalan.jar ,规范版本1.2
我做了一点测试,我将上面的代码作为独立运行(之前工作正常)但这次我将web应用程序中的 xalan.jar 添加到了它的classpath和bingo,与Web应用程序方案中的错误相同
所以我检查了旧的xalan.jar并想知道,是什么导致JVM加载旧的xalan实现而不是JDK?毕竟我的孩子第一类加载器也是父级的,例如系统在中间,说:在父级之前搜索系统类加载器(以避免加载父重写JDK jar,就像父级的xalan.jar覆盖JDK的xalan实现的情况一样)
然后我的眼睛就会发现一个文件: xalan.jar / META-INF / services / 这个内容命名为 javax.xml.transform.TransformerFactory :< / p>
org.apache.xalan.processor.TransformerFactoryImpl
所以我在eclipse中立即按下了 Ctrl + T 并查找了完整的限定名称...仅在 xalan.jar !< / p>
然后我只搜索“TransformerFactoryImpl”,这就是JDK所拥有的:
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
很容易看出差异
所以,如果你读到这里,我的底线问题是:如何让我的TransformerFactory使用JDK的实现而不是旧的Xalan?(我无法从中删除那个jar我的代码将从中加载的Web应用程序)
答案 0 :(得分:7)
似乎答案比我想象的要简单。
在您的类加载器中,添加到类路径(jar不需要)此文件夹:/META-INF/services/
在其中,创建名为javax.xml.transform.TransformerFactory
编辑并将其内容设置为:com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
多数民众赞成!
为什么会这样?请参阅Java用于加载Xalan实现的此类。
请注意,事实上,对于特定的&#34; META-INF&#34;它似乎是父 - 最后(或子优先)加载器的事实。 entry(常规Java类加载器的工作方式,例如parent-first / child-last),但如果我错了,请随时纠正我
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;
}
...
答案 1 :(得分:1)
您还应该注意,
一旦知道了相关类的名称,就可以使用http://download.oracle.com/javase/6/docs/api/javax/xml/xpath/XPathFactory.html#newInstance(java.lang.String,java.lang.String,java.lang.ClassLoader)在您自己的隔离类加载器中使用xalan的副本。
答案 2 :(得分:0)
如果您正在开发Web应用程序,因此无法设置系统属性,那么最直接的方法是显式请求JDK转换器。 以下是内部XSLTC转换器的示例(具有StAXSupport)。
TransformerFactory tf = TransformerFactory.newInstance(
"com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl", null);
答案 3 :(得分:0)
我正在使用wildfly应用程序服务器和使用TransformerFactory的第三个jar。
使用文件资源\ 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。
欢呼