Статьи

Dead Simple Async REST-приложение: начало работы с платформой Atmosphere

В этой части я опишу простое асинхронное REST-приложение с использованием поведения, js, prototype.js и Atmosphere Framework. Как обычно, вы можете развернуть приложение где угодно! Если вы заинтересованы в поиске сложного приложения Atmosphere, взгляните на новое приложение Twitter .

Чтобы понять основы атмосферы , я рекомендую вам быстро взглянуть на части I и II . На этот раз я объясню простое приложение, которое использует длительный опрос для приостановки соединения . Для начала посмотрим, как выглядит структура файла war:

./index.html
./WEB-INF
./WEB-INF/context.xml
./WEB-INF/classes
./WEB-INF/classes/org
./WEB-INF/classes/org/atmosphere
./WEB-INF/classes/org/atmosphere/samples
./WEB-INF/classes/org/atmosphere/samples/rest
./WEB-INF/classes/org/atmosphere/samples/rest/counter
./WEB-INF/classes/org/atmosphere/samples/rest/counter/RestLongPolling.class
./WEB-INF/lib
./WEB-INF/lib/jersey-core-1.1.1-ea-SNAPSHOT.jar
./WEB-INF/lib/atmosphere-core-0.2.jar
./WEB-INF/lib/asm-3.1.jar ./WEB-INF/lib/atmosphere-portable-runtime-0.2.jar
./WEB-INF/lib/jersey-server-1.1.1-ea-SNAPSHOT.jar ./WEB-INF/lib/jsr311-api-1.1.jar ./WEB-INF/web.xml ./javascripts ./javascripts/prototype.js ./javascripts/counter.js
./javascripts/behaviour.js ./META-INF ./META-INF/context.xml ./META-INF/atmosphere.xml ./stylesheets ./stylesheets/styles-site.css

Двумя важными файлами являются RestLongPolling, который содержит код на стороне сервера, и counter.js, который содержит код на стороне клиента. Клиент просто выглядит так:

   var counter = {
       'poll' : function() {
           new Ajax.Request('dispatch/counter', {
               method : 'GET',
               onSuccess : counter.update
          });
       },
       'increment' : function() {
           var count = $('count').innerHTML * 1;
          new Ajax.Request('dispatch/counter/' + count, {
              method : 'POST'
          });
      },
      'update' : function() {
         var count = $('count').innerHTML * 1;
          $('count').innerHTML = count + 1;
          counter.poll();
      }
  }
  
  var rules = {
      '#increment': function(element) {
          element.onclick = function() {
              counter.increment();
          };
      }
  };
  
  Behaviour.register(rules);
  Behaviour.addLoadEvent(counter.poll);

На экране это выглядит так:

click.png

Как только вы нажмете, счетчик отправит запрос, добавив значение счетчика в URL. Каждый подключенный браузер будет обновлен, как только произойдет событие нажатия. Идея в том, чтобы показать что-то простое. Теперь, на стороне сервера, у нас есть только (я не шучу!):

   package org.atmosphere.samples.rest.counter;
   
   import com.sun.jersey.spi.resource.Singleton;
   import java.util.concurrent.atomic.AtomicInteger;
   import javax.ws.rs.GET;
   import javax.ws.rs.POST;
   import javax.ws.rs.Path;
   import javax.ws.rs.PathParam;
   import org.atmosphere.core.annotation.ResumeOnBroadcast;
  import org.atmosphere.core.annotation.Suspend;
  
 @Path("{counter}")
@Singleton
 public class RestLongPolling{
     private final AtomicInteger counter = new AtomicInteger();
      
    @GET
    @Suspend
    public String suspend(){
          return "<!-- Atmosphere is your future-->";
      }   
      
      @POST
      @Path("{counter}")
      @ResumeOnBroadcast
      public String increment(@PathParam("counter") String count){
         counter.incrementAndGet();
         return counter.toString(); 
     }   
 }

Таким образом, когда браузер загружает страницу, counter.poll вызывает на стороне сервера метод suspend (). Поскольку этот метод помечен аннотацией @Suspend , соединение будет приостановлено после выполнения метода, ожидая события. В этой демонстрации вы видите вращающийся значок браузера, поскольку GET еще не вернулся. Как только происходит щелчок (строка 8), POST вызывает метод increment (..) (строка 26), и поскольку метод аннотируется с помощью @ResumeOnBroadcast, затем возвращаемое значение будет транслироваться на все приостановленные соединения, затем записано на провод, и затем соединение будет возобновлено. На стороне браузера вращение остановится на несколько миллисекунд, так как браузер перезапустит GET, который снова будет приостановлен. Обратите внимание, что для этой простой демонстрации я не обновляю клиентскую часть, используя возвращаемое значение метода increment (..), чтобы сделать его чрезвычайно простым.

Теперь технически это приложение использует модуль Atmosphere, называемый core, который построен поверх Comet Portable Runtime (CPR), что позволяет развертывать приложение где угодно. CPR всегда автоматически обнаруживает, что контейнер работает поверх, например:

First with Glassfish v3:

10-Jun-2009 8:02:05 PM org.atmosphere.cpr.AtmosphereServlet loadAtmosphereDotXml
INFO: Sucessfully loaded org.atmosphere.handler.ReflectorServletProcessor@e1ca74 mapped to context-path /dispatch
10-Jun-2009 8:02:05 PM org.atmosphere.cpr.BroadcasterConfig 
INFO: DefaultBroadcaster configured using a Thread Pool of size: 2
10-Jun-2009 8:02:05 PM org.atmosphere.cpr.AtmosphereServlet autoDetectContainer
INFO: Atmosphere Framework running under container javax.servlet version 3.0
...and then Tomcat 6 INFO: DefaultBroadcaster configured using a Thread Pool of size: 2 10-Jun-2009 8:03:46 PM org.atmosphere.cpr.AtmosphereServlet autoDetectContainer INFO: Forcing the use of the container's native Comet API implementation instead of Servlet 3.0 10-Jun-2009 8:03:46 PM org.atmosphere.cpr.AtmosphereServlet autoDetectContainer INFO: Atmosphere Framework running under container Tomcat version 6.0.x

Два журнала выше показывают, что если среда обнаружит реализацию поддержки асинхронной поддержки Servlet 3.0, она будет использовать ее. Если нет, то он будет использовать собственную реализацию Comet.

Наконец, для работы приложения необходимы два дескриптора. Первый, атмосфера.xml, просто говорит Atmosphere использовать сервлет из Джерси для обработки запроса. Под капотом вся обработка аннотаций и поддержка REST предоставляется бесплатно из Джерси:

<atmosphere-handlers>
    <atmosphere-handler context-root="/dispatch" class-name="org.atmosphere.handler.ReflectorServletProcessor">
        <property name="servletClass" value="com.sun.jersey.spi.container.servlet.ServletContainer"/>
    </atmosphere-handler>
</atmosphere-handlers>

Далее web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:j2ee="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="3.0"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee    http://java.sun.com/xml/ns/j2ee/web-app_3_0.xsd">
    <description>Atmosphere DeadSimple</description>
    <display-name>Atmosphere DeadSimple</display-name>
    <servlet>
        <description>AtmosphereServlet</description>
        <servlet-name>AtmosphereServlet</servlet-name>
        <servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class>
        <async-supported>true</async-supported>
        <b><init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>org.atmosphere.samples.rest.counter</param-value>
        </init-param></b>
        <init-param>
            <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
            <param-value>org.atmosphere.core.AtmosphereFilter</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>AtmosphereServlet</servlet-name>
        <url-pattern>/dispatch/*</url-pattern>
    </servlet-mapping>
</web-app>

Единственная важная строка выше — это init-param, который сообщает Джерси, что нужно искать наши ресурсы, например, в org.atmosphere.samples.rest.counter. Вот и все. Вы можете скачать образец здесь (и многие другие здесь ).

По любым вопросам обращайтесь на наш основной сайт и пользуйтесь нашим форумом Nabble (подписка не требуется) или следите за нами в Twitter и пишите там свои вопросы!

С http://weblogs.java.net/blog/jfarcand