在前面的文章中,已经学会了如何通过实例化一个连接器和容器来获得一个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 (#改为@)

本文链接http://www.cnblogs.com/chenying99/p/3249158.html

How Tomcat Works(十七)的更多相关文章

  1. 攻城狮在路上(肆)How tomcat works(零) 前言说明

    最近几篇是关于How tomcat works一书的读书笔记. 通过数个章节逐渐实现一个tomcat的功能. 源码下载地址:http://zhidao.baidu.com/share/7007af0f ...

  2. How Tomcat works — 四、tomcat启动(3)

    上一节说到StandardService负责启动其子组件:container和connector,不过注意,是有先后顺序的,先启动container,再启动connector,这一节先来看看conta ...

  3. How Tomcat Works(十四)补充

    在How Tomcat Works(十四)中,本人并没有对javax.servlet.Filter及javax.servlet.FilterChain做详细的描述,本文在这里做一下补充 FilterC ...

  4. How Tomcat Works(十八)

    在前面的文章中,如果我们要启动tomcat容器,我们需要使用Bootstrap类来实例化连接器.servlet容器.Wrapper实例和其他组件,然后调用各个对象的set方法将它们关联起来:这种配置应 ...

  5. How Tomcat Works(十六)

    本文接下来会介绍Host容器和Engine容器,在tomcat的实际部署中,总是会使用一个Host容器:本文介绍Host接口和Engine接口及其相关类 Host容器是org.apache.catal ...

  6. How Tomcat Works(十五)

    本文接下来分析Context容器,Context容器实例表示一个具体的Web应用程序,其中包括一个或多个Wrapper实例:不过Context容器还需要其他的组件支持,典型的如载入器和Session管 ...

  7. How Tomcat Works(十四)

    我们已经知道,在tomcat中有四种类型的servlet容器,分别为Engine.Host.Context 和Wrapper,本文接下来对tomcat中Wrapper接口的标准实现进行说明. 对于每个 ...

  8. How Tomcat Works(十三)

    本文分析tomcat容器的安全管理,servlet技术支持通过配置部署描述器(web.xml文件)来对受限内容进行访问控制:servlet容器是通过一个名为验证器的阀来支持安全限制的,当servlet ...

  9. How Tomcat Works(十二)

    tomcat容器通过一个称为Session管理器的组件来管理建立的Session对象,该组件由org.apache.catalina.Manager接口表示:Session管理器必须与一个Contex ...

随机推荐

  1. [Linux] Git: 基本使用

    Git 属于分布式版本控制系统( Distributed Version Control System,简称 DVCS )客户端并不只提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来.这么一 ...

  2. Mysql管理工具SQLyog

    SQLyog_Enterprise 用户名:yunjian 注册码:81f43d3dd20872b6   http://download.csdn.net/detail/shel_lee/585361 ...

  3. 【基础数学】质数,约数,分解质因数,GCD,LCM

    1.质数: 质数(prime number)又称素数,有无限个.一个大于1的自然数,除了1和它本身外,不能整除以其他自然数(质数),换句话说就是该数除了1和它本身以外不再有其他的因数. 2.约数: 如 ...

  4. 如何在vmware上创建共享磁盘

    1.先在你本机的vmware安装目录上找到 vmware-vdiskmanager.exe 执行文件. 我的目录是 d:\vmware\vmware-vdiskmanager.exe 再用cmd终端( ...

  5. webpack的学习

    什么是webpack? 他有什么优点? 首先对于很多刚接触webpack人来说,肯定会问webpack是什么?它有什么优点?我们为什么要使用它?带着这些问题,我们来总结下如下: Webpack是前端一 ...

  6. 如何使用 Java 中的数组

    Java 中操作数组只需要四个步骤: 1. 声明数组 语法:  数据类型[ ] 数组名: 或者   数据类型 数组名[ ]: 其中,数组名可以是任意合法的变量名,如: 2. 分配空间 简单地说,就是指 ...

  7. 关于浮动-float

    1.存在浏览器兼容问题:js代码 2.对于这种存在浏览器兼容问题的问题,我们可以绕开兼容性问题,先在css样式写好,然后通过该变className 3.学习的博客 https://paran.io/c ...

  8. php生成百度站点地图sitemap.xml

    <?php header("Content-type:text/html;charset=utf-8"); //php生成百度站点地图sitemap.xml //http:/ ...

  9. python27+django1.9添加api

    我们进入Python的交互 shell 并使用Django提供的API.要进入Python shell,使用python manage.py shell 使用这个而不是简单的输入"pytho ...

  10. margin四个元素的顺序

    如果margin给的是四个值比如:margin:0px 0px 0px 0px;代表:margin: top right bottom left代表从上右下左,顺时针方向.如果margin给的是三个值 ...