8.16.2016

Web Service – JAX-WS, сложные типы данных, JAXB.


План такой, давайте познакомимся с простым примером реализации web  сервиса, посредством библиотеки apahce cxf, потом  посмотрим как использовать JAXB. Иногда нужно очень быстро развернуть web  сервис для теста, опровергнуть или дать положительные ответы на волнующие вас вопросы. Для этого я использую стандартную заготовку,  maven проект который позволяет мне не настраивать сервер приложений, допустим wildfly 8 как в прошлой статье, а пользоваться легковесным сервером jetty,  разместить на нем наш web сервис и создать простое клиентское приложение.  И как приступим к написанию web  сервиса, напишем web  сервис, его  реализацию и интерфейс, сервис делает простую вещь, получает строку от клиента,  прибавляет фразу к строке, полученный результат отсылает клиенту:
package com.lopanov;

import javax.jws.WebService;

@WebService
public interface HelloWorld {
    String sayHi(String text);
}

Реализация интерфейса посредством класса:

package com.lopanov;

import org.apache.cxf.annotations.Logging;
import javax.jws.WebService;

@WebService(endpointInterface = "com.lopanov.HelloWorld")
@Logging
public class HelloWorldImpl implements HelloWorld {

    public String sayHi(String text) {
        return "Hello " + text;
    }
}

Т. к.  мы будем разворачивать наш web  сервис как web  приложение, то нам понадобится  дескриптор развертывания web.xml  и в нем нам будет нужно настроить все для работы библиотеки  apache cxf. Подключаем с начало Spring  посредством слушателя (listener), т. к. cxf написана с использование этой библиотеки, и во вторых нужно подключить сервлет который будет обрабатывать все наши запросы и работа которого сводится к передаче  запросов на обработку нашему web  сервису и передачи ответов от web сервиса клиенту. Смотрим на файл web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
        PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd">


<web-app>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/beans.xml</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <display-name>CXF Servlet</display-name>
        <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

Т. к. мы работаем со  Spring,  то нам нужен еще и файл настроек контекста Spring: WEB-INF/beans.xml. Он будет таким:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jaxrs="http://cxf.apache.org/simple"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/simple http://cxf.apache.org/schemas/simple.xsd">

    <!-- импортируем настройки apache cxf  -->
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-jaxws.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />  

</beans>

Все настроили web  сервис, давайте его запустим и напишем простой клиент который вызовет операцию sayHi():

package com.lopanov;

import javax.xml.namespace.QName;
import javax.xml.ws.Endpoint;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

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

    public static void main(String args[]) throws Exception {
        System.out.println("Запустим сервер jetty и наш web  сервис");
        HelloWorldImpl implementor = new HelloWorldImpl();
        String address = "http://localhost:9000/helloWorld";
        //опубликуем наш web сервис
        Endpoint.publish(address, implementor);

        Service service = Service.create(SERVICE_NAME);//создадим сервис для подключения клиента
        // Endpoint Address
        String endpointAddress = "http://localhost:9000/helloWorld";
        //добавим port в Service
        service.addPort(PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress);

        HelloWorld hw = service.getPort(HelloWorld.class);
        System.out.println(hw.sayHi("World"));//вызовем операцию web сервиса
        System.exit(0);
    }
}

В итоге мы получим такой вывод:

Starting Server
[main] INFO org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean - Creating Service {http://lopanov.com/}HelloWorldImplService from class com.lopanov.HelloWorld
[main] INFO org.apache.cxf.endpoint.ServerImpl - Setting the server's publish address to be http://localhost:9000/helloWorld
[main] INFO org.eclipse.jetty.util.log - Logging initialized @890ms
[main] INFO org.eclipse.jetty.server.Server - jetty-9.2.15.v20160210
[main] WARN org.eclipse.jetty.server.handler.AbstractHandler - No Server set for org.apache.cxf.transport.http_jetty.JettyHTTPServerEngine$1@7c137fd5
[main] INFO org.eclipse.jetty.server.ServerConnector - Started ServerConnector@2a8448fa{HTTP/1.1}{localhost:9000}
[main] INFO org.eclipse.jetty.server.Server - Started @989ms
[main] WARN org.eclipse.jetty.server.handler.ContextHandler - Empty contextPath
[main] INFO org.eclipse.jetty.server.handler.ContextHandler - Started o.e.j.s.h.ContextHandler@38467116{/,null,AVAILABLE}
[main] INFO org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean - Creating Service {http://lopanov.com/}HelloWorld from class com.lopanov.HelloWorld
[main] INFO org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean - Creating Service {http://lopanov.com/}HelloWorld from class com.lopanov.HelloWorld
Hello World

Process finished with exit code 0

У нас все в шоколаде, в одной консольной программе мы разворачиваем наш web сервис, дальше запускаем клиента, нам больше “не нужны” неповоротливые сервера приложений – типа wildfly8.  Переходим ко второй части статьи, а именно к сложным типам данных. В нашем примере мы использовали тип данных String, который преобразовывался в  тип  xml schema “xs:string”,  и передавался посредством xml тега. Напоминаю вам, что вся информация в SOAP  сообщениях представлена в виде xml, и если нам нужно передать какой либо java объект, то его нужно представить как JAXB  объект. Напишем наш класс Person, и пометим его аннотациями JAXB, о них я рассказывал недавно , смотрим на код:

package com.lopanov;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.GregorianCalendar;

@XmlRootElement
@XmlAccessorType(value = XmlAccessType.FIELD)
public class Person {
    public String Name;
    public String SoName;
    public GregorianCalendar berthDay;

    public Person() {
    }

    public Person(String name, String soName, GregorianCalendar berthDay) {
        Name = name;
        SoName = soName;
        this.berthDay = berthDay;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public String getSoName() {
        return SoName;
    }

    public void setSoName(String soName) {
        SoName = soName;
    }

    public GregorianCalendar getBerthDay() {
        return berthDay;
    }

    public void setBerthDay(GregorianCalendar berthDay) {
        this.berthDay = berthDay;
    }

    @Override
    public String toString() {
        return "Person{" +
                "Name='" + Name + '\'' +
                ", SoName='" + SoName + '\'' +
                ", berthDay=" + berthDay +
                '}';
    }
}

Перепишем наш web сервис, он будет отсылать клиенту уже не простую строку результата, а наш JAXB  объект Person:

package com.lopanov;

import javax.jws.WebService;

@WebService
public interface HelloWorld {
    Person sayHi(String text);
}

Класс реализации web  сервиса:

package com.lopanov;

import org.apache.cxf.annotations.Logging;
import javax.jws.WebService;
import java.util.GregorianCalendar;

@WebService(endpointInterface = "com.lopanov.HelloWorld")
@Logging
public class HelloWorldImpl implements HelloWorld {

    public Person sayHi(String text) {
        Person person = new Person(text,"Lopanov", new GregorianCalendar(1981,8,23));
        return person;
    }
}

Клиентский код не изменился, запустим его,  в SOAP ответе нам придет сложный объект Person представленный тремя тегами, обёрнутыми в тег <return>, их я пометил желтым цветом, смотрим  лог:

….
[qtp1412601264-17] INFO org.apache.cxf.services.HelloWorldImplService.HelloWorldImplPort.HelloWorld - Outbound Message
---------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHiResponse xmlns:ns2="http://lopanov.com/">
<return>
<Name>World</Name>
<SoName>Lopanov</SoName>
<berthDay>1981-09-23T00:00:00+04:00</berthDay>
</return>
</ns2:sayHiResponse></soap:Body></soap:Envelope>
….
На этом все и на последок, мой maven pom.xml c зависимостями:

<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>jaxws-cxf_1</groupId>
    <artifactId>cxf-first</artifactId>
    <version>1.0</version>
    <packaging>war</packaging>
    <name>Simple CXF Java-first SOAP project using Spring configuration</name>
    <description>Simple CXF Java-first SOAP project using Spring configuration</description>
    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>3.1.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.1.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>3.1.7</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.21</version>
        </dependency>

        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-rt</artifactId>
            <version>2.2.10</version>
        </dependency>

    </dependencies>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <!-- mvn clean install tomcat:run-war to deploy
                    Look for "Running war on http://xxx" and
                    "Setting the server's publish address to be /yyy"
                    in console output; WSDL browser address will be
                    concatenation of the two: http://xxx/yyy?wsdl
                    -->
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.0</version>
                    <executions>
                        <execution>
                            <id>start-tomcat</id>
                            <goals>
                                <goal>run-war</goal>
                            </goals>
                            <phase>pre-integration-test</phase>
                            <configuration>
                                <port>${test.server.port}</port>
                                <path>/webservice</path>
                                <fork>true</fork>
                                <useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.7</source>
                        <target>1.7</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-eclipse-plugin</artifactId>
                    <configuration>
                        <projectNameTemplate>[artifactId]-[version]</projectNameTemplate>
                        <wtpmanifest>true</wtpmanifest>
                        <wtpapplicationxml>true</wtpapplicationxml>
                        <wtpversion>2.0</wtpversion>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

</project>

Комментариев нет:

Отправить комментарий