RESTEasy是JBoss的開源項目之一,是一個RESTful Web Services框架。RESTEasy的開發者Bill Burke同時也是JAX-RS的J2EE標准制定者之一。JAX-RS是一個JCP制訂的新標准,用於規范基於HTTP的RESTful Web Services的API。我們已經有SOAP了,為什么需要Restful WebServices?用Bill自己的話來說:"如果是為了構建SOA應用,從技術選型的角度來講,我相信REST比SOAP更具優勢。開發人員會意識到使用傳統方式有進行SOA架構有多復雜,更不用提使用這些做出來的接口了。這時他們就會發現Restful Web Services的光明之處。“ 說了這么多,我們使用RESTEasy做一個項目玩玩看。
首先創造一個maven的web項目:
Java代碼:
mvn archetype:create -DgroupId=org.bluedash \ -DartifactId=try-resteasy -DarchetypeArtifactId=maven-archetype-webapp
准備工作完成后,我們就可以開始寫代碼了,假設我們要撰寫一個處理客戶信息的Web Service,它包含兩個功能:一是添加用戶信息;二是通過用戶Id,獲取某個用戶的信息,而交互的方式是標准的WebService形式,數據交換格式為XML。假設一條用戶包含兩個屬性:Id和用戶名。那么我們設計交換的XML數據如下:
<user> <id>1</id> <name>liweinan</name> </user>
首先要做的就是把上述格式轉換成XSD2,網上有在線工具可以幫助我們完成這一工作3,在此不詳細展開。使用工具轉換后,生成如下xsd文件:
<?xml version="1.0" encoding="utf-8"?>
<xsd:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="user" type="userType" />
<xsd:complexType name="userType">
<xsd:sequence>
<xsd:element name="id" type="xsd:int" />
<xsd:element name="name" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
有了xsd文件,我們便可以使用JDK自帶工具的xjc將xsd轉換成為Java的Class。將上述xsd文件存為 user.xsd,並使用如下命令進行轉換:
xjc user.xsd
執行結束后我們會得到一系列的類文件:
Li-Weinans-MacBook-Pro:Desktop liweinan$ xjc user.xsd parsing a schema... compiling a schema... generated/ObjectFactory.java generated/UserType.java
這樣,我們的XML格式的交換數據便轉化為面向對像的Java類了,是不是感覺有點像Hibernate的ORM理念?沒錯,將XML映射成成面向對象的數據類,這個過程叫做XML Binding,即XML綁定。這個過程也有J2EE標准,叫做JAXB4。而RESTEasy是全面支持JAXB的。可以說RESTEasy所支持的JAX-RS標准,當與JAXB標准結合在一起使用時,就可以發揮出最大優勢,讓程序員少寫一堆一堆的代碼。有關JAXB標准,會在 獨立的篇章中 詳細討論,在此先不展開。總之我們將生成的Java類放進項目中等候使用。我們可以看一下UserType類的內容:
package org.bluedash.resteasy;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "userType", propOrder = {
"id",
"name"
})
public class UserType {
protected int id;
@XmlElement(required = true)
protected String name;
/**
* Gets the value of the id property.
*
*/
public int getId() {
return id;
}
/**
* Sets the value of the id property.
*
*/
public void setId(int value) {
this.id = value;
}
/**
* Gets the value of the name property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getName() {
return name;
}
/**
* Sets the value of the name property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setName(String value) {
this.name = value;
}
}
可以看到,XML格式就是通過一些JAXB的標記被映射成了Java類。我們沒寫什么代碼,已經把數據模型定義清楚了。接下來我們撰寫最核心的WebService API。我們的WebService包含兩個接口:一個是添加用戶接口createUser,另一個是獲取用戶接口getUser:
package org.bluedash.resteasy;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
@Path("/users")
public class UserServlet {
private Map<Integer, UserType> userStore =
new ConcurrentHashMap<Integer, UserType>();
private AtomicInteger idGenerator = new AtomicInteger();
@POST
@Consumes("application/xml")
public Response createUser(UserType user) {
user.setId(idGenerator.incrementAndGet());
userStore.put(user.getId(), user);
System.out.println(user.getName() + " created: "
+ user.getId());
return Response.created(URI.create("/users/"
+ user.getId())).build();
}
@GET
@Path("{id}")
@Produces("application/xml")
public UserType getUser(@PathParam("id") int id) {
UserType u = userStore.get(id);
if (u == null) {
throw new WebApplicationException(
Response.Status.NOT_FOUND);
}
return u;
}
}
用幾個簡單的JAX-RS標記,便把普通的函數變成了WebService接口。而這些標記將由RESTEasy支持生效。接下來我們將要進行RESTEasy的配置工作。RESTEasy的配置方法有多種多樣,可以和Spring等容器集成,也可以獨立運行,因為我們用的Servlet的形式使RESTEasy進行工作,這也是最主流的方式,因此在這里使用web容器來加載它,首先定義一個配置類:
package org.bluedash.resteasy;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
public class BluedashResteasyApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> classes = new HashSet<Class<?>>();
public BluedashResteasyApplication() {
// classes.add(UserServlet.class);
singletons.add(new UserServlet());
}
@Override
public Set<Class<?>> getClasses() {
return classes;
}
@Override
public Set<Object> getSingletons() {
return singletons;
}
}
這個類擴展JAX-RS的Application接口,用於封裝我們的WebService API方法。我們可以看到JAX-RS支持兩種封裝方法,一種是classes封裝,由容器管理WebServices類的實例化和銷毀等動作,一個線程一個實例,開發者不需要關心線程安全問題。但這種方法可能比較浪費資源。如果開發者想自己管理線程安全,共線程共用一個WebServices實例,那么就用singletons封裝。我們在這里用的singletons封裝,這也就解釋了為什么我們在 UserServlet中使用了ConcurrentHashMap和AtomicInteger這些保障線程安全的類。接下來就是在web.xml中啟動RESTEasy:
<!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> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>javax.ws.rs.core.Application</param-name> <param-value>org.bluedash.resteasy. BluedashResteasyApplication</param-value> </context-param> <listener> <listener-class>org.jboss.resteasy.plugins.server. servlet.ResteasyBootstrap</listener-class> </listener> <servlet> <servlet-name>Resteasy</servlet-name> <servlet-class>org.jboss.resteasy.plugins.server.servlet. HttpServletDispatcher</servlet-class> </servlet> <servlet-mapping> <servlet-name>Resteasy</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
沒錯,就是這么簡單,這樣,我們的WebService就完成了!還差點什么呢?嗯,還差一個Test Case來使用我們的WebService接口,並驗證它的正確性,讓我們來寫一個TestUserAPI
package org.bluedash.resteasy.test.integration.test;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import junit.framework.TestCase;
public class TestUserAPI extends TestCase {
public static final String USER_API =
"http://127.0.0.1:8080/try-resteasy/users";
public void testCreateUserAndGetUser() throws IOException {
URL url =
new URL(USER_API);
HttpURLConnection connection =
(HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/xml");
connection.setDoOutput(true);
connection.setInstanceFollowRedirects(false);
connection.setConnectTimeout(1000);
String userXML = "<user><name>liweinan</name></user>";
OutputStream os = connection.getOutputStream();
os.write(userXML.getBytes());
os.flush();
assertEquals(HttpURLConnection.HTTP_CREATED, connection
.getResponseCode());
connection.disconnect();
}
}
一切都已經准備就緒,最后我們要配置一下Maven,讓它下載所需的RESTEasy等庫,然后配置Maven使用Jetty Web服務器,來把我們的服務和測試跑起來:
<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>org.bluedash</groupId>
<artifactId>try-resteasy</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>try-resteasy Maven Webapp</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>JBossMavenRepo</id>
<name>JBoss Maven2 repo</name>
<url>http://repository.jboss.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>1.2.RC1</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>1.2.RC1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<build>
<finalName>try-resteasy</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/integration/**</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>integration-tests</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<skip>false</skip>
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/integration/**</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.15</version>
<configuration>
<scanIntervalSeconds>5</scanIntervalSeconds>
<stopKey>foo</stopKey>
<stopPort>9999</stopPort>
</configuration>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<scanIntervalSeconds>5</scanIntervalSeconds>
<daemon>true</daemon>
</configuration>
</execution>
<execution>
<id>stop-jetty</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
有關Maven的配置就不詳細展開了。配置完成后我們便可以運行單元測試,看看WebServices是否正確運行。執行下述命令
mvn integration-test
執行結果如下:
------------------------------------------------------- T E S T S ------------------------------------------------------- Running org.bluedash.resteasy.test.integration.test.TestUserAPI liweinan created: 1 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.372 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
可以看到,我們的測試按預期執行成功了。這篇文章中,我簡單向大家介紹了RESTEasy的初步使用方法,希望對大家在架構SOA應用時,有所幫助。JAX-RS標准做為J2EE家庭中相對較新的一員,其應用前景是十分廣闊的
