The previous articles covered the startup process of Tomcat, and after starting it in its default configuration, you can see that there are actually six threads running in the background. One user Thread and five daemons (as shown in the figure below).

Daemon threads are threads that provide a common service in the background while the program is running, such as garbage collection threads. This thread is not an integral part of the program, and when all non-daemons end, the program terminates, killing all daemons in the process. Conversely, the program does not terminate as long as any non-daemon threads are running.

There is little difference between the user thread and the daemon thread, except that the virtual machine exits: if the user thread exits completely and only the daemon thread remains, the virtual machine exits. Since there is no guardian, there is no work for the daemon thread to do, and there is no need to continue running the program. Converting a Thread to a daemon can be done by calling the setDaemon(true) method on the Thread object.

The shutdown of Tomcat takes advantage of this principle, as long as the only user thread is closed, the entire application is closed.

To understand how the user thread was shut down, start with where the thread was created. In the previous analysis of the Tomcat startup we from org. Apache. Catalina. Startup. The main method of Bootstrap class as the entrance, the class of 453 to 456 line is code that executes when Tomcat startup:

daemon.setAwait(true);
org.apache.catalina.startup.Catalina

Catalina setAwait method code:

    /**
     * Set flag.
     */
    public void setAwait(boolean await) throws Exception { Class<? > paramTypes[] = new Class[1]; paramTypes[0] = Boolean.TYPE; Object paramValues[] = new Object[1]; paramValues[0] = Boolean.valueOf(await); Method method = catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
        method.invoke(catalinaDaemon, paramValues);

    }
Copy the code

As above analysis, when the Tomcat invoked org. Apache. Catalina. Startup. Catalina start method in a class, look at this method code:

1 /** 2 * Start a new server instance. 3 */ 4 public void start() { 5 6 if (getServer() == null) { 7 load(); 8 } 9 10 if (getServer() == null) { 11 log.fatal("Cannot start server. Server instance is not configured."); 12 return; 13 } 14 15 long t1 = System.nanoTime(); 16 17 // Start the new server 18 try { 19 getServer().start(); 20 } catch (LifecycleException e) { 21 log.fatal(sm.getString("catalina.serverStartFail"), e); 22 try { 23 getServer().destroy(); 24 } catch (LifecycleException e1) { 25 log.debug("destroy() failed for failed Server ", e1); 26 } 27 return; 28 } 29 30 long t2 = System.nanoTime(); 31 if(log.isInfoEnabled()) { 32 log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms"); 33 } 34 35 // Register shutdown hook 36 if (useShutdownHook) { 37 if (shutdownHook == null) { 38 shutdownHook = new CatalinaShutdownHook(); 39 } 40 Runtime.getRuntime().addShutdownHook(shutdownHook); 41 42 // If JULI is being used, disable JULI's shutdown hook since 43 // shutdown hooks run in parallel and log messages may be lost 44 // if JULI's hook completes before the CatalinaShutdownHook() 45 LogManager logManager = LogManager.getLogManager(); 46 if (logManager instanceof ClassLoaderLogManager) { 47 ((ClassLoaderLogManager) logManager).setUseShutdownHook( 48 false); 49 } 50 } 51 52 if (await) { 53 await(); 54 stop(); 55} 56}Copy the code

From the previous analysis on startup, Tomcat then starts all the components configured in the configuration file step by step through this method call to getServer().start() on line 19. We have “await” the instance variable of Catalina with “await” as true, so we will execute the “await” method of Catalina:

    /**
     * Await and shutdown.
     */
    public void await() {
    
        getServer().await();
        
    }
Copy the code

This method is a word that means call org. Apache. Catalina. Core. Await StandardServer class methods:

1 /** 2 * Wait until a proper shutdown command is received, then return. 3 * This keeps the main thread alive - the thread pool listening for http 4 * connections is daemon threads. 5 */ 6 @Override 7 public void await() { 8 // Negative values - don't wait on port - tomcat is embedded or we just don't like ports 9 if( port == -2 ) { 10 // undocumented yet - for embedding apps that are around, alive. 11 return; 12 } 13 if( port==-1 ) { 14 try { 15 awaitThread = Thread.currentThread(); 16 while(! stopAwait) { 17 try { 18 Thread.sleep( 10000 ); 19 } catch( InterruptedException ex ) { 20 // continue and check the flag 21 } 22 } 23 } finally { 24 awaitThread = null; 25 } 26 return; 27 } 28 29 // Set up a server socket to wait on 30 try { 31 awaitSocket = new ServerSocket(port, 1, 32 InetAddress.getByName(address)); 33 } catch (IOException e) { 34 log.error("StandardServer.await: create[" + address 35 + ":" + port 36 + "]: ", e); 37 return; 38 } 39 40 try { 41 awaitThread = Thread.currentThread(); 42 43 // Loop waiting for a connection and a valid command 44 while (! stopAwait) { 45 ServerSocket serverSocket = awaitSocket; 46 if (serverSocket == null) { 47 break; 48 } 49 50 // Wait for the next connection 51 Socket socket = null; 52 StringBuilder command = new StringBuilder(); 53 try { 54 InputStream stream; 55 try { 56 socket = serverSocket.accept(); 57 socket.setSoTimeout(10 * 1000); // Ten seconds 58 stream = socket.getInputStream(); 59 } catch (AccessControlException ace) { 60 log.warn("StandardServer.accept security exception: " 61 + ace.getMessage(), ace); 62 continue; 63 } catch (IOException e) { 64 if (stopAwait) { 65 // Wait was aborted with socket.close() 66 break; 67 } 68 log.error("StandardServer.await: accept: ", e); 69 break; 70 } 71 72 // Read a set of characters from the socket 73 int expected = 1024; // Cut off to avoid DoS attack 74 while (expected < shutdown.length()) { 75 if (random == null) 76 random = new Random(); 77 expected += (random.nextInt() % 1024); 78 } 79 while (expected > 0) { 80 int ch = -1; 81 try { 82 ch = stream.read(); 83 } catch (IOException e) { 84 log.warn("StandardServer.await: read: ", e); 85 ch = -1; 86 } 87 if (ch < 32) // Control character or EOF terminates loop 88 break; 89 command.append((char) ch); 90 expected--; 91 } 92 } finally { 93 // Close the socket now that we are done with it 94 try { 95 if (socket ! = null) { 96 socket.close(); 97 } 98 } catch (IOException e) { 99 // Ignore 100 } 101 } 102 103 // Match against our command string 104 boolean match  = command.toString().equals(shutdown); 105 if (match) { 106 log.info(sm.getString("standardServer.shutdownViaPort")); 107 break; 108 } else 109 log.warn("StandardServer.await: Invalid command '" 110 + command.toString() + "' received"); 111 } 112 } finally { 113 ServerSocket serverSocket = awaitSocket; 114 awaitThread = null; 115 awaitSocket = null; 116 117 // Close the server socket and return 118 if (serverSocket ! = null) { 119 try { 120 serverSocket.close(); 121 } catch (IOException e) { 122 // Ignore 123 } 124 } 125 } 126 }Copy the code

This code is not parsed, but generally does what the comments before the method say: “Wait until a correct close command is received and the method returns. This keeps the main thread alive — the thread pool listening for HTTP connections is the daemon thread.

Listen on the Socket connection at the default address (address defined by the instance variable, default localhost) on the default port (port defined by the instance variable, default 8005). The method breaks out of the loop and returns (lines 103 through 107) when it finds that the contents of the input stream for the connection it listens to match the value configured by default (the default value is the string SHUTDOWN). Otherwise, the method will continue in a loop. Normally the user main thread will block (line 56) until there is a connection to localhost:8005. This is why the main thread seen at the beginning is always Running, and because this thread is always Running, other daemons will always exist.

This thread provides a SHUTDOWN mechanism by calling localhost:8005 and sending a SHUTDOWN string.

This is exactly what Tomcat does. Generally, Tomcat is shutdown by executing the shutdown.bat or shutdown.sh script, which you can refer to the article analyzing startup scripts. Will eventually perform org. Apache. Catalina. Startup. The main method of Bootstrap class, and passed into the “stop”, Look at this article org in picture 2. The apache. Catalina. Startup. The Bootstrap class line 458, then calls the org. Apache. Catalina. Startup. Catalina class the stopServer method:

1 public void stopServer(String[] arguments) { 2 3 if (arguments ! = null) { 4 arguments(arguments); 5 } 6 7 Server s = getServer(); 8 if( s == null ) { 9 // Create and execute our Digester 10 Digester digester = createStopDigester(); 11 digester.setClassLoader(Thread.currentThread().getContextClassLoader()); 12 File file = configFile(); 13 FileInputStream fis = null; 14 try { 15 InputSource is = 16 new InputSource(file.toURI().toURL().toString()); 17 fis = new FileInputStream(file); 18 is.setByteStream(fis); 19 digester.push(this); 20 digester.parse(is); 21 } catch (Exception e) { 22 log.error("Catalina.stop: ", e); 23 System.exit(1); 24 } finally { 25 if (fis ! = null) { 26 try { 27 fis.close(); 28 } catch (IOException e) { 29 // Ignore 30 } 31 } 32 } 33 } else { 34 // Server object already present. Must be running as a service 35 try { 36 s.stop(); 37 } catch (LifecycleException e) { 38 log.error("Catalina.stop: ", e); 39 } 40 return; 41 } 42 43 // Stop the existing server 44 s = getServer(); 45 if (s.getPort()>0) { 46 Socket socket = null; 47 OutputStream stream = null; 48 try { 49 socket = new Socket(s.getAddress(), s.getPort()); 50 stream = socket.getOutputStream(); 51 String shutdown = s.getShutdown(); 52 for (int i = 0; i < shutdown.length(); i++) { 53 stream.write(shutdown.charAt(i)); 54 } 55 stream.flush(); 56 } catch (ConnectException ce) { 57 log.error(sm.getString("catalina.stopServer.connectException", 58 s.getAddress(), 59 String.valueOf(s.getPort()))); 60 log.error("Catalina.stop: ", ce); 61 System.exit(1); 62 } catch (IOException e) { 63 log.error("Catalina.stop: ", e); 64 System.exit(1); 65 } finally { 66 if (stream ! = null) { 67 try { 68 stream.close(); 69 } catch (IOException e) { 70 // Ignore 71 } 72 } 73 if (socket ! = null) { 74 try { 75 socket.close(); 76 } catch (IOException e) { 77 // Ignore 78 } 79 } 80 } 81 } else { 82 log.error(sm.getString("catalina.stopServer")); 83 System.exit(1); 85 84}}Copy the code

Lines 8 through 41 read the configuration file, as in the previous Digester analysis article. Starting at line 49, initiate a Socket connection to localhost:8005 and write the SHUTDOWN string. This will shut down the only user thread in Tomcat, all daemon threads will exit (guaranteed by the JVM), and the entire application will shut down.

The default shutdown mechanism of Tomcat is analyzed above, but this is to shut down Tomcat by running scripts. I think this is very troublesome, so can we shut down Tomcat by online access? Of course, more violent game is directly change org. Apache. Catalina. Core. StandardServer source line 500, will be

boolean match = command.toString().equals(shutdown);
Copy the code

to

Boolean match = command.tostring ().equals(" GET /SHUTDOWN HTTP/1.1 ");Copy the code

Or modify the server. XML file, find the server node, and place the original

<Server port="8005" shutdown="SHUTDOWN">  
Copy the code

to

<Server port="8005" shutdown="GET/SHUTDOWN HTTP / 1.1">  
Copy the code

It directly in the browser type http://localhost:8005/SHUTDOWN can close the Tomcat, principles? Read the above article, this should not be difficult.