As we all know, every application has a unique entry (that is, the main function), so the Tomcat server developed in Java language is no exception, find this entry, understand the specific process of each component loading, to understand the entire application implementation process is very helpful. The tomcat startup related classes are located in the catalina.startup package path, and the entry point is the main() function of the class Bootstrap. The Bootstrap startup class mainly completes the following three aspects:

Set catalinaHome and catalinaBase in the static code block.

(2) Initialization of common, Server and shared class loaders;

(3) use of reflection mechanism to instantiate org. Apache. Catalina. Startup. Catalina class.

Set catalinaHome and catalinaBase

CatalinaHome is the Tomcat installation directory and catalinaBase is the Tomcat working directory. The main function of the two directories is to share tomcat code instead of installing multiple copies of Tomcat when multiple Instances of Tomcat are deployed on the same machine. For example, if two Tomcat instances are deployed on the same machine, you only need to create two base directories, base1 and base2, and copy the shared directories in the Tomcat installation directory to these two directories. To start two Tomcat servers at the same time, change the port number in the server. XML file in the conf directory. In this case, the bin and lib directories in the Tomcat installation directory (tomcatHome) are shared.

Code listing (Bootstrap code to set catalinaHome and catalinaBase)
Static {// Obtain the tomcat installation directory String userDir = system.getProperty ("user.dir");
        String home = System.getProperty(Globals.CATALINA_HOME_PROP);
        File homeFile = null;
        if (home != null) {
            File f = new File(home);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }

        if(homeFile == null) {// The root directory of bootstrap.jar is the bin folder in the tomcat directory."bootstrap.jar");
            if (bootstrapJar.exists()) {
                File f = new File(userDir, ".."); try { homeFile = f.getCanonicalFile(); } catch (IOException ioe) { homeFile = f.getAbsoluteFile(); }}}if (homeFile == null) {
            // Second fall-back. Use current directory
            File f = new File(userDir);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }

        catalinaHomeFile = homeFile;
        System.setProperty(
                Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());

        // Then base
        String base = System.getProperty(Globals.CATALINA_BASE_PROP);
        if (base == null) {
            catalinaBaseFile = catalinaHomeFile;
        } else {
            File baseFile = new File(base);
            try {
                baseFile = baseFile.getCanonicalFile();
            } catch (IOException ioe) {
                baseFile = baseFile.getAbsoluteFile();
            }
            catalinaBaseFile = baseFile;
        }
        System.setProperty(
                Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
    }
Copy the code

Initialize class loaders common, Server, and shared

There are three types of Customized Tomcat class loaders: Common, Server, and Shared. They are used to load classes and JAR files in different locations to separate and share class files between different Web applications and Tomcat systems. The main issues addressed are: Java libraries used by multiple Web applications deployed on the same Tomcat server can be separated from each other (different versions of the same library); Java libraries used by multiple Web applications deployed on the same Tomcat server can be shared with each other; The server ensures its security against deployed Web applications (Tomcat system classes can only be loaded through the Server class loader, and Web applications can be loaded by the WebAppClassLoader under the shared class loader). Implement HotSwap functionality (by replacing the JsperLoader class loader).

commonClassLoader

CommonClassLoader is defined in Catalina.properties as: common.loader=”${catalina.base}/lib”,”${catalina.base}/lib/*.jar”,”${catalina.home}/lib”,”${catalina.home}/lib/*.jar” You can see that the libraries it loads are files in lib under Catalina. base and Catalina. home. Common is a class loader shared by both servers and Web applications, and is the parent loader of both servers and shared. The implementation in Bootstrap is as follows

private void initClassLoaders() {
        try {
            commonLoader = createClassLoader("common", null);
            if( commonLoader == null ) {
                commonLoader=this.getClass().getClassLoader();
            }
            catalinaLoader = createClassLoader("server", commonLoader); // Set commonLoader to parent catalinaLoader and sharedLoader. sharedLoader = createClassLoader("shared", commonLoader);
        } catch (Throwable t) {
            handleThrowable(t);
            log.error("Class loader creation threw exception", t); System.exit(1); }}Copy the code

Continue tracing the createClassLoader source code as follows:

private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
        String value = CatalinaProperties.getProperty(name + ".loader");
        if ((value == null) || (value.equals("")))
            return parent;
        value = replace(value);
        List<Repository> repositories = new ArrayList<>();
        String[] repositoryPaths = getPaths(value);
        for (String repository : repositoryPaths) {
            try {
                @SuppressWarnings("unused")
                URL url = new URL(repository);
                repositories.add(new Repository(repository, RepositoryType.URL));
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }
            if (repository.endsWith("*.jar")) {
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
                repositories.add(new Repository(repository, RepositoryType.GLOB));
            } else if (repository.endsWith(".jar")) {
                repositories.add(new Repository(repository, RepositoryType.JAR));
            } else{ repositories.add(new Repository(repository, RepositoryType.DIR)); }}return ClassLoaderFactory.createClassLoader(repositories, parent);
    }

Copy the code

catalinaClassLoader

CatalinaClassLoader sets the path to empty server.loader= in Catalina.properties, so its class loading path is the same as its parent loader, commonClassLoader. After initialization catalinaClassLoader in the init method is set to the current thread class loader, and then complete the rg. The apache. Catalina. Startup. Catalina class loading.

Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); Class<? > startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Copy the code

sharedClassLoader

CatalinaClassLoader’s path in Catalina.properties is also empty, so its default class loading path is the same as that of its parent, commonClassLoader.

Use of reflection mechanism to instantiate org. Apache. Catalina. Startup. Catalina class

Catalina plays an important role in tomcat startup. As the beginning class of Tomcat life cycle, Catalina is mainly responsible for parsing server. XML files and starting and closing server, Service, Connector and other components. Accept tomcat stop instructions to shut down the Tomcat server. Through the reflection mechanism in the Bootstrap org. Apache. Catalina. Startup. Catalina examples, code is as follows

Class<? > startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();
Copy the code

The Catalina class parses server. XML by creating Digester and generates server, Service, Connector, and Container instances. Only Engine and Host containers are configured in XML.

protected String configFile = "conf/server.xml"; // Set the path to server.xml... protected DigestercreateStartDigester() {
        long t1=System.currentTimeMillis();
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true); Map<Class<? >, List<String>> fakeAttributes = new HashMap<>(); List<String> attrs = new ArrayList<>(); attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);
        digester.addObjectCreate("Server"."org.apache.catalina.core.StandardServer"."className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server"."setServer"."org.apache.catalina.Server"); // Omit a large section of code to set up other components, in the same form as StandardServer's generation, for the Digester class to parse XML to generate Java instances refer to Chapter 15 in Depth tomcat. }Copy the code

Catalina class entry is also the start method. Bootstrap calls Catalina’s Start method through reflection

public void start()
        throws Exception {
        if( catalinaDaemon==null ) init();
        Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
        method.invoke(catalinaDaemon, (Object [])null);

    }
Copy the code

The start method calls the Load method to create the Digester and generate the various components. The load class is defined as follows:

public void load() {
        if (loaded) {
            return;
        }
        loaded = true; long t1 = System.nanoTime(); initDirs(); // Before digester - it may be needed initNaming(); // Create and execute our Digester Digester digester = createStartDigester(); InputSource inputSource = null; InputStream inputStream = null; File file = null; try { try { file = configFile(); InputStream = new FileInputStream(file); inputSource = new InputSource(file.toURI().toURL().toString()); } catch (Exception e) {if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail", file), e); }}if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader()
                        .getResourceAsStream(getConfigFile());
                    inputSource = new InputSource
                        (getClass().getClassLoader()
                         .getResource(getConfigFile()).toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail",
                                getConfigFile()), e);
                    }
                }
            }

            // This should be included in catalina.jar
            // Alternative: don't bother with xml, just create it manually. if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream("server-embed.xml"); inputSource = new InputSource (getClass().getClassLoader() .getResource("server-embed.xml").toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", "server-embed.xml"), e); } } } if (inputStream == null || inputSource == null) { if (file == null) { log.warn(sm.getString("catalina.configFail",  getConfigFile() + "] or [server-embed.xml]")); } else { log.warn(sm.getString("catalina.configFail", file.getAbsolutePath())); if (file.exists() && ! file.canRead()) { log.warn("Permissions incorrect, read permission is not allowed on the file."); } } return; } try { inputSource.setByteStream(inputStream); // Parse the server.xml data stream digester.push(this); digester.parse(inputSource); } catch (SAXParseException spe) { log.warn("Catalina.start using " + getConfigFile() + ": " + spe.getMessage()); return; } catch (Exception e) { log.warn("Catalina.start using " + getConfigFile() + ": " , e); return; } } finally { if (inputStream ! = null) { try { inputStream.close(); } catch (IOException e) { // Ignore } } } getServer().setCatalina(this); / / configuration server, start the class for the current Catalina class, the Home base and defined in the Bootstrap getServer () setCatalinaHome (Bootstrap. GetCatalinaHomeFile ()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection initStreams(); // start server try {getServer().init(); } catch (LifecycleException e) { if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new  java.lang.Error(e); } else { log.error("Catalina.start", e); } } long t2 = System.nanoTime(); if(log.isInfoEnabled()) { log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms"); }}Copy the code

Tomcat starts with server, Service, Connector, and Container components implemented through the server.xml configuration file. The next few articles will follow up on how these components are implemented.

Tomcat source code analysis (the first Tomcat source code analysis (the first from the overall architecture)) Tomcat source code analysis (the third Tomcat request principle analysis –Connector source code analysis) Tomcat source code analysis (the fourth Tomcat request processing principle analysis –Container source code analysis