StandardServer analysis-tomcat6.x source code reading

Source: https://my.oschina.net/douglas/blog/161029

 

What is StandardServer

The StandardServer standard implements the Server interface. It can be seen from the tomcat structure hierarchy diagram that the Server is at the outermost layer, and other components are inside it. They are in charge, like the CEO of a company, responsible for managing the entire company. Server represents a complete Servlet container, management and maintenance. Server and global resources share StandardServer resources among various components. During the startup process of tomcat, Catalina loads and parses server.xml through the Digester library, creates a StandardServer object, and initializes and starts the Server. The Server registers itself on JMX, and checks the Server status through the tomcat management page. In addition to implementing the Server interface, StandardServer also uses the following components to complete functions.

Lifecycle  StandardServer implements the Lifecycle interface. Lifecycle is an interface in tomcat for monitoring and monitoring components' life cycle status. Through the 9 states and 5 methods provided by Lifecycle, it is possible to monitor component status updates and operate in different life cycle stages of components.
Status of 9: Indicates component status

  • BEFORE_INIT
  • AFTER_INIT
  • BEFORE_START
  • START
  • AFTER_START
  • BEFORE_STOP
  • STOP
  • AFTER_STOP
  • BEFORE_DESTROY
  • AFTER_DESTROY
  • PERIODIC
  • CONFIG_START
  • CONFIG_STOP

5 methods: Update component state and add component listening component state change notification component

  • addLifecycleListener(LifecycleListener)
  • findLifecycleListeners()
  • removeLifecycleListener(LifecycleListener)
  • start()
  • stop()

addLifecycleListener(LifecycleListener) is a registered LifecycleEvent event listener. LifecycleListener is to listen to the LifecycleEvent event triggered by the status change of the Lifecycle component. When the LifecycleListener listens to the LifecycleEvent event, it will call the LifecycleListener method lifecycleEvent(LifecycleEvent) to respond to the event.

MBeanRegistration
StandardServer implements the MBeanRegistration interface. The MBeanRegistration interface is the content of the MBean method of JMX. The purpose of implementing this interface is to register the StandardServer component in JMX, and the StandardServer can be controlled through JMX.

LifecycleSupport
is an attribute of StandardServer. Its function is to manage the LifecycleListener of the implementation class of the Lifecycle interface. There is only one constructor for the parameter. You must pass in Lifecycle when creating an object. When creating an object in StandardServer, pass in StandardServer itself.
private LifecycleSupport lifecycle = new LifecycleSupport(this);
LifecycleSupport management The listener registered on the StandardServer, when listening to the LifecycleEvent, LifecycleSupport immediately calls the method fireLifecycleEvent(String, Object) to traverse the listener to respond to the event, and the attribute state indicates the current Lifecycle state.

The property of javax.naming.Context
StandardServer is the content in JNDI. I don’t know it. I only know that it provides naming services, that is, to obtain objects and object attribute information according to their names. By giving the resource path, the following resource path can be obtained. Object, the configuration of resources in server.xml, from the configuration information, you can see that the configuration is the login account permission information of the tomcat management page.

<!-- Global JNDI resources Documentation at /docs/jndi-resources-howto.html -->
    <GlobalNamingResources>
        <!-- Editable user database that can also be used by UserDatabaseRealm
            to authenticate users -->
        <Resourcename="UserDatabase"auth="Container"type="org.apache.catalina.UserDatabase"description="User database that can be updated and saved"factory="org.apache.catalina.users.MemoryUserDatabaseFactory"pathname="conf/tomcat-users.xml" />
    </GlobalNamingResources>

 

Java is often used when interacting with third parties, such as database drivers, mysql's JDBC and ODBC, and java does not care about its implementation. JNDI understanding example, from blog lujin55

The attribute of NamingContextListener
StandardServer is the content in JNDI. I don't know it, it's more complicated. I only know that it is responsible for monitoring javax.naming.Context resource events. NamingContextListener implements three interfaces: LifecycleListener, ContainerListener, and PropertyChangeListener, and has the ability to monitor events of Lifecycle component, Container component, and PropertyChange.

The attribute of NamingResources
StandardServer is the content in JNDI. I don't know it, it is more complicated. Knowing that it manages named resources, encapsulates the resources to be loaded into objects, and can directly obtain objects from NamingResources.

The properties of PropertyChangeSupport
StandardServer, referring to the understanding of LifecycleSupport, it is not difficult to see that PropertyChangeSupport manages the object property change listener, which is the same as the mission of LifecycleSupport, listens for PropertyChangeEvent events, and then responds.

StandardServer loads the resources configured in server.xml through JNDI, completes resource configuration, registers listeners to listen for events, and registers itself on ServerFactory. After StandardServer completes the resource loading again, it calls the init() method to initialize. In practice, initialize() is called. During the startup of tomcat, the initialization method is called by Catalina. It is done in 4 steps in the initialize() method

  • The first step is to determine whether it has been initialized
  • The second part triggers the INIT_EVENT event, marking it has been initialized (initialized = true)
  • The third step is to register to the MBeanServer server, add monitoring, and manage the Server on the tomcat management page
  • The fourth step traverses the services array and calls initialize() one by one to initialize
  • /**
         * Invoke a pre-startup initialization. This is used to allow connectors to
         * bind to restricted ports under Unix operating environments.
         */
        publicvoidinitialize()throws LifecycleException {
            if (initialized) {
                log.info(sm.getString("standardServer.initialize.initialized"));
                return;
            }
            lifecycle.fireLifecycleEvent(INIT_EVENT, null);
            initialized = true;
    
            if (oname == null) {
                try {
                    oname = new ObjectName("Catalina:type=Server");
                    Registry.getRegistry(null, null).registerComponent(this, oname,
                            null);
                } catch (Exception e) {
                    log.error("Error registering ", e);
                }
            }
    
            // Register global String cache
            try {
                ObjectName oname2 = new ObjectName(oname.getDomain()
                        + ":type=StringCache");
                Registry.getRegistry(null, null).registerComponent(
                        new StringCache(), oname2, null);
            } catch (Exception e) {
                log.error("Error registering ", e);
            }
    
            // Initialize our defined Services
            for (int i = 0; i < services.length; i++) {
                services[i].initialize();
            }
        }
     
  • After the StandardServer is initialized, call the start() method to start the Server, which is completed in 4 steps in the start() method
  • The first step is to determine whether the Server has been started
  • The second part fires the BEFORE_START_EVENT, START_EVENT event, marking it has started (started = true)
  • The third step is to traverse the services array and call start() one by one to initialize
  • The fourth step triggers the AFTER_START_EVENT event to notify the listener
  • /**
         * Prepare for the beginning of active use of the public methods of this
         * component. This method should be called before any of the public methods
         * of this component are utilized. It should also send a LifecycleEvent of
         * type START_EVENT to any registered listeners.
         *
         * @exception LifecycleException
         *                if this component detects a fatal error that prevents this
         *                component from being used
         */
        publicvoidstart()throws LifecycleException {
    
            // Validate and update our current component state
            if (started) {
                log.debug(sm.getString("standardServer.start.started"));
                return;
            }
    
            // Notify our interested LifecycleListeners
            lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
    
            lifecycle.fireLifecycleEvent(START_EVENT, null);
            started = true;
    
            // Start our defined Services
            synchronized (services) {//Synchronization
                for (int i = 0; i < services.length; i++) {
                    if (services[i] instanceof Lifecycle)
                        ((Lifecycle) services[i]).start();
                    System.out.println(i+":"+services[i].hashCode());//Added by myself, in order to verify whether catalina is in the server's services list, it turns out that catalina is not in the list
                }
            }
    
            // Notify our interested LifecycleListeners
            lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
    
        }
  • After the StandardServer is started, Catalina calls the await() method in the start() method, and calls the await() of the StandardServer in the await() method. The main function of the await() method of the StandardServer is very simple: start the ServerSocket to listen to the network port and request to close the Server It can be seen from the code that StandardServer uses an infinite loop to continuously monitor the port. When it receives the "SHUTDOWN" command, it jumps out of the loop and returns to Catalina's start() method.
  • /**
         * Wait until a proper shutdown command is received, then return. This keeps
         * the main thread alive - the thread pool listening for http connections is
         * daemon threads.
         */
        public void await() {
            // Negative values - don't wait on port - tomcat is embedded or we just
            // don't like ports
            if (port == -2) {
                // undocumented yet - for embedding apps that are around, alive.
                return;
            }
            if (port == -1) {
                try {
                    awaitThread = Thread.currentThread();//Current thread
                    while (!stopAwait) {
                        try {
                            Thread.sleep(10000);
                        } catch (InterruptedException ex) {
                            // continue and check the flag
                        }
                    }
                } finally {
                    awaitThread = null;
                }
                return;
            }
    
            // Set up a server socket to wait on
            try {
                awaitSocket = new ServerSocket(port, 1,
                        InetAddress.getByName("localhost"));
            } catch (IOException e) {
                log.error("StandardServer.await: create[" + port + "]: ", e);
                return;
            }
    
            try {
                awaitThread = Thread.currentThread();//Current thread
    
                // 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 {
                        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
                        }
                    }
    
                    // Match against our command string
                    boolean match = command.toString().equals(shutdown);
                    if (match) {
                        break;
                    } else
                        log.warn("StandardServer.await: Invalid command '"
                                + command.toString() + "' received");
                }
            } finally {
                ServerSocket serverSocket = awaitSocket;
                awaitThread = null;
                awaitSocket = null;
    
                // Close the server socket and return
                if (serverSocket != null) {
                    try {
                        serverSocket.close();
                    } catch (IOException e) {
                        // Ignore
                    }
                }
            }
        }
     In StandardServer, the await() method is called to loop back to Catalina's start() method, and immediately after the await() method is the stop() method, that is, the method of stopping the Server, which is mainly used in the stop() method of StandardServer. Complete the task of stopping the Server in 5 steps
  • The first step is to determine whether the Server has been started
  • The second part fires BEFORE_STOP_EVENT, STOP_EVENT event, marking has stopped (started = false)
  • The third step is to traverse the services array and call stop() one by one to initialize
  • The fourth step triggers the AFTER_STOP_EVENT event to notify the listener
  • The fifth step is to call stopAwait(), close the ServerSocket, and call interrupt() to exit the thread.
  • /**
         * Gracefully terminate the active use of the public methods of this
         * component. This method should be the last one called on a given instance
         * of this component. It should also send a LifecycleEvent of type
         * STOP_EVENT to any registered listeners.
         *
         * @exception LifecycleException
         *                if this component detects a fatal error that needs to be
         *                reported
         */
        publicvoidstop()throws LifecycleException {
    
            // Validate and update our current component state
            if (!started)
                return;
    
            // Notify our interested LifecycleListeners
            lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
    
            lifecycle.fireLifecycleEvent(STOP_EVENT, null);
            started = false;
    
            // Stop our defined Services
            for (int i = 0; i < services.length; i++) {
                if (services[i] instanceof Lifecycle)
                    ((Lifecycle) services[i]).stop();
            }
    
            // Notify our interested LifecycleListeners
            lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
    
            stopAwait();
    
        }
     

    

At this point, the initialization, start, and stop of StandardServer are completed, and the understanding of StandardServer is much clearer. StandardServer first loads resources from server.xml to complete the configuration, provides a method to monitor the status of StandardServer, and operates StandardServer in different life cycles; during initialization Trigger an event in , initialize services, register to MBeanServer, mark has been initialized, in startup, trigger startup event, start services, after startup is completed, trigger await() method by Catalina, start ServerSocket listening request to close Server command, stop Server In the stop() method, the stop server event is triggered, services are stopped, and resources are cleared. The listener registered on the StandardServer is a bit complicated, we will see what happens later.

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326528777&siteId=291194637