您的位置:首页 > 运维架构 > Tomcat

How tomcat works——19 管理Servlet

2017-04-16 18:57 330 查看
概述

Tomcat4/5有一个 Manager 应用程序用于管理部署的应用程序。跟其它应用程序不同,Manager 并不在%CATALINA_HOME%/webapps 目录下而是在%CATALINA_HOME%/server/webapps 下。Manager 有一描述文件 manager.xml在%CATALINA_HOME$/webapps(Tomcat4)或%CATALINA_HOME%/server/webapps(Tomcat5),所以当 Tomcat 启动时就会安装 Manager。

注意:上下文描述符文件已在第 18 章中讨论了。

本章主要介绍 Manager 应用,首先概括的解释了 Manager 是如何工作的,然后介绍了 ContainerServlet 接口。

19.1 使用Manager

Manager 应用可以在%CATALINA_HOME%/server/webapps/manager 目录下找到。该应用中的主 servlet 是 ManagerServlet。在 Tomcat4 中,该类属于org.apache.catalina.servlets 包。在 Tomcat5 中,该类是org.apache.catalina.manager包的一部分,并以JAR包形式部署在WEB-INF/lib目录下。

注意:由于 Tomcat4 中的Manager应用程序相比 Tomcat5 中的要简单,所以它更容易学习,本章主要讨论它。在读完了本章后,你也就可以理解 Tomcat5 中的 Manager是如何工作的了。

这里是 Tomcat4 中对应servlet 元素的部署描述文件:

<servlet>
<servlet-name>Manager</servlet-name>
<servlet-class>org.apache.catalina.servlets.ManagerServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
</servlet>

<servlet>
<servlet-name>HTMLManager</servlet-name>
<servlet-class>org.apache,catalina.servlets.HTMLManagerServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
</servlet>


第一个 servlet 是 org.apache.catalina.servlets.ManagerServlet,第二个是org.apache.catalina.servlets.HTMLManagerServlet。本章主要介绍ManagerServerlet。

本应用程序的描述文件 manager.xml,说明了本应用程序的上下文路径为/manager:

<Context path="/manager" docBase="../server/webapps/manager" debug="0" privileged="true">
<!-- Link to the user database we will get roles from -->
<ResourceLink name="users" global="UserDatabase" type="org.apache.catalina.UserDatabase"/>
</Context>


第一个 servlet 映射元素说明如何调用 ManagerServlet:

<servlet-mapping>
<servlet-name>Manager</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>


换句话说,如下形式的 URL 模式将会调用 MangerServlet:

http://localhost:8080/manager/


但是,注意在部署描述符中还有安全限制元素:

<security-constraint>
<web-resource-collection>
<web-resource-name>Entire Application</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>

<auth-constraint>
<!-- NOTE: This role is not present in the default users file -->
<role-name>manager</role-name>
</auth-constraint>
</security-constraint>


它的意思是说,整个应用程序只能被 manager 角色的用户使用。auth-login 元素规定用户需要提供正确的用户名密码来通过 BASIC 验证:

<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Tomcat Manager Application</realm-name>
</login-config>


在 Tomcat 中,用户角色在%CATALINA_HOME%/conf 目录下的 tomcat-users.xml文件中有列表。因此,要进入 Manager 应用程序,必须给用户添加 manager 角色。如下事例:

<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="manager"/>
<user username="tomcat" password="tomcat" roles="manager "/>
</tomcat-users>


通过该 tomcat-users.xml,我们可以使用用户名tomcat密码tomcat来访问Manager应用程序。

下面的是 ManagerServlet 的可用函数:

•list

•start

•stop

•reload

•remove

•resources

•roles

•sessions

•undeploy

查看servlet 的doGet()方法看下该如何调用一个 function。

19.2 ContainerServlet接口

一个实现了org.apache.catalina.ContainerServlet 接口的servlet 将可以访问代表着它的StandardWrapper 对象。可以访问包装器,它也能访问表示该 web 应用的上下文对象,以及该上下文的部署器(StandardHost 实例)和其它对象。

ContainerServlet 接口如 Listing19.1 所示:

Listing 19.1: The ContainerServlet Interface

package org.apache.catalina;

public interface ContainerServlet {
public Wrapper getWrapper();
public void setWrapper(Wrapper wrapper);
}


Catalina调用实现了ContainerServlet接口的servlet的setWrapper()方法将引用传递给表示该servlet的StandardWrapper。

19.3 初始化ManagerServlet

通常,一个 servlet 用一个org.apache.catalina.core.StandardWrapper实例表示。在第一次调用该 servlet 时,StandardedWrapper 对象的loadServlet()方法会被调用,它又调用 servlet 的 init()方法。接下来我们会看到ManagerServlet 的 loadServlet()方法是如何工作的:

...
// Special handling for ContainerServlet instances
if ((servlet instanceof ContainerServlet) &&isContainerProvidedServlet(actualClass)) {
((ContainerServlet) servlet).setWrapper(this);
}

// Call the initialization method of this servlet
try {
instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,servlet);
servlet.init(facade);
...


该servlet 表示要加载的 servlet(在这里是 ManagerServlet)。

在 if 语句块中,如果 servlet 是 org.apache.catalina.ContainerServlet 的实例并且 isContainerProvidedServlet()方法返回 true。就调用 ContainerServlet接口的 setWrapper() 方法。

ManagerServlet 类实现了 ContainerServlet,因此 servlet 是ContainerSerlvet 的一个实例。在 StanadardWrapper 中的isContainerProvidedServlet()方法如 Listing19.2 所示。

Listing 19.2: The isContainerProvidedServlet method in the StandardWrapper class

private boolean isContainerProvidedServlet(String classname) {
if (classname.startsWith("org.apache.catalina.")) {
return (true);
}

try {
Class clazz = this.getClass().getClassLoader().loadClass(classname);
return (ContainerServlet.class.isAssignableFrom(clazz));
}catch (Throwable t) {
return (false);
}
}


传递给 isContainerProvidedServlet()方法的参数 classname 是 ManagerSerlvet类的完全限定名,即org.apache.catalina.servlets.ManagerServlet。因此,isContainerProvidedServlet()方法返回 true。

如果该 servlet 类是 ContainerServlet 的子类,该方法也返回 true,例如,classname 是继承了 ContainerServlet的接口 或是实现了 ContainerServlet接口的类。

注意:如果表示当前对象的类或接口跟 clazz 表示的类或接口或相同,或是超类、超接口时, java.lang.Class 类的 isAssignableFrom(Class clazz)则返回true。

因此,表示 ManagerServlet 实例的 StandardWrapper 在它的 loadServlet()方法中会调用 ManagerServlet 的 setWrapper()方法。这里是 ManagerServlet 类对setWrapper()方法的实现:

public void setWrapper(Wrapper wrapper) {
this.wrapper = wrapper;
if (wrapper == null) {
context = null;
deployer = null;
}else {
context = (Context) wrapper.getParent();
deployer = (Deployer) context.getParent();
}
}


如果参数 wrapper 不是 null,将会执行 else 语句块。这意味着将表示 Manager 应用的上

下文赋值给 context 变量并将该上下文部署在 StandardHost 实例上。Deployer非常重要,它会在 ManagerServlet 的好几个方法中使用到。

在 StandardWrapper 的 loadServlet()方法调用了ManagerServlet的 setWrapper()方法之后。loadServlet()方法调用 MangerServlet 的 init()方法。

19.4 部署清单

可以使用如下 URL 来查看所部署的所有应用程序:

http://localhost:8080/manager/list


下面是一输出的例子:

OK - Listed applications for virtual host localhost
/admin:stopped:0:../server/webapps/admin
/app1:running:0:C:\123data\JavaProjects\Pyrmont\webapps\app1
/manager:running:0:../server/webapps/manager


上面的 URL 将会调用 ManagerServlet 的list()方法,如 Listing19.3 所示。

Listing 19.3: The list method of ManagerServlet

protected void list(PrintWriter writer) {

if (debug >= 1)
log("list: Listing contexts for virtual host '" +
deployer.getName() + "'");

writer.println(sm.getString("managerServlet.listed",
deployer.getName()));
String contextPaths[] = deployer.findDeployedApps();
for (int i = 0; i < contextPaths.length; i++) {
Context context = deployer.findDeployedApp(contextPaths[i]);
String displayPath = contextPaths[i];
if( displayPath.equals("") )
displayPath = "/";
if (context != null ) {
if (context.getAvailable()) {
writer.println(sm.getString("managerServlet.listitem",
displayPath,
"running",
"" + context.getManager().findSessions().length,
context.getDocBase()));
} else {
writer.println(sm.getString("managerServlet.listitem",
displayPath,
"stopped",
"0",
context.getDocBase()));
}
}
}
}


方法 list()调用部署器的 findDeployedApps()来获得所有部署在 Catalina 中的上下文的路径。然后遍历路径数组获得每个独立的上下文并检查该上下文是否可用。对于每个可用的上下文,打印出上下文路径,’running’字符串,用户的Session个数和文档基(document base)。对于不可用的上下文,打印出上下文路径,’stopperd’字符串,0 ,以及文档基。

19.5 启动Web应用

可以使用如下 URL 来启动一个 web 应用:

http://localhost:8080/manager/start?path=/contextPath


contextPath是想要启动的应用的上下文路径。例如,要启动 admin 应用,可以使用如下路径:

http://localhost:8080/manager/start?path=/admin


如果该应用程序已经启动,你会收到错误提示信息。

根据该 URL,ManagerServlet 调用 start()方法,如 Listing19.4 所示。

Listing 19.4: The start method of the ManagerServlet class

protected void start(PrintWriter writer, String path) {

if (debug >= 1)
log("start: Starting web application at '" + path + "'");

if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
writer.println(sm.getString("managerServlet.invalidPath", path));
return;
}
String displayPath = path;
if( path.equals("/") )
path = "";

try {
Context context = deployer.findDeployedApp(path);
if (context == null) {
writer.println(sm.getString("managerServlet.noContext", displayPath));
return;
}
deployer.start(path);
if (context.getAvailable())
writer.println
(sm.getString("managerServlet.started", displayPath));
else
writer.println
(sm.getString("managerServlet.startFailed", displayPath));
} catch (Throwable t) {
getServletContext().log
(sm.getString("managerServlet.startFailed", displayPath), t);
writer.println
(sm.getString("managerServlet.startFailed", displayPath));
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}

}


在一些检查之后,start()方法调用部署器的 findDeployedApp()方法,该方法根据path路径获得上下文,如果上下文不为空,start()方法则调用部署器的 start()方法来启动该应用程序。

19.6 停止Web应用

可以使用如下命令来停止一个应用程序:

http://localhost:8080/manager/stop?path=/contextPath


contextPath是我们想要停止的 web应用的路径,如果该应用并没有正在运行,则会返回错误信息。

ManagerSerlvet 收到请求时,它调用 stop()方法,如 Listing19.5 所示。

Listing 19.5: The stop method of the ManagerServlet class

protected void stop(PrintWriter writer, String path) {

if (debug >= 1)
log("stop: Stopping web application at '" + path + "'");

if ((path == null) || (!path.startsWith("/") && path.equals(""))) {
writer.println(sm.getString("managerServlet.invalidPath", path));
return;
}
String displayPath = path;
if( path.equals("/") )
path = "";

try {
Context context = deployer.findDeployedApp(path);
if (context == null) {
writer.println(sm.getString("managerServlet.noContext", displayPath));
return;
}
// It isn't possible for the manager to stop itself
if (context.getPath().equals(this.context.getPath())) {
writer.println(sm.getString("managerServlet.noSelf"));
return;
}
deployer.stop(path);
writer.println(sm.getString("managerServlet.stopped", displayPath));
} catch (Throwable t) {
log("ManagerServlet.stop[" + displayPath + "]", t);
writer.println(sm.getString("managerServlet.exception",
t.toString()));
}

}


通过上面的代码我们可以明白stop()方法是如何工作的。ManagerServlet 类中的其它方法也很消化理解。

19.7小结

本章介绍了如何使用特殊接口 ContainerServelet 来创建一个可以访问Catalina 内部类的 Servlet。Manager 应用可以用于管理部署应用,并说明了如何从包装器对象获得其它对象。另外还可以设计一个更精致的 Servlet 来管理Tomcat。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: