Servlet 3 Задача Async для Tomcat 7

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

Я вдохновлен этой статьей: http://www.javaworld.com/javaworld/jw-02-2009/jw-02-servlet3.html?page=3

Мой сервлет выглядит следующим образом.

@WebServlet(name="chatServlet", urlPatterns={"/ChatServlet"}, asyncSupported=true)
public class ChatServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         AsyncContext aCtx = request.startAsync(request, response); 
         ServletContext appScope = request.getServletContext();    
         List<AsyncContext> watchers = (List<AsyncContext>) appScope.getAttribute("watchers");
         watchers.add(aCtx); //register the watcher
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          AsyncContext aCtx = request.startAsync(request, response); 
          ServletContext appScope = request.getServletContext(); 
          Queue<String> messages = (Queue<String>)appScope.getAttribute("messages");
          messages.add(someMessage);
    }
} 

теперь мой прослушиватель выглядит следующим образом:

@WebListener
public class ChatPushService implements ServletContextListener {

        @Override
        public void contextInitialized(ServletContextEvent sce) {
              final List<AsyncContext> watchers = new  ArrayList<AsyncContext>();
             sce.getServletContext().setAttribute("watchers", watchers);
              // store new messages not published yet
             Queue<String> messages = new ConcurrentLinkedQueue<String>();
             sce.getServletContext().setAttribute("messages", messages);
             Executor messageExecutor = Executors.newCachedThreadPool(); 
             final Executor watcherExecutor = Executors.newCachedThreadPool();
             while(true)
              {      

                 if(!messages.isEmpty()) 
                 {
                     System.out.println("notEmpty");
                    String message =  messages.poll(); 
                    messageExecutor.execute(new Runnable(){

                        @Override
                        public void run() {
                             for(final AsyncContext aCtx : watchers){
                                 watcherExecutor.execute(new Runnable(){

                                     @Override
                                        public void run() {
                                           try {
                                            aCtx.getResponse().getWriter().print("brrrrr");
                                        } catch (IOException e) {
                                            // TODO Auto-generated catch block
                                            e.printStackTrace();
                                        }
                                     }
                                 });
                             }
                        }
                 });
              }
        }

    }
    }

Когда я начинаю мой процесс замораживания во время запуска контейнера.

Nov 1, 2011 1:12:09 AM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/lib/jvm/java-6-openjdk/jre/lib/amd64/server:/usr/lib/jvm/java-6-openjdk/jre/lib/amd64:/usr/lib/jvm/java-6-openjdk/jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib/jni:/lib:/usr/lib
Nov 1, 2011 1:12:09 AM org.apache.tomcat.util.digester.SetPropertiesRule begin
WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.jee.server:Servlet3Comet' did not find a matching property.
Nov 1, 2011 1:12:09 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Nov 1, 2011 1:12:09 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
Nov 1, 2011 1:12:09 AM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 624 ms
Nov 1, 2011 1:12:09 AM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina
Nov 1, 2011 1:12:09 AM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.22

Похоже, что функция public void contextInitialized не работает асинхронно на фоне и блокирует дальнейшую инициализацию контейнера.

Почему?

может кто-нибудь помочь мне по этому вопросу?

Ответ 1

Вы выполняете цикл while внутри метода contextInitialized(), что неверно. contextInitialized() вызывается Servlet Container как часть запуска приложения, поскольку цикл while блокирует запуск вашего приложения.

Изменен код, в котором ContextListener запустит один поток демона, который публикует сообщения наблюдателям

@WebListener
public class ChatPushService implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
          final List<AsyncContext> watchers = new  ArrayList<AsyncContext>();
         sce.getServletContext().setAttribute("watchers", watchers);
          // store new messages not published yet
         Queue<String> messages = new ConcurrentLinkedQueue<String>();
         sce.getServletContext().setAttribute("messages", messages);
         new chatManager(sce.getServletContext()).start(); //START DAEMON

      }
}
public class ChatManager implements Runnable
{
ServletContext servletCtx;
public ChatManager(ServletContext ctx)
{
     this.servletCtx = ctx;
}
public void run()
{
         List<AsyncContext> watchers = (List<AsyncContext>) servletCtx.getAttribute("watchers");
     Queue<String> messages = (Queue<String>)appScope.getAttribute("messages");
     Executor messageExecutor = Executors.newCachedThreadPool(); 
         final Executor watcherExecutor = Executors.newCachedThreadPool();
         while(true)
          {      

             if(!messages.isEmpty()) 
             {
                 System.out.println("notEmpty");
                String message =  messages.poll(); 
                messageExecutor.execute(new Runnable(){

                    @Override
                    public void run() {
                         for(final AsyncContext aCtx : watchers){
                             watcherExecutor.execute(new Runnable(){

                                 @Override
                                    public void run() {
                                       try {
                                        aCtx.getResponse().getWriter().print("brrrrr");
                                    } catch (IOException e) {
                                        // TODO Auto-generated catch block
                                        e.printStackTrace();
                                    }
                                 }
                             });
                         }
                    }
             });
          }
    }

}

}

Ответ 2

Я не могу комментировать код Ramesh, поэтому я должен разместить его здесь... Поскольку ни один поток не обходит вокруг запуска ChatManager, я считаю, что вы должны вызвать run() на нем, а не start(). Кроме того, совершенно очевидно, что это должен быть новый ChatManager().. не новый chatManager()... учетная запись Java была чувствительна к регистру.