Author: markt
Date: Tue Jan  9 21:50:44 2018
New Revision: 1820705

URL: http://svn.apache.org/viewvc?rev=1820705&view=rev
Log:
Prevent NullPointerException and other errors if the stock ticker example is 
running when the examples web application is stopped.

Added:
    
tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockContextListener.java
   (with props)
Modified:
    tomcat/trunk/webapps/docs/changelog.xml
    tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java
    tomcat/trunk/webapps/examples/WEB-INF/classes/async/Stockticker.java
    tomcat/trunk/webapps/examples/WEB-INF/web.xml

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1820705&r1=1820704&r2=1820705&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Tue Jan  9 21:50:44 2018
@@ -183,6 +183,11 @@
         application. (markt)
       </fix>
       <fix>
+        <bug>61886</bug>: Prevent <code>NullPointerException</code> and other
+        errors if the stock ticker example is running when the examples web
+        application is stopped. (markt)
+      </fix>
+      <fix>
         <bug>61910</bug>: Clarify the meaning of the <code>allowLinking</code>
         option in the documentation web application. (markt)
       </fix>

Added: 
tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockContextListener.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockContextListener.java?rev=1820705&view=auto
==============================================================================
--- 
tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockContextListener.java
 (added)
+++ 
tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockContextListener.java
 Tue Jan  9 21:50:44 2018
@@ -0,0 +1,44 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package async;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/*
+ * Ensures the Stockticker is shut down cleanly when the context stops. This
+ * also covers the case when the server shuts down.
+ */
+public class AsyncStockContextListener implements ServletContextListener {
+
+    public static final String STOCK_TICKER_KEY = "StockTicker";
+
+    @Override
+    public void contextInitialized(ServletContextEvent sce) {
+        Stockticker stockticker = new Stockticker();
+        ServletContext sc = sce.getServletContext();
+        sc.setAttribute(STOCK_TICKER_KEY, stockticker);
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent sce) {
+        ServletContext sc = sce.getServletContext();
+        Stockticker stockticker = (Stockticker) 
sc.getAttribute(STOCK_TICKER_KEY);
+        stockticker.shutdown();
+    }
+}

Propchange: 
tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockContextListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java?rev=1820705&r1=1820704&r2=1820705&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java 
(original)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/async/AsyncStockServlet.java 
Tue Jan  9 21:50:44 2018
@@ -25,6 +25,7 @@ import java.util.concurrent.atomic.Atomi
 import javax.servlet.AsyncContext;
 import javax.servlet.AsyncEvent;
 import javax.servlet.AsyncListener;
+import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -45,7 +46,6 @@ public class AsyncStockServlet extends H
     private static final ConcurrentLinkedQueue<AsyncContext> clients =
             new ConcurrentLinkedQueue<>();
     private static final AtomicInteger clientcount = new AtomicInteger(0);
-    private static final Stockticker ticker = new Stockticker();
 
     public AsyncStockServlet() {
         log.info("AsyncStockServlet created");
@@ -63,6 +63,8 @@ public class AsyncStockServlet extends H
             resp.setContentType("text/plain");
             clients.add(actx);
             if (clientcount.incrementAndGet()==1) {
+                Stockticker ticker = (Stockticker) 
req.getServletContext().getAttribute(
+                        AsyncStockContextListener.STOCK_TICKER_KEY);
                 ticker.addTickListener(this);
             }
         } else {
@@ -104,8 +106,27 @@ public class AsyncStockServlet extends H
 
 
     @Override
+    public void shutdown() {
+        // The web application is shutting down. Complete any AsyncContexts
+        // associated with an active client.
+        Iterator<AsyncContext> it = clients.iterator();
+        while (it.hasNext()) {
+            AsyncContext actx = it.next();
+            try {
+                actx.complete();
+            } catch (Exception e) {
+                // Ignore. The async error handling will deal with this.
+            }
+        }
+    }
+
+
+    @Override
     public void onComplete(AsyncEvent event) throws IOException {
         if (clients.remove(event.getAsyncContext()) && 
clientcount.decrementAndGet()==0) {
+            ServletContext sc = 
event.getAsyncContext().getRequest().getServletContext();
+            Stockticker ticker = (Stockticker) sc.getAttribute(
+                    AsyncStockContextListener.STOCK_TICKER_KEY);
             ticker.removeTickListener(this);
         }
     }

Modified: tomcat/trunk/webapps/examples/WEB-INF/classes/async/Stockticker.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/async/Stockticker.java?rev=1820705&r1=1820704&r2=1820705&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/classes/async/Stockticker.java 
(original)
+++ tomcat/trunk/webapps/examples/WEB-INF/classes/async/Stockticker.java Tue 
Jan  9 21:50:44 2018
@@ -37,6 +37,12 @@ public class Stockticker implements Runn
         }
 
         public synchronized void stop() {
+            // On context stop this can be called multiple times.
+            // NO-OP is the ticker thread is not set
+            // (i.e. stop() has already completed)
+            if (ticker == null) {
+                return;
+            }
             run = false;
             try {
                 ticker.join();
@@ -47,6 +53,17 @@ public class Stockticker implements Runn
             ticker = null;
         }
 
+        public void shutdown() {
+            // Notify each listener of the shutdown. This enables them to
+            // trigger any necessary clean-up.
+            for (TickListener l : listeners) {
+                l.shutdown();
+            }
+            // Wait for the thread to stop. This prevents warnings in the logs
+            // that the thread is still active when the context stops.
+            stop();
+        }
+
         public void addTickListener(TickListener listener) {
             if (listeners.add(listener)) {
                 if (counter.incrementAndGet()==1) start();
@@ -98,6 +115,7 @@ public class Stockticker implements Runn
 
     public static interface TickListener {
         public void tick(Stock stock);
+        public void shutdown();
     }
 
     public static final class Stock implements Cloneable {

Modified: tomcat/trunk/webapps/examples/WEB-INF/web.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/web.xml?rev=1820705&r1=1820704&r2=1820705&view=diff
==============================================================================
--- tomcat/trunk/webapps/examples/WEB-INF/web.xml (original)
+++ tomcat/trunk/webapps/examples/WEB-INF/web.xml Tue Jan  9 21:50:44 2018
@@ -95,6 +95,11 @@
         <listener-class>listeners.SessionListener</listener-class>
     </listener>
 
+    <!-- Define listeners required by examples -->
+    <listener>
+        <listener-class>async.AsyncStockContextListener</listener-class>
+    </listener>
+
     <!-- Define servlets that are included in the example application -->
 
     <servlet>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to