在前面的文章中,已經(jīng)學(xué)會(huì)了如何通過(guò)實(shí)例化一個(gè)連接器和容器來(lái)獲得一個(gè)servlet容器,并將連接器和容器相關(guān)聯(lián);但在前面的文章中只有一個(gè)連接器可用,該連接器服務(wù)8080端口上的HTTP請(qǐng)求,無(wú)法添加另一個(gè)連接器來(lái)服務(wù)諸如HTTPS之類的其他請(qǐng)求;此外,在前面的文章中的應(yīng)用程序中有些缺憾,即缺少一種啟動(dòng)/關(guān)閉servlet容器的機(jī)制。
org.apache.catalina.Server接口的實(shí)例表示Catalina的整個(gè)servlet引擎,囊括了所有的組件;它使用一個(gè)優(yōu)雅的方式來(lái)啟動(dòng)/關(guān)閉整個(gè)系統(tǒng),不需要再對(duì)連接器和容器分別啟動(dòng)/關(guān)閉(當(dāng)啟動(dòng)Server組件時(shí),它會(huì)啟動(dòng)其中所有的組件,然后它就無(wú)限期地等待關(guān)閉命令;如果想要關(guān)閉系統(tǒng),可以向指定端口發(fā)送一條關(guān)閉命令,Server組件接收到關(guān)閉命令后,就會(huì)關(guān)閉其中所有的組件)。
Server組件使用了另一個(gè)組件(即Service組件)來(lái)包含其他組件,如一個(gè)容器組件和一個(gè)或多個(gè)連接器組件。
下面是Server接口的定義
public interface Server { public String getInfo(); public NamingResources getGlobalNamingResources(); public void setGlobalNamingResources(NamingResources globalNamingResources); public int getPort(); public void setPort( int port); public String getShutdown(); public void setShutdown(String shutdown); public void addService(Service service); public void await(); public Service findService(String name); public Service[] findServices(); public void removeService(Service service); public void initialize() throws LifecycleException; }
shutdown屬性保存了必須發(fā)送給Server實(shí)例用來(lái)關(guān)閉整個(gè)系統(tǒng)的關(guān)閉命令,port屬性定義了Server組件會(huì)從哪個(gè)端口獲取關(guān)閉命令,可以調(diào)用addService()方法為Server組件添加Service組件,或通過(guò)removeService()方法刪除某個(gè)Service組件,findService()方法將會(huì)返回添加到該Server組件中的所有Service組件,initialize()方法包含在系統(tǒng)啟動(dòng)前要執(zhí)行的一些代碼
?org.apache.catalina.core.StandardServer類是Server接口的標(biāo)準(zhǔn)實(shí)現(xiàn),介紹這個(gè)類是因?yàn)槲覀儗?duì)其中的關(guān)閉機(jī)制比較感興趣,而這也是這個(gè)類中最重要的特性;該類的許多方法都與新server.xml文件中的服務(wù)器配置的存儲(chǔ)相關(guān),但這并不是本文的重點(diǎn)。
一個(gè)Server組件可以有多個(gè)Service組件,StandardServer類提供了addService()方法、 removeService()方法和findServices()方法的實(shí)現(xiàn)
StandardServer類有四個(gè)與生命周期相關(guān)的方法,分別是initialize()方法、 start()方法、 stop()方法和await()方法,就像其他組件一樣,可以初始化并啟動(dòng)Server組件,然后調(diào)用await()方法及stop()方法。調(diào)用await()方法后會(huì)一直阻塞住,直到它總8085端口上接收到關(guān)閉命令。當(dāng)await()方法返回時(shí),會(huì)運(yùn)行stop()方法來(lái)關(guān)閉其下的所有子組件。
Server實(shí)例的initialize()方法用于初始化添加到其中的Service組件,下面是tomcat4中StandardServer類中initialize()方法的實(shí)現(xiàn)
public void initialize() throws LifecycleException { if (initialized) throw new LifecycleException ( sm.getString( "standardServer.initialize.initialized" )); initialized = true ; // Initialize our defined Services for ( int i = 0; i < services.length; i++ ) { services[i].initialize(); } }
start()方法用于啟動(dòng)Server組件,在StandardServer類的start()方法的實(shí)現(xiàn)中,它會(huì)啟動(dòng)其所有的Service組件,這些Service組件它們逐個(gè)啟動(dòng)所有其他的組件,如連接器組件和servlet容器。
public void start() throws LifecycleException { // Validate and update our current component state if (started) throw new LifecycleException (sm.getString( "standardServer.start.started" )); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null ); lifecycle.fireLifecycleEvent(START_EVENT, null ); started = true ; // Start our defined Services synchronized (services) { for ( int i = 0; i < services.length; i++ ) { if (services[i] instanceof Lifecycle) ((Lifecycle) services[i]).start(); } } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null ); }
stop()方法用于關(guān)閉Server組件
public void stop() throws LifecycleException { // Validate and update our current component state if (! started) throw new LifecycleException (sm.getString( "standardServer.stop.notStarted" )); // 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 ); }
await()方法負(fù)責(zé)停止整個(gè)tomcat部署的機(jī)制
/** * Wait until a proper shutdown command is received, then return. */ public void await() { // Set up a server socket to wait on ServerSocket serverSocket = null ; try { serverSocket = new ServerSocket(port, 1 , InetAddress.getByName( "127.0.0.1" )); } catch (IOException e) { System.err.println( "StandardServer.await: create[" + port + "]: " + e); e.printStackTrace(); System.exit( 1 ); } // Loop waiting for a connection and a valid command while ( true ) { // Wait for the next connection Socket socket = null ; InputStream stream = null ; try { socket = serverSocket.accept(); socket.setSoTimeout( 10 * 1000); // Ten seconds stream = socket.getInputStream(); } catch (AccessControlException ace) { System.err.println( "StandardServer.accept security exception: " + ace.getMessage()); continue ; } catch (IOException e) { System.err.println( "StandardServer.await: accept: " + e); e.printStackTrace(); System.exit( 1 ); } // Read a set of characters from the socket StringBuffer command = new StringBuffer(); int expected = 1024; // Cut off to avoid DoS attack while (expected < shutdown.length()) { if (random == null ) random = new Random(System.currentTimeMillis()); expected += (random.nextInt() % 1024 ); } while (expected > 0 ) { int ch = -1 ; try { ch = stream.read(); } catch (IOException e) { System.err.println( "StandardServer.await: read: " + e); e.printStackTrace(); ch = -1 ; } if (ch < 32) // Control character or EOF terminates loop break ; command.append(( char ) ch); expected -- ; } // Close the socket now that we are done with it try { socket.close(); } catch (IOException e) { ; } // Match against our command string boolean match = command.toString().equals(shutdown); if (match) { break ; } else System.err.println( "StandardServer.await: Invalid command '" + command.toString() + "' received" ); } // Close the server socket and return try { serverSocket.close(); } catch (IOException e) { ; } }
await()方法創(chuàng)建一個(gè)ServerSocket對(duì)象,監(jiān)聽8085端口,并在while循環(huán)中調(diào)用它的accept()方法,擋在指定端口上接收到消息時(shí),才會(huì)從accept()方法中返回,然后將接收到的消息與關(guān)閉命令的字符串相比較,相同的話就跳出while循環(huán),關(guān)閉ServerSocket,否則會(huì)再次循環(huán),繼續(xù)等待消息。
Service組件是org.apache.catalina.Service接口的實(shí)例,一個(gè)Service組件可以有一個(gè)servlet容器和多個(gè)連接器實(shí)例,可以自由地把連接器實(shí)例添加到Service組件中,所有的連接器都會(huì)與該servlet容器相關(guān)聯(lián)
下面是Service接口的定義
public interface Service { public Container getContainer(); public void setContainer(Container container); public String getInfo(); public String getName(); public void setName(String name); public Server getServer(); public void setServer(Server server); public void addConnector(Connector connector); public Connector[] findConnectors(); public void removeConnector(Connector connector); public void initialize() throws LifecycleException; }
org.apache.catalina.core.StandardService類是Service接口的標(biāo)準(zhǔn)實(shí)現(xiàn),StandardService類的initialize()方法用于初始化添加到其中的所有連接器;此外,還實(shí)現(xiàn)了org.apache.catalina.Lifecycle接口,它的start()方法用于啟動(dòng)servlet容器和所有連接器
StandardService實(shí)例中有兩種組件,分別是連接器和servlet容器,其中servlet容器只有一個(gè),而連接器則可以有多個(gè),多個(gè)連接器使tomcat可以為多種不同的請(qǐng)求協(xié)議提供服務(wù)。例如,一個(gè)連接器處理HTTP請(qǐng)求,而另一個(gè)可以處理HTTPS請(qǐng)求。
StandardService類使用變量container來(lái)指向一個(gè)Container接口的實(shí)例,使用數(shù)組connectors來(lái)保存所有連接器的引用
private Container container = null;
private Connector connectors[] = new Connector[0];
需要調(diào)用setContainer()方法將servlet容器與Service組件相關(guān)聯(lián):
public void setContainer(Container container) { Container oldContainer = this .container; if ((oldContainer != null ) && (oldContainer instanceof Engine)) ((Engine) oldContainer).setService( null ); this .container = container; if (( this .container != null ) && ( this .container instanceof Engine)) ((Engine) this .container).setService( this ); if (started && ( this .container != null ) && ( this .container instanceof Lifecycle)) { try { ((Lifecycle) this .container).start(); } catch (LifecycleException e) { ; } } synchronized (connectors) { for ( int i = 0; i < connectors.length; i++ ) connectors[i].setContainer( this .container); } if (started && (oldContainer != null ) && (oldContainer instanceof Lifecycle)) { try { ((Lifecycle) oldContainer).stop(); } catch (LifecycleException e) { ; } } // Report this property change to interested listeners support.firePropertyChange("container", oldContainer, this .container); }
與Service組件相關(guān)聯(lián)的servlet容器的實(shí)例將被傳給每個(gè)連接器對(duì)象的setContainer()方法,這樣在Service組件中就可以形成每個(gè)連接器和servlet容器之間的關(guān)聯(lián)關(guān)系。
可以調(diào)用addConnector()方法將連接器添加到Service組件中,調(diào)用removeConnector()方法將某個(gè)連接器移除
public void addConnector(Connector connector) { synchronized (connectors) { connector.setContainer( this .container); connector.setService( this ); Connector results[] = new Connector[connectors.length + 1 ]; System.arraycopy(connectors, 0, results, 0 , connectors.length); results[connectors.length] = connector; connectors = results; if (initialized) { try { connector.initialize(); } catch (LifecycleException e) { e.printStackTrace(System.err); } } if (started && (connector instanceof Lifecycle)) { try { ((Lifecycle) connector).start(); } catch (LifecycleException e) { ; } } // Report this property change to interested listeners support.firePropertyChange("connector", null , connector); } }
在上面方法中,會(huì)初始化并啟動(dòng)添加到其中的連接器。
public void removeConnector(Connector connector) { synchronized (connectors) { int j = -1 ; for ( int i = 0; i < connectors.length; i++ ) { if (connector == connectors[i]) { j = i; break ; } } if (j < 0 ) return ; if (started && (connectors[j] instanceof Lifecycle)) { try { ((Lifecycle) connectors[j]).stop(); } catch (LifecycleException e) { ; } } connectors[j].setContainer( null ); connector.setService( null ); int k = 0 ; Connector results[] = new Connector[connectors.length - 1 ]; for ( int i = 0; i < connectors.length; i++ ) { if (i != j) results[k ++] = connectors[i]; } connectors = results; // Report this property change to interested listeners support.firePropertyChange("connector", connector, null ); } }
與生命周期相關(guān)的方法包括從Lifecycle接口中實(shí)現(xiàn)的start()方法和stop()方法,在加上initialize()方法,其中initialize()方法會(huì)調(diào)用該Service組件中所有連接器的上initialize()方法:
public void initialize() throws LifecycleException { if (initialized) throw new LifecycleException ( sm.getString( "standardService.initialize.initialized" )); initialized = true ; // Initialize our defined Connectors synchronized (connectors) { for ( int i = 0; i < connectors.length; i++ ) { connectors[i].initialize(); } } }
start()方法負(fù)責(zé)啟動(dòng)被添加到該Service組件中的連接器和servlet容器:
public void start() throws LifecycleException { // Validate and update our current component state if (started) { throw new LifecycleException (sm.getString( "standardService.start.started" )); } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null ); System.out.println (sm.getString( "standardService.start.name", this .name)); lifecycle.fireLifecycleEvent(START_EVENT, null ); started = true ; // Start our defined Container first if (container != null ) { synchronized (container) { if (container instanceof Lifecycle) { ((Lifecycle) container).start(); } } } // Start our defined Connectors second synchronized (connectors) { for ( int i = 0; i < connectors.length; i++ ) { if (connectors[i] instanceof Lifecycle) ((Lifecycle) connectors[i]).start(); } } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null ); }
stop()方法用于關(guān)閉與該Service組件相關(guān)聯(lián)的servlet容器和所有連接器
public void stop() throws LifecycleException { // Validate and update our current component state if (! started) { throw new LifecycleException (sm.getString( "standardService.stop.notStarted" )); } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null ); lifecycle.fireLifecycleEvent(STOP_EVENT, null ); System.out.println (sm.getString( "standardService.stop.name", this .name)); started = false ; // Stop our defined Connectors first synchronized (connectors) { for ( int i = 0; i < connectors.length; i++ ) { if (connectors[i] instanceof Lifecycle) ((Lifecycle) connectors[i]).stop(); } } // Stop our defined Container second if (container != null ) { synchronized (container) { if (container instanceof Lifecycle) { ((Lifecycle) container).stop(); } } } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null ); }
在前面文章的應(yīng)用程序中,通過(guò)按某個(gè)鍵或強(qiáng)制中斷的方式關(guān)閉servlet容器,Stopper類提供了一種更優(yōu)雅的方式來(lái)關(guān)閉Catalina服務(wù)器;此外,它也保證了所有的生命周期組件的stop()方法都能夠調(diào)用。
public class Stopper { public static void main(String[] args) { // the following code is taken from the Stop method of // the org.apache.catalina.startup.Catalina class int port = 8005 ; try { Socket socket = new Socket("127.0.0.1" , port); OutputStream stream = socket.getOutputStream(); String shutdown = "SHUTDOWN" ; for ( int i = 0; i < shutdown.length(); i++ ) stream.write(shutdown.charAt(i)); stream.flush(); stream.close(); socket.close(); System.out.println( "The server was successfully shut down." ); } catch (IOException e) { System.out.println( "Error. The server has not been started." ); } } }
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創(chuàng)?
轉(zhuǎn)載請(qǐng)注明出處 博客園 刺猬的溫馴?
本人郵箱: ? chenying998179 # 163.com ( #改為@ )
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
