启动Tomcat

这篇随笔的重点关注启动Tomcat时会用到的两个类,分别是Catalina类 和 Bootstrap类,它们都位于org.apache.catalina.startup包下,Catalina类用于启动或关闭Server对象,并负责解析Tomcat文件:server.xml文件。Bootstrap类是一个入口点,负责创建Catalina实例,并调用其process()方法,理论上这两个类可以合并为一个,但是为了支持Tomcat的多种运行模式,而提供了多种启动类,例如,上面说到的Bootstrap类是作为一个独立的应用程序运行Tomcat的,而另一个类org.apahce.catalina.starup.BootstrapService可以使Tomcat作为一个Windows NT服务来运行。   为了使用户使用方便,Tomcat附带了批处理文件 和 shell 脚本,可以方便的启动或者关闭servlet容器,在这些批处理 文件 和 shell 脚本的帮助下, 用户无须为了执行Bootstrap类 而记下 java.exe程序的选项,相反,用户只需要运行相应的批处理文件或者 shell脚本即可, 先介绍一下Catalina类  Catalina类   org.apache.catalina.startup.Catalina类是启动类,它包含了一个Digester对象,用于解析位于%CATALIN_HOME%conf目录下的server.xml文件。理解了添加到Digester对象中的规则之后,就可以自行配置Tomcat了。   Catalina类还封装了一个Server对象,该对象有一个Service对象,正如之前学习的那样,Service对象包含一个Servlet容器 和 一个 或者多个连接器,可以使用Catalina类来启动或者关闭Server对象,   可以通过实例化Catalina类,并调用其process方法来运行Tomcat,但是在调用该方法时,需要传入适当的参数,第一个参数是start 表示要启动Tomcat,或 stop 表示要向Tomcat发送一条关闭命令,还有其他的参数可选,包括-help、-config、-debug和-nothing   注意:当使用nothing参数时,表示将不对JNDI 命名提供支持,更多关于Tomcat中 对JNDI命名的支持 看org.apahce.naming包中的类吧   一般情况下,及时Catalina类提供了main方法作为程序的入口点,也需要使用Bootstrap类来实例化Catalina类,并调用其process方法,下面给出procee的实现 复制代码 /** * 实例主程序。 * * @param args Command line arguments */ public void process(String args[]) { setCatalinaHome(); setCatalinaBase(); try { if (arguments(args)) execute(); } catch (Exception e) { e.printStackTrace(System.out); } } 复制代码 process 方法设置了两个系统属性,分别是 catalina.home 和 catalina.base,默认值均与user.dir值相同, 注意:user.dir属性的值指明了用户的工作目录,即,会从哪个目录下调用java命令,更多系统属性的列表,可以参见java.lang.System类 getProperties方法的介绍 然后procee 方法会调用arguments方法,并传入参数列表,arguments方法处理命令行参数,如果Catalina对象能够继续处理的话,arguments方法会返回true, 复制代码 /** * 处理指定的命令行参数,如果应该继续处理,则返回true;否则返回false。 * * @param args * Command line arguments to process */ protected boolean arguments(String args[]) { boolean isConfig = false; if (args.length < 1) { usage(); return (false); } for (int i = 0; i < args.length; i++) { if (isConfig) { configFile = args[i]; isConfig = false; } else if (args[i].equals("-config")) { isConfig = true; } else if (args[i].equals("-debug")) { debug = true; } else if (args[i].equals("-nonaming")) { useNaming = false; } else if (args[i].equals("-help")) { usage(); return (false); } else if (args[i].equals("start")) { starting = true; } else if (args[i].equals("stop")) { stopping = true; } else { usage(); return (false); } } return (true); } 复制代码   process方法会检查arguments方法的返回值,如果返回值为true,则调用execute方法 复制代码 1 /** 2 * 执行 命令行配置后 处理。 3 * 4 * 5 */ 6 protected void execute() throws Exception { 7 8 // 如果命令启动则启动 9 if (starting) 10 start(); 11 // 如果命令停止则停止 12 else if (stopping) 13 stop(); 14 15 } 复制代码 execute方法会调用start方法 启动Tomcat 或者 调用stop方法来关闭Tomcat。 注意:在Tomcat 5 以及之后,没有execute方法,会在procee方法中调用 start方法 或者 stop方法 Start方法   start方法 会创建一个Digester实例来解析server.xml文件(Tomcat 配置文件),在解析 server.xml文件之前,start方法会调用Digester对象的push方法,传入当前Catalin对象作为参数,这样Catalina对象就成为了 Digester对象的内部栈中的第一个对象,解析Server.xml文件之后,会使变量server引用一个Server对象,默认是org.apache.catalina.core.StandardServer类型的对象,然后start方法会调用Server对象的initialize方法 和 start方法,接着,Catalina对象的start方法会调用Server对象的await反方循环等待,直到接收到正确的关闭命令,当await方法返回时,Catalina对象的start方法会调用Server对象的stop方法,从而关闭Server对象和其他的组件,此外 start方法 还会使用关闭钩子,确保用户突然退出应用程序时会执行Server对象的stop方法。 复制代码 1 /** 2 * 启动一个新的Server实例 3 */ 4 protected void start() { 5 6 // 创建一个解析XML文件的Digester 7 Digester digester = createStartDigester(); 8 // 包含 服务器配置文件的File引用 9 File file = configFile(); 10 try { 11 InputSource is = new InputSource("file://" + file.getAbsolutePath()); 12 FileInputStream fis = new FileInputStream(file); 13 is.setByteStream(fis); 14 // 将该Catalina对象压入Digester对象的内部栈中,成为内部栈中的第一个元素 15 digester.push(this); 16 digester.parse(is); 17 fis.close(); 18 } catch (Exception e) { 19 System.out.println("Catalina.start: " + e); 20 e.printStackTrace(System.out); 21 System.exit(1); 22 } 23 24 // 设置附加变量 25 if (!useNaming) { 26 System.setProperty("catalina.useNaming", "false"); 27 } else { 28 System.setProperty("catalina.useNaming", "true"); 29 String value = "org.apache.naming"; 30 String oldValue = System.getProperty(javax.naming.Context.URL_PKG_PREFIXES); 31 if (oldValue != null) { 32 value = value + ":" + oldValue; 33 } 34 System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value); 35 value = System.getProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY); 36 if (value == null) { 37 System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY, 38 "org.apache.naming.java.javaURLContextFactory"); 39 } 40 } 41 42 // If a SecurityManager is being used, set properties for 43 // checkPackageAccess() and checkPackageDefinition 44 if (System.getSecurityManager() != null) { 45 String access = Security.getProperty("package.access"); 46 if (access != null && access.length() > 0) 47 access += ","; 48 else 49 access = "sun.,"; 50 Security.setProperty("package.access", access + "org.apache.catalina.,org.apache.jasper."); 51 String definition = Security.getProperty("package.definition"); 52 if (definition != null && definition.length() > 0) 53 definition += ","; 54 else 55 definition = "sun.,"; 56 Security.setProperty("package.definition", 57 // FIX ME package "javax." was removed to prevent HotSpot 58 // fatal internal errors 59 definition + "java.,org.apache.catalina.,org.apache.jasper."); 60 } 61 62 // Replace System.out and System.err with a custom PrintStream 63 SystemLogHandler log = new SystemLogHandler(System.out); 64 System.setOut(log); 65 System.setErr(log); 66 67 // 创建一个关闭钩子 68 Thread shutdownHook = new CatalinaShutdownHook(); 69 70 // 初始化 Server对象 71 if (server instanceof Lifecycle) { 72 try { 73 server.initialize(); 74 ((Lifecycle) server).start(); 75 try { 76 // 注册关闭钩子 77 Runtime.getRuntime().addShutdownHook(shutdownHook); 78 } catch (Throwable t) { 79 // This will fail on JDK 1.2. Ignoring, as Tomcat can run 80 // fine without the shutdown hook. 81 } 82 // 等待关闭命令 阻塞 直到 收到正确的关闭命令 83 server.await(); 84 } catch (LifecycleException e) { 85 System.out.println("Catalina.start: " + e); 86 e.printStackTrace(System.out); 87 if (e.getThrowable() != null) { 88 System.out.println("----- Root Cause -----"); 89 e.getThrowable().printStackTrace(System.out); 90 } 91 } 92 } 93 94 // 关闭服务器组件 95 if (server instanceof Lifecycle) { 96 try { 97 try { 98 // 首先删除关闭钩子,以便server.stop() 99 100 // 不会被调用两次 101 Runtime.getRuntime().removeShutdownHook(shutdownHook); 102 } catch (Throwable t) { 103 // This will fail on JDK 1.2. Ignoring, as Tomcat can run 104 // fine without the shutdown hook. 105 } 106 // 关闭服务器组件 107 ((Lifecycle) server).stop(); 108 } catch (LifecycleException e) { 109 System.out.println("Catalina.stop: " + e); 110 e.printStackTrace(System.out); 111 if (e.getThrowable() != null) { 112 System.out.println("----- Root Cause -----"); 113 e.getThrowable().printStackTrace(System.out); 114 } 115 } 116 } 117 118 } 复制代码 stop方法   stop方法用来关闭Catalina 和 Server对象 复制代码 1 /** 2 * 停止现有的服务器实例。 其实就是手动 向 服务器组件负责监听关闭命令的端口发送 关闭名命令 3 */ 4 protected void stop() { 5 6 // 创建一个解析服务器组件配置XML文件的Digester 7 Digester digester = createStopDigester(); 8 File file = configFile(); 9 try { 10 InputSource is = new InputSource("file://" + file.getAbsolutePath()); 11 FileInputStream fis = new FileInputStream(file); 12 is.setByteStream(fis); 13 digester.push(this); 14 digester.parse(is); 15 fis.close(); 16 } catch (Exception e) { 17 System.out.println("Catalina.stop: " + e); 18 e.printStackTrace(System.out); 19 System.exit(1); 20 } 21 22 // 向服务器组件 指定监听关闭命令端口发送关闭命令 23 try { 24 Socket socket = new Socket("127.0.0.1", server.getPort()); 25 OutputStream stream = socket.getOutputStream(); 26 String shutdown = server.getShutdown(); 27 for (int i = 0; i < shutdown.length(); i++) 28 stream.write(shutdown.charAt(i)); 29 stream.flush(); 30 stream.close(); 31 socket.close(); 32 } catch (IOException e) { 33 System.out.println("Catalina.stop: " + e); 34 e.printStackTrace(System.out); 35 // 如果发送命令时异常 则直接退出 使用关闭钩子来关闭服务器组件 36 System.exit(1); 37 } 38 39 } 复制代码 启动Digester对象   Catalina类的createStartDigester方法创建了一个Digester实例,然后为其添加规则,以解析server.xml文件,server.xml文件用来配置Tomcat,位于%CATALINA_HOME%/conf目录下,添加到Digester对象中的规则 是理解Tomcat配置的关键, 复制代码 /** * 创建和配置我们将用于启动的Digester */ protected Digester createStartDigester() { // 初始化Digester Digester digester = new Digester(); if (debug) digester.setDebug(999); // 不校验XML的格式内容 digester.setValidating(false); // 配置我们将要使用的操作 // 遇到Server模式时,使用Server元素的className的值来创建Server实例 digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className"); // 配置Server 各种属性 digester.addSetProperties("Server"); // 将Server实例 与当前Catalina对象关联上,利用setServer方法,将Server实例传入,Server对象类型为 // "org.apache.catalina.Server" digester.addSetNext("Server", "setServer", "org.apache.catalina.Server"); // 遇到Server/GlobalNamingResources模式时,创建 // org.apache.catalina.deploy.NamingResources实例 digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResources"); // 设置模式 Server/GlobalNamingResources 生成的对象各种属性 digester.addSetProperties("Server/GlobalNamingResources"); // 将其与Server对象关联起来,利用 Server对象的setGlobalNamingResources方法传入 // GlobalNamingResources类型实例 digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResources"); // xml规则到这里 如果存在上面 Server/GlobalNamingResources元素 到这里可定会遇到结束元素标签 // 会将该值从内部栈中移除 // 遇到 Server/Listener 必须制定 Listener实现类 digester.addObjectCreate("Server/Listener", null, // MUST be specified // in the element "className"); // 配置Listener对象属性 digester.addSetProperties("Server/Listener"); // 将Listener与Server对象关联起来通过 addLifecycleListener digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); // 同样 在这里 如果存在 Listener标签 也会遇到结束标签 然后将 Listener对象移除从内部栈中 // 遇到Server/Service模式 创建 Service digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className"); digester.addSetProperties("Server/Service"); digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service"); // 真滴累 不想在写了 如果熟悉 Digester的人其实不用再写了 如果不熟悉的话 可以参考之前写的随笔Digester digester.addObjectCreate("Server/Service/Listener", null, // MUST be // specified // in the // element "className"); digester.addSetProperties("Server/Service/Listener"); digester.addSetNext("Server/Service/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); digester.addObjectCreate("Server/Service/Connector", "org.apache.catalina.connector.http.HttpConnector"
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信