技术开发 频道

使用WAS CE开发基于JAX-WS的RESTful服务

【IT168 专稿】

    本文是WebSphere软件技术征文大赛(http://tech.it168.com/focus/200904/webspheregame/index.html)二等奖获奖作品。

    一、什么是REST

    REST是REpresentational State Transfer的缩写,代表分布式超媒体系统(如World Wide Web)上的一种软件架构体系,并不仅仅是创建Web Service的一种方法。它最早由Roy Fielding于2000年在其博士论文“Architectural Styles and the Design of Network-based Software Architectures”中提出,并定义了一些基本原则。简单的说,放到World Wide Web上,就是所有的应用程序对象和功能都可以抽象为一种资源(Resource),并通过URI来定位并使用。因此,我们可以把符合REST原则的系统称为RESTful。也就是说,REST是一种架构风格,而不是一个标准,你永远不会看到W3C发布一个叫REST的Specification。

    RESTful Web Service与基于SOAP和WSDL的Web Service有着很多的不同,它有着以下特点:

    ·将Web Service作为一种资源,并通过URI来定位

    ·使用HTTP中的POST、GET、PUT和DELETE方法来代表对资源的CREATE、READ、UPDATE、DELETE(CRUD)操作

    ·使用无状态通信

    ·传输XML或者SON

    在JAX-WS中提供了对开发和部署一个RESTful的Web Service的基本支持,即通过实现Provider接口使得Web Serivce可以对传输的XML消息进行完全的控制,因此我们可以在WAS CE中使用JAX-WS开发一个RESTful的Web Service。

    对RESTful Web Service提供完整支持的JAX-RS Specification将会加入Java EE 6.0的大家庭中。当前的WAS CE V2.1.x是遵循Java EE 5.0的企业级应用服务器。因此,若想使用JAX-RS开发RESTful Web Service,请关注WAS CE的后续版本。

    二、开发环境设置

    本文基于WAS CE的最新版本V2.1.1.2开发一个RESTful的Web Service,在开始编写代码之前,请确认如下的开发环境:

    ·Sun JDK V5.0

    ·Eclipse IDE for Java EE Developers - Ganymede

    ·WASCE Eclipse Plug-in (WEP) V2.1.1.2

    此外,WAS CE使用Axis2-1.3作为JAX-WS引擎,但是由于其存在一个已知的关于HTTP Content-Type Header的问题(在Axis2-1.4中才解决),所以我们需要将JAX-WS引擎切换成Apache CXF (WAS CE使用版本为V2.0.8)。不用担心,WAS CE的模块化架构,使这个过程十分简单,过程如下:

    1. 启动WAS CE

    2. 打开Web Console:http://localhost:8080/console

    3. 进入Application -> Plugins页面,点击Add Repository

    4. 由于WAS CE V2.1.1.2是基于Geronimo V2.1.4开发,所以我们也可以使用Geronimo的Server plug-ins。在New Repository中输入:

    http://geronimo.apache.org/plugins/geronimo-2.1.4/ 然后点击Add Repository。

    5. 选择刚刚添加的Repository,然后点击Show Plugins in selected repository。

    6. 勾选上以下plug-ins并且点击install按钮。

    7. 在以上CXF相关的Plugin安装完成之后, 我们需要更新WAS CE的配置文件,以使得WAS CE在启动时加载CXF以代替Axis2。(注意:在更改配置文件前先要停止WAS CE服务器)

    8. 停止WAS CE后,打开<WASCE_HOME>/var/config/config.xml

    去掉以下四个module的condition属性:

    <module name="org.apache.geronimo.configs/axis2-deployer/2.1.4/car" condition="…"/>
    <module name="org.apache.geronimo.configs/axis2-ejb-deployer/2.1.4/car" condition="…"/>
    <module name="org.apache.geronimo.configs/cxf-deployer/2.1.4/car" condition="…"/>
    <module name="org.apache.geronimo.configs/cxf-ejb-deployer/2.1.4/car" condition="…"/>

    增加load属性,axis2相关的为false,cxf相关的为true:

    <module name="org.apache.geronimo.configs/axis2-deployer/2.1.4/car" load="false"/>
    <module name="org.apache.geronimo.configs/axis2-ejb-deployer/2.1.4/car" load="false"/>
    <module name="org.apache.geronimo.configs/cxf-deployer/2.1.4/car" load="true"/>
    <module name="org.apache.geronimo.configs/cxf-ejb-deployer/2.1.4/car" load="true"/>

    9. 重新启动WAS CE服务器。

    三、开发一个简单的RESTful Web Service

    1. 在Eclipse中创建一个Dynamic Web Project作为Web Service的宿主:

    选择File->New->Dynamic Web Project

    输入Project Name为HelloRestfulService

    2. 右击Java Resources: src,新建一个class,其中package、Name、Interfaces如下设置:

    3. 加入如下代码:

package com.ibm.wasce.samples.jaxws.rest;

import java.io.ByteArrayInputStream;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.BindingType;
import javax.xml.ws.Provider;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPBinding;
import javax.xml.ws.http.HTTPException; 
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

@WebServiceProvider
@BindingType (value = HTTPBinding. HTTP_BINDING )
public  class HelloWorld  implements Provider<Source> {
   @Resource
   protected WebServiceContext wsContext ;

   public Source invoke(Source source) {
       try {
           String targetName = null ;

           if (source == null ) {
               //Get: Getting input from query string
               MessageContext mc = wsContext .getMessageContext();
               String query = (String) mc.get(MessageContext. QUERY_STRING );
               System. out .println( "Query String = " + query);
               ServletRequest req = (ServletRequest) mc.get(MessageContext. SERVLET_REQUEST );
               targetName = req.getParameter( "target" );
           } else {
               //POST: Getting input from input box
               Node n = null ;
               if (source instanceof DOMSource) {
                   n = ((DOMSource) source).getNode();
               } else  if (source instanceof StreamSource) {
                   StreamSource streamSource = (StreamSource) source;
                   DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
                   InputSource inputSource = null ;
                   if (streamSource.getInputStream() != null ) {
                       inputSource = new InputSource(streamSource.getInputStream());
                   } else  if (streamSource.getReader() != null ) {
                       inputSource = new InputSource(streamSource.getReader());
                   }
                   n = builder.parse(inputSource);
               } else {
                   throw  new RuntimeException( "Unsupported source: " + source);
               }
               NodeList children = n.getChildNodes();
               for ( int i = 0; i < children.getLength(); i++) {
                   Node child = children.item(i);
                   if (child.getNodeName().equals( "people" )) {
                       targetName = child.getAttributes().getNamedItem( "target" ).getNodeValue();
                       break ;
                   }
               }
           }
            
           String body = "<ns:return xmlns:ns=\"http://rest.jaxws.samples.wasce.ibm.com\">"
                   + "<ns:HelloWorldResponse>" + this .sayHello(targetName) + "</ns:HelloWorldResponse>"
                   + "</ns:return>" ;
           return  new StreamSource( new ByteArrayInputStream(body.getBytes()));
 
       } catch (Exception e) {
           e.printStackTrace();
           throw  new HTTPException(500);
       }
   }
 
   private String sayHello(String target){
       return  "Hello " + target;
   }

    让我们看一看代码中的几个关键点:

    a) @WebServiceProvider 表明这个Web Service实现了Provider接口,可以对XML消息进行完全的处理。

    b) Provider 是这类Web Service都要实现的接口,它只有一个方法需要实现,即:

    public abstractjava.lang.Object invoke(java.lang.Object arg0);

    c) Source 是交换信息的载体:

    当Source对象为空时,表示是一个GET Request。因为这种情况下,所有信息是被拼成一个URI的参数,并传到这个URI对应的Web Service。

    否则,是一个POST Request,其内容会包括在一个Source对象内;

    另外,Response的内容也要放到一个Source对象内。

    4. 编写web.xml

    为了使我们前面编写的Web Service能够成功部署到WAS CE中,我们需要将如下内容加入到web.xml中:

<servlet>
    <servlet-name>Hello</servlet-name>
    <servlet-class>com.ibm.wasce.samples.jaxws.rest.HelloWorld</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>Hello</servlet-name>
    <url-pattern>/Hello</url-pattern>
</servlet-mapping>

    注意,这里只是借用了<servlet>和<servlet-mapping>标签来帮助暴露Web Service,并不是真是要求这个Web Service必须要实现HttpServlet接口。

    5. 部署,运行并测试这个Web Service

    右击这个HelloRestfulService工程,选择Run As -> Run on Server,会将其部署到WAS CE中,当Status栏变为Synchronized时,在Console中会有类似如下信息显示:

    通过访问如下地址,测试使用GET方式调用RESTful Web Service返回的结果:

    http://localhost:8080/HelloRestfulService/Hello?target=Rex

    四、开发一个简单的RESTful Web Service Client

    1. 创建一个Dynamic Web Project作为Client

    选择File->New->Dynamic Web Project

    输入Project Name为HelloRestfulClient

    2. 新建一个testget.jsp,加入如下内容:

<form method="POST" action="HelloGetMethodRequester">
  Target Name: <input type="text" name="target">
  <input type="submit" value="Submit">
</form>

    这个JSP用来为HelloGetMethodRequester Servlet提供参数。

    3. 创建HelloGetMethodRequester Servlet,加入如下内容:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   PrintWriter ut = response.getWriter();
   String target = request.getParameter( "target" );
         
   String queryRequest = "http://localhost:8080/HelloRestfulService/Hello?target=" + target;
   GetMethod method = new GetMethod(queryRequest);
   HttpClient client = new HttpClient();
   int statusCode = client.executeMethod(method);
   if (statusCode != 200) { //HttpStatus.SC_OK
       System. err .println( "Method failed: " + method.getStatusLine());
   }
         
   try {
       DocumentBuilder builder= DocumentBuilderFactory.newInstance().newDocumentBuilder();
       Document queryResponse = builder.parse(method.getResponseBodyAsStream());
       XPath xPath = XPathFactory.newInstance().newXPath();
       NodeList nodes = (NodeList) xPath.evaluate( "/return" , queryResponse, XPathConstants. NODESET );
       for ( int i = 0; i < nodes.getLength(); i++) {
           // Get eachxpathexpression as a string
           String str = (String) xPath.evaluate( "HelloWorldResponse" , nodes.item(i), XPathConstants. STRING );
           out.println( "Service return: " + str);
       }
   } catch (Exception e) {
       e.printStackTrace();
   }

    在这个Servlet中我们用到了commons-codec-1.3.jar和commons-httpclient-3.0.1.jar两个包,因此我们需要将它们加入到Build Path中:

    这两个包在WAS CE的如下目录中可以找到:

    <WASCE_HOME>\repository\commons-codec\commons-codec\1.3\commons-codec-1.3.jar

    <WASCE_HOME >\repository\commons-httpclient\commons-httpclient\3.0.1\commons-httpclient-3.0.1.jar

    让我们看一看这段Servlet代码中的一些关键点:

    a) 首先创建了一个HttpClient对象,并运行了GetMethod,即使用GET请求如下URI:

    "http://localhost:8080/HelloRestfulService/Hello?target=" + target

    b) 如果成功返回,即statusCode为200,则可以从method对象中得到返回的结果:

    method.getResponseBodyAsStream()

    c) 因为返回的结果为自定义的一段XML文档,所以我们可以使用XPath来处理并输出到页面上。

    4. 编写部署计划geronimo-web.xml

    为使这个Web Client能够成功部署到WAS CE中,我们还需要在geronimo-web.xml的<environment>中加入如下依赖:

<dep:dependencies>
    <dep:dependency>
        <dep:groupId>commons-codec</dep:groupId>
        <dep:artifactId>commons-codec</dep:artifactId>
        <dep:version>1.3</dep:version>
        <dep:type>jar</dep:type>
    </dep:dependency>
    <dep:dependency>
        <dep:groupId>commons-httpclient</dep:groupId>
        <dep:artifactId>commons-httpclient</dep:artifactId>
        <dep:version>3.0.1</dep:version>
        <dep:type>jar</dep:type>
    </dep:dependency>
</dep:dependencies>

    5. 部署和运行

    右击这个HelloRestfulClient工程,选择Run As -> Run on Server,会将其部署到WAS CE中,当Status栏变为Synchronized时,表示部署成功。

    在浏览器中打开如下页面:http://localhost:8080/HelloRestfulClient/testget.jsp

    输入"Rex",并点击Submit,可得到如下结果:

    五、总结

    本文介绍了REST的基本概念,以及如何在WAS CE V2.1.1.2下开发一个RESTful Web Service和一个使用GET方式的Client。如果读者朋友有兴趣的话,也可以尝试扩展这个Client,如增加testpost.jsp和HelloPostMethodRequester Servlet两个文件:

    - testpost.jsp包括一个文件上载框,用以上传一个XML文件;

    - HelloPostMethodRequester Servlet用于将XML文件以POST方式传送给HelloWorld这个Service。

    事实上,我们的HelloWorld RESTful Web Service已经具备了处理接收一个XML文件的能力。

    六、资源链接

    ·WAS CE及Samples下载

    http://www.ibm.com/developerworks/downloads/ws/wasce/

    ·WAS CE Eclipse Plug-in (aka WEP, WAS CE’s WTP Server Adapter)下载

    http://download.boulder.ibm.com/ibmdl/pub/software/websphere/wasce/updates

    ·WAS CE文档

    http://publib.boulder.ibm.com/wasce/Front_en.html

    ·WAS CE主页

    http://www.ibm.com/developerworks/websphere/zones/was/wasce.html

0
相关文章