JAX-WS Provider和Dispatch

在之前的文章中,涉及到了WebService的搭建。所有的EndPoint均是高度面向对象,面向逻辑了。Server与Client之间交互的消息,均由JAXB转为JAVA类型。如果想对消息的原始数据进行修改,可以使用Handler Chain。

然而,JAXWS也提供了另一种编程方式,Provider和Dispatch,让我们抛开高度抽象的EndPoint和JAXB,抛开了工具wsgen,wsimport,直接面向消息编程。

Provider

介绍

Provider是server端直接面向消息编程的接口。我们先看下Provider接口中的方法:

package javax.xml.ws.Provider
public interface Provider<T> {
  public T invoke(T request);
}

服务端EndPoint必须实现此接口。Provider就像是HttpServlet,invoke()就像是service()。invoke方法参数是接收的原始消息,返回值是返回的消息。

T是对消息封装的一种泛型。结合Provider的Annotation @ServiceMode,它可以为三种封装类型:

  1. javax.xml.transform.Source 将消息中的Payload封装为XML类型Source。适用于@ServiceMode(value=Service.Mode.PAYLOAD)。

  2. javax.xml.soap.SOAPMessage 将消息整体封装为SOAPMessage。适用于@ServiceMode(value=Service.Mode.MESSAGE)。

  3. javax.activation.DataSource

Provider无法使用wsgen命令生成WSDL文件,所以,只能先用WebService Interface生成WSDL,然后再编写Provider。

下面的例子是基于之前的图书馆系统而修改的,所以复用之前图书馆系统的WSDL文件。

创建Java Web Project

创建一个java web project,取名叫LibraryProvider。然后将之前图书馆系统的WSDL文件复制到新项目的WEB-INF目录下面。

JAX-WS Provider和Dispatch_第1张图片

创建Provider

创建java类,LibraryProvider:

@ServiceMode(value=Service.Mode.PAYLOAD)
@WebServiceProvider()
public class LibraryProvider implements Provider<Source> {
    private static int currentId = 0;
    private static Map<Integer, Book> books= new HashMap<Integer, Book>();
    private static class Book {
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAuthor() {
            return author;
        }
        public void setAuthor(String author) {
            this.author = author;
        }
        private int id;
        private String name;
        private String author;
    }

    @Override
    public Source invoke(Source request) {
        try {
            DOMResult dom = new DOMResult();
            Transformer trans = TransformerFactory.newInstance().newTransformer();
            trans.transform(request, dom);
            Node node = dom.getNode();
            Node root = node.getFirstChild();
            String operation = root.getLocalName();
            if ("addRawBook".equals(operation)) {
                return addRawBook(root);
            }
            
            if ("getRawBook".equals(operation)) {
                return getRawBook(root);
            }

            if ("deleteBook".equals(operation)) {
                return deleteBook(root);
            }
            
            return request;
        } catch(Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Error in provider endpoint", e);
        }
    }

    private Source addRawBook(Node root) {
        String name = root.getChildNodes().item(0).getChildNodes().item(0).getNodeValue();
        String author = root.getChildNodes().item(1).getChildNodes().item(0).getNodeValue();
        Book b = new Book();
        b.setName(name);
        b.setAuthor(author);
        b.setId(++currentId);
        books.put(b.getId(), b);
        String body =
                "<ns2:addRawBookResponse xmlns:ns2=\"http://library.mycompany.com\"><id>"
                +currentId
                +"</id></ns2:addRawBookResponse>";
        Source source = new StreamSource(new ByteArrayInputStream(body.getBytes()));
        return source;
    }
    
    private Source getRawBook(Node root) {
        String idString = root.getChildNodes().item(0).getChildNodes().item(0).getNodeValue();
        Book b = books.get(Integer.parseInt(idString));
        String body =
                "<ns2:getRawBookResponse xmlns:ns2=\"http://library.mycompany.com\"><rawBook>"
                +b.toString()
                +"</rawBook></ns2:getRawBookResponse>";
        Source source = new StreamSource(new ByteArrayInputStream(body.getBytes()));
        return source;
    }
    
    private Source deleteBook(Node root) {
        // 略
    }
}

sun-jaxws.xml

在WEB-INF下创建sun-jaxws.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<endpoints
    xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
    version="2.0">

    <endpoint
        name="provider"
        implementation="com.mycompany.library.LibraryProvider"
        wsdl="WEB-INF/wsdl/LibraryService.wsdl"
        service="{http://library.mycompany.com}LibraryService"
        port="{http://library.mycompany.com}LibraryPort"
        url-pattern="/service" />

</endpoints>

打包部署运行

将project导出为war包library.war,部署到tomcat下。访问http://127.0.0.1:8080/library/service?wsdl 

然后使用wsimport,产生client端的代码。对client端进行调用,查看结果。

异步

关于提供服务端异步执行功能的AsyncProvider,我会另写一篇文章介绍。

Dispatch

介绍

接口Dispatch和Provider相对,用于client端。其功能与Provider一样,提供了面向消息的编程方法。先看下Dispatch提供了哪些方法:

package javax.xml.ws;

import java.util.concurrent.Future;
public interface Dispatch<T> extends BindingProvider {

    public T invoke(T msg);
    public Response<T> invokeAsync(T msg);
    public Future<?> invokeAsync(T msg, AsyncHandler<T> handler);
    public void invokeOneWay(T msg);
}

T泛型与Provider中的T一样。而方法invoke与Provider中的invoke也相同。Dispatch还提供了另外3个方法:

  1. Response<T> invokeAsync(T msg); 为异步poll的方式, Response是一个Future。

  2. Future<?> invokeAsync(T msg, AsyncHandler<T> handler); 为异步callback方式。Handler会在另一个线程中处理返回的值。

  3. void invokeOneWay(T msg); 处理单向消息。

异步

关于异步,会在另一篇文章中同意描述。

创建client

由于Dispatch摆脱了JAXB,所以不需要调用wsimport命令。只需要一个简单的client程序,就可以调用服务器端的service。我们以图书馆webservice为例。

public class Client {
    public static void main(String[] a) throws Exception {
        URL url = new URL("http://127.0.0.1:8080/library/service?wsdl");
        final QName serviceQName = new QName("http://library.mycompany.com", "LibraryService");
        Service service = Service.create(url, serviceQName);
        
        // 创建一个新的port,也可以使用WSDL中已有的port。如果WSDL中已经有此port,则不需要再进行binding。
        QName portName = new QName("http://library.mycompany.com", "RandomPort");
        service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING,"http://127.0.0.1:8080/library/service");
        // 为port创建一个dispatch。所有流向port的消息都将由dispatch处理。
        Dispatch<Source> sourceDispatch = service.createDispatch(portName, Source.class, Service.Mode.PAYLOAD);
    
        // add book
        String body = "<ns2:addRawBook xmlns:ns2=\"http://library.mycompany.com\"><name>java</name><author>xpbug</author></ns2:addRawBook>";
        Source result = sourceDispatch.invoke(new StreamSource(new StringReader(body)));
        System.out.println(sourceToXMLString(result));
    
    }
    
    private static String sourceToXMLString(Source result) {

        String xmlResult = null;
        try {
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
            OutputStream out = new ByteArrayOutputStream();
            StreamResult streamResult = new StreamResult();
            streamResult.setOutputStream(out);
            transformer.transform(result, streamResult);
            xmlResult = streamResult.getOutputStream().toString();
        } catch (TransformerException e) {
            e.printStackTrace();
        }
        return xmlResult;
    }
}

不需要任何额外的命令,依赖,以及部署,便可直接运行上面的程序。

你可能感兴趣的:(Provider,Dispatch,jaxws)