How tomcat works——19 管理Servlet

来源:互联网 发布:淘宝大学怎么进入 编辑:程序博客网 时间:2024/05/29 11:43

概述

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 instancesif ((servlet instanceof ContainerServlet) &&isContainerProvidedServlet(actualClass)) {    ((ContainerServlet) servlet).setWrapper(this);}// Call the initialization method of this servlettry {    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 classprivate 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 ManagerServletprotected 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 classprotected 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 classprotected 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。

0 0
原创粉丝点击