<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>cachalot</title>
    <description></description>
    <link>http://cachalot.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>整合 GWT 与 Jetty Continuations</title>
        <author>cachalot</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://cachalot.javaeye.com">cachalot</a>&nbsp;
          链接：<a href="http://cachalot.javaeye.com/blog/118895" style="color:red;">http://cachalot.javaeye.com/blog/118895</a>&nbsp;
          发表时间: 2007年08月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          虽然GWT充满了争议，但是对于Java开发者而言，能够使用自己最熟悉的语言来进行Ajax开发，Google推出的GWT的确具有相当大的诱惑力，而且作为一个快速发展的开源框架，相信以后它的用户群应该会越来越多。<br /><br />Jetty Continuations 很好的解决了服务器更新客户端的问题，服务器不用再为每一个等待响应的客户端单独建立一个线程，借助Continuations和新IO，可以在有限的线程内支持更多用户。<br />更多内容参见<a href="http://docs.codehaus.org/display/JETTY/Continuations" target="_blank">http://docs.codehaus.org/display/JETTY/Continuations</a><br /><br />最近在做一个浏览器上的在线聊天项目，用来做为在线客服支持，打算使用GWT编写界面，但是GWT所提供RPC机制并不直接支持Jetty Continuations。为了支持比当前线程数更多的用户，Jetty中的Continuation.suspend()会抛出一个特殊的运行时异常：RetryRequest。这个异常将传播到servlet以外，然后通过过滤器传回，再由SelectChannelConnector捕获，将请求放入处于等待状态的Continuation队列中，此时HTTP连接并不关闭，而当前的线程却可以被放回线程池，供别的请求使用。但是使用GWT时，GWT捕获了所有的Throwable，这样就会导致Continuations机制失败。而且GWT提供的RemoteServiceServlet类中很多方法都被定义为final或static，于是你无法通过继承这个类来重写相关方法，让它放过RetryRequest。<br /><br />但是因为GWT是开源项目，于是Jetty组织改写RemoteServiceServlet，提供了OpenRemoteServiceServlet，将Continuations所敏感的方法去掉final和static，然后继承OpenRemoteServiceServlet，提供了它们自己的AsyncRemoteServiceServlet，这样我们GWT服务器端的RPC接口的实现类直接继承AsyncRemoteServiceServlet，无须其它更改，就可以使用Jetty Continuations的API了。<br /><br />AsyncRemoteServiceServlet14.java中放过RetryRequest的相关代码：<br /><pre name="code" class="java">
/**
* Throws the Jetty RetryRequest if found.
*
* @param caught the exception
*/
protected void throwIfRetyRequest(Throwable caught) {
    if (caught instanceof UnexpectedException) {
        caught = caught.getCause();
    }
    if (caught instanceof RuntimeException && JETTY_RETRY_REQUEST_EXCEPTION.equals(caught.getClass().getName())) {
        throw (RuntimeException) caught;
    }
}
</pre><br />更多内容参见<a href="http://blogs.webtide.com/gregw/2006/12/07/1165517549286.html" target="_blank">http://blogs.webtide.com/gregw/2006/12/07/1165517549286.html</a><br /><br />这里提供一个完整的小例子给大家，也作为我这段时间学习的总结<img src="/images/smiles/icon_wink.gif"/>：<br /><br />下载最新的<br />GWT <a href="http://code.google.com/webtoolkit/download.html" target="_blank">http://code.google.com/webtoolkit/download.html</a><br />Jetty <a href="http://docs.codehaus.org/display/JETTY/Downloading+and+Installing#download" target="_blank">http://docs.codehaus.org/display/JETTY/Downloading+and+Installing#download</a><br />然后在<a href="http://jira.codehaus.org/browse/JETTY-399" target="_blank">http://jira.codehaus.org/browse/JETTY-399</a>上面找到OpenRemoteServiceServlet14.java和AsyncRemoteServiceServlet14.java，这是OpenRemoteServiceServlet和AsyncRemoteServiceServlet的新版本，支持GWT1.4版本。<br /><br />首先使用GWT的命令行工具创建eclipse下的GWT项目，然后导入到eclipse中，再导入jetty-util-6.1.3.jar。<br /><br />RPC接口定义：<br /><pre name="code" class="java">
public interface TestService extends RemoteService {

	public String getNews();
	
}
</pre><br /><br />GWT的入口类：<br /><pre name="code" class="java">
public class GCEntry implements EntryPoint {
	
	public void onModuleLoad() {
		final TestServiceAsync testService = (TestServiceAsync)GWT.create(TestService.class);
		ServiceDefTarget target = (ServiceDefTarget)testService;
		target.setServiceEntryPoint(GWT.getModuleBaseURL() + "test");
		
		final TextArea printArea = new TextArea();
		printArea.setVisibleLines(10);
		printArea.setCharacterWidth(30);
		
		testService.getNews(new AsyncCallback() {

			public void onFailure(Throwable caught) {			}

			public void onSuccess(Object result) {
				printArea.setText(printArea.getText() + result);
				testService.getNews(this);
			}
			
		});
		
		DockPanel dp = new DockPanel();
		dp.add(printArea, DockPanel.CENTER);
	    
		RootPanel.get().add(dp);
	}

}
</pre><br />界面将显示一个文本框，反复调用testService.getNews()，将异步返回的结果输出到文本框中。<br /><br />服务器端RPC接口的实现：<br /><pre name="code" class="java">
public class TestServiceImpl extends AsyncRemoteServiceServlet14 implements
		TestService {
	
	private NewsCreator newsCreator;
	
	public void init() {
		newsCreator = new NewsCreator();
	}
	
	public String getNews() {
		return newsCreator.getNews(getThreadLocalRequest());
	}

}
</pre><br />注意这里继承的是AsyncRemoteServiceServlet14。通过一个辅助类NewsCreator来生成新的时间：<br /><pre name="code" class="java">
public class NewsCreator implements Runnable {

	private Set&lt;Continuation> readers;
	
	public NewsCreator() {
		readers = new HashSet&lt;Continuation>();
		new Thread(this).start();
	}
	
	public void run() {
		while (true) {
			synchronized(this) {
				for (Continuation continuation : readers) {
					continuation.setObject(new Date().toString() + "\r\n");
					continuation.resume();
				}
				readers.clear();
			}
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	public String getNews(HttpServletRequest request) {
		Continuation continuation = ContinuationSupport.getContinuation(request, null);
		synchronized(this) {
			readers.add(continuation);
		}
		continuation.suspend(10000);
		String news = continuation.getObject() == null ? "No Time" : (String)continuation.getObject();
		continuation.setObject(null);
		
		return news;
	}

}
</pre><br />getNews()为每个请求获取一个Continuation实例，然后将这个实例保存起来，阻塞10秒，同时NewsCreator每隔两秒发布一次时间，发布时将当前时间附在Continuation上，恢复被阻塞的Continuation。Continuation在被恢复或超时之后相对应的请求都会被重新执行，当再次执行到Continuation.suspend()时，这个方法会马上返回，然后继续执行suspend()后面的代码，返回上一次发布的时间或是"No Time"，当然这里发布的间隔比阻塞的时间小，不会出现"No Time"。<br /><br />完整代码见附件
          <br/><br/>
          <span style="color:red;">
            <a href="http://cachalot.javaeye.com/blog/118895#comments" style="color:red;">已有 <strong>0</strong> 人发表留言，猛击-&gt;&gt;<strong>这里</strong>&lt;&lt;-参与讨论</a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 31 Aug 2007 16:44:34 +0800</pubDate>
        <link>http://cachalot.javaeye.com/blog/118895</link>
        <guid>http://cachalot.javaeye.com/blog/118895</guid>
      </item>
  </channel>
</rss>