Tomcat中的服务器组件和 服务组件

  开始学习Tocmat时,都是学习如何通过实例化一个连接器 和 容器 来获得一个Servlet容器,并将连接器  和 servlet容器相互关联,但是之前学习的都只有一个连接器可以使用,该连接器服务8080端口上的HTTP请求,无法添加另一个连接器来服务 诸如 HTTPS之类的其他请求,而且前面所有学的示例,都缺少一种启动或者 关闭servlet容器的机制,那么下面学习一下提供这两种机制的特性的组件,分别是服务器组件 和 服务组件。

  服务器组件.

  org.apahce.catalina.Server接口的实例 表示Catalina的整个Servlet引擎,囊括了所有的组件,服务器组件是非常有用的,因为它使用了一种方法很容易的就启动/关闭整个系统,不需要对连接器 和 servlet容器 分别操作。

下面来说明一下启动/关闭机制的具体工作原理,当启动服务器组件是,它会启动它囊括的所有组件,然后它就无期限的等待关闭命令,如果想要关闭系统,可以向指定的端口发送一条关闭命令,服务器组件接收到关闭命令之后,就会关闭其中所有的组件。

  服务器组件使用了另一个组件(即服务组件)来包含其他组件,如一个容器组件 和 一个或者多个连接器组件,

下面先来看一下服务器组件的接口定义

  1 package org.apache.catalina;
  2 
  3 import org.apache.catalina.deploy.NamingResources;
  4 
  5 /**
  6  * 
  7  * <p>
  8  * <b>Title:Server.java</b>
  9  * </p>
 10  * <p>
 11  * Copyright:ChenDong 2018
 12  * </p>
 13  * <p>
 14  * Company:仅学习时使用
 15  * </p>
 16  * <p>
 17  * 类功能描述:Server元素表示整个Catalina
 18  * servlet容器。它的属性代表了servlet容器的整体特性。服务器可以包含一个或多个服务以及顶级命名资源集。
 19  * 
 20  * 
 21  * 
 22  * 通常,该接口的实现还将实现生命周期,这样当调用start()和stop()方法时,所有定义的服务也会启动或停止。
 23  * 
 24  * 
 25  * 
 26  * 在这两者之间,实现必须打开端口属性指定的端口号上的服务器套接字。当接受连接时,读取第一行并与指定的关闭命令进行比较。如果命令匹配,则启动服务器关闭。
 27  * </p>
 28  * 
 29  * @author 
 30  * @date 2018年12月17日 下午8:04:51
 31  * @version 1.0
 32  */
 33 
 34 public interface Server {
 35 
 36     // ------------------------------------------------------------- Properties
 37 
 38     /**
 39      * 
 40      * 
 41      * <p>
 42      * Title: getInfo
 43      * </p>
 44      * 
 45      * @date 2018年12月17日 下午8:05:15
 46      * 
 47      *       <p>
 48      *       功能描述:返回该类的实现信息
 49      *       </p>
 50      * 
 51      * @return
 52      */
 53     public String getInfo();
 54 
 55     /**
 56      * 返回全局命名资源。
 57      */
 58     public NamingResources getGlobalNamingResources();
 59 
 60     /**
 61      * 
 62      * 设置全局命名资源
 63      * 
 64      * @param namingResources
 65      *            新的全局命名资源
 66      */
 67     public void setGlobalNamingResources(NamingResources globalNamingResources);
 68 
 69     /**
 70      * 
 71      * 
 72      * <p>
 73      * Title: getPort
 74      * </p>
 75      * 
 76      * @date 2018年12月17日 下午8:06:42
 77      * 
 78      *       <p>
 79      *       功能描述:返回我们用来监听关闭命令的端口号
 80      *       </p>
 81      * 
 82      * @return
 83      */
 84     public int getPort();
 85 
 86     /**
 87      * 
 88      * 
 89      * <p>
 90      * Title: setPort
 91      * </p>
 92      * 
 93      * @date 2018年12月17日 下午8:07:27
 94      * 
 95      *       <p>
 96      *       功能描述:设置我们用来监听关闭命令的端口号
 97      *       </p>
 98      * 
 99      * @param port
100      *            新的监听端口号
101      */
102     public void setPort(int port);
103 
104     /**
105      * 返回我们正在等待的关闭命令字符串。
106      * 
107      * 
108      */
109     public String getShutdown();
110 
111     /**
112      * 设置我们正在等待的关闭命令字符串
113      *
114      * @param shutdown
115      *            新的关闭命令字符串
116      */
117     public void setShutdown(String shutdown);
118 
119     // --------------------------------------------------------- Public Methods
120 
121     /**
122      * 向定义的服务集添加新服务。
123      *
124      * @param service
125      *            要被添加的新服务
126      */
127     public void addService(Service service);
128 
129     /**
130      * 等待直到收到正确的关闭命令,然后返回。
131      */
132     public void await();
133 
134     /**
135      * 
136      * 
137      * <p>
138      * Title: findService
139      * </p>
140      * 
141      * @date 2018年12月17日 下午8:10:42
142      * 
143      *       <p>
144      *       功能描述:返回指定的服务(如果存在);否则返回<code>null</code>
145      *       </p>
146      * 
147      * @param name
148      * @return
149      */
150     public Service findService(String name);
151 
152     /**
153      * 
154      * 
155      * <p>
156      * Title: findServices
157      * </p>
158      * 
159      * @date 2018年12月17日 下午8:11:18
160      * 
161      *       <p>
162      *       功能描述:返回在此服务器中定义的服务集。
163      *       </p>
164      * 
165      * @return
166      */
167     public Service[] findServices();
168 
169     /**
170      * 
171      * 
172      * <p>
173      * Title: removeService
174      * </p>
175      * 
176      * @date 2018年12月17日 下午8:12:02
177      * 
178      *       <p>
179      *       功能描述:从该服务器 定义的服务集中删除指定的服务
180      *       </p>
181      * 
182      * @param service
183      */
184     public void removeService(Service service);
185 
186     /**
187      * 
188      * 
189      * <p>
190      * Title: initialize
191      * </p>
192      * 
193      * @date 2018年12月17日 下午8:12:58
194      * 
195      *       <p>
196      *       功能描述:调用启动前的初始化。这用于允许连接器绑定到Unix操作环境中的受限端口。当然只要是在系统执行前 你爱写啥写啥
197      *       </p>
198      * 
199      * @throws LifecycleException
200      */
201     public void initialize() throws LifecycleException;
202 }

  shutdown属性保存了必须发送给Server实例用来关闭整个系统的关闭命令,port属性定义了服务器组件会从哪一个端口获取关闭命令,可以调用其addService方法为服务器组件添加服务组件,或者通过removeService方法来删除某个服务组件,findService方法返回添加到此服务器组件中的服务集合,initialize方法包含了在系统启动之前要执行的一些代码

  在Catalina中 server同样有其标准实现类,StandardServer

StandardServer类

  org.apahce.catalina.core.StandardServer类是Server接口的标准实现,为什么要说下这个类,主要是对其中的关闭机制 也就上文提到的一个关闭所有组件的机制感兴趣,而这也是这个类最重要的特性,该类中的许多方法都与新server.xml文件中的服务器配置的存储相关,但这些并不是下面要说的重点,

  一个服务器组件可以有0个或者多个服务组件,StandardServer类提供了addService方法、removeService方法、和findService方法的实现。

  StandardServer类有四个与生命周期相关的方法,分别是initialize方法、start方法、stop方法,就像其他组件一样,可以初始化并且启动服务器组件。也可以调用 await方法 和 stop方法,调用await方法会一直阻塞住,直到它从定义 的 8005;端口(也可以自己配置其他端口)上接收到关闭命令,当await方法返回的时候,会运行stop方法来关闭其下的所有服务组件。

下面会分别讨论上面的 四个与生命周期相关的方法。

initialize方法

  方法主要是用于初始化添加到StandardServer的服务组件,

 1 /**
 2      * 调用启动前的初始化。
 3      */
 4     public void initialize() throws LifecycleException {
 5         if (initialized)
 6             throw new LifecycleException(sm.getString("standardServer.initialize.initialized"));
 7         initialized = true;
 8 
 9         // 初始化我们定义的服务集中的每一个服务
10         for (int i = 0; i < services.length; i++) {
11             services[i].initialize();
12         }
13     }

start方法

  start方法用于启动服务器组件,在StandardServer类的start方法的实现中,它会启动器所有的服务组件,逐个启动所有的组件,如连接器组件 和 Servlet容器,

 1 /**
 2      * 该服务器组件的启动方法,它会启动器所有的服务组件,逐个启动所有的组件,
 3      */
 4 
 5     public void start() throws LifecycleException {
 6 
 7         // 验证和更新当前组件状态 如果已经启动直接报错
 8         if (started)
 9             throw new LifecycleException(sm.getString("standardServer.start.started"));
10         // 向监听器发送 BEFORE_START_EVENT事件 与 START_EVENT事件
11         lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
12 
13         lifecycle.fireLifecycleEvent(START_EVENT, null);
14         started = true;
15 
16         // 逐个启动当前定义的服务集合中的每一个服务
17         synchronized (services) {
18             for (int i = 0; i < services.length; i++) {
19                 if (services[i] instanceof Lifecycle)
20                     ((Lifecycle) services[i]).start();
21             }
22         }
23 
24         // 向监听器发送 AFTER_START_EVENT事件
25         lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
26 
27     }

StandardServer为了防止服务器组件重复启动,在start中设置为true 在stop方法中 重置为false。

stop方法

  stop方法用于关闭服务器组件,在方法中会关闭服务器中的额所有组件

 1     /**
 2      * 优雅地终止该组件的公共方法的主动使用。此方法应该是调用该组件的给定实例的最后一个方法。
 3      * 它还应该向任何注册的监听器发送STOP_EVENT类型的停止事
 4      */
 5     public void stop() throws LifecycleException {
 6 
 7         // 如果还没有启动则 直接抛出错误
 8         if (!started)
 9             throw new LifecycleException(sm.getString("standardServer.stop.notStarted"));
10 
11         // 向监听器发送监听事件
12         lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
13 
14         lifecycle.fireLifecycleEvent(STOP_EVENT, null);
15         // 将started标志 置为false 这样才可以再次启动
16         started = false;
17 
18         // 逐个停止我们定义服务集中的每一个服务
19         for (int i = 0; i < services.length; i++) {
20             if (services[i] instanceof Lifecycle)
21                 ((Lifecycle) services[i]).stop();
22         }
23 
24         // 发送监听事件
25         lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
26 
27     }

调用stop方法hi关闭所有的服务组件并重置布尔变量started,这样才可以再次启动服务器组件。

await方法

  await方法负责等待关闭整个Tomcat部署的命令。

 

 1     /**
 2      * 等待直到收到正确的关闭命令,然后返回。
 3      */
 4     public void await() {
 5 
 6         // 设置要等待的服务器套接字
 7         ServerSocket serverSocket = null;
 8         try {
 9             // 利用咱们自己定义的port(默认 8005 )端口号来 初始化服务器套接字
10             serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
11         } catch (IOException e) {
12             System.err.println("StandardServer.await: create[" + port + "]: " + e);
13             e.printStackTrace();
14             System.exit(1);
15         }
16 
17         // 循环等待连接和有效命令
18         while (true) {
19 
20             // 等待下一个连接
21             Socket socket = null;
22             InputStream stream = null;
23             try {
24                 socket = serverSocket.accept();//
25                 socket.setSoTimeout(10 * 1000); // 接收到Socket之后 read方法仅在十秒钟之内有效
26                                                 // 超时 将会抛出
27                                                 // java.net.SocketTimeoutException
28                 stream = socket.getInputStream();
29             } catch (AccessControlException ace) {
30                 System.err.println("StandardServer.accept security exception: " + ace.getMessage());
31                 continue;
32             } catch (IOException e) {
33                 System.err.println("StandardServer.await: accept: " + e);
34                 e.printStackTrace();
35                 System.exit(1);
36             }
37 
38             // 从套接字中读取一组字符
39             StringBuffer command = new StringBuffer();
40             int expected = 1024; // 切断以避免DoS攻击
41             while (expected < shutdown.length()) {
42                 if (random == null)
43                     random = new Random(System.currentTimeMillis());
44                 expected += (random.nextInt() % 1024);
45             }
46             while (expected > 0) {
47                 int ch = -1;
48                 try {
49                     ch = stream.read();
50                 } catch (IOException e) {
51                     System.err.println("StandardServer.await: read: " + e);
52                     e.printStackTrace();
53                     ch = -1;
54                 }
55                 if (ch < 32) // 控制字符或EOF终止循环
56                     break;
57                 command.append((char) ch);
58                 expected--;
59             }
60 
61             // 既然我们用完了,就把socket关上。
62             try {
63                 socket.close();
64             } catch (IOException e) {
65                 ;
66             }
67 
68             // 匹配命令字符串
69             boolean match = command.toString().equals(shutdown);
70             if (match) {
71                 //匹配到就 跳出循环
72                 break;
73             } else
74                 System.err.println("StandardServer.await: Invalid command '" + command.toString() + "' received");
75 
76         }
77 
78         //关闭服务器套接字并返回
79         try {
80             serverSocket.close();
81         } catch (IOException e) {
82             ;
83         }
84 
85     }

   await方法创建一个ServerSocket对象,监听8005端口,并在while循环中调用它的accpect方法挡在指定端口上接收到消息时,才会从accept方法中返回一个socket,然后将接收到的消息与关闭命令字符串做比较,相同的话就跳出循环,关闭ServerSocke,否则再次循环。接续等待消息。

Service

  服务组件是org.apche.catalina.Service接口的实例,一个服务组件可以有一个servlet容器和 一个或者多个连接器实例,可以自由的把连接器实例添加到服务组件中,所有的连接器都会与这个servlet容器相关联,

package org.apache.catalina;

/**
 * 
 * <p>
 * <b>Title:Service.java</b>
 * </p>
 * <p>
 * Copyright:ChenDong 2018
 * </p>
 * <p>
 * Company:仅学习时使用
 * </p>
 *
 * 
 * <p>
 * 服务组件是org.apache.catalina.Service接口的实例,一个服务组件可以有一个servlet容器和
 * 多个连接器实例,可以自由的把连接器实例添加到服务组件中的,所有的 连接器都会与这个servlet容器相关联
 * </p>
 * 
 * @author 
 * @date 2018年12月17日 下午8:02:49
 * @version 1.0
 */

public interface Service {

    // ------------------------------------------------------------- Properties

    /**
     * 
     * 
     * <p>
     * Title: getContainer
     * </p>
     * 
     * @date 2018年12月19日 下午7:47:23
     * 
     *       <p>
     *       功能描述: 返回处理与此服务关联的所有<code>Connectors</code> 请求的servlet容器。
     *       </p>
     * 
     * @return
     */
    public Container getContainer();

    /**
     * 
     * 
     * <p>
     * Title: setContainer
     * </p>
     * 
     * @date 2018年12月19日 下午7:48:10
     * 
     *       <p>
     *       功能描述:设置处理此服务中所有连接器的servlet容器
     *       </p>
     * 
     * @param container
     *            被设置的servlet容器
     */
    public void setContainer(Container container);

    /**
     * 
     * 
     * <p>
     * Title: getInfo
     * </p>
     * 
     * @date 2018年12月19日 下午7:49:07
     * 
     *       <p>
     *       功能描述:返回该类的实现信息
     *       </p>
     * 
     * @return
     */
    public String getInfo();

    /**
     * 返回此服务的名称.
     */
    public String getName();

    /**
     * 设置此服务的名称
     *
     * @param name
     *            新的服务名
     */
    public void setName(String name);

    /**
     * Return the <code>Server</code> with which we are associated (if any).
     */
    /**
     * 
     * 
     * <p>
     * Title: getServer
     * </p>
     * 
     * @date 2018年12月19日 下午7:51:23
     * 
     *       <p>
     *       功能描述:返回与该服务关联的服务器组件
     *       </p>
     * 
     * @return
     */
    public Server getServer();

    /**
     * 
     * 
     * <p>
     * Title: setServer
     * </p>
     * 
     * @date 2018年12月19日 下午7:52:01
     * 
     *       <p>
     *       功能描述: 设置拥有该服务的服务器组件
     *       </p>
     * 
     * @param server
     *            与该服务关联的服务器组件
     */
    public void setServer(Server server);

    // --------------------------------------------------------- Public Methods

    /**
     * 
     * 
     * <p>
     * Title: addConnector
     * </p>
     * 
     * @date 2018年12月19日 下午7:53:50
     * 
     *       <p>
     *       功能描述:向当前定义的 连接器集合 中添加新的 连接器,并将其与该服务中唯一的一个Servlet容器相关联
     *       </p>
     * 
     * @param connector
     *            添加的连接器
     */
    public void addConnector(Connector connector);

    /**
     * 
     * 
     * <p>
     * Title: findConnectors
     * </p>
     * 
     * @date 2018年12月19日 下午7:57:20
     * 
     *       <p>
     *       功能描述:查找并返回该服务中定义的连连接器集合
     *       </p>
     * 
     * @return
     */
    public Connector[] findConnectors();

    /**
     * Remove the specified Connector from the set associated from this Service.
     * The removed Connector will also be disassociated from our Container.
     *
     * @param connector
     *            The Connector to be removed
     */
    /**
     * 
     * 
     * <p>
     * Title: removeConnector
     * </p>
     * 
     * @date 2018年12月19日 下午8:00:09
     * 
     *       <p>
     *       功能描述:从此服务中的连接器集合中移除指定的连接器,并将其与服务中唯一的一个servlet容器分离
     *       </p>
     * 
     * @param connector
     */
    public void removeConnector(Connector connector);

    

    /**
     * 
     * 
     * <p>
     * Title: initialize
     * </p>
     * 
     * @date 2018年12月19日 下午8:01:45
     * 
     *       <p>
     *       功能描述:初始化方法
     *       </p>
     * 
     * @throws LifecycleException
     */
    public void initialize() throws LifecycleException;

}

在Catalina中,StandardService类是其标准实现

StandardSetvice

  org.apahce.catalina.core.StandardService类是 服务组件Service接口的标准实现,StandardService类的initialize方法用于初始化添加到其中所有的连接器,此外,StandardService类还实现了Service以及org.apache.catalina.Lifecycle声明周期接口,然后,它的start方法也可以启动连接器和所有的servlet容器。

  connector 和 container

StandardService类有两种组件,分别是连接器和 servlet容器,其中servlet容器只能有一个,而连接器可以有多个,多个连接器使Tomcat可以为多种不同的请求协议提供服务,例如,一个连接器处理HTTP请求,而另外一个连接器处理HTTPS请求。

StandardService类使用变量Container 来指向一个Container接口的实例,使用数组connectors来保存所有连接器的引用。

1 /**
2      * 保存服务中所有连接器引用的数组
3      */
4     private Connector connectors[] = new Connector[0];
5 
6     /**
7      * 保存服务中唯一的一个Servlet容器
8      */
9     private Container container = null;

  需要调用setContainer方法将servlet容器与服务组件相关联,

 1 /**
 2      * 
 3      * 设置处理此服务中所有连接器的servlet容器
 4      * 
 5      * @param container
 6      *            新的servlet容器
 7      */
 8     public void setContainer(Container container) {
 9         // 取出当前服务中唯一的servlet容器
10         Container oldContainer = this.container;
11         // 如果取出的servlet容器不为空 且为Engine级别的servlet容器
12         if ((oldContainer != null) && (oldContainer instanceof Engine))
13             // 将调用其setSevice方法 断掉与服务的关联
14             ((Engine) oldContainer).setService(null);
15         // 设置新的servlet容器
16         this.container = container;
17         // 如果新的servlet容器 不为空 且 是Engine级别的容器
18         if ((this.container != null) && (this.container instanceof Engine))
19             // 将该服务实例传入容器 与之关联
20             ((Engine) this.container).setService(this);
21         // 如果该服务已经启动 且 服务中的容器实例不为空 且 这个容器实例是一个生命周期的实现类
22         if (started && (this.container != null) && (this.container instanceof Lifecycle)) {
23             try {
24                 // 调用其start方法
25                 ((Lifecycle) this.container).start();
26             } catch (LifecycleException e) {
27                 ;
28             }
29         }
30         // 逐个调用连接器集合中的每一个连接器的setContainer方法 与服务中 的容器相挂关联
31         synchronized (connectors) {
32             for (int i = 0; i < connectors.length; i++)
33                 connectors[i].setContainer(this.container);
34         }
35         // 如果服务已经启动且旧的容器不为空 且是一个生命周期实现 则 调用旧容器的stop方法
36         if (started && (oldContainer != null) && (oldContainer instanceof Lifecycle)) {
37             try {
38                 ((Lifecycle) oldContainer).stop();
39             } catch (LifecycleException e) {
40                 ;
41             }
42         }
43 
44         // 通知属性变更监听器
45         support.firePropertyChange("container", oldContainer, this.container);
46 
47     }

  与服务组件相关联的servlet容器的实例将被传给每个连接器对象的setContainer方法中,这样在服务组件中就可以形成每个连接器和servlet容器之间的关联关系。

  可以调用addConnector方法将连接器添加到服组件中,调用removeConnectoe方法将某个连接器移除,

 1     /**
 2      * 
 3      * 
 4      * 
 5      * 添加一个新的连接器到连接器集合中 并且将之与服务中的servlet容器相关联
 6      * 
 7      * @param connector
 8      *            要被添加连接器
 9      */
10 
11     public void addConnector(Connector connector) {
12 
13         synchronized (connectors) {
14             // 添加
15             connector.setContainer(this.container);
16             connector.setService(this);
17             Connector results[] = new Connector[connectors.length + 1];
18             System.arraycopy(connectors, 0, results, 0, connectors.length);
19             results[connectors.length] = connector;
20             connectors = results;
21 
22             // 若该服务已经初始化过了
23             if (initialized) {
24                 try {
25                     // 则初始化这个新加的连接器
26                     connector.initialize();
27                 } catch (LifecycleException e) {
28                     e.printStackTrace(System.err);
29                 }
30             }
31 
32             // 若服务已经被启动则将新添加的连接器启动
33             if (started && (connector instanceof Lifecycle)) {
34                 try {
35                     ((Lifecycle) connector).start();
36                 } catch (LifecycleException e) {
37                     ;
38                 }
39             }
40 
41             // Report this property change to interested listeners
42             support.firePropertyChange("connector", null, connector);
43         }
44 
45     }

removeConnector

 1 /**
 2      * 从该服务组件的连接器集合中移除指定的连接器,
 3      * 
 4      * @param connector
 5      *            被指定移除的连接器
 6      */
 7     public void removeConnector(Connector connector) {
 8 
 9         synchronized (connectors) {
10             // 被移除的连接器在集合中的索引位置
11             int j = -1;
12             for (int i = 0; i < connectors.length; i++) {
13                 // 找到被移除的连接器在数组中的索引之后 赋值给j 并跳出循环
14                 if (connector == connectors[i]) {
15                     j = i;
16                     break;
17                 }
18             }
19             // 如果j小于0 说明 被指定要移除的连接器 并不存在于连接器数组中
20             if (j < 0)
21                 return;
22             // 如果服务已经被启动过了,且被删除的连接器 又是一个声明周期接口的实现
23             if (started && (connectors[j] instanceof Lifecycle)) {
24                 try {
25                     // 调用被删除连接器的stop方法
26                     ((Lifecycle) connectors[j]).stop();
27                 } catch (LifecycleException e) {
28                     ;
29                 }
30             }
31             // 并向被被删除的连接器setContainer方法中传入null,清除与该服务中唯一的servlet容器的关联关系
32             connectors[j].setContainer(null);
33             // 去掉与该服务的关联关系
34             connector.setService(null);
35             int k = 0;
36             // 重新组合新的数组 去掉 被删除的连接器
37             Connector results[] = new Connector[connectors.length - 1];
38             for (int i = 0; i < connectors.length; i++) {
39                 if (i != j)
40                     results[k++] = connectors[i];
41             }
42             connectors = results;
43 
44             // 并触发属性改变监听器
45             support.firePropertyChange("connector", connector, null);
46         }
47 
48     }

 与生命周期有关的方法

  与生命周期有关的方法包括从Lifecycle接口中实现的start和stop方法,在加上initialize方法,其中initialize方法会调用该服务组件中所有连接器的initialize方法,

 1 /**
 2      * 该服务的初始化方法 将会调用该服务组件中连接器集合中的每一个连接器的初始化方法
 3      */
 4     public void initialize() throws LifecycleException {
 5         if (initialized)
 6             throw new LifecycleException(sm.getString("standardService.initialize.initialized"));
 7         initialized = true;
 8 
 9         //逐个调用连接器集合中的每个连接器的初始化方法
10         synchronized (connectors) {
11             for (int i = 0; i < connectors.length; i++) {
12                 connectors[i].initialize();
13             }
14         }
15     }

start方法负责启动被添加到该服务组件中的连接器和 servlet容器,

 1 /**
 2      * 该服务组件的声明周期接口 开始方法的实现,其内部会将调用该 服务中的所有连接器和 servlet容器的start方法 并通知相关监听事件
 3      */
 4     public void start() throws LifecycleException {
 5 
 6         // 这个组件是否已经被启动过了
 7         if (started) {
 8             throw new LifecycleException(sm.getString("standardService.start.started"));
 9         }
10 
11         // 向声明周期监听器发送 BEFORE_START_EVENT事件
12         lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
13 
14         System.out.println(sm.getString("standardService.start.name", this.name));
15         // 向生命周期监听器 发送START_EVENT事件
16         lifecycle.fireLifecycleEvent(START_EVENT, null);
17         // 将启动标志置为 true 表示已经启动
18         started = true;
19 
20         // 首先启动服务组件中定义的唯一的一个servlet容器
21         if (container != null) {
22             synchronized (container) {
23                 if (container instanceof Lifecycle) {
24                     ((Lifecycle) container).start();
25                 }
26             }
27         }
28 
29         // 第二步 启动我们定义的连接器集合中的每一个连接器
30         synchronized (connectors) {
31             for (int i = 0; i < connectors.length; i++) {
32                 if (connectors[i] instanceof Lifecycle)
33                     ((Lifecycle) connectors[i]).start();
34             }
35         }
36 
37         // 向生命周期监听器发送AFTER_START_EVENT事件
38         lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
39 
40     }

stop方法 用于关闭与该服务组件相关联的servlet容器和所有的连接器,

/**
     * 生命周期接口的停止方法的实现,会将该服务中唯一一个 servlet容器 和 被添加到该服务组件中的连接器 关闭
     */
    public void stop() throws LifecycleException {

        // 验证状态
        if (!started) {
            throw new LifecycleException(sm.getString("standardService.stop.notStarted"));
        }

        // 向生命周期监听器发送 BEFORE_STOP_EVENT事件
        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);

        // 向生命周期监听器STOP_EVENT 事件
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);

        System.out.println(sm.getString("standardService.stop.name", this.name));
        // 将启动标志置为 false 这样才可以继续启动
        started = false;

        // 第一步 停止服务组件中的所有连接器
        synchronized (connectors) {
            for (int i = 0; i < connectors.length; i++) {
                if (connectors[i] instanceof Lifecycle)
                    ((Lifecycle) connectors[i]).stop();
            }
        }

        // 第二步 停止服务组件中唯一一个servlet容器
        if (container != null) {
            synchronized (container) {
                if (container instanceof Lifecycle) {
                    ((Lifecycle) container).stop();
                }
            }
        }

        // 向声明周期监听器 发送 AFTER_STOP_EVENT事件
        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

    }

那上面就大概的将 Tomcat中的服务和 服务器组件都大致的展示了一下,下面我们来写一个简单的应用程序来实际操作一下。

下面的 应用程序重在说明如何使用服务器组件 和 服务组件,特别是在StandardServer中如何利用启动 和 关闭机制。

  1 package myex14.pyrmont.startup;
  2 
  3 import org.apache.catalina.Connector;
  4 import org.apache.catalina.Context;
  5 import org.apache.catalina.Engine;
  6 import org.apache.catalina.Host;
  7 import org.apache.catalina.Lifecycle;
  8 import org.apache.catalina.LifecycleListener;
  9 import org.apache.catalina.Loader;
 10 import org.apache.catalina.Server;
 11 import org.apache.catalina.Service;
 12 import org.apache.catalina.Wrapper;
 13 import org.apache.catalina.connector.http.HttpConnector;
 14 import org.apache.catalina.core.StandardContext;
 15 import org.apache.catalina.core.StandardEngine;
 16 import org.apache.catalina.core.StandardHost;
 17 import org.apache.catalina.core.StandardServer;
 18 import org.apache.catalina.core.StandardService;
 19 import org.apache.catalina.core.StandardWrapper;
 20 import org.apache.catalina.loader.WebappLoader;
 21 
 22 import ex14.pyrmont.core.SimpleContextConfig;
 23 
 24 /**
 25  * <p>
 26  * <b>Title:Bootstrap.java</b>
 27  * </p>
 28  * <p>
 29  * Copyright:ChenDong 2018
 30  * </p>
 31  * <p>
 32  * Company:仅学习时使用
 33  * </p>
 34  * <p>
 35  * 类功能描述:应用程序启动类,应用程序重在说明如何使用 服务器组件 和 服务组件,重点是 StandardServer中的开启和关闭机制
 36  * </p>
 37  * 
 38  * @author 陈东
 39  * @date 2018年12月19日 下午9:33:03
 40  * @version 1.0
 41  */
 42 public class Bootstrap {
 43 
 44     /**
 45      * 
 46      * <p>
 47      * Title: main
 48      * </p>
 49      * 
 50      * @date 2018年12月19日 下午9:33:03
 51      * 
 52      *       <p>
 53      *       功能描述:
 54      *       </p>
 55      * 
 56      * @param args
 57      * 
 58      */
 59     public static void main(String[] args) {
 60         System.setProperty("catalina.base", System.getProperty("user.dir"));
 61         // 初始化一个连接器
 62         Connector connector = new HttpConnector();
 63 
 64         // 初始化 对应servlet的Wrapper容器
 65         Wrapper wrapper1 = new StandardWrapper();
 66         wrapper1.setName("Primitive");
 67         wrapper1.setServletClass("PrimitiveServlet");
 68 
 69         Wrapper wrapper2 = new StandardWrapper();
 70         wrapper2.setName("Modern");
 71         wrapper2.setServletClass("ModernServlet");
 72 
 73         // 初始化一个Context容器
 74         Context context = new StandardContext();
 75         // 设置根路径
 76         context.setPath("/app1");
 77         // 设置根文件夹
 78         context.setDocBase("app1");
 79         // 添加子容器
 80         context.addChild(wrapper1);
 81         context.addChild(wrapper2);
 82 
 83         // 给StandardContext 创建一个 配置监听器
 84         LifecycleListener listener = new SimpleContextConfig();
 85 
 86         ((Lifecycle) context).addLifecycleListener(listener);
 87 
 88         // 初始化一个Host级别的容器
 89         Host host = new StandardHost();
 90         host.addChild(context);
 91         host.setName("localhost");
 92         host.setAppBase("webapps");
 93 
 94         // 初始化一个加载器
 95         Loader loader = new WebappLoader();
 96         context.setLoader(loader);
 97 
 98         // 添加servletMapping映射
 99         context.addServletMapping("/Primitive", "Primitive");
100         context.addServletMapping("/Modern", "Modern");
101 
102         // 初始化一个Engine级别的容器
103         Engine engine = new StandardEngine();
104         // 添加孩子host
105         engine.addChild(host);
106         // 根据 host的name属性设置默认的Host
107         engine.setDefaultHost("localhost");
108 
109         // -----------------------重点来了
110         // 初始化一个Service
111         Service servie = new StandardService();
112         servie.setName("stand-alone Service");
113         // 初始化一个Server
114         Server server = new StandardServer();
115         server.addService(servie);
116         servie.addConnector(connector);
117         servie.setContainer(engine);
118 
119         // 启动服务器组件
120         if (server instanceof Lifecycle) {
121             try {
122                 server.initialize();
123                 ((Lifecycle) server).start();
124                 //启动监听关闭命令的端口开始监听关闭指令,进入循环等待
125                 //此时连接器已经处于运行状态
126                 //在await方法没有接到关闭命令之前 是不回复返回的 一旦接到关闭命令则 会执行下面的stop反方
127                 server.await();
128             } catch (Exception e) {
129                 e.printStackTrace();
130             }
131         }
132         
133         //关闭服务器组件
134         if(server instanceof Lifecycle){
135             try {
136                 ((Lifecycle)server).stop();
137                 
138             } catch (Exception e) {
139                 // TODO: handle exception
140             }
141             
142         }
143 
144     }
145 
146 }

Stopper类

  Stopper类提供了一种 优雅的方式来关闭Catalina服务器,它保证了所有生命周期组件的stop方法都能够调用

 1 package myex14.pyrmont.startup;
 2 
 3 import java.io.IOException;
 4 import java.io.OutputStream;
 5 import java.net.Socket;
 6 
 7 /**
 8  * <p>
 9  * <b>Title:Stopper.java</b>
10  * </p>
11  * <p>
12  * Copyright:ChenDong 2018
13  * </p>
14  * <p>
15  * Company:仅学习时使用
16  * </p>
17  * <p>
18  * 类功能描述:目的是向Server负责监听关闭命令的端口发送 关闭命令
19  * </p>
20  * 
21  * @author 陈东
22  * @date 2018年12月19日 下午10:02:37
23  * @version 1.0
24  */
25 public class Stopper {
26 
27     /**
28      * 
29      * <p>
30      * Title: main
31      * </p>
32      * 
33      * @date 2018年12月19日 下午10:02:37
34      * 
35      *       <p>
36      *       功能描述:
37      *       </p>
38      * 
39      * @param args
40      * 
41      */
42     public static void main(String[] args) {
43         int port = 8005;
44         String shutdown = "SHUTDOWN";
45         try {
46             Socket socket = new Socket("127.0.0.1", port);
47             
48             OutputStream stream = socket.getOutputStream();
49             
50             for(int i=0;i<shutdown.length();i++)
51                 stream.write(shutdown.charAt(i));
52             
53             stream.flush();
54             stream.close();
55             socket.close();
56             System.out.println("这个服务已经成功关闭");
57             
58             
59             
60         } catch (IOException e) {
61             System.out.println("这个服务还没有启动");
62         }
63             
64 
65     }
66 
67 }

 当然了 这个Stopper类 只是一个简单的例子 真正部署的时候 不会像现在这样的简单 ,只要是想关闭服务器 其实就是自己 创建了一个 连接到 指定ip指定端口的 套接字实例,并且使用该套接字,直接发送 关闭命令

posted @ 2018-12-17 21:16  陈东的博客  阅读(957)  评论(0编辑  收藏  举报