Author: kkolinko
Date: Wed Feb  2 15:02:49 2011
New Revision: 1066492

URL: http://svn.apache.org/viewvc?rev=1066492&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=50673
Improve Catalina shutdown when running as a service. Do not call System.exit().

Modified:
    tomcat/tc6.0.x/trunk/STATUS.txt
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardServer.java
    tomcat/tc6.0.x/trunk/java/org/apache/catalina/startup/Catalina.java
    tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml

Modified: tomcat/tc6.0.x/trunk/STATUS.txt
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/STATUS.txt?rev=1066492&r1=1066491&r2=1066492&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/STATUS.txt (original)
+++ tomcat/tc6.0.x/trunk/STATUS.txt Wed Feb  2 15:02:49 2011
@@ -100,10 +100,3 @@ PATCHES PROPOSED TO BACKPORT:
      defaultProtocol, defaultKeystoreType, though I do not see much
      concerns against it.
    markt: Happy to exclude those changes
-
-* Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=50673
-  Improve Catalina shutdown when running as a service. Do not call
-  System.exit().
-  https://issues.apache.org/bugzilla/attachment.cgi?id=26591
-  +1: kkolinko, mturk, markt
-  -1:

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardServer.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardServer.java?rev=1066492&r1=1066491&r2=1066492&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardServer.java 
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/core/StandardServer.java Wed 
Feb  2 15:02:49 2011
@@ -181,7 +181,17 @@ public final class StandardServer
      */
     protected PropertyChangeSupport support = new PropertyChangeSupport(this);
 
-    private boolean stopAwait = false;
+    private volatile boolean stopAwait = false;
+
+    /**
+     * Thread that currently is inside our await() method.
+     */
+    private volatile Thread awaitThread = null;
+
+    /**
+     * Server socket that is used to wait for the shutdown command.
+     */
+    private volatile ServerSocket awaitSocket = null;
 
     // ------------------------------------------------------------- Properties
 
@@ -344,6 +354,24 @@ public final class StandardServer
 
     public void stopAwait() {
         stopAwait=true;
+        Thread t = awaitThread;
+        if (t != null) {
+            ServerSocket s = awaitSocket;
+            if (s != null) {
+                awaitSocket = null;
+                try {
+                    s.close();
+                } catch (IOException e) {
+                    // Ignored
+                }
+            }
+            t.interrupt();
+            try {
+                t.join(1000);
+            } catch (InterruptedException e) {
+                // Ignored
+            }
+        }
     }
 
     /**
@@ -358,92 +386,117 @@ public final class StandardServer
             return;
         }
         if( port==-1 ) {
-            while( true ) {
-                try {
-                    Thread.sleep( 10000 );
-                } catch( InterruptedException ex ) {
+            try {
+                awaitThread = Thread.currentThread();
+                while(!stopAwait) {
+                    try {
+                        Thread.sleep( 10000 );
+                    } catch( InterruptedException ex ) {
+                        // continue and check the flag
+                    }
                 }
-                if( stopAwait ) return;
+            } finally {
+                awaitThread = null;
             }
+            return;
         }
-        
+
         // Set up a server socket to wait on
-        ServerSocket serverSocket = null;
         try {
-            serverSocket =
+            awaitSocket =
                 new ServerSocket(port, 1,
                                  InetAddress.getByName("localhost"));
         } catch (IOException e) {
             log.error("StandardServer.await: create[" + port
                                + "]: ", e);
-            System.exit(1);
+            return;
         }
 
-        // Loop waiting for a connection and a valid command
-        while (true) {
+        try {
+            awaitThread = Thread.currentThread();
 
-            // Wait for the next connection
-            Socket socket = null;
-            InputStream stream = null;
-            try {
-                socket = serverSocket.accept();
-                socket.setSoTimeout(10 * 1000);  // Ten seconds
-                stream = socket.getInputStream();
-            } catch (AccessControlException ace) {
-                log.warn("StandardServer.accept security exception: "
-                                   + ace.getMessage(), ace);
-                continue;
-            } catch (IOException e) {
-                log.error("StandardServer.await: accept: ", e);
-                System.exit(1);
-            }
-
-            // Read a set of characters from the socket
-            StringBuffer command = new StringBuffer();
-            int expected = 1024; // Cut off to avoid DoS attack
-            while (expected < shutdown.length()) {
-                if (random == null)
-                    random = new Random();
-                expected += (random.nextInt() % 1024);
-            }
-            while (expected > 0) {
-                int ch = -1;
+            // Loop waiting for a connection and a valid command
+            while (!stopAwait) {
+                ServerSocket serverSocket = awaitSocket;
+                if (serverSocket == null) {
+                    break;
+                }
+    
+                // Wait for the next connection
+                Socket socket = null;
+                StringBuilder command = new StringBuilder();
                 try {
-                    ch = stream.read();
-                } catch (IOException e) {
-                    log.warn("StandardServer.await: read: ", e);
-                    ch = -1;
+                    InputStream stream = null;
+                    try {
+                        socket = serverSocket.accept();
+                        socket.setSoTimeout(10 * 1000);  // Ten seconds
+                        stream = socket.getInputStream();
+                    } catch (AccessControlException ace) {
+                        log.warn("StandardServer.accept security exception: "
+                                           + ace.getMessage(), ace);
+                        continue;
+                    } catch (IOException e) {
+                        if (stopAwait) {
+                            // Wait was aborted with socket.close()
+                            break;
+                        }
+                        log.error("StandardServer.await: accept: ", e);
+                        break;
+                    }
+
+                    // Read a set of characters from the socket
+                    int expected = 1024; // Cut off to avoid DoS attack
+                    while (expected < shutdown.length()) {
+                        if (random == null)
+                            random = new Random();
+                        expected += (random.nextInt() % 1024);
+                    }
+                    while (expected > 0) {
+                        int ch = -1;
+                        try {
+                            ch = stream.read();
+                        } catch (IOException e) {
+                            log.warn("StandardServer.await: read: ", e);
+                            ch = -1;
+                        }
+                        if (ch < 32)  // Control character or EOF terminates 
loop
+                            break;
+                        command.append((char) ch);
+                        expected--;
+                    }
+                } finally {
+                    // Close the socket now that we are done with it
+                    try {
+                        if (socket != null) {
+                            socket.close();
+                        }
+                    } catch (IOException e) {
+                        // Ignore
+                    }
                 }
-                if (ch < 32)  // Control character or EOF terminates loop
+
+                // Match against our command string
+                boolean match = command.toString().equals(shutdown);
+                if (match) {
                     break;
-                command.append((char) ch);
-                expected--;
-            }
+                } else
+                    log.warn("StandardServer.await: Invalid command '" +
+                                       command.toString() + "' received");
+            }
+        } finally {
+            ServerSocket serverSocket = awaitSocket;
+            awaitThread = null;
+            awaitSocket = null;
 
-            // Close the socket now that we are done with it
-            try {
-                socket.close();
-            } catch (IOException e) {
-                ;
+            // Close the server socket and return
+            if (serverSocket != null) {
+                try {
+                    serverSocket.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
             }
-
-            // Match against our command string
-            boolean match = command.toString().equals(shutdown);
-            if (match) {
-                break;
-            } else
-                log.warn("StandardServer.await: Invalid command '" +
-                                   command.toString() + "' received");
-
         }
-
-        // Close the server socket and return
-        try {
-            serverSocket.close();
-        } catch (IOException e) {
-            ;
-        }
-
     }
 
 
@@ -738,8 +791,7 @@ public final class StandardServer
         // Notify our interested LifecycleListeners
         lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
 
-        if (port == -1)
-            stopAwait();
+        stopAwait();
 
     }
 

Modified: tomcat/tc6.0.x/trunk/java/org/apache/catalina/startup/Catalina.java
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/java/org/apache/catalina/startup/Catalina.java?rev=1066492&r1=1066491&r2=1066492&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/java/org/apache/catalina/startup/Catalina.java 
(original)
+++ tomcat/tc6.0.x/trunk/java/org/apache/catalina/startup/Catalina.java Wed Feb 
 2 15:02:49 2011
@@ -34,6 +34,7 @@ import java.util.logging.LogManager;
 import org.apache.catalina.Container;
 import org.apache.catalina.Lifecycle;
 import org.apache.catalina.LifecycleException;
+import org.apache.catalina.Server;
 import org.apache.catalina.core.StandardServer;
 import org.apache.juli.ClassLoaderLogManager;
 import org.apache.tomcat.util.digester.Digester;
@@ -382,7 +383,8 @@ public class Catalina extends Embedded {
             arguments(arguments);
         }
 
-        if( getServer() == null ) {
+        Server s = getServer();
+        if( s == null ) {
             // Create and execute our Digester
             Digester digester = createStopDigester();
             
digester.setClassLoader(Thread.currentThread().getContextClassLoader());
@@ -401,17 +403,25 @@ public class Catalina extends Embedded {
             }
         } else {
             // Server object already present. Must be running as a service
-            // Shutdown hook will take care of clean-up
-            System.exit(0);
+            if (s instanceof Lifecycle) {
+                try {
+                    ((Lifecycle) s).stop();
+                } catch (LifecycleException e) {
+                    log.error("Catalina.stop: ", e);
+                }
+                return;
+            }
+            // else fall down
         }
 
         // Stop the existing server
+        s = getServer();
         try {
-            if (getServer().getPort()>0) { 
+            if (s.getPort()>0) { 
                 String hostAddress = 
InetAddress.getByName("localhost").getHostAddress();
                 Socket socket = new Socket(hostAddress, getServer().getPort());
                 OutputStream stream = socket.getOutputStream();
-                String shutdown = getServer().getShutdown();
+                String shutdown = s.getShutdown();
                 for (int i = 0; i < shutdown.length(); i++)
                     stream.write(shutdown.charAt(i));
                 stream.flush();

Modified: tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml?rev=1066492&r1=1066491&r2=1066492&view=diff
==============================================================================
--- tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/tc6.0.x/trunk/webapps/docs/changelog.xml Wed Feb  2 15:02:49 2011
@@ -51,6 +51,10 @@
         Based on the patch provided by Marc Guillemot. (kkolinko)
       </update>
       <fix>
+        <bug>50673</bug>: Improve Catalina shutdown when running as a service.
+        Do not call System.exit(). (kkolinko)
+      </fix>
+      <fix>
         <bug>50689</bug>: Provide 100 Continue responses at appropriate points
         during FORM authentication if client indicates that they are expected.
         (kkolinko)



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

Reply via email to