How tomcat works——19 管理Servlet

概述

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。

猜你喜欢

转载自blog.csdn.net/LoveJavaYDJ/article/details/70197108
今日推荐