8.31.2016

Web Service – JAX-WS, используем WS-Policy, включаем MTOM.



Продолжим серию статей про web  сервисы, здесь я вам расскажу про технологию WS-Policy. WS-Policy  это стандарт который позволяет внедрять  в файл  описания wsdl новый функционал, т.е.  мы вносим в логику web  сервиса новые правила поведения. И так по умолчанию в нашем примере механизм MTOM выключен, мы посредством политики включим его, он нужен нам для оптимизации передачи бинарных данных. Подробно, как включать на уровне java  кода MTOM, читайтев моей прошлой статье. Я для примера буду использовать Oracle Weblogic 12c application server, т. к.  в apache CXF я не какими методами не смог включить MTOM  посредством внедрения политики WS-Policy в wsdl  файл, Weblogic позволяет это сделать. Напишем наш web сервис:

package org.lopanov;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public interface Hello {
    @WebMethod
    public String sayHello(String name);
    @WebMethod
    public void getBinary(byte[] b);
}

И класс который реализует его:

package org.lopanov;

import javax.jws.WebService;

@WebService( endpointInterface = "org.lopanov.Hello")
public class HelloWorld implements Hello {


    public String sayHello(String name){
        String result = String.format("Hello, %s!", name);
        System.out.println(result);
        return result;
    }

    public void getBinary(byte[] b) {
           try {
                System.out.write(b);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
}

Соберите наш web  сервис  в .war архив и разверните его на сервере.  Напишем клиента который будет работать с ним:

package org.lopanov;

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;

public class Main1 {
    private static final QName SERVICE_NAME = new QName("http://lopanov.org/", "HelloWorldService");
    private static final QName PORT_NAME = new QName("http://lopanov.org/", "HelloWorldPort");

    public static void main(String args[]) throws Exception {
        Service service = Service.create(new URL("http://169.254.201.140:7001/hello-ws/HelloWorldService?WSDL"), SERVICE_NAME);       
        Hello hw = service.getPort(PORT_NAME, Hello.class);
        File file = new File("c:/1.txt");
        FileInputStream fis = new FileInputStream(file);
        byte[] b = new byte[1024 * 1];
        fis.read(b, 0, b.length);
        hw.getBinary(b);
        //System.out.println(hw.sayHi("World"));
        System.exit(0);
    }
}

Клиент посылает байтовый массив, считанный из файла,  операции  getBinary() web  сервиса, операция распечатывает его в стандартный вывод, при этом используется такое SOAP сообщение:

C:\jdk1.8.0_31\bin\java -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true org.lopanov.Main1

---[HTTP request - http://169.254.201.140:7001/hello-ws/HelloWorldService]---
Accept: text/xml, multipart/related
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://lopanov.org/Hello/getBinaryRequest"
User-Agent: JAX-WS RI 2.2.11-b150616.1732 svn-revision#a247ba216861f2c0baac9a3657c5690bce0c744d
<?xml version='1.0' encoding='UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
  <ns0:getBinary xmlns:ns0="http://lopanov.org/">
     <arg0>PT09 … T24tbA==</arg0>
   </ns0:getBinary>
</S:Body></S:Envelope>--------------------

Заметьте, что данные пересылаются в теле SOAP сообщения <arg0>PT09 … T24tbA==</arg0>,  и у нас не используется технология MTOM. Для включения логирования SOAP  сообщений я использовал параметр -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true при вызове клиента, используйте его только в стеке METRO (Glassfish и Weblogic). Давайте встроим политику в наш wsdl  файл которая включит нам механизм MTOM. Как вы заметили мы используем метод разработки  contract-last”, т.е сначало код, потом генерация файла wsdl, еще этот метод разработки называют “code-first”.  И так нам нужно внедрить файл с политикой ws-mtom, это для Weblogic  делается посредствам двух аннотаций, основной @Policies() которая содержит в себе массив аннотаций @Policy(), которые в свою очередь содержат сылки в параметре uri  на файлы ws-policy, которые будут внедрены в wsdl файл.  Перепишем наш класс используя аннотации:

package org.lopanov;

import weblogic.jws.Policies;
import weblogic.jws.Policy;
import javax.jws.WebService;

@WebService( endpointInterface = "org.lopanov.Hello")
@Policies( { @Policy(uri = "policy:Mtom.xml",attachToWsdl = true,direction = Policy.Direction.both) } )
public class HelloWorld implements Hello {

    public void getBinary(byte[] b) {
           try {
                System.out.write(b);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
}

Нужно сказать про одну особенность  параметра uri, он берет все файлы из деректории WEB-INF/policies  хотя использует префикс “policy uri = "policy:Mtom.xml" ,  смотрите на рисунок.

Создадим файл Mtom.xml:

<?xml version="1.0" encoding="utf-8"?>
<wsp:Policy
        xmlns="http://schemas.xmlsoap.org/wsdl/"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
        xmlns:wsu="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"
        xmlns:wsoma="http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization"
        xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
        xmlns:wsam="http://www.w3.org/2007/01/addressing/metadata"
        wsu:Id="AddNumbersAsynch_policy">
    <wsp:ExactlyOne>
        <wsp:All>
            <wsoma:OptimizedMimeSerialization wsp:Optional="false"  xmlns:wsoma="http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization"/>
        </wsp:All>       
    </wsp:ExactlyOne>
</wsp:Policy>

Расмотрим файл Mtom.xml,  это обычный xml  файл, главный тег <wsp:Policy> в схеме xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"  описаны все возможные теги, их 4 и один атрибут.  Они являются обертками для других тегов из других пространств имен.  Логика в них вложена булевская (boolean).  Включение MTOM  производит тег <wsoma:OptimizedMimeSerialization  из пространства имен xmlns:wsoma="http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization"  Тег  <wsp:ExactlyOne> говорит что должен сработать/включится одно из правил, булевское “ИЛИ”.
 Для примера введем еще тег <wsam:Addressing />:

<wsp:ExactlyOne>
        <wsp:All>
            <wsoma:OptimizedMimeSerialization  wsp:Optional="false" />
        </wsp:All>
ИЛИ
        <wsp:All>
            <wsam:Addressing />
        </wsp:All>
</wsp:ExactlyOne>

Т.е. должно быть включено/сработать правило или <wsoma:OptimizedMimeSerialization /> или  <wsam:Addressing /> или оба сразу. Тег <wsp:All> включает все правила, работает как булевское “И”:

<wsp:All>
  <wsp:ExactlyOne>
            <wsoma:OptimizedMimeSerialization wsp:Optional="false" />
           “И” вложенное правило   
           <wsp:All>
             <wsp:ExactlyOne>
               <wsam:Addressing />
             </wsp:ExactlyOne>
          </wsp:All>
  </wsp:ExactlyOne>
 </wsp:All>

Должны примениться оба правила и <wsoma:OptimizedMimeSerialization /> и <wsam:Addressing />. Атрибут может быть применен к любому тегу  который включает определенную политику <wsoma:OptimizedMimeSerialization wsp:Optional="false" /> и он говорит сервису что любое правило может быть не обязательным, если выставлено в true. Т.е. мы посредством его можем нарушить наши логические  “ИЛИ” ,  “И” тегов <wsp:ExactlyOne>, <wsp:All>, а точнее это наша страховка от тех реализаций web  сервисов которые не поддерживают определенные политики. Например для Apache CXF  не поддерживается политика ws-mtom  и если провернуть этот пример который вам я описываю c Apache CXF, то мы получим такие ошибки:

--------------------------------------
[qtp372469954-16] ERROR org.apache.cxf.ws.policy.PolicyVerificationInInterceptor - Inbound policy verification failed: These policy alternatives can not be satisfied:
{http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization}OptimizedMimeSerialization
[qtp372469954-16] WARN org.apache.cxf.phase.PhaseInterceptorChain - Interceptor for {http://lopanov.com/}HelloWorldImplService#{http://lopanov.com/}getBinary has thrown exception, unwinding now
org.apache.cxf.ws.policy.PolicyException: These policy alternatives can not be satisfied:
{http://schemas.xmlsoap.org/ws/2004/09/policy/optimizedmimeserialization}OptimizedMimeSerialization

Если мы выставим так  <wsoma:OptimizedMimeSerialization wsp:Optional="true" />, то Apache CXF не найдя реализации политики ws-mtom в своих библиотеках, просто проигнорирует  обязательное правило и не выдаст ошибки. Пересоберем проект и заново его развернем, запустим клиента и получим такое SOAP  сообщение:

---[HTTP request - http://169.254.201.140:7001/hello-ws/HelloWorldService]---
Accept: text/xml, multipart/related
Content-Type: multipart/related;start="<rootpart*7d971f60-868e-44ec-98bb-68b0d5cb720e@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:7d971f60-868e-44ec-98bb-68b0d5cb720e";start-info="text/xml"
SOAPAction: "http://lopanov.org/Hello/getBinaryRequest"
User-Agent: JAX-WS RI 2.2.11-b150616.1732 svn-revision#a247ba216861f2c0baac9a3657c5690bce0c744d
--uuid:7d971f60-868e-44ec-98bb-68b0d5cb720e
Content-Id: <rootpart*7d971f60-868e-44ec-98bb-68b0d5cb720e@example.jaxws.sun.com>
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
Content-Transfer-Encoding: binary

<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body>
<ns0:getBinary xmlns:ns0="http://lopanov.org/">
<arg0>
<xop:Include href="cid:56c625fb-2cc0-4188-8654-6e39d4274c50@example.jaxws.sun.com" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
</arg0>
</ns0:getBinary>
</S:Body></S:Envelope>
--uuid:7d971f60-868e-44ec-98bb-68b0d5cb720e
Content-Id: <56c625fb-2cc0-4188-8654-6e39d4274c50@example.jaxws.sun.com>
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary

===========================================================================
 ......Realtek PCIe GBE Family Controller
......VMware Virtual Ethernet Adapter for VMnet1
......VMware Virtual Ethernet Adapter for VMnet8
  1...........................Software Loopback Interface 1
 14...00 00 00 00 00 00 00 e0 ������ Microsoft ISATAP
 15...00 00 00 00 00 00 00 e0 ������ Microsoft ISATAP #2
 16...00 00 00 00 00 00 00 e0 ������ Microsoft ISATAP #3
  127.255.255.255  255.255.255.255         On-l
--uuid:7d971f60-868e-44ec-98bb-68b0d5cb720e----------------------

Из него видно что политика сработала для нашего примера и MTOM включен. Давайте расмотрим файл  WSDL, как в него встроилась наша политика:

<definitions targetNamespace="http://lopanov.org/" name="HelloWorldService">
 <wsp:UsingPolicy wssutil:Required="true"/>
   <wsp1_2:Policy wssutil:Id="Mtom">
     <wsp1_2:ExactlyOne>
       <wsp1_2:All>
         <ns1:OptimizedMimeSerialization/>
       </wsp1_2:All>
     </wsp1_2:ExactlyOne>
   </wsp1_2:Policy>
<types>
  <xsd:schema>
    <xsd:import namespace="http://lopanov.org/" schemaLocation="http://169.254.201.140:7001/hello-ws/HelloWorldService?xsd=1"/>
  </xsd:schema>
</types>
<message name="sayHello">
  <part name="parameters" element="tns:sayHello"/>
</message>
<message name="sayHelloResponse">
  <part name="parameters" element="tns:sayHelloResponse"/>
</message>
<message name="getBinary">
  <part name="parameters" element="tns:getBinary"/>
</message>
<message name="getBinaryResponse">
  <part name="parameters" element="tns:getBinaryResponse"/>
</message>
<portType name="Hello">
  <operation name="sayHello">
     <input wsam:Action="http://lopanov.org/Hello/sayHelloRequest" message="tns:sayHello"/>
     <output wsam:Action="http://lopanov.org/Hello/sayHelloResponse" message="tns:sayHelloResponse"/>
  </operation>
  <operation name="getBinary">
     <input wsam:Action="http://lopanov.org/Hello/getBinaryRequest" message="tns:getBinary"/>
     <output wsam:Action="http://lopanov.org/Hello/getBinaryResponse" message="tns:getBinaryResponse"/>
  </operation>
</portType>
<binding name="HelloWorldPortBinding" type="tns:Hello">
  <wsp:PolicyReference URI="#Mtom"/>
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="sayHello">
      <soap:operation soapAction=""/>
      <input><soap:body use="literal"/></input>
      <output><soap:body use="literal"/></output>
    </operation>
    <operation name="getBinary">
      <soap:operation soapAction=""/>
      <input><soap:body use="literal"/></input>
      <output><soap:body use="literal"/></output>
    </operation>
</binding>
<service name="HelloWorldService">
  <port name="HelloWorldPort" binding="tns:HelloWorldPortBinding">
    <soap:address location="http://169.254.201.140:7001/hello-ws/HelloWorldService"/>
  </port>
</service>
</definitions>

 В файле появились строки из нашего файла Mtom.xml  и появился  четвертый тег <wsp:PolicyReference URI="#Mtom"/>  который применяет политику к уровню “Конечная точка”, тегу <binding болле подробно о теории  ws-policy о привязке,  можно почитать перейдя по сылке http://www.ibm.com/developerworks/ru/library/j-jws18/
И на последок привожу свой maven pom.xml с зависимостями библиотек, определенные зависимости я беру в домашней деректории куда установлен Weblogic, пометил желтым цветом:

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.lopanov</groupId>
    <artifactId>hello-ws</artifactId>
    <packaging>war</packaging>
    <version>1.0</version>
    <name>hello-ws Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>weblogic</groupId>
            <artifactId>weblogic</artifactId>
            <version>12.2.1-0-0</version>
            <scope>system</scope>
            <systemPath>C:/Oracle_Home/wlserver/server/lib/weblogic.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>wls-api</groupId>
            <artifactId>wls-api</artifactId>
            <version>12.2.1</version>
            <scope>system</scope>
            <systemPath>C:/Oracle_Home/wlserver/server/lib/wls-api.jar</systemPath>
        </dependency>
    </dependencies>
    <build>
        <finalName>hello-ws</finalName>
        <plugins>           
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>