Поставил целью запустить простейший XSLT-фреймворк на GAE/J. Требования к процессору:
- XSLT 1.0 в полном объеме. А неплохо бы и 2.0 на всякий случай;
- Скорость;
- Поддержка EXSLT. Хотя-бы частичная, но node-set обязательно.
- Расширяемость внешними Java-функциями. Возможность вызова методов класса;
- Лицензия на коммерческое применение;
- Желательна бесплатность и исходные тексты;
- Желательна поддержка TrAX - transformation API for XML.
Вначале долго искал хоть какие результаты тестов производительности. Все найденные данные либо безбожно устарели, либо составлены так, что понять что-либо абсолютно невозможно. Любимый прием авторов сравнений – утаить подробности. Например, пишут “Saxon X.X.X.X”. Что за Saxon? Saxon-B или Saxon-SA? Для дотнета или для явы? И так везде. В общем путем долгих мучений выяснилось что известными более-менее быстрыми процессорами, написанными на яве, являются Gregor, Xalan/XSLTC, XT, Saxon 6.5/Saxon-B/Saxon-SA.
Gregor декларировался как фантастически быстрый компилятор нового поколения. На деле оказался заброшенным коммерческим проектом, абсолютно недоступным для отдельного скачивания. Жаль.
Xalan/XSLTC. Входит в JDK. Правда какой-то древней версии. Попробовал использовать свежий xalan-j 2.7.1. Сразу выяснилось, что по сути XSLTC не поддерживает внешние нестатические функции, да и без их использования валится на GAE с криками “Illegal type in constant pool”. Без использования режима компиляции xalan на GAE работоспособен, но загаживает лог криками “Failed calling setMethod method” и “Failed calling setIndent method”. Т.е. не понимает тег <xsl:output>. Да и самый медленный он из всех перечисленных. Короче – в сад.
XT-20051206. По отзывам – очень быстр. Похоже, что проект заброшен. Не смог обработать мой шаблон даже локально, до GAE не дошел. В брак.
Saxon-SA это коммерческий продукт. Значит и пробовать нужно когда проект работает под нагрузкой и приносит прибыль. Не проверял. Но похоже его и не нужно как-то особо проверять ибо полностью внешне совместим с Saxon-B.
Saxon 6.5.5 сейчас подзаброшен. Не проверял, т.к. каких-либо преимуществ перед Saxon-B не имеет, скорее наоборот.
Saxon-B 9.1.0.7 j. Завелся с полпинка как локально, так и на GAE. Оказался быстрее xalan’а на моем простом примере. Работает стабильно. На нем и остановился.
Единственный замеченный глюк – в некоторых случаях xml-файлы могут самопроизвольно искаться в каталоге “/base/” (и не находиться, естественно). Чтобы этого не происходило, нужно использовать классы FileInputStream и File. В них с обработкой путей всё в порядке.
В итоге получился краткий и лаконичный код. Ключевые моменты выделены жирным.
public class XstlServlet
extends HttpServlet
{
@Override
public void doGet(final HttpServletRequest req,
final HttpServletResponse resp)
throws IOException, ServletException
{
request = req;
resp.setContentType("text/html; charset=UTF-8");
try {
getTransformer("default").transform(
new StreamSource(new FileInputStream("default.xml")),
new StreamResult(resp.getOutputStream()));
} catch (TransformerException e) {
throw new ServletException(e);
}
}
public Transformer getTransformer(final String xsltName)
throws FileNotFoundException, TransformerException,
MalformedURLException
{
if (!transformers.containsKey(xsltName)) {
final Transformer newXslt = xsltFactory.newTransformer(
new StreamSource(new FileInputStream(xsltName + ".xslt")));
newXslt.setParameter("this", this);
transformers.put(xsltName, newXslt);
}
return transformers.get(xsltName);
}
//
// callback methods for xslt transformation
//
public static String getServletPath(final Object context)
{
return ((XstlServlet) context).request.getServletPath();
}
public static String getParameter(final Object context,
final String paramName)
{
return ((XstlServlet) context).request.getParameter(paramName);
}
//
// private declarations
//
private HttpServletRequest request;
private final Map<String, Transformer> transformers =
new Hashtable<String, Transformer>();
private final static TransformerFactory xsltFactory =
TransformerFactory.newInstance();
}
В xslt-шаблоне для вызова внешних методов применяется классический прием – объявление специального пространства имен, ссылка на наш объект через параметр в корневом узле и передача ссылки на объект как первый параметр вызываемой функции.
<xsl:stylesheet version="1.0"
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
xmlns:this=”http://xml.apache.org/xalan/java/com.test.XstlServlet”
exclude-result-prefixes="this">
<xsl:param name="this" />
<xsl:template match="/">
<xsl:value-of select="this:getParameter($this, @параметр)" />
<xsl:value-of select="this:getServletPath($this)" />
…
</xsl:template>
…
</xsl:stylesheet>
UPDATE: Статья обновлена. В настоящее время GAE не имеет своего xslt-процессора. Это моя случайная ошибка – в каталоге lib был забыт saxon9.jar, чего оказалось достаточно чтобы гугл его подхватил.
1 коммент.:
Я с помощью htmpparser on java на GAE запустил грабер работает прекрасно, только столкнулся с проблемой получения количества записей в базе когда их колличество подошло к 1000
Отправить комментарий