In normal start Tomcat 7 cases, the article analysis the execution org. Apache. Catalina. Core. StandardServer init and start method here, then take a look at these two methods in what is done.

Neither method is found in the StandardServer class:

1 @Override 2 public final synchronized void init() throws LifecycleException { 3 if (! state.equals(LifecycleState.NEW)) { 4 invalidTransition(Lifecycle.BEFORE_INIT_EVENT); 5 } 6 setStateInternal(LifecycleState.INITIALIZING, null, false); 7 8 try { 9 initInternal(); 10 } catch (Throwable t) { 11 ExceptionUtils.handleThrowable(t); 12 setStateInternal(LifecycleState.FAILED, null, false); 13 throw new LifecycleException( 14 sm.getString("lifecycleBase.initFail",toString()), t); 15 } 16 17 setStateInternal(LifecycleState.INITIALIZED, null, false); 18 } 19 20 21 protected abstract void initInternal() throws LifecycleException;Copy the code

Ignoring the setStateInternal method call that interferes with your program’s view (more on that in the next article), one thing you do is call the abstract method initInternal() that you define next (line 21). In fact, if you look at the LifecycleBase implementation class, you’ll see that almost all component classes inherit from the LifecycleBase class, so these component classes generally only have the definition of initInternal methods. (StandardServer, StandardService, StandardEngine, StandardHost, StandardContext, etc.)

The component we’re talking about here can be thought of as the individual nodes in the XML file when we first analyze server.xml. Parent-child relationships are the parent-child nodes in the XML file. If you look through the subclasses of LifecycleBase, you’ll see that the node’s implementation classes are all subclasses of that class (remember, we’ll talk about that later).

1 /** 2 * {@inheritDoc} 3 */ 4 @Override 5 public final synchronized void start() throws LifecycleException { 6 7 if (LifecycleState.STARTING_PREP.equals(state) || 8 LifecycleState.STARTING.equals(state) || 9 LifecycleState.STARTED.equals(state)) { 10 11 if (log.isDebugEnabled()) { 12 Exception e = new LifecycleException(); 13 log.debug(sm.getString("lifecycleBase.alreadyStarted", 14 toString()), e); 15 } else if (log.isInfoEnabled()) { 16 log.info(sm.getString("lifecycleBase.alreadyStarted", 17 toString())); 18 } 19 20 return; 21 } 22 23 if (state.equals(LifecycleState.NEW)) { 24 init(); 25 } else if (state.equals(LifecycleState.FAILED)){ 26 stop(); 27 } else if (! state.equals(LifecycleState.INITIALIZED) && 28 ! state.equals(LifecycleState.STOPPED)) { 29 invalidTransition(Lifecycle.BEFORE_START_EVENT); 30 } 31 32 setStateInternal(LifecycleState.STARTING_PREP, null, false); 33 34 try { 35 startInternal(); 36 } catch (Throwable t) { 37 ExceptionUtils.handleThrowable(t); 38 setStateInternal(LifecycleState.FAILED, null, false); 39 throw new LifecycleException( 40 sm.getString("lifecycleBase.startFail",toString()), t); 41 } 42 43 if (state.equals(LifecycleState.FAILED) || 44 state.equals(LifecycleState.MUST_STOP)) { 45 stop(); 46 } else { 47 // Shouldn't be necessary but acts as a check that sub-classes are 48 // doing what they are supposed to.  49 if (! state.equals(LifecycleState.STARTING)) { 50 invalidTransition(Lifecycle.AFTER_START_EVENT); 51 } 52 53 setStateInternal(LifecycleState.STARTED, null, false); 54 } 55 } 56 57 58 /** 59 * Sub-classes must ensure that the state is changed to 60 * {@link LifecycleState#STARTING} during the execution of this method. 61 * Changing state will trigger the {@link Lifecycle#START_EVENT} event. 62 * 63 *  If a component fails to start it may either throw a 64 * {@link LifecycleException} which will cause it's parent to fail to start 65 * or it can place itself in the error state in which case {@link #stop()} 66 * will be called on the failed component but the parent component will 67 * continue to start normally. 68 * 69 * @throws LifecycleException 70 */ 71 protected abstract void startInternal() throws LifecycleException;Copy the code

Lines 7 through 21 are pre-validation of the start function, where if the start method is found to have already been called, it is logged and returned directly. Lines 23 to 30 will call the precursors first if the start method is not called, or if the call fails. Line 35, which is the essence of the method, will call the abstract method startInternal() defined in this class (line 71). The following code, like above, is error handling of some errors that may occur during a start method call.

From the above definition of init and start methods, you can see that these two methods will eventually call initInternal and startInternal defined in subclasses.

The initInternal and startInternal methods of the StandardServer class will be called. So what’s going on in these two methods?

InitInternal method:

1 /** 2 * Invoke a pre-startup initialization. This is used to allow connectors 3 * to bind to restricted ports under Unix operating environments. 4 */ 5 @Override 6 protected void initInternal() throws LifecycleException { 7 8 super.initInternal(); 9 10 // Register global String cache 11 // Note although the cache is global, if there are multiple Servers 12 // present in the JVM (may happen when embedding) then the same cache 13 // will be registered under multiple names 14 onameStringCache = register(new StringCache(), "type=StringCache"); 15 16 // Register the MBeanFactory 17 MBeanFactory factory = new MBeanFactory(); 18 factory.setContainer(this); 19 onameMBeanFactory = register(factory, "type=MBeanFactory"); 20 21 // Register the naming resources 22 globalNamingResources.init(); 23 24 // Populate the extension validator with JARs from common and shared 25 // class loaders 26 if (getCatalina() ! = null) { 27 ClassLoader cl = getCatalina().getParentClassLoader(); 28 // Walk the class loader hierarchy. Stop at the system class loader. 29 // This will add the shared (if present) and common class loaders 30 while (cl ! = null && cl ! = ClassLoader.getSystemClassLoader()) { 31 if (cl instanceof URLClassLoader) { 32 URL[] urls = ((URLClassLoader) cl).getURLs(); 33 for (URL url : urls) { 34 if (url.getProtocol().equals("file")) { 35 try { 36 File f = new File (url.toURI()); 37 if (f.isFile() && 38 f.getName().endsWith(".jar")) { 39 ExtensionValidator.addSystemResource(f); 40 } 41 } catch (URISyntaxException e) { 42 // Ignore 43 } catch (IOException e) { 44 // Ignore 45 } 46 } 47 } 48 } 49 cl = cl.getParent(); 50 } 51 } 52 // Initialize our defined Services 53 for (int i = 0; i < services.length; i++) { 54 services[i].init(); 55} 56}Copy the code

The init method does several things and covers a lot of topics, but I’ll focus on lines 53 to 55 at the end, which loops through the init method of the Service array built into the Server class.

StartInternal method:

1 /** 2 * Start nested components ({@link Service}s) and implement the requirements 3 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. 4 * 5 * @exception LifecycleException if this component detects  a fatal error 6 * that prevents this component from being used 7 */ 8 @Override 9 protected void startInternal() throws  LifecycleException { 10 11 fireLifecycleEvent(CONFIGURE_START_EVENT, null); 12 setState(LifecycleState.STARTING); 13 14 globalNamingResources.start(); 15 16 // Start our defined Services 17 synchronized (services) { 18 for (int i = 0; i < services.length; i++) { 19 services[i].start(); 20} 21} 22}Copy the code

Focus on lines 17 through 21, which, like the code we analyzed in the previous section, loops through the start method of the Service array built into the Sever class.

So what is the object of a Service?

The previous article on Digester mentioned that “parsing an XML file will result Org. Apache. Catalina. Core. StandardServer, org. Apache. Catalina. Core. StandardService, org. Apache. Catalina. Connector. The connector And org. Apache. Catalina. Core. StandardEngine, org. Apache. Catalina. Core. StandardHost, org. Apache. Catalina. Core. StandardContext And so on, a list of objects that contain references to the next object from front to back (one-to-one or one-to-many relationships).”

So under normal circumstances the Service here will be org. Apache. Catalina. Core. StandardService object (see the code org. Apache. Catalina. Startup. Catalina class of 339 341 lines).

So following this analysis, we’ll call the init and start methods of the StandardService class, which is actually a subclass of the LifecycleBase class, The initInternal and startInternal methods of this class are also called.

InitInternal method:

1 /** 2 * Invoke a pre-startup initialization. This is used to allow connectors 3 * to bind to restricted ports under Unix operating environments. 4 */ 5 @Override 6 protected void initInternal() throws LifecycleException { 7 8 super.initInternal(); 9 10 if (container ! = null) { 11 container.init(); 12 } 13 14 // Initialize any Executors 15 for (Executor executor : findExecutors()) { 16 if (executor instanceof LifecycleMBeanBase) { 17 ((LifecycleMBeanBase) executor).setDomain(getDomain()); 18 } 19 executor.init(); 20 } 21 22 // Initialize our defined Connectors 23 synchronized (connectors) { 24 for (Connector connector : connectors) { 25 try { 26 connector.init(); 27 } catch (Exception e) { 28 String message = sm.getString( 29 "standardService.connector.initFailed", connector); 30 log.error(message, e); 31 32 if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) 33 throw new LifecycleException(message); 34} 35} 36} 37}Copy the code

This calls the init method in the various child components of the Service.

StartInternal method:

1 /** 2 * Start nested components ({@link Executor}s, {@link Connector}s and 3 * {@link Container}s) and implement the requirements of 4 * {@link org.apache.catalina.util.LifecycleBase#startInternal()}. 5 * 6 * @exception LifecycleException if this component detects  a fatal error 7 * that prevents this component from being used 8 */ 9 @Override 10 protected void startInternal() throws LifecycleException { 11 12 if(log.isInfoEnabled()) 13 log.info(sm.getString("standardService.start.name", this.name)); 14 setState(LifecycleState.STARTING); 15 16 // Start our defined Container first 17 if (container ! = null) { 18 synchronized (container) { 19 container.start(); 20 } 21 } 22 23 synchronized (executors) { 24 for (Executor executor: executors) { 25 executor.start(); 26 } 27 } 28 29 // Start our defined Connectors second 30 synchronized (connectors) { 31 for (Connector connector: connectors) { 32 try { 33 // If it has already failed, don't try and start it 34 if (connector.getState() ! = LifecycleState.FAILED) { 35 connector.start(); 36 } 37 } catch (Exception e) { 38 log.error(sm.getString( 39 "standardService.connector.startFailed", 40 connector), e); 41} 42} 43} 44}Copy the code

Similarly, the start method in the various child components of the Service is called.

The child container of StandardService is StandardEngine. Take a look at StandardEngine’s startInternal method:

1 protected synchronized void startInternal() throws LifecycleException { 2 3 // Log our server identification information 4 if(log.isInfoEnabled()) 5 log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo()); 6 7 // Standard container startup 8 super.startInternal(); 9}Copy the code

Instead of calling the parent class’s startInternal method, we call the startInternal method directly:

1 protected synchronized void startInternal() throws LifecycleException { 2 3 // Start our subordinate components, if any 4 if ((loader ! = null) && (loader instanceof Lifecycle)) 5 ((Lifecycle) loader).start(); 6 logger = null; 7 getLogger(); 8 if ((manager ! = null) && (manager instanceof Lifecycle)) 9 ((Lifecycle) manager).start(); 10 if ((cluster ! = null) && (cluster instanceof Lifecycle)) 11 ((Lifecycle) cluster).start(); 12 Realm realm = getRealmInternal(); 13 if ((realm ! = null) && (realm instanceof Lifecycle)) 14 ((Lifecycle) realm).start(); 15 if ((resources ! = null) && (resources instanceof Lifecycle)) 16 ((Lifecycle) resources).start(); 17 18 // Start our child containers, if any 19 Container children[] = findChildren(); 20 List> results = new ArrayList>(); 21 for (int i = 0; i < children.length; i++) { 22 results.add(startStopExecutor.submit(new StartChild(children[i]))); 23 } 24 25 boolean fail = false; 26 for (Future result : results) { 27 try { 28 result.get(); 29 } catch (Exception e) { 30 log.error(sm.getString("containerBase.threadedStartFailed"), e); 31 fail = true; 32 } 33 34 } 35 if (fail) { 36 throw new LifecycleException( 37 sm.getString("containerBase.threadedStartFailed")); 38 } 39 40 // Start the Valves in our pipeline (including the basic), if any 41 if (pipeline instanceof Lifecycle) 42 ((Lifecycle) pipeline).start(); 43 44 45 setState(LifecycleState.STARTING); 46 47 // Start our thread 48 threadStart(); 49 50}Copy the code

Lines 19 through 34 are the code that starts the children of the current container in a separate thread. The core code for calling the start method of the child container is in the call method of the StartChild class:

1 private static class StartChild implements Callable { 2 3 private Container child; 4 5 public StartChild(Container child) { 6 this.child = child; 7 } 8 9 @Override 10 public Void call() throws LifecycleException { 11 child.start(); 12 return null; 14 13}}Copy the code

JDK 5 thread of execution is used here, please refer to the documentation if you do not understand.

StartInternal in StandardHost is similar to StandardEngine. It is recommended that you Review it one by one. AddRuleInstances new *RuleSet addRuleInstances new *RuleSet addRuleInstances New *RuleSet addRuleInstances New *RuleSet addRuleInstances This call will eventually execute all the initInternal and startInternal methods in the implementation class of the node configured in server.xml.

In this way, Tomcat 7 generates objects in memory for the series of components, establishes the object invocation relationship, and calls their respective initialization and startup methods. The overall process of startup is described. These objects will respond to the client’s request and serve the user. Of course, the loading process of the servlet, filter, listener, etc. configured in the Web application is not involved here. The question of how a browser-initiated request passes through the components of Tomcat to a specific application has not yet been addressed. Because Tomcat itself is huge and complex, we need to find a perspective to enter into it. For the sake of simplicity, we should first have a general understanding of the generation mechanism of components contained in Tomcat as a whole, so as to provide a unified background for the subsequent introduction.

The next article will examine the role of the setStateInternal method, which remains from the beginning of this article, and how Lifecycle is implemented in Tomcat.