在前面的文章中,已经学会了如何通过实例化一个连接器和容器来获得一个servlet容器,并将连接器和容器相关联;但在前面的文章中只有一个连接器可用,该连接器服务8080端口上的HTTP请求,无法添加另一个连接器来服务诸如HTTPS之类的其他请求;此外,在前面的文章中的应用程序中有些缺憾,即缺少一种启动/关闭servlet容器的机制。
org.apache.catalina.Server接口的实例表示Catalina的整个servlet引擎,囊括了所有的组件;它使用一个优雅的方式来启动/关闭整个系统,不需要再对连接器和容器分别启动/关闭(当启动Server组件时,它会启动其中所有的组件,然后它就无限期地等待关闭命令;如果想要关闭系统,可以向指定端口发送一条关闭命令,Server组件接收到关闭命令后,就会关闭其中所有的组件)。
Server组件使用了另一个组件(即Service组件)来包含其他组件,如一个容器组件和一个或多个连接器组件。
下面是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属性保存了必须发送给Server实例用来关闭整个系统的关闭命令,port属性定义了Server组件会从哪个端口获取关闭命令,可以调用addService()方法为Server组件添加Service组件,或通过removeService()方法删除某个Service组件,findService()方法将会返回添加到该Server组件中的所有Service组件,initialize()方法包含在系统启动前要执行的一些代码
org.apache.catalina.core.StandardServer类是Server接口的标准实现,介绍这个类是因为我们对其中的关闭机制比较感兴趣,而这也是这个类中最重要的特性;该类的许多方法都与新server.xml文件中的服务器配置的存储相关,但这并不是本文的重点。
一个Server组件可以有多个Service组件,StandardServer类提供了addService()方法、 removeService()方法和findServices()方法的实现
StandardServer类有四个与生命周期相关的方法,分别是initialize()方法、 start()方法、 stop()方法和await()方法,就像其他组件一样,可以初始化并启动Server组件,然后调用await()方法及stop()方法。调用await()方法后会一直阻塞住,直到它总8085端口上接收到关闭命令。当await()方法返回时,会运行stop()方法来关闭其下的所有子组件。
Server实例的initialize()方法用于初始化添加到其中的Service组件,下面是tomcat4中StandardServer类中initialize()方法的实现
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()方法用于启动Server组件,在StandardServer类的start()方法的实现中,它会启动其所有的Service组件,这些Service组件它们逐个启动所有其他的组件,如连接器组件和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()方法用于关闭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()方法负责停止整个tomcat部署的机制
/** * 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()方法创建一个ServerSocket对象,监听8085端口,并在while循环中调用它的accept()方法,挡在指定端口上接收到消息时,才会从accept()方法中返回,然后将接收到的消息与关闭命令的字符串相比较,相同的话就跳出while循环,关闭ServerSocket,否则会再次循环,继续等待消息。
Service组件是org.apache.catalina.Service接口的实例,一个Service组件可以有一个servlet容器和多个连接器实例,可以自由地把连接器实例添加到Service组件中,所有的连接器都会与该servlet容器相关联
下面是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接口的标准实现,StandardService类的initialize()方法用于初始化添加到其中的所有连接器;此外,还实现了org.apache.catalina.Lifecycle接口,它的start()方法用于启动servlet容器和所有连接器
StandardService实例中有两种组件,分别是连接器和servlet容器,其中servlet容器只有一个,而连接器则可以有多个,多个连接器使tomcat可以为多种不同的请求协议提供服务。例如,一个连接器处理HTTP请求,而另一个可以处理HTTPS请求。
StandardService类使用变量container来指向一个Container接口的实例,使用数组connectors来保存所有连接器的引用
private Container container = null;
private Connector connectors[] = new Connector[0];
需要调用setContainer()方法将servlet容器与Service组件相关联:
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组件相关联的servlet容器的实例将被传给每个连接器对象的setContainer()方法,这样在Service组件中就可以形成每个连接器和servlet容器之间的关联关系。
可以调用addConnector()方法将连接器添加到Service组件中,调用removeConnector()方法将某个连接器移除
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); } }
在上面方法中,会初始化并启动添加到其中的连接器。
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 ); } }
与生命周期相关的方法包括从Lifecycle接口中实现的start()方法和stop()方法,在加上initialize()方法,其中initialize()方法会调用该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()方法负责启动被添加到该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()方法用于关闭与该Service组件相关联的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 ); }
在前面文章的应用程序中,通过按某个键或强制中断的方式关闭servlet容器,Stopper类提供了一种更优雅的方式来关闭Catalina服务器;此外,它也保证了所有的生命周期组件的stop()方法都能够调用。
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系本人原创
转载请注明出处 博客园 刺猬的温驯
本人邮箱: chenying998179 # 163.com ( #改为@ )