使用 Apache Wink、Eclipse 和 Maven 开发 RESTful Web 服务

来源:互联网 发布:淘宝暂停发货通知 编辑:程序博客网 时间:2024/05/05 04:06

Apache Wink 是一个促进创建和使用 REST Web 服务的 Apache 孵化器项目。通过 RESTWeb 服务,客户机和服务之间的交互局限于一组预定义的操作,客户机和服务器之间的交互的复杂性限制为客户机和服务之间交换的资源表示。这种方法支持构建可互操作、可伸缩、可靠的、基于 REST 的分布式超媒体系统。

本文介绍如何使用 Apache WinkEclipse IDE 以及 Maven 项目管理工具开发、部署和运行 RESTful Web 服务。

Web 服务的 REST 方法

设计 Web 服务的 REST 方法将客户机和服务之间的交互限制到一组创建、读取、更新和删除(CRUD)操作。这些操作直接映射到 HTTP 方法具体而言,映射到 POSTGETPUT DELETE。尽管 RESTful 样式没有绑定到 HTTP 协议,本文假设 HTTP 用于客户机和服务之间的通信。

REST Web 服务在资源上执行 CRUD 操作。客户机使用资源状态的 REST 服务表示进行交换。这些表示使用的数据格式在 HTTP 请求或响应的头部中指定 — XML JSON 是广泛使用的格式。数据格式可能在不同操作之间发生变化;例如,创建资源的数据格式与用于读取资源的数据格式不同。REST 服务保持资源的状态,但servlets 不同的是不保持客户机会话信息。

REST 方法支持构建可互操作、可伸缩和可靠的基于 REST 的分布式系统。例如,GETPOST DELETE 方法是等幂的,即多次执行它们与执行一次的结果相同。由于 GET 操作不会更改资源的状态,因此 GET 请求的结果可以缓存起来以加快请求-响应循环。

JAX-RS 为基于 HTTP 协议的 RESTful Java Web 服务定义了一个 APIJAX-RS 实现包括 Apache WinkSun Jersey JBoss RESTEasy。本文将使用 Apache Wink

JAX-RS 利用 Java 注释的威力,使用注释来执行诸如以下的操作:

  • HTTP 方法和 URIs 绑定到 Java 类的方法
  • 将来自 URI HTTP 头部的元素作为方法参数注入
  • HTTP 消息体和 Java 类型之间来回转换
  • URI 模式绑定到 Java 类和方法@Path 注释
  • HTTP 操作绑定到 Java 方法@GET@POST@PUT @DELETE 注释

JAX-RS 还提供了一个框架来构建新功能。例如,对于自定义数据格式,程序员可以开发消息阅读器并将 Java 对象编组到 HTTP 消息并从 HTTP 消息解组它们。

在本文中,您将使用 Eclipse Maven 下载Apache Wink,运行 Apache Wink 中包含的HelloWorld 示例,然后将您自己的 REST Web 服务创建为一个 Eclipse 项目。

通过 Eclipse 获取 Apache Wink

在这个小节中,您将使用 Eclipse 以及 Maven Integration for Eclipse(称为 m2eclipse)和 Subclipse 插件来安装 Apache Wink。(M2eclipse 提供从 Eclipse Maven 的访问;Subclipse 提供对 Subversion 资源库的访问。)您还可以将 Eclipse 用作一个平台,从这个平台构建并运行 Web 服务。

先决条件

在获取 Apache Wink 之前,要先下载并安装以下软件包(参见 考资料 获取下载 URLs):

  • Java Software Development Kit (JDK) version 6设 置 JAVA_HOME 环境变量并添加到路径 %JAVA_HOME%/bin(在 Windows® 中)或 $JAVA_HOME/bin(在 Linux® 中)。
  • Apache Tomcat version 6.0设置 CATALINA_HOME 环境变量以指向安装目录。
  • Eclipse IDE for Java™ Platform, Enterprise Edition (Java EE) developers本文撰写之时的当前版本为 Eclipse Galileo

安装 Subclipse

要使用 Eclipse 管理具有 Maven 感知的项目,要安装 Eclipse 插件 Subclipse m2eclipse。要安装 Subclipse 插件,执行以下步骤:

  1. 启动 Eclipse
  2. 单击菜单栏中的 Help,然后选择 Install new software
  3. Available Software 窗口中,单击 Add
  4. Add Site 窗口中,输入:

5.  · 

     Name:     Subclipse
     Location: http://subclipse.tigris.org/update_1.6.x/

6.  

7. 然后单击 OK

8.      ·  Available Software 窗口中,选择 Subclipse 下的 Subclipse(Required) SVNKitClient Adapter (Not required) 复选框(如 图 1 所示),然后单击 Next

9.  · 
1. 安装Subclipse 插件

10.·  Install Details 窗口中,单击Next

11.·  Review Licenses 窗口中,检查许可,接受许可协议条款,然后单击Finish

12.  ·  单击 Yes 重新启动 Eclipse

 

 

安装 m2eclipse

m2eclipse 插件的安装步骤与安装 Subclipse 插件类似,但有以下几点例外:

  • Add Site 窗口中输入:

     Name:     Maven Integration for Eclipse
     Location: http://m2eclipse.sonatype.org/update/

  •  
  • 然后单击 OK
  • Available Software 窗口中,选择 Maven Integration for Eclipse (Required) Maven SCM handler for Subclipse (Optional) 复选框(如 图 2 所示),然后单击 Next

    2. 安装 m2eclipse 插件

获取 Apache Wink

现在,您可以使用 Eclipse 从资源库检查 Apache Wink 示例,将必要的 Java 归档(JAR)文件(包括Apache Wink JAR 文件)下载到一个本地资源库,构建并运行 Apache WinkHelloWorld 示例。为此,执行以下步骤:

  1. Eclipse 中,选择 File > Import 启动 Import Wizard
  2. Select 向导页面的 Select and import source 文本框中输入 maven
  3. Maven 下,选择 Materialize Maven Projects 并单击 Next
  4. Select Maven artifacts 向导页面上单击 Add
  5. Add Dependency 页面上的 Enter groupId, artifactId 文本框中输入 org.apache.wink.example
    注意: Artifact(工件) 是一个用于 Maven 的术语,指的是设置了版本并存储在资源库中的软件包的层级结构。
  6. Search Results 区域中,选择 org.apache.wink.example apps(如 图 3 所示)并单击 OK

    3. org.apache.wink.example 组中的应用程序工件
  7. Select Maven artifacts 向导页面上,单击 Next, 然后单击 Finish
  8. Maven Projects 向导页面上,只选择 /pom.xml 复选框,然后单击 Finish

Maven 处理一个工件的所有依赖项的方式是从远程资源库下载它们并构建一个本地资源库。Maven的优势之一是能够处理临时依赖项;因此,在 Maven Project Object Model (POM) 文件中,只需声明工件的传递依赖项(transitive dependencies),Maven 将为您处理高阶依赖项(higher-order dependencies)。

步骤 8 完成后,将创建一个 Eclipse 项目,它包含 Apache Wink 示例的 apps 模块中的代码。在 Eclipse ProjectExplorer 中浏览项目文件。

Apache Wink HelloWorld 服务

我们来检查一下 apps 模块中的 HelloWorld Java 类。在 Project Explorer 视图中,单击 apps> HelloWorld > src > main, 然后打开文件HelloWorld.java,该文件的结构如 单1 所示。


清单 1.HelloWorld.java 文件

                              
package org.apache.wink.example.helloworld;
...

@Path("/world")
public class HelloWorld {

   public static final String ID = "helloworld:1";
  
   @GET
   @Produces(MediaType.APPLICATION_ATOM_XML)
   public SyndEntry getGreeting() {
      SyndEntry synd = new SyndEntry(new SyndText("Hello World!"), ID, new Date());
      return synd;
   }
}

 

HelloWorld 是一个 JAX-RS 资源(或服务),正如其类定义前面的 @Path("/world") 注释所示。字符串 "/world" 是该资源的相对根 URIJAX-RS将匹配相对 URI "/world" HTTP 请求路由到 HelloWorld 类的方法。

HelloWorld 类的惟一方法getGreeting 拥有一个 @GET 注释,表示它将服务于 HTTP GET 请求。

@Produces(MediaType.APPLICATION_ATOM_XML) 注释表明 HTTP 响应的媒体类型即,HTTP 响应头部中的 Content-Type 字段的值。

与相对 URI "/world" 关联的绝对 URI 由部署描述符 web.xml 中的设置决定,这个部署描述符驻留在 HelloWorld/src/main/webapp/WEB-INF 中,如图 4 所示。


4. HelloWorld 应用程序的部署描述符

注意,URI 模式 /rest/* 被绑定到 restSdkService servlet,这个 servlet context path HelloWorld, 由 HelloWorld/pom.xml 文件中的 <finalName> 元素定义。这样,HelloWorld 资源的绝对 URI 是:

     http://host:port/HelloWorld/rest/world

图 4 中的 servlet 名称restSdkService 引用 org.apache.wink.server.internal.servlet.RestServlet Apache Wink 类,如图中的 <servlet> 元素所示。RestServlet 类使用初始参数 applicationConfigLocation 传递 HelloWorld 类的名称,这个初始参数反过来指向位于 HelloWorld/src/main/webapp/WEB-INF 文件夹中的 web.xml 旁边的 application 文件。这个 application 文件只有一行,即 HelloWorld 资源的限定名:

org.apache.wink.example.helloworld.HelloWorld

 

RestServlet servlet 可以在 JAX-RS 感知 servlet 容器中运行,从而支持将 JAX-RS 服务轻松部署到JAX-RS 感知的容器中。

我们现在开始构建 HelloWorld 服务,然后将其部署到 Tomcat 6.0 servlet 容器中并运行。

构建 HelloWorld

Eclipse 中,可以使用 m2eclipse 插件来构建、部署和运行 HelloWorld。首先,编译源代码并为 HelloWorld 服务构建 Web 归档 (WAR) 文件。要构建这个服务,指示 m2eclipse 执行安装生 命周期阶段。(执行一个 Maven 生命周期阶段将触发项目生命周期中的此前阶段的执行。)要执行安装阶段,在 Project Explorer 视图中右键单击 HelloWorld,然后单击Run As > Maven install,如 图 5 所示。


5. 执行 Maven安装阶段

Maven (从中央资源库 http://repo1.maven.org/maven2或从镜像资源库)下载所有依赖项,并在 Windows® 中的%HOMEPATH%/.m2/repository Linux® 中的 $HOME/.m2/repository 下构建本地资源库。Maven 执行的动作记录在 Eclipse 窗口的 Console 视图中。

如果安装阶段成功结束,那么 WAR 文件 HelloWorld.war 就构建在目标目录下并部署到本地资源库中,Console 视图将显示消息 “Build successful”

HelloWorld 部署到 Tomcat

您将使用 Maven Tomcat 插件。(与 Eclipse 一样,Maven 通过插件提供大量功能。)您必须将 Tomcat 插件的位置告知 Maven,方法是指定插件的 groupId artifactId,如 图 6 所示(pom.xml 中的 45-46 行)。


6. Tomcat Maven 插件

您还需要告知 Maven 用于访问 Tomcat manager 应用程序的用户名和密码。Maven 使用 manager 应用程序来指导 Tomcat 部署或取消部署一个 Web 应用程序。按照以下步骤告知 Maven 身份验证信息:

  1. 声明 tomcat-maven-plugin 工件的一个 Tomcat 服务器配置称为 tomcat-localhost, 如 图 6 所示(pom.xml 中的 47-49 行)
  2. Maven 设置文件 settings.xml(位于 Windows %HOMEPATH%/.m2/ Linux $HOME/.m2 下)中定义这个配置,如 单 2 所示。

  1. 清单 2. Maven settings.xml 文件

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

<settings
    xmlns="http://maven.apache.org/SETTINGS/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                        http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <servers>
    <server>
      <id>tomcat-localhost</id>
      <username>admin</username>
      <password>admin</password>
    </server>
  </servers>

</settings>

现在可以将这个服务部署到 Tomcat 了,方法是执行 Tomcat 插件的 redeploy 目标。(一个Maven 目标(goal) 被自身执行,这与生命周期阶段不同,后者的执行由生命周期中的此前阶段自动推进。)要执行 redeploy 目标,执行以下步骤:

  1. Tomcat 配置文件 conf/tomcat-users.xml(位于 CATALINA_HOME 下)中插入身份验证信息(如 图 7 所示),但要使用一个密码,而不是 admin

    7. Tomcat 身份验证配置
  2. 通过运行以下命令启动 Tomcat

In Windows:   %CATALINA_HOME%/bin/catalina.bat start
In Linux:     $CATALINA_HOME/bin/catalina.sh start

  1. 执行 maven tomcat:redeploy,这条命令使用 Tomcat manager 应用程序将 HelloWorld.war 部署到 Tomcat。为此,执行以下步骤:
    1. Project Explorer 视图中,右键单击 HelloWorld,然后单击 Run As > Run configurations
    2. Create, manage, and run configurations 窗口中,选择 Maven Build > New_configuration,然后单击 Main 选项卡(参见 图 8)。

      8. 运行 tomcat:redeploy 目标
    3. 单击 Browse workspace,然后选择 apps > HelloWorld,然后单击 OK
    4. Goals 文本框中,输入 tomcat:redeploy, 然后单击 Run

如果 redeploy 目标成功执行,Console 视图将显示消息 “Build successful”

现在,您可以使用一个 HTTP 客户机(比如 Curl)调用HelloWorld 服务,如 单3 所示。


清单 3. Curl 中调用 HelloWorld 服务

                              
  $ curl -X GET  http://localhost:8080/HelloWorld/rest/world
  <?xml version="1.0"encoding="UTF-8" standalone="yes"?>
  <entryxmlns="http://www.w3.org/2005/Atom"
        xmlns:ns2="http://a9.com/-/spec/opensearch/1.1/"
        xmlns:ns3="http://www.w3.org/1999/xhtml">
    <id>helloworld:1</id>
   <updated>2010-01-06T13:26:43.924+01:00</updated>
    <titletype="text">Hello World!</title>
  </entry>

开发一个 REST Web 服务

在这个小节中,您将从头开始创建一个 REST Web 服务来管理一个图书资源集合。这个 REST 服务的源代码可以从下面的 表中获取。

开发这样一个服务的关键步骤是:

  1. 定义资源的 URIs,用于操作资源的方法,以及每个方法的数据格式。
  2. 定义表示图书资源的 Java 对象,提供 Java 代码或注释来编组和解组 Java 对象。
  3. 定义将 URIs HTTP 方法绑定到 Java 方法的 Java 服务。
  4. 提供 javax.ws.rs.core.Application 抽象类的一个具体子类。

下面我们就执行这些步骤。

将路径和 HTTP 方法绑定到 Java 方法

这个服务支持 GET, POSTPUT DELETE HTTP 方法,并按如下方式将 HTTP 请求映射到 Java 方法:

  • POST /books 请求映射到带有 createBook(@Context UriInfo, Book) 签名的 Java 方法。(@Context 注释将在 JAX-RS Web 服务 小节中介绍。)用于创建一个图书资源的数据格式为:

     POST /books HTTP/1.1
     Content-Type: application/xml

     <book>
          <title> ... </title>
          <isbn> .... </isbn>
     </book>

  • GET /books 请求映射到 Java 方法 getBook()。用 于获取图书资源的数据格式为:

     GET /books HTTP/1.1
     Content-Type: application/xml

  • GET /books/{id} 请求映射到带有 getBook(@PathParam("id") int) 签名的 Java 方法。(@PathParam 注释将在 JAX-RS Web 服务 小节中介绍。)用于获取带有 URI 模式 /books/{id} 的图书资源的数据格式为:

     GET /books/{id} HTTP/1.1
     Content-Type: application/xml

  • PUT /books/{id} 请求映射到带有 updateBook(@PathParam("id") int, Book) 签名的 Java 方法。用于更新带有 URI 模式 /books/{id} 的图书资源的数据格式为:

     PUT /books/{id} HTTP/1.1
     Content-Type: application/xml
    
     <book>
         <title> ... </title>
         <isbn> .... </isbn>
     </book>

  • DELETE /books/{id} 请求映射到带有 deleteBook(@PathParam("id") int) 签名的 Java 方法。用于删除带有 URI 模式 /books/{id} 的图书资源的数据格式为:

     DELETE /books/{id} HTTP/1.1
     Content-Type: application/xml

图书的对象模型

用于实现这个图书集合的 Java 对象模型有 3 个类:

  • Book,如 单 4 所示。
  • BookList,如 单 5 所示。
  • Link,如 单 6 所示。


清单 4. Book

                              
package com.ibm.devworks.ws.rest.books;
import javax.xml.bind.annotation.*;

@XmlRootElement(name="book")
@XmlAccessorType(XmlAccessType.FIELD)
public class Book {

        @XmlAttribute(name="id")
        private int id;

        @XmlElement(name="title")
        private String title;

        @XmlElement(name="isbn")
        private String ISBN;

        @XmlElement(name = "link")
        private Link link;

        public Book() { }

        public int getId() { return id; }
        public void setId(int id) { this.id = id; }

        public String getTitle() { return title; }
        public void setTitle(String title) { this.title = title; }

        public String getISBN() { return ISBN; }
        public void setISBN(String ISBN) { this.ISBN = ISBN; }
       
        public Link getLink() { return link; }
        public void setLink(String uri) { this.link = new Link(uri); }
}

 

Book 类中,您使用 Java Architecture for XMLBinding (JAXB) 注释来执行 Java 对象的编组和解组。Apache Wink 自动编组和解组带有 JAXB 注释的 Java 对象,包括创建执行编组和解组工作的 Marshaller Unmarshaller 实例。

您将使用 BookList 类来表示图书对象的集合,BookList 类将这些对象以内部存储方式存储在 ArrayList 类型的一个字段中,ArrayList 带有 JAXB 注释,以便转换为 XML 格式。


清单 5. BookList

                              
package com.ibm.devworks.ws.rest.books;

import java.util.ArrayList;
import java.util.Map;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElementRef;

@XmlRootElement(name="books")
public class BookList {

        @XmlElementRef
        ArrayList<Book> books;

        public BookList() {  }

        public BookList( Map<Integer, Book> bookMap ) {
               books = new ArrayList<Book>( bookMap.values() );
        }      
}

 

您将使用 Link 类将链接元素插入到图书对象的 XML 表示中(参见 单6)。


清单 6. Link

                              
package com.ibm.devworks.ws.rest.books;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

@XmlRootElement(name="link")
@XmlAccessorType(XmlAccessType.FIELD)
public class Link {

        @XmlAttribute
        String href = null;
       
        @XmlAttribute
        String rel = "self";
       
        public Link() { }

        public Link( String uri) {
               this.href = uri;      
        }
       
        public Link( String href, String rel) {
               this.href = href;     
               this.rel = rel;
        }
}

 

JAX-RS Web 服务

现在,您已经定义了将 URIs HTTP 方法绑定到Java 方法的 Java 服务。您可以将这个服务定义为一个Java 接口或一个类;对于前者,还需要定义实现接口的类。在接口中,您本地化 JAX-RS 注释并添加一个类来实现接口。

单7 展示了 BookService Java 接口。@javax.ws.rs.Path 注释将这个接口指定为一个 JAX-RS 服务。注释的 /books 值定义这个服务的相对根 URI。注释 @javax.ws.rs.POST@javax.ws.rs.GET@javax.ws.rs.PUT @javax.ws.rs.DELETE HTTP POSTGETPUT DELETE 操作绑定到紧随其后的 Java 方法。


清单 7. BookService接口

                              
package com.ibm.devworks.ws.rest.books;
import javax.ws.rs.*;
import javax.ws.rs.core.*;

@Path("/books")
public interface BookService {
       
        @POST
        @Consumes(MediaType.APPLICATION_XML)
        @Produces(MediaType.APPLICATION_XML)
        public Response createBook(@Context UriInfo uriInfo, Book book);
              
        @GET
        @Produces(MediaType.APPLICATION_XML)
        public BookList getBook();
       
        @Path("{id}")
        @GET
        @Produces(MediaType.APPLICATION_XML)
        public Book getBook(@PathParam("id") int id);
       
        @Path("{id}")
        @PUT
        @Consumes(MediaType.APPLICATION_XML)
        public void updateBook(@PathParam("id") int id, Book book_updated);
       
        @Path("{id}")
        @DELETE
        public void deleteBook(@PathParam("id") int id);    
}

 

对于其 URI 与接口的 @PATH("/books") 注释指定的值不同的方法,您将一个特定的 @PATH 注释附加到那些方法。@PATH 注释是可以累积的,即,一个路径表达式后面的另一个路径表达式将附加到前面的表达式。因此,绑定到 deleteBook 方法的路径模式为 /books/{id}

JAX-RS 提供了一种机制,用于从一个 HTTP 请求提取信息,将信息赋予一个 Java 对象,然后将该对象注入一个 Java 方法参数。这种机制称为注入(injection)。注入使用 Java 注释指定。在 BookService 接口中,您将插入两个注入注释:

  • @Context 注释从 HTTP 请求提取 URI 信息,将其转换为 UriInfo 对象,然后将这个对象作为方法参数 uriInfo 注入。
  • @PathParam("id") 注释匹配前面的 @Path({"id"}) 注释。这个注释从 URI 模式 /books/{id} 提取 {id},并将其注入 getBook 方法的 id 参数的值。

注意,BookService 的方法将 Java 对象(比如Book)接收为参 数并返回 Java 对象;这是可能的,因为这些对象带有JAXB 注释,因此 Apache Wink 能够对它们进行自动编组和解组。

单8 展示了实现 BookService 的类的结构。BookServiceImpl 是一个 singleton 类,使用一个内存映射图(in-memory map)来维护图书对象集合。singleton 实例使用多线程服务并发请求,因此它必须是线程安全的(thread-safe)。


清单 8. BookService实现类

                              
package com.ibm.devworks.ws.rest.books;
import javax.ws.rs.*;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

@Path("/books")
public class BookServiceImpl implements BookService {
       
        private static BookServiceImpl instance = null;
       
        private BookServiceImpl() {  }
       
        public synchronized static BookServiceImpl getInstance() {
               if(instance == null) {
                       instance = new BookServiceImpl();
               }
               return instance;
        }
       
        private Map <Integer, Book> bookMap =
               new ConcurrentHashMap <Integer, Book>();
       
        private AtomicInteger seqNumber = new AtomicInteger();

        public Response createBook(@Context UriInfo uriInfo, Book book)
        throws WebApplicationException {     
          ...
        }
       
        public BookList getBook() {
           ... 
        }
       
        public Book getBook(@PathParam("id") int id)
        throws WebApplicationException {
           ...
        }
              
        public void updateBook(@PathParam("id") int id, Book book_updated)
        throws WebApplicationException {     
          ...
        }
       
        public void deleteBook(@PathParam("id") int id)
        throws WebApplicationException {
            ...               
        }      
}

 

图书集合存储在一个映射图中。要提供对这个映射图的线程安全访问,您将使用一个ConcurrentHashMap。 要生成惟一的图书 IDs,您将使用一个 AtomicInteger,它能确保增量操作是原子级的(atomic),从而确 保线程安全。

设置 Application 抽象类的子类

JAX-RS Web 应用程序包含一组资源(REST Web 服务),这些资源作为 JAX-RS 抽象类 javax.ws.rs.core.Application 的子类出现。 单9 中显示的 BookWebApp 类扩展了 Application 类。


清单 9.javax.ws.rs.core.Application 的具体子类

                              
package com.ibm.devworks.ws.rest.books;

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;

public class BookWebApp extends Application {

        private Set<Object> svc_singletons = new HashSet<Object>();    
        private Set<Class<?>> svc_classes  = new HashSet<Class<?>>();

        public BookWebApp() {
                svc_singletons.add(BookServiceImpl.getInstance());
        }
       
        @Override
        public Set<Object> getSingletons() {
               return svc_singletons;
        }
         
        @Override
        public Set<Class<?>> getClasses() {
               return svc_classes;
        }

}

 

BookWebApp 的构造函数获取这个图书服务实现对象的 singleton 实例。getSingletons 方法返回一个组,其惟一元素就是这个 singletonJAX-RS 在服务一个请求后并不删除这个 singleton

构建和部署 REST Web 服务

您将使用 Eclipse 来创建一个动态 Web 项目,这个项目包含上一小节介绍过的 Java 类和其他资源,比如 Web 应用程序部署描述符和必要的 JAR 文件。

注意:创建一个 Eclipse 动态 Web 项目的另一种方法是从一个 archetype(比如 maven-archetype-webapp webapp-jee5) 创建一个 Maven 项目(archetype 是一个 Maven 项目模板)。但是,在 Maven 当前版本中,生成 Eclipse 使用的项目元数据时会出现一些问题:要支持 IDE 中的自动代码构建,必须手动修改一些 Eclipse 项目文件。因此,本文使用 Eclipse 动态 Web 项目。

Web 服务的 Eclipse 项目

Eclipse 动态 Web 项目支持开发在一个 servlet 容器中运行的 Web 应用程序,因此它是用于开发 Apache Wink 服务的一个不错的项目模板。要为这个图书 REST 服务创建一个动态 Web 项目,执行以下步骤:

  1. Eclipse 中,单击 File > New > Dynamic Web Project
  2. Dynamic Web Project Wizard 中,在 Project name 文本框中输入 Books
  3. 单击 Target runtime 方框旁边的 New 创建一个 Tomcat 运行时。
  4. New Server Runtime Environment 向导页面上,单击 Apache > Apache Tomcat v6.0,然后单击 Next
  5. Tomcat Server 向导页面上,单击 Browse, 然后导航到 决条件 小节中定义的 CATALINA_HOME 目录。
  6. 单击 OK,然后单击 Finish
  7. Dynamic Web Project 页面上,单击 Finish

将上一小节中介绍的 Java 类添加到刚才创建的 Books 项目中。为此,执行以下步骤:

  1. Project Explorer 视图中,打开 Books 文件夹。右键单击 Java Resources:src,然后单击 New > Package
  2. Java Package 窗口中的 Name 文本框中,输入包名称com.ibm.devworks.ws.rest.books 并单击 Finish
  3. Java 类添加到 com.ibm.devworks.ws.rest.books 包:
    1. Project Explorer 视图中,右键单击上述包,然后单击 New > Class
    2. Name 文本框中,输入类名称(比如为 Book.java 输入 Book),然后单击 Finish
    3. 使用 Eclipse Java 编辑器来创建类。

然后,向项目添加构建和运行一个 Apache Wink 服务所需的 JAR 文件。当您创建这个应用程序项目时,Maven JAR 文件下载到本地资源库,本地资源库驻留在 %HOMEPATH%/.m2/repositoryWindows)或 $HOME/.m2/repositoryLinux)中。

要将 JAR 文件添加到这个项目,执行以下步骤:

  1. Project Explorer 视图中,导航到 Books/Web Content/WEB-INF
  2. 右键单击 lib,然后单击 Import > General > File System,然后单击 Next
  3. File System 窗口中,从 Maven 资源库(已在导入应用程序 Apache Wink 模块时由 m2eclipse 填充)导入 JAR 文件 jsr311-api-1.0.jar,方法是导航到包含这个 JAR 文件的目录,然后单击 OK。在 File System 窗口中,选中这个 JAR 文件,然后单击 Finish
  4. 重复步骤 2 到步骤 3,分别导入 JAR 文件 wink-server-1.0-incubating.jar wink-common-1.0-incubating.jarslf4j-api-1.5.8.jar slf4j-simple-1.5.8.jar

所有库都导入后,lib 目录将包含如 图 9 所示的 JAR 文件。


9. 一个Apache Wink 服务所需的库

您还可以通过从下面的 部分中提供的代码样例中导入Book 服务来创建这个 Eclipse 项目。为此,下载并解压文件,然后在 Eclipse 中单击 File > Import >Existing Project Into Workspace。在 Import ProjectsWizard 中,单击 Browse, 导航到 Books 文件夹,单击 OK,然后单击 Finish

将服务部署到 Tomcat

在部署描述符文件 web.xml(如 单10 所示)中,您将带有相对根路径 /* URIs 映射到 Wink servlet org.apache.wink.server.internal.servlet.RestServletRestServlet 被传递为 init-param,这是 Books 服务提供的 Application 子类的名称。


清单 10.BookService 部署服务描述符 web.xml

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

<web-app
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                       http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  id="WebApp_ID"
  version="2.5">

  <display-name>Book Web Application</display-name>

  <servlet>
    <servlet-name>restSdkService</servlet-name>
        <servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
        <init-param>
               <param-name>javax.ws.rs.Application</param-name>
               <param-value>com.ibm.devworks.ws.rest.books.BookWebApp</param-value>
        </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>restSdkService</servlet-name>
        <url-pattern>/*</url-pattern>
  </servlet-mapping>

</web-app>

 

部署描述符设置好后,就可以将这个 Books 服务部署到 Tomcat 了。为了进行测试,将这个 Books 服务部署到一个由 Eclipse 托管的 Tomcat 实例中并运行。为此,执行以下步骤:

  1. Project Explorer 视图中,右键单击 Books,然后单击 Run As > Run on Server
  2. Run on Server 窗口中,单击 Finish, 这将把这个 Books 服务部署到创建这个 Books 项目时配置的 Tomcat 运行时中并启动 Tomcat

测试完成后,您将这个 Books 服务打包到一个 WAR 文件中,以备部署到生产环境中。为此,在 Project Explorer 视图中,右键单击 Books,然后单击Export > WAR file。 在 WARExport Wizard 中,单击 Browse,选择创建 WAR 文件的文件夹,然后单击 Save Finish

调用 REST Web 服务

可以使用一个支持 HTTP 操作 GETPOSTPUT DELETE HTTP 客户机来调用这个Books 服务。 单 11 展示了如何使用 Curl 来调用这个服务。首先,您将得到所有图书的表示(最初没有图书);然后,您使用jaxrs.xml rest.xml 两个表示来创建两本图书;最后,您得到所有图书的表示和 id=2 的图书的表示。


清单 11. 调用 Books 服务

                               
$ curl   -X GET  http://localhost:8080/Books/books
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<books/>


$ more Books/xml/jaxrs.xml
<?xml version="1.0" encoding="UTF-8"?>
<book>
   <titleRESTful Java with JAX-RS</title>
   <isbn>978-0-596-15804-0</isbn>
</book>

$ more Books/xml/rest.xml
<?xml version="1.0" encoding="UTF-8"?>
<book>
   <title>RESTful Web Services</title>
   <isbn>978-0-596-52926-0</isbn>
</book>


$  curl -H "Content-Type: application/xml" -T Books/xml/jaxrs.xml /
        -X POST http://localhost:8080/Books/books

$  curl -H "Content-Type: application/xml" -T Books/xml/rest.xml /
        -X POST http://localhost:8080/Books/books


$ curl   -X GET  http://localhost:8080/Books/books
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<books>
  <book id="2">
     <title>RESTful Web Services</title>
     <isbn>978-0-596-52926-0</isbn>
     <link rel="self" href="http://localhost:8080/Books/books/2"/>
  </book>
  <book id="1">
    <title>RESTful Java with JAX-RS</title>
    <isbn>978-0-596-15804-0</isbn>
    <link rel="self" href="http://localhost:8080/Books/books/1"/>
  </book>
</books>

$ curl   -X GET  http://localhost:8080/Books/books/2
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book id="2">
  <title>RESTful Web Services</title>
  <isbn>978-0-596-52926-0</isbn>
  <link rel="self" href="http://localhost:8080/Books/books/2"/>
</book>

结束语

本文展示了如何联合使用几种技术来支持 Apache Wink REST Web 服务的开发、构建和部署。Java 注释的威力简化了开发工作。

REST Web 服务支持应用程序交换链接数据。TimBerners-Lee 先生预言,链接数据将改变 Web 的面貌。Bill Burke 在他撰写的图书 RESTful Java with JAX-RS 中指出,通过将服务交互的复杂性限制为数据表示,REST Web 服务将服务组合性和重用提高到一个新的水平。

本文仅仅涉及 Apache Wink 服务开发的皮毛,ApacheWink 的其他重要特性包括链接构建器、自定义消息体阅读器以及针对不受支持的数据格式的编写器。要深入了解Apache Wink 框架,一定要看一看 Apache Wink 中包含的其他示例。