Chapter 19: Manager Servlet

注意:由于Tomcat 4中的Manager应用程序比Tomcat 5中的稍微简单一些,它是一个更好的学习工具,因此在本章中进行了讨论。

阅读本章后,您应该也能理解Tomcat 5中的Manager应用程序的工作原理。

以下是Tomcat 4中部署描述符中的Servlet元素。

Overview(概述)

Tomcat 4 and 5 come with the Manager application that you can use to manage deployed applications. Unlike other applications, Manager does not reside in the default deployment directory %CATALINA_HOME%/webapps but in %CATALINA_HOME%/server/webapps. Manager is installed when Tomcat starts because Manager has a descriptor, the manager.xml file, in the %CATALINA_HOME$/webapps directory in Tomcat 4 and the %CATALINA_HOME%/server/webapps directory in Tomcat 5.

Tomcat 4和5都附带了Manager应用程序,您可以使用它来管理已部署的应用程序。与其他应用程序不同,Manager不驻留在默认的部署目录%CATALINA_HOME%/webapps中,而是位于%CATALINA_HOME%/server/webapps中。当Tomcat启动时,会安装Manager,因为Manager在Tomcat 4中的%CATALINA_HOME$/webapps目录和Tomcat 5中的%CATALINA_HOME%/server/webapps目录中都有一个描述符,即manager.xml文件。

Note Context descriptors were discussed in Chapter 18.

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

This chapter describes the Manager application. It starts by outlining how to use this application to give you the feel of how Manager works. It then explains the ContainerServlet interface.

本章介绍了Manager应用程序。首先概述了如何使用该应用程序,以便让您了解Manager的工作方式。然后解释了ContainerServlet接口。

Using the Manager Application(使用Manager应用程序)

The Manager application can be found in the %CATALINA_HOME%/server/webapps/manager directory for both Tomcat 4 and 5. The main servlet in this application is ManagerServlet. In Tomcat 4 this class belongs to the org.apache.catalina.servlets package, which is one of the packages in Catalina. In Tomcat 5 this class is part of the org.apache.catalina.manager package and is deployed as a JAR file in the WEB-INF/lib directory.

Manager应用程序可以在Tomcat 4和5的%CATALINA_HOME%/server/webapps/manager目录中找到。该应用程序的主要Servlet是ManagerServlet。

在Tomcat 4中,该类属于org.apache.catalina.servlets包,该包是Catalina中的一个包。

在Tomcat 5中,该类属于org.apache.catalina.manager包,并且作为一个JAR文件部署在WEB-INF/lib目录中。

Note Because the Manager application in Tomcat 4 is slightly simpler than that in Tomcat 5, it is a better learning tool and is therefore discussed in this chapter. You should be able to understand how the Manager application in Tomcat 5 works too, after reading this chapter.

注意 因为 Tomcat 4 中的管理器应用程序比 Tomcat 5 中的稍简单,它是一个更好的学习工具,因此在本章中讨论。

读完本章后,你应该也能理解 Tomcat 5 中的管理器应用程序是如何工作的。

Here are the servlet elements in the deployment descriptor in Tomcat 4.

以下是 Tomcat 4 部署描述符中的 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>

The first servlet is org.apache.catalina.servlets.ManagerServlet and the second org.apache.catalina.servlets.HTMLManagerServlet. This chapter focuses on the ManagerServlet class.

第一个 servlet 是 org.apache.catalina.servlets.ManagerServlet,第二个是 org.apache.catalina.servlets.HTMLManagerServlet。本章将重点讨论 ManagerServlet 类。

The descriptor for this application, manager.xml, tells that the context path for this application is /manager.

此应用程序的描述符 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>

The first servlet-mapping element tells you how to invoke ManagerServlet.

第一个 servlet 映射元素告诉你如何调用 ManagerServlet。

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

In other words, a URL beginning with the following pattern invokes ManagerServlet:\

换句话说,以下列模式开头的 URL 会调用 ManagerServlet:\

http://localhost:8080/manager/

However, note that there is also this security-constraint element in the deployment descriptor

不过,请注意部署描述符中还有这样一个安全约束元素

 <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>

This means, the entire application is restricted to users belonging to the manager role. The auth-login element states that a user can be allowed access to the restricted contents if he/she can supply the correct user name and password using the BASIC authentication.

这意味着,整个应用程序仅限于属于管理者角色的用户使用。

auth-login 元素规定,如果用户能使用 BASIC 身份验证提供正确的用户名和密码,就可以访问受限内容。

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

This means, the entire application is restricted to users belonging to the manager role. The auth-login element states that a user can be allowed access to the restricted contents if he/she can supply the correct user name and password using the BASIC authentication.

这意味着,整个应用程序仅限于属于管理者角色的用户使用。

auth-login 元素规定,如果用户能使用 BASIC 身份验证提供正确的用户名和密码,就可以访问受限内容。

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

In Tomcat, roles and users are listed in the tomcat-users.xml file in the %CATALINA_HOME%/conf directory. Therefore, to access the Manager application, you must add a manager role with a user belonging to that role. Here is an example:

在 Tomcat 中,角色和用户列在 %CATALINA_HOME%/conf 目录下的 tomcat-users.xml 文件中。

因此,要访问管理器应用程序,必须添加一个管理器角色和属于该角色的用户。

下面是一个示例:

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

With this tomcat-users.xml file, you can access the Manager application using the user name tomcat and password tomcat.

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

The following functions are available in the ManagerServlet.

ManagerServlet 提供以下功能。

  • list
  • start
  • stop
  • reload
  • remove
  • resources
  • roles
  • sessions
  • undeploy
  • 列表
  • 启动
  • 停止
  • 重新加载
  • 删除
  • 资源
  • 角色
  • 会话
  • 撤消部署

Check the servlet's doGet method to see how you can invoke a function.

查看 servlet 的 doGet 方法,了解如何调用函数。

The ContainerServlet Interface

A servlet that implements the org.apache.catalina.ContainerServlet interface will have access to the StandardWrapper object that represents it. Having access to the wrapper, it can also access the context object representing the web application, the deployer (StandardHost instance) in which the context resides, and other objects.

实现 org.apache.catalina.ContainerServlet 接口的 servlet 可以访问代表它的 StandardWrapper 对象。有了对包装器的访问权限,它还可以访问代表网络应用程序的上下文对象、上下文所在的部署器(StandardHost 实例)以及其他对象。

The ContainerServlet interface is given in Listing 19.1

ContainerServlet 接口见清单 19.1

Listing 19.1: The ContainerServlet Interface

清单 19.1:ContainerServlet 接口

package org.apache.catalina;
public interface ContainerServlet {
 public Wrapper getWrapper();
 public void setWrapper(Wrapper wrapper);
}

Catalina invokes the setWrapper method of a servlet implementing ContainerServlet to pass the reference to the StandardWrapper representing that servlet.

Catalina 调用实现 ContainerServlet 的 servlet 的 setWrapper 方法来传递对代表该 servlet 的 StandardWrapper 的引用。

Initializing ManagerServlet

As always, a servlet is represented by an org.apache.catalina.core.StandardWrapper instance. The first time the servlet is invoked, the StandardWrapper object's loadServlet is called, which in turns calls the servlet's init method. (See Chapter 11.) In the case of ManagerServlet, you should look at a fragment in the loadServlet method:

与往常一样,Servlet 由 org.apache.catalina.core.StandardWrapper 实例表示。

首次调用 servlet 时,会调用 StandardWrapper 对象的 loadServlet,然后再调用 servlet 的 init 方法。(请参见第 11 章。)

在 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);
...

where servlet represents the servlet to be loaded (in this case, ManagerServlet).

其中 servlet 表示要加载的 servlet(本例中为 ManagerServlet)。

The if statement in the code fragment above says that if servlet is an instance of org.apache.catalina.ContainerServlet and the isContainerProvidedServlet method returns true, call the ContainerServlet interface's setWrapper method.

上面代码片段中的 if 语句表示,如果 servlet 是 org.apache.catalina.ContainerServlet 的实例,且 isContainerProvidedServlet 方法返回 true,则调用 ContainerServlet 接口的 setWrapper 方法。

The ManagerServlet class implements ContainerServlet, therefore servlet is an instance of ContainerServlet. The isContainerProvidedServlet method in StandardWrapper is given in Listing 19.2

ManagerServlet 类实现了 ContainerServlet,因此 servlet 是 ContainerServlet 的实例。

清单 19.2 给出了 StandardWrapper 中的 isContainerProvidedServlet 方法

Listing 19.2: The isContainerProvidedServlet method in the StandardWrapper class

清单 19.2:StandardWrapper 类中的 isContainerProvidedServlet 方法

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);
 }
}

The classname argument passed to isContainerProvidedServlet is the fully-qualified name of the ManagerServlet, which is org.apache.catalina.servlets.ManagerServlet. As such, the isContainerProvidedServlet method returns true.

传递给 isContainerProvidedServlet 的 classname 参数是 ManagerServlet 的全称,即 org.apache.catalina.servlets.ManagerServlet。

因此,isContainerProvidedServlet 方法返回 true。

This method also returns true if the servlet class inidicated by classname is a subtype of ContainerServlet, i.e. if classname is an interface that extends ContainerServlet or a class that implements ContainerServlet.

如果 classname 所指示的 servlet 类是 ContainerServlet 的子类型,即 classname 是扩展 ContainerServlet 的接口或实现 ContainerServlet 的类,则此方法也返回 true。

Note The java.lang.Class class's isAssignableFrom(Class clazz) method returns true if the class or interface represented by the current Class object is either the same as, or is a superclass or superinterface of, the class or interface represented by the specified clazz parameter.

注意 如果当前 Class 对象所代表的类或接口与指定的 clazz 参数所代表的类或接口相同,或者是其超类或超接口,则 java.lang.Class 类的 isAssignableFrom(Class clazz) 方法返回 true。

Therefore, the loadServlet method of the StandardWrapper instance representing ManagerServlet will call the ManagerServlet's setWrapper method. Here is the implementation of the setWrapper method in ManagerServlet.

因此,代表 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();
 }
}

Since the parameter wrapper is not null, the else block will be executed, which means context is assigned the context object representing the Manager application and deployer the StandardHost instance hosting the context. The deployer is especially important because it is used in several methods in ManagerServlet to perform various functions.

由于参数包装器不为空,else 块将被执行,这意味着上下文将被分配给代表 Manager 应用程序的上下文对象,而部署器则是托管上下文的 StandardHost 实例。

部署器尤为重要,因为在 ManagerServlet 的多个方法中都会使用它来执行各种功能。

After the setWrapper method of ManagerServlet is called by the StandardWrapper class's loadServlet method, the loadServlet method calls the init method of ManagerServlet.

在 StandardWrapper 类的 loadServlet 方法调用 ManagerServlet 的 setWrapper 方法后,loadServlet 方法会调用 ManagerServlet 的 init 方法。

Listing Deployed Web Applications

You list all deployed web applications by using the following URL:

您可以使用以下 URL 列出所有已部署的网络应用程序

http://localhost:8080/manager/list

Here is an example of the output:

下面是一个输出示例:

OK - Listed applications for virtual host localhost

OK - 列出了虚拟主机 localhost 的应用程序


/admin:stopped:0:../server/webapps/admin
/app1:running:0:C:\123data\JavaProjects\Pyrmont\webapps\app1
/manager:running:0:../server/webapps/manager

The URL above invokes the list method of ManagerServlet, which is presented in
Listing 19.3

上面的 URL 调用了 ManagerServlet 的 list 方法,如下所示
清单 19.3

Listing 19.3: The list method of ManagerServlet

清单 19.3:ManagerServlet 的 list 方法

  
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()));  
            }  
        }  
    }  
}

The list method calls the deployer's findDeployedApps to obtain the paths of all the deployed contexts in Catalina. It then iterates the path array to get each individual context and checks whether or not the context is available. For every context that is available, the list method prints the context path, the string running, the number of user sessions, and the document base. For contexts that are unavailable, the list method prints the context path, the string stopped, 0, and the document base.

list 方法调用部署器的 findDeployedApps 来获取 Catalina 中所有已部署上下文的路径。

然后遍历路径数组以获取每个上下文,并检查该上下文是否可用。

对于每个可用的上下文,list 方法会打印上下文路径、运行的字符串、用户会话数和文档库。

对于不可用的上下文,列表方法会打印上下文路径、字符串 stopped、0 和文档库。

Starting A Web Application

You use the following URL to start a web application:

您可以使用以下 URL 启动网络应用程序:

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

where contextPath is the context path of the application you want to start. For example, to start the admin application, you use

其中 contextPath 是要启动的应用程序的上下文路径。

例如,要启动管理应用程序,请使用

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

If the application has been started, you'll receive an error notification.
Upon receiving this URL, ManagerServlet invokes the start method, which is presented in Listing 19.4.

如果应用程序已启动,您将收到错误通知。

收到此 URL 后,ManagerServlet 会调用启动方法,该方法如清单 19.4 所示。

Listing 19.4: The start method of the ManagerServlet class

清单 19.4: ManagerServlet 类的启动方法


    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()));
        }
    }

After some checking, the start method calls the deployer's findDeployedApp method in the try block. This method returns the context object whose path is passed to it. If the context is not null, the start method calls the deployer's start method to start the application.

经过一些检查后,start 方法会在 try 代码块中调用部署器的 findDeployedApp 方法。

该方法会返回传入路径的上下文对象。如果上下文对象不为空,start 方法就会调用部署程序的 start 方法来启动应用程序。

Stopping A Web Application

You use the following URL to stop an application:

您可以使用以下 URL 停止应用程序:

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

where contextPath is the context path of the application you want to stop. If the application is not running, you will get an error message.

其中 contextPath 是要停止的应用程序的上下文路径。如果应用程序未运行,您将收到一条错误消息。

When the ManagerServlet receives the request, it invokes the stop method. The stop method is given in Listing 19.5.

ManagerServlet 收到请求后会调用 stop 方法。停止方法见清单 19.5。

Listing 19.5: The stop method of the ManagerServlet class

清单 19.5:ManagerServlet 类的 stop 方法


    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()));
        }
    }

You should be able to figure out how the stop method works. Other methods in the ManagerServlet class should be easy to digest too.

你应该能够理解 stop 方法是如何工作的。ManagerServlet 类中的其他方法也应该很容易理解。

Summary

In this chapter you have seen how you can use a special interface, ContainerServlet, to create a servlet with access to the Catalina internal classes. The Manager application that you can use to manage deployed applications has demonstrated how to obtain other objects from the wrapper object. It is very possible to design a servlet with more sophisticated functionality for managing Tomcat

在本章中,你将看到如何使用特殊接口 ContainerServlet 来创建可访问 Catalina 内部类的 servlet。

您可以用来管理已部署应用程序的管理器应用程序演示了如何从封装对象中获取其他对象。

您完全可以设计一个具有更复杂功能的 servlet 来管理 Tomcat


Xander
195 声望50 粉丝