Сервлет 3 Асинхронное задание на Tomcat 7 - PullRequest
11 голосов
/ 01 ноября 2011

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

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

Мой сервлет выглядит такthis.

@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 функция не работает асинхронно в фоновом режиме и блокирует дальнейшую инициализацию контейнера.

Почему?

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

Ответы [ 2 ]

8 голосов
/ 01 ноября 2011

Вы выполняете цикл внутри contextInitialized () метод, который является неправильным. contextInitialized () вызывается контейнером сервлетов как часть запуска приложения, поскольку цикл 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();
                                    }
                                 }
                             });
                         }
                    }
             });
          }
    }

}

}
0 голосов
/ 13 февраля 2015

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

...