tomcat阅读第五篇 (catalina类)
上一篇我们已经分析了Digester类,有基础分析Catalina类的代码,所以这篇主要是看下Catalina类,之前我们分析Bootstrap类的时候,知道Bootstrap的类主要是通过CatalinaClassLoader加载Catalina类调用Catalina的load(arguments), start(),stop(),stopServer(),现在我们逐个方法来看。
- load(arguments)方法:看源码load(arguments)最后调用的是load方法。
1.1. load方法:
看load方法之前先对比看server.xml和createStartDigester方法
createStartDigester方法server片段,
1 long t1=System.currentTimeMillis(); 2 // Initialize the digester 3 Digester digester = new Digester(); 4 digester.setValidating(false); 5 digester.setRulesValidation(true); 6 HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>(); 7 ArrayList<String> attrs = new ArrayList<>(); 8 attrs.add("className"); 9 fakeAttributes.put(Object.class, attrs); 10 digester.setFakeAttributes(fakeAttributes); 11 digester.setUseContextClassLoader(true); 12 13 // Configure the actions we will be using 14 digester.addObjectCreate("Server", 15 "org.apache.catalina.core.StandardServer", 16 "className"); 17 digester.addSetProperties("Server"); 18 digester.addSetNext("Server", 19 "setServer", 20 "org.apache.catalina.Server");
可以看到我们之前分析的Rule,ObjectCreateRule,SetPropertiesRule,SetNextRule,当解析到<Server>的时候,会创建StandardServer,Push进Digester【ObjectCreateRule的作用】,设置<Server>的attribute到对应的StandardServer的setXXXX例如SetPort【SetPropertiesRule的作用】,会调用Catalina的方法setServer(StandardServer)【SetNextRule的作用】。这个时候Digester的内存结构是如下图
下面看load方法代码片段,最后还是调用server.init。
1 。。。。 2 //根据System参数java.io.tmpdir判断临时文件夹 3 initDirs(); 4 // 5 initNaming(); 6 7 //创建startDigester,这个我们后面重点分析 8 Digester digester = createStartDigester(); 9 10 InputSource inputSource = null; 11 InputStream inputStream = null; 12 File file = null; 13 try { 14 try { 15 //默认是config/server.xml 16 file = configFile(); 17 inputStream = new FileInputStream(file); 18 inputSource = new InputSource(file.toURI().toURL().toString()); 19 } catch (Exception e) { 20 if (log.isDebugEnabled()) { 21 log.debug(sm.getString("catalina.configFail", file), e); 22 } 23 } 24 …………… 25 26 try { 27 inputSource.setByteStream(inputStream); 28 //将catalina,push进Digester 29 digester.push(this); 30 //parse server.xml,解析过程中回调之前我们分析的rule 31 digester.parse(inputSource); 32 } catch (SAXParseException spe) { 33 log.warn("Catalina.start using " + getConfigFile() + ": " + 34 spe.getMessage()); 35 return; 36 } catch (Exception e) { 37 log.warn("Catalina.start using " + getConfigFile() + ": " , e); 38 return; 39 } 40 } finally { 41 if (inputStream != null) { 42 try { 43 inputStream.close(); 44 } catch (IOException e) { 45 // Ignore 46 } 47 } 48 } 49 //parse之后catalina已经调用setServer方法创建得到server 50 getServer().setCatalina(this); 51 getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); 52 getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); 53 54 // Stream redirection 55 initStreams(); 56 57 // Start the new server 58 try { 59 //调用server.init方法 60 getServer().init(); 61 } catch (LifecycleException e) { 62 …….. 63 }
2. start方法
1 ……… 2 3 try { 4 //调用Server的start方法 5 getServer().start(); 6 } catch (LifecycleException e) { 7 ……… 8 return; 9 } 10 11 long t2 = System.nanoTime(); 12 if(log.isInfoEnabled()) { 13 log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms"); 14 } 15 16 //注册shutdownhook,jvm退出的时候会回调这个runnable 17 if (useShutdownHook) { 18 if (shutdownHook == null) { 19 shutdownHook = new CatalinaShutdownHook(); 20 } 21 Runtime.getRuntime().addShutdownHook(shutdownHook); 22 23 ………. 24 } 25 26 if (await) { 27 await(); 28 stop(); 29 }
3. stopServer()方法
1 ……. 2 3 Server s = getServer(); 4 if (s == null) { 5 //cmd 调用shutdown.bat的时候,走这里,当为空将创建StandServer 6 Digester digester = createStopDigester(); 7 File file = configFile(); 8 try (FileInputStream fis = new FileInputStream(file)) { 9 InputSource is = 10 new InputSource(file.toURI().toURL().toString()); 11 is.setByteStream(fis); 12 digester.push(this); 13 digester.parse(is); 14 } catch (Exception e) { 15 log.error("Catalina.stop: ", e); 16 System.exit(1); 17 } 18 } else { 19 //不为空直接调用server stop,作为一个service运行的时候 20 try { 21 s.stop(); 22 } catch (LifecycleException e) { 23 log.error("Catalina.stop: ", e); 24 } 25 return; 26 } 27 28 s = getServer(); 29 if (s.getPort()>0) { 30 //给监听ServerSocket,发送SHUTDOWN信息,关闭Server 31 try (Socket socket = new Socket(s.getAddress(), s.getPort()); 32 OutputStream stream = socket.getOutputStream()) { 33 String shutdown = s.getShutdown(); 34 for (int i = 0; i < shutdown.length(); i++) { 35 stream.write(shutdown.charAt(i)); 36 } 37 stream.flush(); 38 } catch (ConnectException ce) { 39 log.error(sm.getString("catalina.stopServer.connectException", 40 s.getAddress(), 41 String.valueOf(s.getPort()))); 42 log.error("Catalina.stop: ", ce); 43 System.exit(1); 44 } catch (IOException e) { 45 log.error("Catalina.stop: ", e); 46 System.exit(1); 47 } 48 } else { 49 log.error(sm.getString("catalina.stopServer")); 50 System.exit(1); 51 }
4. Stop()方法
1 ………… 2 try { 3 Server s = getServer(); 4 LifecycleState state = s.getState(); 5 if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0 6 && LifecycleState.DESTROYED.compareTo(state) >= 0) { 7 //stop已经被调用 8 } else { 9 //调用server stop方法 10 s.stop(); 11 s.destroy(); 12 } 13 } catch (LifecycleException e) { 14 log.error("Catalina.stop", e); 15 }
总结:catalina类中,load方法调用的时候会将server.xml里面的配置信息通过Digester解析成对象,分析到目前为止可以知道的对象是Catalina->Server,start方法调用server.start同时注册shutdownHook,stopServer方法当双击ShutDown.bat来关闭tomcat的时候,将发送SHUTDOWN给正在运行的Server的ServerSocket,当tomcat作为一个service运行的时候,会直接调用Server的stop方法停止tomcat