整合GWT与Jetty Continuations

虽然GWT充满了争议,但是对于Java开发者而言,能够使用自己最熟悉的语言来进行Ajax开发,Google推出的GWT的确具有相当大的吸引力,而且作为一个快速发展的开源框架,相信以后它的用户群应该会越来越多。

Jetty Continuations 很好的解决了服务器更新客户端的问题,服务器不用再为每一个等待响应的客户端单独建立一个线程,借助Continuations和新IO,可以在有限的线程内支持更多用户。
更多内容参见
http://docs.codehaus.org/display/JETTY/Continuations

最近在做一个浏览器上的在线聊天项目,用来做为在线客服支持,打算使用GWT编写界面,但是GWT所提供RPC机制并不直接支持Jetty Continuations。为了支持比当前线程数更多的用户,Jetty中的Continuation.suspend()会抛出一个特殊的运行时异常:RetryRequest。这个异常将传播到servlet以外,然后通过过滤器传回,再由SelectChannelConnector捕获,将请求放入处于等待状态的Continuation队列中,此时HTTP连接并不关闭,而当前的线程却可以被放回线程池,供别的请求使用。但是使用GWT时,GWT捕获了所有的Throwable,这样就会导致Continuations机制失败。而且GWT提供的RemoteServiceServlet类中很多方法都被定义为final或static,于是你无法通过继承这个类来重写相关方法,让它放过RetryRequest。

但是因为GWT是开源项目,于是Jetty组织改写RemoteServiceServlet,提供了OpenRemoteServiceServlet,将Continuations所敏感的方法去掉final和static,然后继承OpenRemoteServiceServlet,提供了它们自己的AsyncRemoteServiceServlet,这样我们GWT服务器端的RPC接口的实现类直接继承AsyncRemoteServiceServlet,无须其它更改,就可以使用Jetty Continuations的API了。

AsyncRemoteServiceServlet14.java中放过RetryRequest的相关代码:
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;   
    }   
}  

更多内容参见
http://blogs.webtide.com/gregw/2006/12/07/1165517549286.html

这里提供一个完整的小例子给大家,也作为我这段时间学习的总结 :

下载最新的
GWT
http://code.google.com/webtoolkit/download.html
Jetty
http://docs.codehaus.org/display ... Installing#download
然后在
http://jira.codehaus.org/browse/JETTY-399上面找到OpenRemoteServiceServlet14.java和AsyncRemoteServiceServlet14.java,这是OpenRemoteServiceServlet和AsyncRemoteServiceServlet的新版本,支持GWT1.4版本。

首先使用GWT的命令行工具创建eclipse下的GWT项目,然后导入到eclipse中,再导入jetty-util-6.1.3.jar。

RPC接口定义:
Java代码
public interface TestService extends RemoteService {   
  
    public String getNews();   
      
}  

GWT的入口类:
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);   
    }   
  
}  

界面将显示一个文本框,反复调用testService.getNews(),将异步返回的结果输出到文本框中。

服务器端RPC接口的实现:
Java代码
public class TestServiceImpl extends AsyncRemoteServiceServlet14 implements  
        TestService {   
      
    private NewsCreator newsCreator;   
      
    public void init() {   
        newsCreator = new NewsCreator();   
    }   
      
    public String getNews() {   
        return newsCreator.getNews(getThreadLocalRequest());   
    }   
  
}  

注意这里继承的是AsyncRemoteServiceServlet14。通过一个辅助类NewsCreator来生成新的时间:
Java代码
public class NewsCreator implements Runnable {   
  
    private Set<Continuation> readers;   
      
    public NewsCreator() {   
        readers = new HashSet<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;   
    }   
  
}  

getNews()为每个请求获取一个Continuation实例,然后将这个实例保存起来,阻塞10秒,同时NewsCreator每隔两秒发布一次时间,发布时将当前时间附在Continuation上,恢复被阻塞的Continuation。Continuation在被恢复或超时之后相对应的请求都会被重新执行,当再次执行到Continuation.suspend()时,这个方法会马上返回,然后继续执行suspend()后面的代码,返回上一次发布的时间或是"No Time",当然这里发布的间隔比阻塞的时间小,不会出现"No Time"。

 

转自:http://codinghome.cn/viewthread.php?tid=367

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值