Существуют множество популярных XSLT-процессоров, таких как Xalan, Saxon, Altova и т.п. Каждый из них имеет свои особенности и зачастую уникальные функции несовместимые с другими XSLT-процессорами.
В проектах по интеграции подсистем, зачастую возможна ситуация, когда необходимо использовать XSL-трансформации разработанные для другой подсистемы. В подсистемах, реализованных на разных платформах и/или использующих различные XSLT-процессоры, необходимо производить адаптацию трансформаций: заменять функции, способы приведение типов и прочее. Такую работу необходимо выполнять при каждом обновлении трансформаций, что крайне трудозатратно, подвержено ошибкам и изменению ожидаемого поведения.
В проектах по интеграции подсистем, зачастую возможна ситуация, когда необходимо использовать XSL-трансформации разработанные для другой подсистемы. В подсистемах, реализованных на разных платформах и/или использующих различные XSLT-процессоры, необходимо производить адаптацию трансформаций: заменять функции, способы приведение типов и прочее. Такую работу необходимо выполнять при каждом обновлении трансформаций, что крайне трудозатратно, подвержено ошибкам и изменению ожидаемого поведения.
Пример: OSS и Oracle JDK 7 по умолчанию использует XSLT-процессор Xalan, в то время как нашей системе предоставляются трансформации, написанные для SAXON, они регулярно обновляются и править их мы не можем, по каким-либо бизнес-ограничениям.
Мы имеем несколько путей решения:
1. Установить глобально, для всей платформы, XSLT-процессором по умолчанию SAXON.
Для этого скачиваем необходимую редакцию библиотеки с официального сайта проекта SAXON и добавляем её на наш сервер приложений, например, скопировав в $DOMAIN_HOME/lib.
Далее в дескрипторе развертывания weblogic-application.xml определяем стандартный класс для transformer-factory.
<xml> <parser-factory> <transformer-factory>net.sf.saxon.TransformerFactoryImpl</transformer-factory> </parser-factory> </xml>
Плюсы:
- простая реализация;
Минусы:
- в уже существующей системе может потребоваться переработка ранее созданных трансформаций;
- если ваша система развернута на общем с другими системами сервере приложений, это повлияет на их работу;
- решение менять ключевой механизм, под который оптимизирована платформа, может породить больше проблем чем принести пользы.
2. Использовать Java Embedding для локального применения трансформаций (реализация будет описана ниже).
Плюсы:
- отсутствие влияния на сторонние подсистемы сервера приложений;
- простота реализации;
Минусы:
- при наличии большого количества мест, в которых необходимо использовать данный функционал, для избегания дублирования кода, необходимо реализовать общее решение, например, отдельный веб-сервис (что не проблема для SOA), либо написать свой компонент для OSS, что несколько сложнее.
3. Написание прослойки для предобработки трансформаций и замены функций, но реализовать и сопровождать такой функционал крайне трудоёмко, так как не всегда есть аналогичные функции и их поведение может быть различным в зависимости от XSLT-процессора.
Отмечу, что наверняка есть и другие варианты решения которые не рассмотрены в статье, и с развитием продуктов появятся более простые и надежные способы из реализации.
В нашем случае очень удобно использовать второй подход, который будет описан далее.
Использование Java Embedding для XSL-трансформаций в BPEL процессе.
Реализуем простой синхронный SOAP веб-сервис, стандартными средствами OSS, для чего создадим композит с одним BPEL-процессом. Единственной обязанностью веб-сервиса будет возврат результата выполнение XSL-преобразования над входными данными.
Использование Java Embedding для XSL-трансформаций в BPEL процессе.
Реализуем простой синхронный SOAP веб-сервис, стандартными средствами OSS, для чего создадим композит с одним BPEL-процессом. Единственной обязанностью веб-сервиса будет возврат результата выполнение XSL-преобразования над входными данными.
Первым делом необходимо добавить библиотеку SAXON в композит, для этого загруженную jar-библиотеку копируем в ProjectName\SCA-INF\lib, после чего добавляем её в classpath в свойствах проекта, как это показано на рисунке 1.
Рисунок 1 - Свойства проекта
Далее реализуем логику работы веб-сервиса в BPEL-процессе. Описанный ниже BPEL-процесс разбит на отдельные шаги, исключительно для наглядности. Логику процесса можно скомпоновать и переработать под требование вашего проекта.
Определим входящее и исходящее сообщения в интерфейсе веб-сервиса, описанного в WSDL.
Определим входящее и исходящее сообщения в интерфейсе веб-сервиса, описанного в WSDL.
Входящее сообщение, 2 элемента:
- данные подвергаемые трансформации;
- тип данных (который будет совпадать с названием файла трансформации).
Исходящее сообщение, 2 элемента:
Вторым шагом, с помощью компонента Java Embedding, производим трансформацию (рис.2 блок JavaTransform). Самое важное в этом шаге инстанцировать фабрику реализацией SAXON (net.sf.saxon.TransformerFactoryImpl).
Третьим и последним шагом записываем данные в ответное сообщение.
Все примеры, используемые в статье, доступны на github.
- данные после трансформации;
- результат работы (success/failure).
Первым шагом нам необходимо подготовить XSL-трансформацию.
Так как OSS платформой наложено ограничение на прямое обращение к MDS из BPEL-процесса, нам необходимо расположить трансформации в самом композите, например, в папке 'xsl'.
Так как OSS платформой наложено ограничение на прямое обращение к MDS из BPEL-процесса, нам необходимо расположить трансформации в самом композите, например, в папке 'xsl'.
С помощью функций ora:doc(), которая возвращает содержимое xml файла, загружаем трансформацию в переменную (рис.2 блок AssignXSLT). Вызов функции ora:doc() следует обернуть oraext:get-content-as-string(), для избежания проблем с переименованием корневых тэгов и работы с немодифицированным кодом трансформации.
<assign name="AssignXSLT"> <copy> <from expression="concat('xsl/', bpws:getVariableData('inputVariable','payload','/client:processRequest/client:dataType'), '.xsl')"/> <to variable="xsltPath"/> </copy> <copy> <from expression="oraext:get-content-as-string(ora:doc($xsltPath))"/> <to variable="xsltVar"/> </copy> </assign>Используя комбинацию данных функций, возможно использовать файлы трансформаций расположенных как локально в композите, так и в MDS (обращаясь через префикс 'oramds:').
Вторым шагом, с помощью компонента Java Embedding, производим трансформацию (рис.2 блок JavaTransform). Самое важное в этом шаге инстанцировать фабрику реализацией SAXON (net.sf.saxon.TransformerFactoryImpl).
try { String xsltVar = (String) getVariableData("xsltVar"); XMLElement xmlVarInput = (XMLElement) getVariableData("inputVariable", "payload", "/client:processRequest/client:inputData/*"); StringWriter xmlVarWriter = new StringWriter(); xmlVarInput.print(xmlVarWriter); String xmlVar = xmlVarWriter.toString(); StringReader xsltReader = new StringReader(xsltVar); TransformerFactory factory = TransformerFactoryImpl.newInstance("net.sf.saxon.TransformerFactoryImpl", null); Transformer transformer = factory.newTransformer(new StreamSource(xsltReader)); StringReader xmlReader = new StringReader(xmlVar); StringWriter resultWriter = new StringWriter(); transformer.transform(new StreamSource(xmlReader), new StreamResult(resultWriter)); setVariableData("xmlVar", resultWriter.toString()); } catch (Exception ex) { addAuditTrailEntry(ex.getMessage()); }
Третьим и последним шагом записываем данные в ответное сообщение.
Так как в примере мы работали со строковым представлением XML, для дальнейшей работы необходимо распарсить её в XML, что можно сделать с помощью компонента Assign и стандартной функцией oraext:parseXML() (рис.2 блок AssignResult).
<assign name="AssignResult"> <copy> <from expression="oraext:parseXML($xmlVar)"/> <to variable="outputVariable" part="payload" query="/client:processResponse/client:outputData"/> </copy> <copy> <from expression="'success'"/> <to variable="outputVariable" part="payload" query="/client:processResponse/client:resultState"/> </copy> </assign>
Готовый веб-сервис необходимо собрать и развернуть на сервере приложений. Артефакт должен содержать используемую jar-библиотеку в папке ArtefactName.jar/SCA-INF/lib/, иначе, при разворачивании, возникнет ошибка неправильного classpath композита.
Обратите внимание: при разворачивании композита через Oracle Enterprise Manager, без перезагрузки сервера приложения, время работы первого инстанса будет существенно больше времени его обычной работы. Такое поведение связанно с внутренним механизмом обращения к классам сторонних библиотек и их первичной загрузкой.
Таким образом, реализован механизм позволяющий локально использовать нестандартный XSLT-процессор. Рассмотренный подход, а именно поддержка Java и наличие стандартного компонента Java Embedding в платформе OOS, позволяет обеспечить большую гибкость и изоляцию механизмов платформы от реализуемого функционала.
Все примеры, используемые в статье, доступны на github.
Комментариев нет:
Отправить комментарий