barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)
Показаны сообщения с ярлыком xml. Показать все сообщения
Показаны сообщения с ярлыком xml. Показать все сообщения

среда, 20 ноября 2019 г.

grep: поиск по всем файлам в папке по наличию некой подстроки внутри CDATA-секции

Задача

Есть папка с xml-файлами, причем папка может содержать подпапки любой глубины вложенности. Нужно найти xml-файлы, содержащие некую подстроку внутри CDATA-секции.

Решение

grep -REil ".*<!\[CDATA\[([^\]]|(\][^\]])|(\]\][^>]))*ИСКОМАЯПОДСТРОКА.*" C:/sf_phoenix --include=*.xml
где ИСКОМАЯПОДСТРОКА - собственно та подстрока, которую ищем.

пятница, 25 октября 2019 г.

soapUI: "Trying XBeans path engine... Trying XQRL... Trying delegated path engine... FAILED on declare namespace..."

Проблема

В groovy-скрипте, использующем com.eviware.soapui.support.XmlHolder, при попытке выполнить xpath-поиск по XML
def requestXml = new XmlHolder(requestText);
def result = requestXml["//*[local-name() = 'xxx']"]
возникает ошибка:
Trying XBeans path engine... Trying XQRL... Trying delegated path engine... FAILED on declare namespace...
Решение

Добавить в classpath библиотеки saxon-x.x.x.jar и saxon-dom-x.x.x.jar из инсталляции soapUI.

среда, 26 декабря 2018 г.

Wireshark: ищем в дампе траффика soap-запрос по значению одного из тегов

Задача

Нужно в дампе траффика найти soap-запрос по заданному значению некоторого тега (имя тега в поиске не участвует, только значение).

Решение

Используем фильтра xml.cdata == "{искомое значение}"

пятница, 9 ноября 2018 г.

soapUI и кириллица в теле POST-запроса к REST-сервису

Проблема

Нужно из soapUI (запускается под Windows 10) вызвать REST-сервис, передав ему в теле POST-запроса некий XML, содержащий кириллицу. При попытке сделать это получаю от сервиса ответ о наличии некорректного UTF8-байта в запросе.

Решение

В bin-директории soapUI открываем файл SoapUI-x.x.x.vmoptions (где x.x.x. - номер версии soapUI) и добавляем туда опцию:
-Dfile.encoding=UTF8
Перезапускаем soapUI. В поле Media Type запроса добавляем постфикс "; charset=UTF-8". Успех, запрос уходит с корркетным Content-Type заголовком, в котором указана используемая кодировка UTF-8, а также с корректно закодированным в UTF-8 телом.

пятница, 25 декабря 2015 г.

WSO2 ESB 4.9.0: пропадает контент бинарного узла при его создании с помощью ByteArrayInputStream + org.apache.axis2.builder.unknowncontent.InputStreamDataSource

Проблема

Некий кастомный Java-медиатор создает бинарный XML-узел, наполняя его из массива байт byte[], используя ByteArrayInputStream и org.apache.axis2.builder.unknowncontent.InputStreamDataSource:
ByteArrayInputStream stream = new ByteArrayInputStream(myBytes);
DataSource ds = new InputStreamDataSource(stream);
DataHandler dataHandler = new DataHandler(ds);
OMText binaryNode = factory.createOMText(dataHandler, true);
При этом созданный узел ведет себя крайне странно: если попробовать 2 раза подряд залогировать созданный XML, в первый раз он логируется корректно (бинарный узел логируется base64-кодированным), а при повторном логировании оказывается, что бинарный узел уже пуст.

Решение

Особо не разбирался, реализовал создание DataSource напрямую из массива байт:
DataSource ds = new org.apache.axiom.attachments.ByteArrayDataSource(myBytes);

вторник, 16 декабря 2014 г.

XPath 1.0: тернарный оператор

Как такового, тернарного оператора в XPath 1.0 нет. Можно реализовать аналогичное поведение, правда, немного замысловатым образом. Следующее выражение даст $stringA, если $cond = true, и $stringB - в противном случае:
concat(substring($stringA, 1 div $cond), substring($stringB, 1 div not($cond)))

WSO2 ESB 4.5.1: использование пространства имен http://ws.apache.org/ns/synapse в XPath-выражениях

Проблема

Есть прокси-сервис, в нем есть property-медиатор, у которого в xpath-выражении в атрибуте expression используется пространство имен "http://ws.apache.org/ns/synapse" с помощью некоторого префикса, который объявлен в этом же property-теге:
<property name="XXXProp" expression="//syn:abc/text()" xmlns:syn="http://ws.apache.org/ns/synapse"/>
Если прокси-сервис отредактировать через веб-консоль шины, объявление префикса пропадает, после чего property-медиатор, естественно, начинает выдавать ошибки из-за неизвестного префикса пространства имен. Если объявление префикса перенести из медиатора выше (хотя даже в сам корневой тег прокси-сервиса), ситуация не меняется.

Решение

Похоже на баг, и вызван он скорее всего тем, что сам конфигурационный xml прокси-сервиса имеет пространство имен по-умолчанию "http://ws.apache.org/ns/synapse". Копаться в исходниках времени не было, поэтому пришлось воспользоваться workaround'ом:
<property name="XXXProp" expression="//*[local-name()='abc']/text()" />

суббота, 11 октября 2014 г.

IE8: аналог getElementsByTagNameNS для объекта Microsoft.XMLDOM

Задача

В Internet Explorer 8 есть объект Microsoft.XMLDOM (передаваемый dojo-вским xjrPost в обработчик success), нужно из него вытащить элемент по имени тега, причем элемент имеет пространство имен.

Решение

Немного замысловато (здесь xml - объект Microsoft.XMLDOM):

xml.setProperty("SelectionNamespaces", "xmlns:myns='http://myns.com/ns'");
xml.setProperty("SelectionLanguage", "XPath");
alert(xml.documentElement.selectNodes("//myns:MyElement").length);

пятница, 18 апреля 2014 г.

Spring Beans: свой bean вместо String

Проблема

В xml-конфиге спринговых бинов есть некий бин, принимающий аргументом конструктора строку:
<constructor-arg index="0">
<value>xxx</value>
</constructor-arg>
 Хочется эту строку формировать своим бином вместо хардкода ее в xml.

Решение

Первая идея - унаследоваться от String и заменить <value> на <bean class="my.super.Bean">. Но нет - java.lang.String объявлен как final. Следующая мысль - использовать конструктор класса String, принимающий StringBuilder, и опять таки переопределить SpringBuilder своим классом. Но снова неудача - java.lang.StringBuilder также является final. Но StringBuilder уже лучше String'а тем, что имеет конструктор, принимающий CharSequence. А это - уже интерфейс, и его можно свободно имплементировать, реализовав ту логику, которую нужно. В итоге получаем нечто подобное:
<constructor-arg index="0">
<bean class="java.lang.String">
<constructor-arg>
<bean class="java.lang.StringBuilder">
<constructor-arg>
<bean class="my.super.CharSequenceImpl">
<!-- ... -->
</bean>
</constructor-arg>
</bean>
</constructor-arg>
</bean>
</constructor-arg>

вторник, 17 декабря 2013 г.

WSO2 ESB JMS-прокси: использование transport.jms.ContentType для POX и SOAP-сообщений в очереди

Вопрос

Пусть есть некий JMS-to-XXX прокси-сервис (в роли XXX может быть HTTP или что-то более экзотическое). Вопрос: какое значение нужно устанавливать в параметре transport.jms.ContentType сервиса в зависимости от того, что лежит в JMS: готовое SOAP-сообщение или только полезная нагрузка (т.е. POX):
    <parameter name="transport.jms.ContentType">
        <rules xmlns="http://ws.apache.org/ns/synapse">
            <jmsProperty>contentType</jmsProperty>
            <default>???</default>
        </rules>
    </parameter>
Ответ
  • Для SOAP 1.1: text/xml
  • Для SOAP 1.2: application/soap+xml
  • Для POX: application/xml
(насчет SOAP 1.2 точно не уверен - сам не тестировал)

ЗЫ Описанное выше справедливо и для VFS-to-XXX прокси-сервиса.

четверг, 5 сентября 2013 г.

WSO2 ESB: получение значения Local Entry типа "Inline XML" внутри прокси-сервиса и работа с ним как с XML

Задача

Есть Local Entry типа "Inline XML", содержащая некоторый XML. Необходимо в прокси-сервисе извлечь эту XML и обработать ее, например, извлечь значение определенного узла с помощью XPath.

Решение

Для начала получаем значение Local Entry (пусть она называется 'localEntryName') как XML в свойство (пусть будет "localEntryXml"):
<property action="set" expression="get-property('localEntryName')" name="localEntryXml" scope="default" type="OM"/>
Здесь важно, что тип свойства указан "OM".
Теперь с этим свойством можно оперировать, например, выполнять на основе него фильтрацию:
<filter xpath="count($localEntryXml/*[text()='qwerty'])">
     <!-- -->
</filter>

понедельник, 18 февраля 2013 г.

WSO2 ESB: замена пространств имен в сообщении перед проксированием на конечный сервис

Задача:

Во входящем сообщении перед его проксированием на конечный сервис у всех элементов в пространстве имен "http://ns1" установить пространство имен "http://ns2".

Решение:

Возможно, можно как-то лучше, но пока реализовал так:

<script language="js"><![CDATA[
var targetNs = new Namespace("http://ns2");
var srcNs = new Namespace("http://ns1");
var payload = mc.getPayloadXML();
var payloadElems = payload..srcNs::*;
for(i in payloadElems)
{
if(payloadElems[i].namespace()!=null)
{
payloadElems[i].setNamespace(targetNs);
}
}
mc.setPayloadXML(payload);
]]></script>
Проверка пространства имен на null в цикле выполняется из-за того, что в payloadElems попадают зачем-то текстовые узлы, у которых пространство имен равно null и попытка установить его с помощью setNamespace приведет к ошибке. 

понедельник, 28 января 2013 г.

WSO2 ESB 4.5.1: проблема обрезки XML, читаемых с помощью VFS + ApplicationXMLBuilder

Проблема:

Есть прокси сервис VFS->HTTP, читающий XML-сообщения из файлов и отправляющий их по HTTP. Сообщения в файлах лежат без SOAP-обертки, соответственно, ContentType в параметрах сервиса установлен в:
<parameter name="transport.vfs.ContentType">application/xml</parameter>
(в axis2.xml этом типу содержимого соотнесен билдер org.apache.axis2.builder.ApplicationXMLBuilder).
Проблема заключается в том, что объемные сообщение отправляются обрезанными: может не хватать нескольких тегов в конце, в дополнение к чему содержимое последнего из все же переданных тегов также может быть обрезано. При этом корректность XML соблюдается, т.е. содержимое обрезается, а закрывающие теги все равно есть. 
Если класть в файлы сообщения сразу в SOAP-обертке, используя
<parameter name="transport.vfs.ContentType">text/xml</parameter>
, ситуация не меняется.
При этом наблюдается интересная особенность: если логировать сообщение перед отправкой (используя лог-медиатор с уровнем full), то оно и в лог пишется целиком, и на конечный сервис отправляется без обрезки.
Если же попытаться до отправки сообщение не логировать, а логировать после <send/>, то ловим странное:
org.apache.axiom.om.OMException: Parser has already reached end of the document. No siblings found
at org.apache.axiom.om.impl.llom.OMElementImpl.getNextOMSibling(OMElementImpl.java:338)
at org.apache.axiom.om.impl.traverse.OMChildrenIterator.getNextNode(OMChildrenIterator.java:36)
at org.apache.axiom.om.impl.traverse.OMAbstractIterator.hasNext(OMAbstractIterator.java:58)
... at org.apache.axiom.om.impl.llom.OMElementImpl.toString(OMElementImpl.java:988)
at java.lang.String.valueOf(String.java:2854)
at java.lang.StringBuffer.append(StringBuffer.java:232)
at org.apache.synapse.mediators.builtin.LogMediator.getFullLogMessage(LogMediator.java:184)
... at org.apache.synapse.transport.vfs.VFSTransportListener.processFile(VFSTransportListener.java:481)
...
Workaround:

Видимо, проблема связана с какой-то "ленивой" загрузкой входящего XML. При логировании сообщения (и, как оказалось, также при других операциях над сообщением, например,  при выполнении над ним XPath) он все-таки загружается полностью. А вот при выполнении <send/> (а также при любых операциях после него) - нет.
В очередной раз копаться в исходниках и отлаживать ситуацию времени не было. Поэтому нашел простой (временный) workaround для проблемы: перед отправкой обращаться к содержимому сообщения (используя несложный xpath в property-медиаторе):
 <property action="set" name="dummyprop" scope="default" type="STRING" expression="count($body/*)"/>


WSO2 ESB: валидация запросов и ответов по схеме, импортирующей другие схемы

Валидация в WSO2 ESB выполняется Validate-медиатором. Вот только документация, имеющаяся  по нему  на сайте WSO2, неполная: в ней ни слова не говорится о работе с импортируемыми схемами.
В действительности, настройка валидации по схеме, импортирующей другие схемы, аналогична настройке публикации WSDL, ссылающейся на внешние схемы (я про неё писал когда-то очень-очень давно). Ниже я опишу XML-конфигурацию медиатора Validate.
Пусть схема (назовем её MainSchema.xsd), по которой необходимо осуществить валидацию всего тела запроса к сервису, импортирует 2 другие схемы по относительным URL`ам:
  • Service?xsd=ImportedSchema1.xsd
  • Service?xsd=ImportedSchema2.xsd
Во-первых, все 3 схемы нужно разместить в реестре (во встроенном или подключенном GREG). Я их разместил в коллекции conf:/services/TestValidate/.
После этого во входящую цепочку медиации сервиса нужно поместить такой validate-медиатор:
<validate>
<schema key="conf:/services/TestValidate/MainSchema.xsd"/>
<resource location="Service?xsd=ImportedSchema1.xsd"
key="conf:/services/TestValidate/ImportedSchema1.xsd"/>
<resource location="Service?xsd=ImportedSchema2.xsd"
key="conf:/services/TestValidate/ImportedSchema2.xsd"/>
<on-fail>
<!-- Последовательность, выполняемая при ошибке валидации-->
</on-fail>
</validate>
<!-- Последовательность, выполняемая при успешной валидации -->

Как видно, тегами <resource/> устанавливается соответствие между URL`ами импорта и путями к соответствующим импортируемым схемам в реестре. В случае, если импортируемые схемы, в свою очередь, импортируют другие схемы (или перекрестно уже имеющиеся) - их все нужно перечислить как ресурсы (предварительно разместив в реестре).
Атрибутом "source" у тега <validate/> можно задать xpath, выбирающие узлы, подлежащие валидации (в моем случае, при отсутствии этого атрибута, валидируется все тело сообщения).

вторник, 25 декабря 2012 г.

JAX-WS: обертка ответов от разных веб-методов в одинаковые теги

Задача:

В JAX-WS-веб-сервисе (используется имплементация JAX-WS RI)  есть несколько методов, возвращающих объекты одного типа (пусть это будет класс OperResultClass):
@WebMethod(operationName = "operationA")
public OperResultClass operationA(){...}
@WebMethod(operationName = "operationB")
public OperResultClass operationB(){...}
@WebMethod(operationName = "operationC")
public OperResultClass operationC(){...}...
Необходимо, чтобы XML, получаемый на их выходе, выглядел так:
<MyResponseWrapperTag xmlns="http://soap.response">
<MyResponseTag>
<!-- содержимое объекта-ответа -->
</MyResponseTag>
</MyResponseWrapperTag>
Т.е. необходимо задать для всех методов:
  1. Имя наружного тега и его пространство имен
  2. Имя внутреннего тега и его пространство имен

Решение:

Второй пункт делается просто: каждый метод аннотируется следующим образом:
@WebResult(name="MyResponseTag", targetNamespace="http://soap.response")
После этого мы будем иметь правильный внутренний тег, а вот наружный будет генерироваться JAX-WS`ом автоматически, основываясь на имени метода (он будет состоять из имени метода + "Response").

Для задания имени наружного тега используется аннотация @ResponseWrapper. Но использовать её так:
  @ResponseWrapper(
          localName="MyResponseWrapperTag",
          targetNamespace="http://soap.response"
        )
не получится - будет валиться исключение:
Two classes have the same XML type name "{http://soap.response}MyResponseWrapperTag". Use @XmlType.name and @XmlType.namespace to assign different names to them.
Тут дело в том, что для оберток вокруг ответа JAX-WS генерирует свои классы, по одному на каждый метод. Т.о, для каждого веб-метода будет использоваться свой класс, в то время как из-за @ResponseWrapper всем им будет назначен одно и тот же имя XML-типа. 
Выход из ситуации - самостоятельно задавать имя класса-обертки, чтобы JAX-WS не генерировал своих классов. Делается это следующим образом:
@ResponseWrapper(          className="my.MyResponseWrapper",
          localName="MyResponseWrapperTag",
          targetNamespace="http://soap.response"
        )
Теперь нужно создать этот самый класс-wrapper "my.MyResponseWrapper". Он будет выглядеть примерно так:
@XmlType(name="MyResponseWrapperType",namespace="http://soap.response")
@XmlAccessorType(XmlAccessType.FIELD)
public class MyResponseWrapper{
  @XmlElement(name="MyResponseTag",namespace="http://soap.response")
  public OperResultClass MyResponseTag;
}
Здесь важно, чтобы параметры аннотации @XmlElement совпадали с таковыми в аннотации @WebResult метода. Namespace можно не указывать, если он задан на уровне пакета в package-info.java:
@XmlSchema(namespace = "http://soap.response", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package my;
import javax.xml.bind.annotation.XmlSchema;



вторник, 11 декабря 2012 г.

WSO2 ESB 4.5.1: разделитель - новая строка или табуляция в Log-медиаторе

Задача:

Выполнять логирование Log-медиатором с разделителем - новой строкой (чтобы, например, при уровне лога "full" все логируемые параметры сообщения, а также его содержимое, располагались на разных строках).

Решение:

Использовать в XML-конфигурации в качестве разделителя числовую ссылку на символ, соответствующую символу перевода строки (line feed) - "&#x000A;" или табуляции - "&#x0009;":
<log level="full" category="INFO" separator="&#x000A;">
<property name="myprop" expression="synapse:get-property('myprop')"/>
</log>

Carriage return и line feed в виде numeric character reference в XML

Символу Carriage return (CR, \r) соответствует числовая ссылка на символ (numeric character reference, не знаю как менее коряво перевести) "&#x000D;", а line feed (LF, \n) - "&#x000A;".

вторник, 23 октября 2012 г.

Ошибка "org.xml.sax.SAXParseException: The processing instruction target matching [xX][mM][lL] is not allowed" при парсинге XML

Проблема:

При разборе XML-документа валится исключение:
org.xml.sax.SAXParseException: The processing instruction target matching "[xX][mM][lL]" is not allowed.
at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
Возможная причина:

Во входящем документе несколько раз присутствует XML Declaration (например, <?xml version="1.0" encoding="UTF-8" ?>).


понедельник, 17 сентября 2012 г.

JAX-WS: IllegalAnnotationsException "Two classes have the same XML type name" на ровном месте

Проблема:

Есть веб-сервис, построенный на JAX-WS, у которого есть веб-метод, реализующий soap-операцию "op", тип его возврата - OpResponse: 
@WebService(serviceName = " MyService ")
public class MyService
{
...
@WebMethod(operationName = "op")
public OpResponse op(...) {...}
...
}
...
public class OpResponse { ... }
При развертывании этого веб-сервиса валится ошибка:
com.sun.xml.ws.transport.http.servlet.WSServletException: WSSERVLET11: failed to parse runtime descriptor: javax.xml.ws.WebServiceException: Unable to create JAXBContext
...
Caused by: javax.xml.ws.WebServiceException: Unable to create JAXBContext
...
Caused by: java.security.PrivilegedActionException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Two classes have the same XML type name "{...}opResponse". Use @XmlType.name and @XmlType.namespace to assign different names to them.
Хотя вроде бы никаких двух классов с одинаковым XML-типом в проекте нет (впрочем, XML-типы пока явно вообще нигде не фигурируют).

Причина:

Причину ошибки можно объяснить с двух сторон - с точки зрения классов или с точки зрения генерируемых jaxws`ом WSDL/XSD. Я приведу второе объяснение.
Генерируя WSDL, JAX-WS использует имя "opResponse" как имя XSD-типа ответа операции "op" (т.е. он просто конкатенирует имя операции и "Response"), который потом уже, в XSD-схеме, раскрывается с использованием XSD-типа, соответствующего возвращаемому Java-классу:
<operation name="op">
<input wsam:Action="http://.../opRequest" message="tns:op"/>
<output wsam:Action="http://.../opResponse" message="tns:opResponse"/>
</operation>
<xs:complexType name="opResponse">
<xs:sequence>
<xs:element name="return" type="tns:<responseClassName>" minOccurs="0"/>
</xs:sequence>
</xs:complexType> 
Здесь <responseClassName> - это имя класса, который является типом возврата метода "op", написанное со строчной первой буквы. В нашем случае это имя должно было бы быть равным снова "opResponse", т.к. тип возврата метода "op" - именно класс OpResponse, что сделало бы XSD-схему некорректной.

Решение:

Аннотировать класс OpResponse с помощью аннотации @XmlType, как и советует JAX-WS, так, чтобы имя XSD-типа, соответствующего классу OpReponse, отличалось от "opResponse", например:
@XmlType(name = "OpResponseType")
public class OpResponse {...}
Тогда схема примет вид:
...
<xs:complexType name="opResponse">
 <xs:sequence>
  <xs:element name="return" type="tns:OpResponseType" minOccurs="0"/>
 </xs:sequence>
</xs:complexType>
...
и веб-сервис стартанет без проблем.