понедельник, 27 октября 2014 г.

BPEL Tool: шаги по тонкому льду



Пошаговая видео-инструкция:
http://www.oasis-open.org/events/webinars/
http://ode.apache.org/ws-bpel-20.html
http://ode.apache.org/index.html

Начало в ней немного устарело, но его можно выхватить из других источников.

По умолчанию в процессе  создаётся блок FIX_ME-Add_Business_Logic_Here. Его нужно убрать, чтобы не думать, почему приходит такая ошибка:

<soapenv:Fault xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <faultcode>wsa:ActionNotSupported</faultcode>
   <faultstring>The [action] cannot be processed at the receiver.</faultstring>
   <detail/>
</soapenv:Fault>

Нельзя использовать не инициализированные переменные.
http://stackoverflow.com/questions/8629088/bpel-and-selectionfailure-error

Даже дефолтный выходной параметр нужно инициализировать. Для этого добавить Assign. Справа выбрать этот параметр и щёлкнуть куда-нибудь ещё. Появится окно, предлагающее сгенерировать иницализацию выходного параметра.






В противном случае будет ошибка:

<soapenv:Fault xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <faultcode>soapenv:Server</faultcode>
   <faultstring>axis2ns18:uninitializedVariable</faultstring>
   <detail/>
</soapenv:Fault>

пятница, 24 октября 2014 г.

IP вместо имени хоста

Чтобы вместо имени хоста в WSDL попадал IP, нужно изменить настройки axis2.xml разделе Transport Ins
http://stackoverflow.com/questions/10943176/how-to-change-the-binding-hostname-in-wsdl-when-use-the-wso2-esb
    <!-- ================================================= -->
    <!--             Transport Ins (Listeners)             -->
    <!-- ================================================= -->
<transportReceiver name="http" class="org.apache.synapse.transport.passthru.PassThroughHttpListener">
<parameter name="port" locked="false">8280</parameter>
<parameter name="non-blocking" locked="false">true</parameter>
<!--parameter name="bind-address" locked="false">hostname or IP address</parameter-->
<parameter name="WSDLEPRPrefix" locked="false">http://192.168.4.107:8280/services</parameter>
<parameter name="httpGetProcessor" locked="false">org.wso2.carbon.transport.nhttp.api.PassThroughNHttpGetProcessor</parameter>
<!--<parameter name="priorityConfigFile" locked="false">location of priority configuration file</parameter>-->
</transportReceiver>
<transportReceiver name="https" class="org.apache.synapse.transport.passthru.PassThroughHttpSSLListener">
<parameter name="port" locked="false">8243</parameter>
<parameter name="non-blocking" locked="false">true</parameter>
<!--parameter name="bind-address" locked="false">hostname or IP address</parameter-->
<parameter name="WSDLEPRPrefix" locked="false">https://192.168.4.107:8247/services</parameter>
<parameter name="httpGetProcessor" locked="false">org.wso2.carbon.transport.nhttp.api.PassThroughNHttpGetProcessor</parameter>
<parameter name="keystore" locked="false">

Невалидный WSDL

Имеется сервис с невалидным WSDL.

0][1]src-resolve.4.2: Error resolving component 's:schema'. It was detected that 's:schema' is in namespace 'http://www.w3.org/2001/XMLSchema', but components from this namespace are not referenceable from schema document 'http://192.168.4.185/tcwebservice.asmx?WSDL'. If this is the incorrect namespace, perhaps the prefix of 's:schema' needs to be changed. If this is the correct namespace, then an appropriate 'import' tag should be added to 'http://192.168.4.185/tcwebservice.asmx?WSDL'. WSDL DOCUMENT IS INVALID

BPS процесс отмечен в Eclipse красным цветом.

ESB с этим сервисом работала нормально.

Создал WSDL-прокси через веб-интерфейс. 

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="proxy_f1"
       transports="https,http"
       statistics="disable"
       trace="disable"
       startOnLoad="true">
   <target>
      <outSequence>
         <send/>
      </outSequence>
      <endpoint>
         <wsdl service="TCWebService"
               port="TCWebServiceSoap"
               uri="http://192.168.4.185/tcwebservice.asmx?WSDL"/>
      </endpoint>
   </target>
   <description/>
</proxy>

У него всего одна операция - mediate с аргументом content.


среда, 22 октября 2014 г.

Выпарсиватель на JavaScript

HTML, который пришёл в письме содержит вместо угловых скобок тэгов заменители. Тэг <td> выглядит как &lt;td&gt;

Извлечь нужное значение при помощи XPath не удаётся.
//m:td
https://docs.wso2.com/display/ESB481/Synapse+XPath+Variables
http://www.w3schools.com/xpath/default.asp

Если речь об одном числе, то можно использовать медиатор Script и разобрать в нём почтовое сообщение при помощи регулярного выражения.

https://docs.wso2.com/display/ESB481/Script+Mediator

<script language="js"> 
var f1TaskId = mc.getPayloadXML().text().match(/TaskID=(\d+)/m)[1]; 
mc.setProperty('F1TaskId', f1TaskId); 
print(f1TaskId);
</script>

<log>
      <property name="F1TaskId" expression="get-property('F1TaskId')"/> 

</log>

Здесь из контекста SOAP-сообщения mc мы извлекаем собственно его содержимое методом getPayloadXML. Получаем объект XML c единственным элементом, в котором сидит HTML письма. Извлекаем из объекта строку методом text и применяем к ней регулярное выражение с помощью match. Из массива найденных вариантов берём второй с индексом 1. Это ID задачи, ограниченный скобками внутри регулярного выражения - (\d+).

Далее создаём новое свойство в контексте SOAP-сообщения методом setProperty. Это равноценно использованию медиатора Property.

В следующих медиаторах мы можем получить извлечённый из текста письма идентификатор задачи через get-expression, как продемонстрировано медиатором Log.

Сервис.Net


При отправке на веб-сервис, написанный на .Net, необходимо чтобы в последовательности был медиатор Header.
<header name="Action" value="http://taskcenter.ru/GetFullTaskInfo"/>
Иначе ошибка:
System.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: urn:mediate. at System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest() at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message) at System.Web.Services.Protocols.SoapServerProtocol.Initialize() at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)
http://stackoverflow.com/questions/11921727/wso2-esb-mediate-soap-services

SOAP-сообщение:

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<m:GetFullTaskInfo xmlns:m="http://taskcenter.ru/">
<m:taskId>10350</m:taskId>
<m:adminUserId>1</m:adminUserId>
</m:GetFullTaskInfo>
</soapenv:Body>
</soapenv:Envelope>

вторник, 21 октября 2014 г.

Смотрим в базу

Работа через подключение к базе, а не через приложение - это, конечно, грязный хак и костыль.

Подключился к MS SQL Server из медиатора DBLookup

Приблизительно так:

<dblookup xmlns="http://ws.apache.org/ns/synapse">
   <connection>
      <pool>
         <password>wso2esb</password>
         <user>wso2esb</user>
         <url>jdbc:jtds:sqlserver://192.168.4.185:1433/D10Task2;useLOBs=false</url>
         <driver>net.sourceforge.jtds.jdbc.Driver</driver>
      </pool>
   </connection>
   <statement>
      <sql>
         <![CDATA[SELECT [Description] FROM [Tasks] WHERE [TaskId]=10339;]]></sql>
         <result name="F1TaskDescription" column="Description"></result>
      </statement>
   </dblookup>

О тонкостях настройки базы данных можно почитать в том месте документации, где говорят о подключении MS SQL Server к Карбону вместо H2.

Великое тайное знание заключается в том, что что нужно скачать драйвер базы по указанной там ссылке и положить в папку <PRODUCT_HOME>/repository/conf/datasources/. Сервисную шину после этого необходимо перезапустить.

Важно указать в строке подключения к MS SQL Server параметр ";useLOBs=false", чтобы драйвер базы данных возвращал строки, а не ссылки на объекты для некоторых типов полей.
http://stackoverflow.com/questions/2130375/error-with-varcharmax-column-when-using-net-sourceforge-jtds-jdbc-driver



понедельник, 20 октября 2014 г.

WSO2 ESB Class Mediator

Создал виртуальную машину Ubuntu 14.04 x64

Прописал настройки прокси для apt-get

Установил Java 7 от Oracle

Установил Maven

Определил переменные среды http_proxy, https_proxy, ftp_proxy, socks_proxy, JAVA_HOME, M2_HOME, MAVEN_OPTS, M2, PATH

Скачал Eclipse Luna для Java-разработчиков x64 под Линукс

Настроил прокси в Eclipse
Window->Preferences->General->Network Connections

Настроил Maven в Еclipse
Window->Preferences->Maven->Installations
Window->Preferences->Maven->User Settings

Создал проект Maven
- поставил галку на Create Simple Project
- в поле GroupId написал ru.tplatforms от названия организации (дефисы запрещены)
- в поле ArtifactId написал выдуманное мной имя пакета wso2.esb.mediator

Внёс изменения в pom-файл в корне проекта.

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>ru.tplatforms</groupId>
  <artifactId>wso2.esb.mediator</artifactId>
  <version>0.0.1-SNAPSHOT</version>
 
    <repositories>
       <repository>
           <id>wso2-maven2-repository</id>
           <url>http://dist.wso2.org/maven2</url>
       </repository>
       <repository>
           <id>apache-Incubating-repo</id>
           <name>Maven Incubating Repository</name>
           <url>http://people.apache.org/repo/m2-incubating-repository</url>
       </repository>
       <repository>
           <id>apache-maven2-repo</id>
           <name>Apache Maven2 Repository</name>
           <url>http://repo1.maven.org/maven2/</url>
       </repository>
   </repositories>


    <dependencies>
        <dependency>
            <groupId>org.apache.synapse</groupId>
            <artifactId>synapse-core</artifactId>
            <version>2.1.1-wso2v7</version>
        </dependency>
    </dependencies>


</project>


Добавлены адреса репозиториев, из которых Maven подгрузит недостающий код. Определена зависимость от пакета org.apache.synapse. Он лежит в основе сервисной шины WSO2. Зависимость добавлял через вкладку Dependencies.

Создал пустой Java-класс EncodingMediator, выбрав соответствующую опцию в контекстном меню проекта

package wso2.esb.mediator;

import org.apache.synapse.MessageContext;
import org.apache.synapse.Mediator;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.xml.namespace.QName;

public class EncodingMediator implements Mediator {

    private static final Log log = LogFactory.getLog(EncodingMediator.class);
   
    private String incomingEncoding;
    private String outgoingEncoding;

    public EncodingMediator(){}

    public boolean mediate(MessageContext mc) {
       
        System.out.println("Encoding Mediator -------------------------------------------------------------------------------------.");
       
       
        return true;
    }

    public String getType() {
        return null;
    }
    public void setTraceState(int traceState) {
        traceState = 0;
    }
    public int getTraceState() {
        return 0;
    }
    public void setIncomingEncoding(String newIncomingEncoding) {
        incomingEncoding = newIncomingEncoding;
    }
    public String getIncomingEncoding() {
        return incomingEncoding;
    }
    public void setOutgoingEncoding(String newOutgoingEncoding){
        outgoingEncoding = newOutgoingEncoding;
    }
    public String getOutgoingEncoding(){
        return outgoingEncoding;
    }
    public String getDescription() {
        // TODO Auto-generated method stub
        return null;
    }
    public void setDescription(String arg0) {
        // TODO Auto-generated method stub
       
    }
    public boolean isContentAware() {
        // TODO Auto-generated method stub
        return false;
    }
}


В этом классе определены два нужных мне свойства incomingEncoding и outgoingEncoding, а также геттеры и сеттеры для них setIncomingEncoding, getIncomingEncoding, setOutgoingEncoding, getOutgoingEncoding. В классе присутствует пустой пока конструктор. Самое интересное будет происходить в методе mediate. Пока что в нём лишь вывод на экран некоторой заметной издали строки, которая нужна для проверки работоспособности медиатора.

Закрыл проект без удаления файлов

Запустил в папке проект mvn eclipse:eclipse

Импортировал проект обратно в Eclipse

Выполнил в папке проекта mvn install

Перенёс созданный файл из подпапки проекта target в WSO2 ESB /repository/components/lib

Удалил ненужное расширение zip в конце файла

Перезапустил сервисную шину

Добавил медиатор Class в одну из имеющихся последовательностей


Увидел в консоли, что медиатор работает



Ссылки:
https://docs.wso2.com/display/ESB481/Class+Mediator
https://docs.wso2.com/display/ESB481/Sample+380%3A+Writing+your+own+Custom+Mediation+in+Java
http://mohamednabeel.blogspot.ru/2014/06/writing-simple-class-mediator-and.html
https://docs.wso2.com/display/ESB481/Writing+Custom+Mediator+Implementations
http://synapse.apache.org/apidocs/org/apache/synapse/MessageContext.html


Хитрый Maven

В предыдущих статьях, которые касались сборки из исходников, уже упоминалась система Maven. Сборка запускалась в них из командной строки: mvn clean install

Сейчас настраивал виртуальную машину Ubuntu 14 под работу с Eclipse Luna для создания собственных медиаторов на Java. Потребовалось создать в IDE проект Maven, чтобы подтянуть зависимости Synapse. После создания проекта появляется ошибка:

Could not calculate build plan: Plugin org.apache.maven.plugins:maven-resources-plugin:2.6 or one of its dependencies could not be resolved: Failed to read artifact descriptor for org.apache.maven.plugins:maven-resources-plugin:jar:2.6 Plugin org.apache.maven.plugins:maven-resources-plugin:2.6 or one of its dependencies could not be resolved: Failed to read artifact descriptor for org.apache.maven.plugins:maven-resources-plugin:jar:2.6

Оказалось, что в настройках Eclipse, нужно изменить путь к settings.xml, который по умолчанию принимается за /home/u/.m2/settings.xml

Проблема ушла, когда в меню Windows->Preferences->Maven->User Settings->User Settings указал верный путь: /usr/local/apache-maven/apache-maven-3.0.5/conf/settings.xml

Выполнил ещё один комплекс пассов, чтобы оживить, наконец, Eclipse:
1. Закрыл проект без удаления файлов
2. Удалил все файлы из репозитория Maven
3. Запустил в папке проекта mvn eclipse:eclipse
4. Импортировал проект в Eclipse

Лет через 50 программирование совсем перестанет отличаться от шаманства.

вторник, 7 октября 2014 г.

Почтовый сервер для опытов

В одной из следующих статей планирую рассказать, как с помощью шины WSO2 ESB выполнять мониторинг электронной почты. Сейчас же о том, как на локальной машине с Windows установить почтовый сервер. Удобен в использовании hMailServer. Ниже ссылка с инструкцией:

https://www.hmailserver.com/documentation/v5.5/?page=scenario_single_server_static_ip

Последовательность действий такая.

Загрузить установочный файл.


 



 Запускаем установочный файл на исполнение.





 
 



В качестве пароля я ввёл 12345, т.к. пароль должен быть не менее 5 символов в длину.

 
 
 

 Установка программы окончена.

Настроить почтовый сервер.




 Определить почтовый домен. В моём случае это mail.local, потому что имя домена должно состоять из двух частей (или более) и потому что я хочу, чтобы был адрес esb@mail.local.


Переходим к созданию учётных записей.



Пароль любой, но я оставил 12345, чтобы меньше путаться. Имя учётки - esb@mail.local.

 

Настройка сервера завершена.

Убедиться, что брандмауер не блокирует почтовые порты.


 Мне беспокоиться не о чем, потому что брандмауер у меня отключён полностью. Лучше выполнить более тонкую настройку.


 Настройка брандмауера завершена.

Настроить почтовый клиент.

Открываю Outlook->Файл->Сведения->Настройка учётных записей->Настройка учётных записей...


 Важно! В поле "Пользователь" нужно вписать почтовый адрес целиком.

Выполнить проверку.



Почтовый сервер работает.

Для сведения. MX-записи настраиваются как-то иначе.


Для проверки есть встроенный инструмент.



























































































среда, 24 сентября 2014 г.

Из пустого в порожнее

Статья о том, как перекладывать файлы. По мотивам вебинара "SOA Pattern: File Gateway". Задача состоит в том, чтобы переложить пустой файл из одной директории в другую при помощи WSO2 ESB.

Для перемещения файлов нужно использовать VFS-транспорт. По умолчанию он заблокирован и не отображается в списке доступных протоколов передачи при создании прокси-сервисов.

Открываем файл <ESB_HOME>/repository/conf/axis2/axis2.xml и раскомментируем две строчки. Первую  - в разделе входящих соединений "Transport Ins (Listeners)":

<transportReceiver name="vfs" class="org.apache.synapse.transport.vfs.VFSTransportListener"/>

Вторую - в разделе исходящих соединений "Transport Outs (Senders)":

<transportSender name="local" class="org.wso2.carbon.core.transports.local.CarbonLocalTransportSender"/>

Теперь при создании нового прокси-сервиса появляется опция vfs.



Будем добавлять параметры, чтобы настроить передачу файлов. Задаём интервал опроса файловой системы:
transport.PollInterval = 2000ms


Подобным образом добавляем следующие параметры.
Папка источник:
transport.vfs.FileURI = file://D:/q/in
Шаблон названия файла:
transport.vfs.FileNamePattern = .*\.txt
Тип содержимого файлов:
transport.vfs.ContentType = text/plain
Действие после обработки файла:
transport.vfs.ActionAfterProcess = MOVE
transport.vfs.MoveAfterProcess = file://D:/q/processed
Действие в случае ошибки при обработки файла:
transport.vfs.ActionAfterFailure = MOVE
transport.vfs.MoveAfterFailure = file://D:/q/failure 

Если перемещать обработанные файлы не нужно, пишем DELETE вместо COPY и надобность в указании папок для перемещаемых файлов в этом случае отпадает (см. здесь)


Переходим к созданию последовательностей медиаторов.




 Ограничимся созданием встроенной входящеё последовательности.



 Определим сначала свойства медиатора Send, который зададим позже. Важно, чтобы свойства предшествовали мадиатору, т.к. мы находимся внутри последовательности. Если свойства будут определены после медиатора, в котором они используются, их значения будут недоступны.


Свойство OUT_ONLY = true  говорит шине о том, что не нужно ждать ответа от сервиса, которому отправлено сообщение.



 Добавим ещё один медиатор Property и запишем в него имя обрабатываемого файла.


 Извлекаем имя фала с помощью выражения:  
get-property('transport', 'FILE_NAME')
 


Создадим третий медиатор Property.
Передаём определяем имя конечного файла, который запишет VFS. Если не указать параметр transport.vfs.ReplyFileName, то будет создан файл с именем по умолчанию - response.xml.
Выполняемый трюк с перебросом имени файла описан в нескольких источниках:
 http://stackoverflow.com/questions/10492050/wso2-how-to-set-replyfilename-in-vfs

 

 Создадим теперь медиатор Send, который запишет обрабатываемый файл в новое место.


Конечная точка (Endpoint) будет у нас встроенной, чтобы сократить число шагов.


 Выбираем адресную конечную точку (Address Endpoint).


 В качестве адреса передаём путь к директории, в которую будем складывать файлы:
 vfs:file://D:\q\out
 Префиксы vfs: и file: означают, что мы будем работать с файловой системой через VFS.


 Запоминаем настройки медиатора Send, содержащего созданную только что конечную точку.


 Сохраняем последовательность.





 Завершаем определение прокси-сервиса.








 В итоге получилась такая конфигурация:

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="fileproxy"
       transports="vfs"
       statistics="disable"
       trace="disable"
       startOnLoad="true">
   <target>
      <inSequence>
         <property name="OUT_ONLY" value="true" scope="default" type="STRING"/>
         <property name="filename"
                   expression="get-property('transport', 'FILE_NAME')"
                   scope="default"
                   type="STRING"/>
         <property name="transport.vfs.ReplyFileName"
                   expression="get-property('filename')"
                   scope="transport"
                   type="STRING"/>
         <send>
            <endpoint>
               <address uri="vfs:file://D:\q\out"/>
            </endpoint>
         </send>
      </inSequence>
   </target>
   <parameter name="transport.vfs.ActionAfterProcess">MOVE</parameter>
   <parameter name="transport.PollInterval">2000ms</parameter>
   <parameter name="transport.vfs.MoveAfterProcess">file://D:/q/processed</parameter>
   <parameter name="transport.vfs.FileURI">file://D:/q/in</parameter>
   <parameter name="transport.vfs.MoveAfterFailure">file://D:/q/failure</parameter>
   <parameter name="transport.vfs.FileNamePattern">.*\.txt</parameter>
   <parameter name="transport.vfs.ContentType">text/plain</parameter>
   <parameter name="transport.vfs.ActionAfterFailure">MOVE</parameter>
   <description/>
</proxy>
                                

Ссылки на документацию:
https://docs.wso2.com/display/ESB481/VFS+Transport
https://docs.wso2.com/display/ESB481/Configuring+Transports