Я нахожусь на этапе проверки концепции создания некоторого преобразования DocBook → PDF в веб-приложение. Основные требования:
TLDR: Как мне инкапсулировать таблицы стилей DocBook XSLT в JAR (что не требует разбивки JAR на файлы в файловой системе)?
Как недавно обсуждалось в списке рассылки docbook-apps, я могу многое сделать, начав с таблиц стилей в src/main/resources/xsl (с некоторыми настройками на этом уровне, а затем таблиц стилей DocBook в src/main/resources/xsl/docbook-xsl-1.79.2), каталога, который начинается так:
<?xml version = "1.0" encoding = "utf-8"?>
<catalog xmlns = "urn:oasis:names:tc:entity:xmlns:xml:catalog">
<uri name = "file:/xsl/juno-driver.xsl"
uri = "classpath:/xsl/juno-driver.xsl" />
<uri name = "file:/xsl/header-footer.xsl"
uri = "classpath:/xsl/header-footer.xsl" />
<uri name = "file:/xsl/table.xsl"
uri = "classpath:/xsl/table.xsl" />
<uri name = "file:/xsl/titlepage.xsl"
uri = "classpath:/xsl/titlepage.xsl" />
<uri name = "file:/xsl/docbook-xsl-1.79.2/fo/docbook.xsl"
uri = "classpath:/xsl/docbook-xsl-1.79.2/fo/docbook.xsl" />
<uri name = "file:/xsl/docbook-xsl-1.79.2/VERSION.xsl"
uri = "classpath:/xsl/docbook-xsl-1.79.2/VERSION.xsl" />
<uri name = "file:/xsl/docbook-xsl-1.79.2/fo/param.xsl"
uri = "classpath:/xsl/docbook-xsl-1.79.2/fo/param.xsl" />
(и продолжает сопоставлять каждый файл .xsl, .xml, .ent и .dtd с его эквивалентом classpath: URI) и некоторый код, подобный этому:
DOMResult result = new DOMResult();
TransformerFactory factory = TransformerFactory.newInstance();
InputStream is = XmlTest.class.getResourceAsStream("/xsl/juno-driver.xsl");
Source source = new StreamSource(is, "file:/xsl/juno-driver.xsl");
Transformer transformer = factory.newTransformer(source);
transformer.transform(new DOMSource(document), result);
return (Document) result.getNode();
Это почти приводит нас туда, но терпит неудачу:
Error at char 9 in expression in xsl:param/@select on line 18 column 57 of l10n.xsl:
FODC0002 I/O error reported by XML parser processing
file:///xsl/docbook-xsl-1.79.2/common/l10n.xsl. Caused by java.io.FileNotFoundException:
/xsl/docbook-xsl-1.79.2/common/l10n.xsl (No such file or directory)
at parameter local.l10n.xml on line 18 column 57 of l10n.xsl:
invoked by global parameter local.l10n.xml at file:///xsl/docbook-xsl-1.79.2/common/l10n.xsl#18
Где эта строка включает вызов document(''):
<xsl:param name = "local.l10n.xml" select = "document('')"/>
Похоже, он настаивает на загрузке себя из файла, а затем (очевидно) не может найти его по этому URI. Как мы сообщаем тому, кто обрабатывает вызовы функции document(), использовать путь к классам?
Я отправил минимальный пример проблемы на GitHub: вы можете клонировать репозиторий и запустить mvn clean test для воспроизведения.
Я бы также согласился на совет относительно любого другого подхода к выполнению этого, который соответствует списку ограничений в верхней части поста!
Вместо того, чтобы использовать URI пути к классам и перечислять их все в каталоге, рассматривали ли вы использование jar: URI, например. new StreamSource(is, "jar:file:///docbook-xsl!/xsl/foo.xsl")
Кроме того, может случиться так, что написанный пользователем URIResolver (или Saxon ResourceResolver) может обрабатывать вызовы document("") и делегировать все остальное.
Я могу опубликовать минимальный пример, и я попробую jar: URI — спасибо.
Судя по pom.xml, ваш минимальный пример, похоже, ориентирован на Java 8. Требуется ли для вас работа на Java 8?




Я думаю, что есть несколько способов сделать это. Один из способов сделать это — добавить поддержку доступа к ресурсам в пути к классам по URL-адресам. Таким образом, вы можете указать на таблицы стилей в своем пути к классам с помощью URL-адреса без необходимости иметь каталог.
Вы можете сделать это, например, зарегистрировав приведенный ниже класс как реализацию URLStreamHandlerProvider. Реализация адаптирована из этого ответа, но изменена для поддержки необязательной косой черты в начале URL-адреса, а также изменена для использования имени схемы cp: вместо более обычного classpath:.
cp: связано с тем, что Saxon-HE (по крайней мере, версия 12.3), по-видимому, имеет обходной путь, специфичный для URL-адресов classpath:, что вызывает проблему с удалением начального слэша из пути, когда он разрешает относительные classpath: URL-адреса.В Java 9 и выше вы можете зарегистрировать провайдера, указав полное имя класса в файле конфигурации META-INF/services/java.net.spi.URLStreamHandlerProvider.
С этим вы сможете указывать на свои таблицы стилей с помощью URL-адреса, например cp:/xsl/docbook-xsl-1.79.2/html/docbook.xsl, и заставить его работать без каталога, включая относительный импорт, до тех пор, пока ваш XSLT-процессор использует (или, по крайней мере, возвращается) этот метод разыменования. URL-адреса. Судя по быстрому тесту, этот подход работает по крайней мере с XSLT-процессорами Xalan-Java и Saxon-HE. (Я думаю, что процессор XSLT по умолчанию, включенный в Java, может иметь некоторые проблемы при использовании таблиц стилей docbook-xsl.)
package com.stackoverflow.q76848364;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.spi.URLStreamHandlerProvider;
/**
* URL stream handler for "cp:/" URLs for accessing resources in the classpath.
* Supports a leading slash in the the path so that the scheme is treated as a
* hierarchical scheme for resolving relative URL references.
*
* <p>
* Register this provider by putting the fully qualified name of this class in
* the configuration file
* META-INF/services/java.net.spi.URLStreamHandlerProvider.
*/
public class ClasspathURLStreamHandlerProvider extends URLStreamHandlerProvider {
private static final String PROTOCOL = "cp";
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
if (PROTOCOL.equals(protocol)) {
return new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL url) throws IOException {
String urlPath = url.getPath();
String resourcePath = urlPath.startsWith("/") ? urlPath.substring(1) : urlPath;
return ClassLoader.getSystemClassLoader().getResource(resourcePath).openConnection();
}
};
}
return null;
}
}
При работе с относительными ссылками URI в Java обратите внимание, что в методе java.net.URI.resolve() есть ошибка, которая влияет на разрешение ссылок относительных URI, когда относительный URI пуст (баг JDK-8218962 в базе данных ошибок Java). Таблицы стилей docbook-xsl полагаются на то, что это работает правильно, поэтому возникнут проблемы, если кто-то попытается использовать что-либо, зависящее от класса java.net.URI для этой функциональности. Поскольку и Xalan-Java, и Saxon-HE работают нормально, они должны использовать что-то другое.
Я создал пул реквест здесь, демонстрируя это решение на предоставленном минимальном примере. (Исходный пример был настроен на Java 8. Поскольку метод регистрации реализации URLStreamHandler отличается для Java 8 и Java 9+, вместо этого я изменил цель компиляции на Java 9, чтобы продемонстрировать новый подход.)
Спасибо за этот исчерпывающий ответ, включая запрос на включение примера проекта! Я подтвердил, что это работает. Java 8 — это всего лишь мягкое требование. Еще раз спасибо — превосходно.
Можете ли вы обновить свой вопрос, включив в него полный минимальный пример, включая pom.xml, который воспроизводит проблему?