回顾
在前面的章节中,你已经知道了如何有一个servlet容器,通过一个连接器和一个容器并使之彼此关联。一旦一个连接器可以使用,接下来就是在端口号8080上接受HTTP请求。你不会再需要一个连接器来服务HTTPS请求。
另外,与前面章节相关联的应用程序都缺少一件事情:一个好的启动和关闭servlet容器的机制。在这一章节,我们将看另外两个组件,提供了这两种机制:server和service
Server
org.apache.catalina.Server接口表示了整个Catalina servlet容器和所有其他组件。一个server是非常重要的因为他提供了一个优雅的机制来启动和关闭整个系认。不必再分开启动连接器和容器。
这里是启动和停止机制如何工作的。当你启动一个server,在里面启动了所有的组件。之后就等待一个关闭指令。如果你想关闭系统,你就发送一个指定端口的关闭指令。这个将发送到server并且其如果接受到正确的关闭指令,就会关闭所有的组件。
一个服务器使用另一个组件,一个service,来保持组件比如一个容器和一个或者更多的连接器。
Server接口代码如下:
shutdown属性持有一个字符串必须发送到一个服务器的实例来关闭它。port属性定义了一个服务器等待关闭命令的端口。你可以增加service对象给server,通过调用它的addService方法。稍后,通过调用removeService方法来移除一个service。findServices方法返回现有server中的所有service。initialize方法包含了必须在启动前执行的代码。
StandardServer
org.apache.catalian.core.StandardServer类是一个server的标准实现。我们要注意的是这个类提供的关闭机制,也是这个类中的最重要的功能。许多方法与server 配置相关的都存储在一个server xml文件中,但是这里不讨论。
一个服务器可以有一个或者多个services,StandardServer提供了addService,removeService和findService的实现。
与StandardServer的生命周期相关的四个方法是:initialize,start,stop和await。就像其他组件,你启动和开启一个server,紧跟着stop方法需要调用await方法。这个await方法不返回直到在指定端口上接受一个关闭命令。当await方法返回的时候,运行stop方法来关闭所有的子组件。在这一章节相关联的应用中,你将学会如何实现关闭的机制。
the initialize method
initialize方法用来初始化添加到这个server实例的services,Tomcat 4中的initialize方法代码如下:
注意这个initialize方法指定了一个名字为initialized的布尔,来避免这个服务被初始化两次。在Tomcat 5中,虽然initialize方法相似,但是了也包含了与JMX相关联的核心代码。stop方法不重置initialized的值,这样如果服务器关闭再开启后,它的initialize方法不会再被调用。
the start method
调用start方法来启动服务。在StandardServer中的这个方法的实现启动了所有的服务,其中轮流启动所有其他组件,比如连接器和容器。清单14.3显示了start方法。
start方法指定了一个start 布尔变量来阻止一个服务器开启两次。stop方法重置这个值。
the stop method
stop方法停止这个服务。清单14.4显示了这个方法。
调用stop方法停止所有的服务并重置started值。这样服务器可以再次启动。
the await method
await方法为整个tomcat的部署的停止机制负责。代码清单如下:
public void await(){
//Set up a server 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.accpet();
socket.setTimeout(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方法创建了一个ServerSockt对象,并指定了端口号,之后在一个while循环中调用accept方法。如果在指定的端口上有来到的消息这个accep方法才会返回。每条消息将与shutdown命令匹配。如果匹配了,就跳出while循环并关闭serverSocket。如果不匹配,控制始终停留在while循环中直到另一个消息的到来。