开始学习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,否则再次循环。接续等待消息。
小累 休息休息 早点睡觉 ,,,,,,,,,,,,,,,,,,,,,,,,,,,,
原文地址:https://www.cnblogs.com/ChenD/p/Tomcat-Server-StandardServer.html