java tjws

RESTEasy除了能够运行于标准Web服务器(如Tomcat),或是与J2EE容器(如JBoss AS)整合之外,还可以嵌入式的方式运行,本文通过一些代码实例来对这些功能和RESTEasy中相关的classes进行说明。

TJWS
首先,我们需要理解,RESTEasy使用TJWS作为嵌入式RESTFul WebService服务的容器。TJWS是一个开源的Servlet容器,它相当轻便小巧,占用资源少,启动速度快,因此做为嵌入式容器非常适合。在 RESTEasy中,已经自包含了一个tjws服务器,如果签出RESTEasy的代码则可以看到它位于代码中的tjws目录:

Bash代码   收藏代码
  1. liweinan@smart:~/projs/resteasy/jaxrs$ ls -l | grep tjws  
  2. drwxr-xr-x   7 liweinan  staff    238 Sep  5 22:37 tjws  



基于TJWS这个容器,RESTEasy为我们提供了TJWSEmbeddedJaxrsServer,开发人员可以很方便地使用它来启动一个内嵌于代码之中的JAX-RS服务。下面我们动手实际做一个例子试试看:

HelloWorldService
首先我们来创建一个最简单的RESTFul WebService:

Java代码   收藏代码
  1. import javax.ws.rs.GET;  
  2. import javax.ws.rs.Path;  
  3. import javax.ws.rs.Produces;  
  4.   
  5. @Path("/helloworld")  
  6. public interface HelloWorldService {  
  7.   
  8.     @GET  
  9.     @Produces("text/plain")  
  10.     public String printHelloWorld();  
  11. }  



以下是它的实现类:

Java代码   收藏代码
  1. public class HelloWorldServiceImpl implements HelloWorldService {  
  2.     public static final String HELLO_WORLD = "Hello, world!";  
  3.   
  4.     public String printHelloWorld() {  
  5.         return HELLO_WORLD;  
  6.     }  
  7. }  



很简单的一个WebService,不需做太多说明。接下来的重点是如何使用TJWSEmbeddedJaxrsServer来将这个WebService启动起来:

TJWSEmbeddedJaxrsServer
我们可以通过JUnit单元测试的形式来实现这个例子:

Java代码   收藏代码
  1. import org.jboss.resteasy.plugins.server.tjws.TJWSEmbeddedJaxrsServer;  
  2. import org.junit.After;  
  3. import org.junit.Before;  
  4. import org.junit.Test;  
  5.   
  6. import java.io.BufferedReader;  
  7. import java.io.IOException;  
  8. import java.io.InputStreamReader;  
  9. import java.net.HttpURLConnection;  
  10. import java.net.URL;  
  11.   
  12. import static org.junit.Assert.assertEquals;  
  13.   
  14. public class TestTJWSEmbeddedJAXRSServer {  
  15.   
  16.     private static TJWSEmbeddedJaxrsServer tjws;  
  17.   
  18.     @Before  
  19.     public void setUp() {  
  20.         tjws = new TJWSEmbeddedJaxrsServer();  
  21.         tjws.setPort(8081);  
  22.         tjws.getDeployment().getActualResourceClasses().add(HelloWorldServiceImpl.class);  
  23.         tjws.start();  
  24.     }  
  25.   
  26.     @After  
  27.     public void tearDown() {  
  28.         tjws.stop();  
  29.     }  
  30.   
  31.     @Test  
  32.     public void testEmbeddedJAXRSServer() throws IOException {  
  33.         URL url = new URL("http://127.0.0.1:8081/helloworld");  
  34.         HttpURLConnection connection = (HttpURLConnection) url.openConnection();  
  35.         connection.setRequestMethod("GET");  
  36.         connection.setRequestProperty("Content-Type""text/plain");  
  37.         connection.setDoOutput(true);  
  38.         connection.setInstanceFollowRedirects(false);  
  39.         connection.setConnectTimeout(1000);  
  40.   
  41.         BufferedReader ir = new BufferedReader(new InputStreamReader(connection.getInputStream()));  
  42.   
  43.         assertEquals(HttpURLConnection.HTTP_OK, connection.getResponseCode());  
  44.         assertEquals("Hello, world!", ir.readLine());  
  45.         ir.close();  
  46.         connection.disconnect();  
  47.   
  48.     }  
  49.   
  50. }  



从上面的代码中,我们可以看到,在setUp()方法中,我们将HelloWorldServiceImpl这个WebService添加进了 TJWSEmbeddedJaxrsServer当中并启动了这个server,同时设定的服务端口为8081。值得说明的是这行代码:

Java代码   收藏代码
  1. tjws.getDeployment().getActualResourceClasses().add(HelloWorldServiceImpl.class);  



我们是以perRequestResource的形式将HelloWorldServiceImpl服务进行注册,这一点从org.jboss.resteasy.spi.ResteasyDeployment的源代码中可以看出来:

Java代码   收藏代码
  1. ...  
  2. registry.addPerRequestResource(actualResourceClass);  
  3. ...  



因此,我们的HelloWorldServiceImpl,将在每次客户端请求服务时被RESTEasy进行实例化,服务处理完成后销毁。如果我们想以singleton方式来添加我们的WebService,可以使用getResourceFactories()方法:

Java代码   收藏代码
  1. tjws.getDeployment().getResourceFactories().add(HelloWorldServiceImpl.class);  



这种情况下我们必须自己来保证HelloWorldServiceImpl是线程安全的。如果你不了解我这里说的singleton模式,可参考 蓝点上这篇 "RESTEasy的入门文章":http://bluedash.net/spaces/RESTEasy ,理解了RESTEasyr的基本使用方法后,再回来看本篇文章。

我们继续看这个单元测试的源代码,在setUp()方法中,我们进行了服务注册及参数的配置之后,便启动了这个内嵌的WebService服务:

Java代码   收藏代码
  1. tjws.start();  



接下来,在主测试程序中,我们通过使用最基本的HTTP协议来调用WebService服务,并查看返回值与期望是否相附,代码很明白,没有需要特别说明的地方。

最后,在tearDown() 方法中,我们关闭了WebService服务:

Java代码   收藏代码
  1. tjws.stop();  



这是测试完成后必要的清理工作。

InMemoryClientExecutor
如果我们不需要对外提供WebService服务,而只是会在自己的项目代码中,以编码的形式来调用自己的WebService,RESTEasy还提供了更为轻量级的InMemoryClientExecutor, 下面是一个例子:

Java代码   收藏代码
  1. import org.jboss.resteasy.client.ClientRequest;  
  2. import org.jboss.resteasy.client.core.executors.InMemoryClientExecutor;  
  3. import org.junit.Test;  
  4.   
  5. import static org.junit.Assert.assertEquals;  
  6.   
  7. public class TestInMemoryClientExecutor {  
  8.   
  9.     @Test  
  10.     public void testHelloWorld() throws Exception {  
  11.         InMemoryClientExecutor executor = new InMemoryClientExecutor();  
  12.         executor.getDispatcher().getRegistry().addPerRequestResource(HelloWorldServiceImpl.class);  
  13.         ClientRequest request = new ClientRequest("/helloworld", executor);  
  14.         assertEquals(HelloWorldServiceImpl.HELLO_WORLD, request.getTarget(String.class));  
  15.     }  
  16. }  



一般是在RESTEasy自身的测试代码中多见这个InMemoryClientExecutor的使用,在一般的应用编码情况下,可能InMemoryClientExecutor用到的场合并不会太多。

TJWSServletContainer
在RESTEasy的单元测试代码中,提供了一个很方便的TJWSServletContainer类,它对TJWSEmbeddedJaxrsServer进行了封装,我们也可以使用这个来实现我们的嵌入式WebService服务:

Java代码   收藏代码
  1. import org.jboss.resteasy.client.ProxyFactory;  
  2. import org.jboss.resteasy.plugins.providers.RegisterBuiltin;  
  3. import org.jboss.resteasy.spi.ResteasyDeployment;  
  4. import org.jboss.resteasy.spi.ResteasyProviderFactory;  
  5. import org.jboss.resteasy.test.TJWSServletContainer;  
  6. import org.junit.AfterClass;  
  7. import org.junit.BeforeClass;  
  8. import org.junit.Test;  
  9.   
  10. import static org.junit.Assert.assertEquals;  
  11.   
  12. public class TestTJWSServletContainer {  
  13.   
  14.     @BeforeClass  
  15.     public static void setUp() throws Exception {  
  16.   
  17.         ResteasyDeployment deployment = TJWSServletContainer.start();  
  18.         deployment.getRegistry().addPerRequestResource(HelloWorldServiceImpl.class);  
  19.     }  
  20.   
  21.     @AfterClass  
  22.     public static void stop() throws Exception {  
  23.         TJWSServletContainer.stop();  
  24.     }  
  25.   
  26.   
  27.     @Test  
  28.     public void testIt() throws Exception {  
  29.         RegisterBuiltin.register(ResteasyProviderFactory.getInstance());  
  30.         HelloWorldService client = ProxyFactory.create(HelloWorldService.class"http://127.0.0.1:8081");  
  31.         assertEquals("Hello, world!", client.printHelloWorld());  
  32.   
  33.     }  
  34. }  



这个例子在本质上和直接使用TJWSEmbeddedJaxrsServer没有任何区别(实际上是有一点区别,有兴趣的读者可以参考下一节深入 讨论的内容)。另外,在这个单元测试的主测试功能testIt当中,我们并没有使用最直接的HTTP的调用方式,而是利用了RESTEasy客户端的方式 来调用了WebService,可以看到,和第一个例子中那个单元测试相比,少写了多少代码,所以使用RESTEasy客户端来调用RESTEasy的 WebService应该是最省事的。

深入讨论
如果对RESTEasy的源代码实现没兴趣,本节内容可以无视。我们要讨论第一个例子(针对TJWSEmbeddedJaxrsServer)和第三个例子(针对TJWSServletContainer)中的一处代码上的区别。在第一个例子中,我们注册服务的代码为:

Java代码   收藏代码
  1. tjws.getDeployment().getActualResourceClasses().add(HelloWorldServiceImpl.class);  



而在第三个例子中,我们使用的方法为:

Java代码   收藏代码
  1. deployment.getRegistry().addPerRequestResource(HelloWorldServiceImpl.class);  



实际上我们同为使用ResteasyDeploy这个类来注册我们的Service,为什么两个代码中使用的方法不同呢?

仔细看下我们会发现,两个方法所处的上下文环境不同。再看下第一个:

Java代码   收藏代码
  1. ...  
  2. tjws.getDeployment().getActualResourceClasses().add(HelloWorldServiceImpl.class);  
  3. tjws.start();  
  4. ...  



我们是在start()之前来注册服务。而第三个例子中:

Java代码   收藏代码
  1. ...  
  2. ResteasyDeployment deployment = TJWSServletContainer.start();  
  3. deployment.getRegistry().addPerRequestResource(HelloWorldServiceImpl.class);  
  4. ...  



我们是在start()之后来注册服务。这就是两者使用不同方法的重要区别,看下ResteasyDeployment这个RESTEasy中的 核心类,我们会发现,它是在start()中进行registry的加载工作,并调用registration()方法将我们在 getActualResourceClasses中添加的服务注册进Registry的:

Java代码   收藏代码
  1. package org.jboss.resteasy.spi;  
  2. ...  
  3. public class ResteasyDeployment {  
  4.    public void start() {  
  5.       registry = dispatcher.getRegistry();  
  6.       ...  
  7.       registration();  
  8.       ...  
  9.    }  
  10.   
  11.    ...  
  12.    public void registration() {  
  13.       ...  
  14.       if (resourceClasses != null)  
  15.       {  
  16.          for (String resource : resourceClasses)  
  17.          {  
  18.             Class clazz = null;  
  19.             try  
  20.             {  
  21.                clazz = Thread.currentThread().getContextClassLoader().loadClass(resource.trim());  
  22.             }  
  23.             catch (ClassNotFoundException e)  
  24.             {  
  25.                throw new RuntimeException(e);  
  26.             }  
  27.             registry.addPerRequestResource(clazz);  
  28.          }  
  29.       }  
  30.    }  
  31. ...  
  32. }  




了解了细节之后,我们便知道第一个例子和第三个例子当中这一差异的原因了。在这里要特别说明一下,我们在使用RESTEasy的时候,应该使用第 一种方式,因为这是写在RESTEasy官方文档中的标准使用及调用方法。第三个例子中介绍的先start(),再添加服务的调用方法,并没有写在 RESTEasy的文档里,是一种"非标准"的使用,了解即可。

有关样例代码
我将本文中使用的代码放在了github上面,有兴趣的读者可以自己下载代码玩玩看:

Html代码   收藏代码
  1. https://github.com/liweinan/try-resteasy  



使用这个git命令签出代码:

Bash代码   收藏代码
  1. git clone git://github.com/liweinan/try-resteasy.git  



代码签出后可以用maven命令来执行本文中的代码:

Bash代码   收藏代码
  1. mvn test 

http://weli.iteye.com/blog/1299497

你可能感兴趣的:(java tjws)