Как предотвратить использование xalan.jar, в котором META-INF \ services \ javax.xml.transform.TransformerFactory, встроенной в Xalan реализацию JDK 1.6? - PullRequest
3 голосов
/ 27 марта 2011

Рассмотрим этот код (полностью основанный на коде «начала работы» летающей тарелки, их права защищены):

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();
        }
    }

Несколько фактов:

  1. Автономный запуск (вызов main) с JDK 1.6 или 1.5 работает отлично (PDF генерируется)
  2. Но при загрузке через 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? (Я не могу удалить эту банку из веб-приложение, из которого будет загружен мой код)

Ответы [ 4 ]

7 голосов
/ 27 марта 2011

Кажется, ответ проще, чем я думал.

  1. В вашем загрузчике классов добавьте в classpath (баночка не требуется) эту папку: /META-INF/services/

  2. В ней создайте файл с именемjavax.xml.transform.TransformerFactory

  3. Измените его и установите в качестве его содержания: com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl

Вот и все!

Почему это работает?см. этот класс, используемый Java для загрузки реализации Xalan.

Обратите внимание, что де-факто кажется, что загружается последний родительский (или дочерний первый) для этой конкретной записи "META-INF" (противоположность того, какработают обычные загрузчики классов 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;
        }

        ...
2 голосов
/ 27 марта 2011

Следует также отметить, что вам не требуется для использования механизма 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 в вашем собственном изолированном загрузчике классов, если вы знаете имя соответствующего класса.

0 голосов
/ 02 мая 2019

Я использую сервер приложений wildfly и 3-й jar, который использует TransformerFactory.

Переопределение TransformerFactory с использованием файловых ресурсов \ META-INF \ services \ javax.xml.transform.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.

Приветствия

0 голосов
/ 08 февраля 2016

Если вы разрабатываете веб-приложение и, следовательно, не можете установить системное свойство, то самый прямой способ - это явный запрос преобразователя JDK. Вот пример для внутреннего преобразователя XSLTC (имеет StAXSupport).

TransformerFactory tf = TransformerFactory.newInstance(
        "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl", null);
...