Рассмотрим этот код (полностью основанный на коде «начала работы» летающей тарелки, их права защищены):
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();
}
}
Несколько фактов:
- Автономный запуск (вызов main) с JDK 1.6 или 1.5 работает отлично (PDF генерируется)
- Но при загрузке через URLClassLoader из существующего веб-приложения происходит сбой с этой ошибкой:
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
Пройдя некоторое время в неправильном месте (например, я создал загрузчик класса child-first / parent-last, подозревающий jar-файлы xalan / xerces, но он все равно не работает), я наконец сузил основную причину:
Кажется, что веб-приложение, которое загружает мой код, имеет старый xalan.jar , версия спецификации 1.2
Я провел небольшой тест, я запустил приведенный выше код как автономный (раньше он работал нормально), но на этот раз я добавил xalan.jar из веб-приложения в его classpath и bingo, то же самое ошибка как в сценарии веб-приложения
Итак, я осмотрел этот старый xalan.jar и подумал, что может заставить JVM загрузить его старую реализацию xalan вместо JDK? в конце концов, мой загрузчик первого класса также является последним родителем, например, система посередине, скажем: поиск системного загрузчика классов перед родителем (чтобы избежать загрузки переопределенных JAR JAR-файлов родителями, точно так же, как этот случай родительского xalan.jar, переопределяющего реализацию xalan JDK)
Тогда что-то бросилось в глаза - файл в: xalan.jar / META-INF / services / с именем javax.xml.transform.TransformerFactory с таким содержанием:
org.apache.xalan.processor.TransformerFactoryImpl
Поэтому я немедленно нажал Ctrl + T в затмении и искал полное имя ... только в xalan.jar !
Тогда я искал только «TransformerFactoryImpl», и вот что имеет JDK:
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
Легко увидеть разницу
Итак, если вы прочитали до сих пор, мой вопрос в нижней строке: Как мне заставить мой TransformerFactory использовать реализацию JDK, а не старый Xalan? (Я не могу удалить эту банку из веб-приложение, из которого будет загружен мой код)