使用JAX-WS发布RESTful Web Service

JAX-WS的EA3版本或以JWSDP 2.0发布的JAX-WS都支持发布和使用RESTful Web Services。下面的示例将说明如何用JAX-WS发布一个RESTful Web Service。

      用JAX-WS发布RESTful Web Service,首先要创建一个对javax.xml.ws.Provider<T>接口的实现。Provider接口是对标准终端实现类的另一种动态实现办法。它与javax.xml.ws类似。Dispatch接口用在客户端。你会注意到Provider<T>是一个生成类。它能用SOAP/HTTP绑定支持Provider<javax.xml.transform.Source>和Provider<javax.xml.soap.SOAPMessage>,或者用XML/HTTP绑定支持Provider<javax.activation.DataSource>和Provider<javax.xml.transform.Source>。当创建Provider的实现时,你可以选择处理哪种形式的请求与响应消息。

      我们要创建的示例Web服务非常简单,它把两个数相加然后返回结果,而我们将在Provider实现中使用Provider<Source>以及XML/HTTP绑定。首先,我们写一个AddNumbers Provider的实现来声明AddNumbers类。

public class AddNumbersImpl implements Provider {}

      接下来,声明@Resource标记,它被用来在JAX-WS 运行时注射WebServiceContext到我们的AddNumbersImpl 实例中。

public class AddNumbersImpl implements Provider {     @Resource     protected WebServiceContext wsContext; }

      下一步是实现T Provider.invoke(T request)方法。我们首先写出下面的方法声明,它是一个简单的try-catch块用来处理异常。注意,该方法以一个Source对象为请求,并返回一个Source对象作为响应。这符合我们创建的Provider类型。

public class AddNumbersImpl implements Provider {     @Resource     protected WebServiceContext wsContext;

    public Source invoke(Source request) {         try {

        } catch(Exception e) {             e.printStackTrace();             throw new HTTPException(500);         }     } }

       在本例中,AddNumbers Web服务将通过从URL路径或HTTP请求中抽取要相加的数字来获得请求。请求字符串和路径字符串可以从MessageContext中获取,而它又从WebServiceContext wsContext中获得,它会被注射到我们的AddNumbers对象中。下面的代码背用来从URL中获取PATH_INFO并检查看它是否是合适的格式。

            String path = (String)mc.get(MessageContext.PATH_INFO);             if (path != null && path.contains("/num1") &&                        path.contains("/num2")) {                 return createResultSource(path);             }

      createResultSource(String str)方法只从合适的被格式化的MessageContext.PATH_INFO字符串中创建Source对象。它从路径中抽取要相加的数字,然后把它们相加并调用createResultSource(int sum)方法。下面是这两个方法的源代码:

    private Source createResultSource(String str) {         StringTokenizer st = new StringTokenizer(str, "=&/");         String token = st.nextToken();         int number1 = Integer.parseInt(st.nextToken());         st.nextToken();         int number2 = Integer.parseInt(st.nextToken());         int sum = number1+number2;         return createResultSource(sum);     }

    private Source createResultSource(int sum) {         String body =             "<ns:addNumbersResponse xmlns:ns=/"http://duke.org/"><ns:return>"             +sum             +"</ns:return></ns:addNumbersResponse>";         Source source = new StreamSource(             new ByteArrayInputStream(body.getBytes()));         return source;     }

于是,我们的示例invoke方法就会是下面这个样子。

   public Source invoke(Source source) {         try {             MessageContext mc = wsContext.getMessageContext();             // check for a PATH_INFO request             String path = (String)mc.get(MessageContext.PATH_INFO);             if (path != null && path.contains("/num1") &&                        path.contains("/num2")) {                 return createResultSource(path);             }             throw new HTTPException(404);         } catch(Exception e) {             e.printStackTrace();             throw new HTTPException(500);         }     }

      你会发现,如果MessageContext.PATH_INFO没有找到,该示例会抛出HTTPException(404)异常。我们的示例终端并不抛出该异常,而是能为方法参数检查请求字符串。例如MessageContext.PATH_INFO,请求字符串可以用下面的代码从MessageContext中获得。

            String query = (String)mc.get(MessageContext.QUERY_STRING);

      于是,我们可以把请求字符串传递到createResultSource(String str)方法中来解析参数,如果要为我们的示例部署一个servlet容器也可以使用标准ServletRequest对象来抽取请求字符串。而如果我们想部署一个基于Java SE的终端,我们就必须使用MessageContext.QUERY_STRING。

      下面的代码获取了num1和num2的值,相加后调用createResultSource(int sum)方法来创建Source对象,它将从invoke方法中被返回。

            ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST);             int num1 = Integer.parseInt(req.getParameter("num1"));             int num2 = Integer.parseInt(req.getParameter("num2"));             return createResultSource(num1+num2);

       至此,我们的AddNumbers类应该如下所示:

public class AddNumbersImpl implements Provider {

    @Resource     protected WebServiceContext wsContext;

    public Source invoke(Source source) {         try {             MessageContext mc = wsContext.getMessageContext();             // check for a PATH_INFO request             String path = (String)mc.get(MessageContext.PATH_INFO);             if (path != null && path.contains("/num1") &&                        path.contains("/num2")) {                 return createResultSource(path);             }             String query = (String)mc.get(MessageContext.QUERY_STRING);             System.out.println("Query String = "+query);             ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST);             int num1 = Integer.parseInt(req.getParameter("num1"));             int num2 = Integer.parseInt(req.getParameter("num2"));             return createResultSource(num1+num2);         } catch(Exception e) {             e.printStackTrace();             throw new HTTPException(500);         }     }         private Source createResultSource(String str) {         StringTokenizer st = new StringTokenizer(str, "=&/");         String token = st.nextToken();         int number1 = Integer.parseInt(st.nextToken());         st.nextToken();         int number2 = Integer.parseInt(st.nextToken());         int sum = number1+number2;         return createResultSource(sum);     }         private Source createResultSource(int sum) {         String body =             "<ns:addNumbersResponse xmlns:ns=/"http://duke.org/"><ns:return>"             +sum             +"</ns:return></ns:addNumbersResponse>";         Source source = new StreamSource(             new ByteArrayInputStream(body.getBytes()));         return source;     }   }

要完成AddNumbers,我们需要声明一些标记来说明JAX-WS运行时如何使用这个类。@WebServiceProvider表明该类是一个基于Provider的终端而不是一个需要用@WebService指明的服务终端实现类。最后需要我们添加的标记是@BindingType(value=HTTPBinding.HTTP_BINDING)。该标记指明了AddNumbers终端将用HTTPBinding.HTTP_BINDING而不是SOAPBinding.SOAP11HTTP_BINDING或者SOAPBinding.SOAP12HTTP_BINDING来绑定。下面是最终该类的代码:

@WebServiceProvider @BindingType(value=HTTPBinding.HTTP_BINDING) public class AddNumbersImpl implements Provider {

    @Resource     protected WebServiceContext wsContext;

    public Source invoke(Source source) {         try {             MessageContext mc = wsContext.getMessageContext();             // check for a PATH_INFO request             String path = (String)mc.get(MessageContext.PATH_INFO);             if (path != null && path.contains("/num1") &&                        path.contains("/num2")) {                 return createResultSource(path);             }             String query = (String)mc.get(MessageContext.QUERY_STRING);             System.out.println("Query String = "+query);             ServletRequest req = (ServletRequest)mc.get(MessageContext.SERVLET_REQUEST);             int num1 = Integer.parseInt(req.getParameter("num1"));             int num2 = Integer.parseInt(req.getParameter("num2"));             return createResultSource(num1+num2);         } catch(Exception e) {             e.printStackTrace();             throw new HTTPException(500);         }     }         private Source createResultSource(String str) {         StringTokenizer st = new StringTokenizer(str, "=&/");         String token = st.nextToken();         int number1 = Integer.parseInt(st.nextToken());         st.nextToken();         int number2 = Integer.parseInt(st.nextToken());         int sum = number1+number2;         return createResultSource(sum);     }         private Source createResultSource(int sum) {         String body =             "<ns:addNumbersResponse xmlns:ns=/"http://duke.org/"><ns:return>"             +sum             +"</ns:return></ns:addNumbersResponse>";         Source source = new StreamSource(             new ByteArrayInputStream(body.getBytes()));         return source;     }   }

       要在运行了JAX-WS RI的servlet容器上部署我们的终端,我们需要创建一个WAR文件。例如,我们把WAR文件命名为jaxws-restful.war。该文件需要一个非常简单的web.xml文件来配置JAX-WS Ri servlet。代码如下:

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">   <listener>     <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener   </listener>   <servlet>     <servlet-name>restful-addnumbers</servlet-name>     <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet     <load-on-startup>1</load-on-startup>   </servlet>   <servlet-mapping>     <servlet-name>restful-addnumbers</servlet-name>     <url-pattern>/addnumbers/*</url-pattern>   </servlet-mapping>   <session-config>     <session-timeout>60</session-timeout>   </session-config> </web-app>

    接着,我们添加一个 sun-jaxws.xml部署描述符到该WAR文件。下面是示例:

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

    <endpoint         name="restful-addnumbers"         implementation="restful.server.AddNumbersImpl"         wsdl="WEB-INF/wsdl/AddNumbers.wsdl"         url-pattern="/addnumbers/*" /> </endpoints>

      你会注意到在web.xml和sun-jaxws.xml中有<url-pattern>/addnumbers/*</url-pattern>元素和url-pattern="/addnumbers/*"属性,于是我们能够在路径中匹配任何附加的参数。(例如,.../num1/10/num2/20)

      你还会从sun-jaxws.xml部署描述符中注意到指定的一个wsdl文件。它很重要的告诉JAX-WS RI不要为该终端生成一个WSDL。下面是一个简单的AddNumbers.wsdl的内容。

<?xml version="1.0" encoding="UTF-8"?> <definitions> </definitions>

       一旦部署了AddNumbers终端,你就能在浏览器中输入下面的URL来测试它了。(根据你配置的不同可能会有一点点不同)

http://localhost:8080/jaxws-restful/addnumbers/num1/10/num2/20 Should return the following: <ns:addNumbersResponse xmlns:ns="http://duke.org">    <ns:return>30</ns:return> </ns:addNumbersResponse>

http://localhost:8080/jaxws-restful/addnumbers?num1=10&num2=50 Should return the following: <ns:addNumbersResponse xmlns:ns="http://duke.org">    <ns:return>60</ns:return> </ns:addNumbersResponse>

你可能感兴趣的:(使用JAX-WS发布RESTful Web Service)