Chapter 14: Server and Service

Overview

In previous chapters you have seen how you can have a servlet container by instantiating a connector and a container and then associating them with each other. Only one connector could be used, and that was to serve HTTP requests on port 8080. You could not add another connector to service HTTPS requests, for example.

在前面的章节中,您已经看到了如何通过实例化连接器和容器,然后将它们相互关联来拥有一个Servlet容器。

只能使用一个连接器,在端口8080上提供HTTP请求服务。

例如,您不能添加另一个连接器来提供HTTPS请求服务。

In addition, all applications accompanying the previous chapters lack one thing: a good mechanism for starting and stopping the servlet container. In this chapter, we'll look at two other components that offer this mechanism as well as offer many other features: server and service.

此外,伴随前几章的所有应用程序都缺少一个重要的机制:一个良好的启动和停止Servlet容器的机制

在本章中,我们将介绍另外两个组件,它们不仅提供这种机制,还提供许多其他功能:服务器和服务。

Server(服务器)

The org.apache.catalina.Server interface represents the entire Catalina servlet container and engulfs all other components. A server is particularly useful because it provides an elegant mechanism for starting and stopping the whole system. There is no need to start the connector and the container individually any longer.

org.apache.catalina.Server接口代表整个 Catalina Servlet 容器,并包含所有其他组件。

服务器特别有用,因为它提供了一种优雅的机制来启动和停止整个系统。

不再需要单独启动连接器和容器。

Here is how the start and stop mechanism works. When you start a server, it starts all components inside it. It then waits indefinitely for a shutdown command. If you want to shut the system down, you send a shutdown command to a designated port. This will reach the server and if it receives the right shutdown command, it will obey by stopping all its components.

以下是启动和停止机制的工作原理。当您启动一个服务器时,它会启动其中的所有组件。

然后,它会无限期地等待关闭命令。如果您想关闭系统,您可以向指定端口发送一个关闭命令。

这将传达到服务器,如果它接收到正确的关闭命令,它将停止所有组件。

A server uses another component, a service, to contain components such as a container and one or more connectors. Service is explained in the section "Service" later in this chapter.

服务器使用另一个组件,即服务,来包含诸如容器和一个或多个连接器之类的组件。

服务将在本章后面的"服务"部分进行解释。

The Server interface is given in Listing 14.1.

Server接口如下所示(见列表14.1)。

Listing 14.1: The Server interface

列表14.1:Server接口


    package org.apache.catalina;
import org.apache.catalina.deploy.NamingResources;
    public interface Server {
        /**
         * Return descriptive information about this Server implementation
         * and the corresponding version number, in the format
         * <code>&lt;description&gt;/&lt;version&gt;</code>.
         */
        public String getInfo();
        /**
         * Return the global naming resources.
         */
        public NamingResources getGlobalNamingResources();
        /**
         * Set the global naming resources.
         *
         * @param namingResources The new global naming resources
         */
        public void setGlobalNamingResources
        (NamingResources globalNamingResources);
        /**
         * Return the port number we listen to for shutdown commands.
         */
        public int getPort();
        /**
         * Set the port number we listen to for shutdown commands.
         *
         * @param port The new port number
         */
        public void setPort(int port);
        /**
         * Return the shutdown command string we are waiting for.
         */
        public String getShutdown();
        /**
         * Set the shutdown command we are waiting for.
         *
         * @param shutdown The new shutdown command
         */
        public void setShutdown(String shutdown);
        /**
         * Add a new Service to the set of defined Services.
         *
         * @param service The Service to be added
         */
        public void addService(Service service);
        /**
         * Wait until a proper shutdown command is received, then return.
         */
        public void await();
        /**
         * Return the specified Service (if it exists); otherwise return
         * <code>null</code>.
         *
         * @param name Name of the Service to be returned
         */
        public Service findService(String name);
        /**
         * Return the set of Services defined within this Server.
         */
        public Service[] findServices();
        /**
         * Remove the specified Service from the set associated from this
         * Server.
         *
         * @param service The Service to be removed
         */
        public void removeService(Service service);
        /**
         * Invoke a pre-startup initialization. This is used to allow
         * onnectors to bind to restricted ports under Unix operating
         * environments.
         *
         * @exception LifecycleException If this server was already
         * initialized.
         */
        public void initialize() throws LifecycleException;
    }

The shutdown property holds the string that must be sent to a Server instance to shut it down. The port property defines the port the server waits for a shutdown command. You can add Service objects to a server by calling its addService method. Later, a service can be removed by invoking the removeService method. The findServices method returns all services added to the server. The initialize method contains code that needs to be executed before start-up.

shutdown 属性包含关闭服务器实例时必须发送的字符串。

端口属性定义了服务器等待关闭命令的端口。

调用 addService 方法可以向服务器添加服务对象。

之后,可以通过调用 removeService 方法删除服务。

findServices 方法会返回添加到服务器的所有服务。

initialize 方法包含启动前需要执行的代码。

StandardServer(标准服务器)

The org.apache.catalina.core.StandardServer class is the standard implementation of Server. We're particularly interested in the shutdown mechanism offered by this class, which is also the most important feature in this class. Many methods are related to the storing of the server configuration into a new server.xml file, but they will not be discussed here. For those interested, these methods are not difficult to understand, though.

org.apache.catalina.core.StandardServer类是 Server 的标准实现。

我们尤其关注该类提供的关闭机制,这也是该类中最重要的特性。

有许多方法与将服务器配置存储到新的server.xml文件相关,但这里不会讨论它们。

对于感兴趣的人来说,这些方法并不难理解。

A Server can have zero or more services. The StandardServer provides the implementations of the addService, removeService, and findServices methods.

一个Server可以有零个或多个服务。

StandardServer 提供了 addServiceremoveServicefindServices 方法的实现。

Four methods are related to the lifecycle of the StandardServer: initialize, start, stop, and await. Just like any other components, you initialize and start a server. You then call the await method followed by the stop method. The await method does not return until it receives a shutdown command on port 8085 (or some other port). When the await method returns, the stop method is run to stop all the sub-components. In the accompanying application of this chapter, you will learn how you can implement this shutdown mechanism.

四个方法与 StandardServer 的生命周期相关:initializestartstopawait

和其他组件一样,你需要初始化并启动一个服务器。然后调用await方法,再调用stop方法。await方法在接收到端口8085(或其他端口)上的关闭命令之前不会返回。当await方法返回时,stop方法会运行以停止所有子组件。在本章的附带应用程序中,你将学习如何实现这个关闭机制。

The initialize, start, stop, and await methods are discussed in the following sub-sections.

initialize、start、stop和await方法将在下面的小节中讨论。

The initialize Method(初始化方法)

The initialize method is used to initialize services added to the Server instance. The initialize method in the StandardServer class in Tomcat 4 is given in Listing 14.2.

initialize方法用于初始化添加到Server实例的服务。Tomcat 4中StandardServer类的initialize方法如下所示(见代码清单14.2)

Listing 14.2: The initialize method

清单 14.2: 初始化方法

public void initialize() throws LifecycleException {
     if (initialized)
         throw new LifecycleException 
     (
         sm.getString("StandardServer.initialize.initialized"));
         initialized = true;
         // Initialize our defined Services
         for (int i = 0; i < services.length; i++) {
         services[i].initialize();
     }
}

Note that the initialize method employs a boolean named initialized that prevents the server from being initialized twice. In Tomcat 5, the initialize method is similar, but it also includes code related to JMX (discussed in Chapter 20). The stop method does not reset the value of initialized, so that if the server is stopped and started again, its initialized method is not called again.

请注意,初始化方法使用了一个名为 initialized 的布尔值,防止服务器被初始化两次。

在Tomcat 5中,初始化方法类似,但还包括与JMX相关的代码(在第20章中讨论)。

停止方法不会重置 initialized 的值,因此如果服务器停止并重新启动,则不会再次调用其初始化方法。

The start Method(start 方法)

You call the start method to start a Server. The implementation of this method in StandardServer starts all services which in turn starts all other components, such as the connector(s) and the container. Listing 14.3 presents the start method.

调用start方法来启动服务器。

StandardServer 中的此方法实现会启动所有服务,进而启动所有其他组件,例如连接器和容器。

代码清单14.3展示了start方法。

Listing 14.3: The start method

代码清单14.3:start 方法

public void start() throws LifecycleException {
 // Validate and update our current component state
 if (started)
 throw new LifecycleException
 (sm.getString("standardServer.start.started"));
 // Notify our interested LifecycleListeners
 lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
 lifecycle.fireLifecycleEvent(START_EVENT, null);
 started = true;
 // Start our defined Services
 synchronized (services) {
 for (int i = 0; i < services.length; i++) {
 if (services[i] instanceof Lifecycle)
 ((Lifecycle) services[i]).start();
 }
 // Notify our interested LifecycleListeners
 lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}

The start method employs the start boolean variable to prevent a server from being started twice. The stop method resets this variable.

start 方法使用 start 布尔变量来防止服务器被启动两次。停止方法会重置该变量。

The stop Method(停止法)

The stop method stops the server. This method is given in Listing 14.4.

stop 方法用于停止服务器。

该方法在清单 14.4 中给出。

Listing 14.4: The stop method

清单 14.4: 停止方法

public void stop() throws LifecycleException {
 // Validate and update our current component state
 if (!started)
 throw new LifecycleException
 (sm.getString("standardServer.stop.notStarted"));
 // Notify our interested LifecycleListeners
 lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
 lifecycle.fireLifecycleEvent(STOP_EVENT, null);
 started = false;
 // Stop our defined Services
 for (int i = 0; i < services.length; i++) {
 if (services[i] instanceof Lifecycle)
 ((Lifecycle) services[i]).stop();
 }
 // Notify our interested LifecycleListeners
 lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
}

Calling the stop method stops all the services and resets the started boolean, so that the server can be started again

调用 stop 方法会停止所有服务并重置已启动布尔值,以便再次启动服务器

The await Method(等待方法)

  
/**  
 * Wait until a proper shutdown command is received, then return. */public void await() {  
    // Set up a server socket to wait on  
    ServerSocket serverSocket = null;  
    try {  
        serverSocket = new ServerSocket(port, 1,  
                InetAddress.getByName("127.0.0.1"));  
    }  
    catch (IOException e) {  
        System.err.println("StandardServer.await: create[" +  
                port + "]: " + e);  
        e.printStackTrace();  
        System.exit(1);  
    }  
    // Loop waiting for a connection and a valid command  
    while (true) {  
        // Wait for the next connection  
        Socket socket = null;  
        InputStream stream = null;  
        try {  
            socket = serverSocket.accept();  
            socket.setSoTimeout(10 * 1000); // Ten seconds  
            stream = socket.getInputStream();  
        }  
        catch (AccessControlException ace) {  
            System.err.println("StandardServer.accept security exception: "  
                    + ace.getMessage());  
            continue;        }  
        catch (IOException e) {  
            System.err.println("StandardServer.await: accept: " + e);  
            e.printStackTrace();  
            System.exit(1);  
        }  
        // Read a set of characters from the socket  
        StringBuffer command = new StringBuffer();  
        int expected = 1024; // Cut off to avoid DoS attack  
        while (expected < shutdown.length()) {  
            if (random == null)  
                random = new Random(System.currentTimeMillis());  
            expected += (random.nextInt() % 1024);  
        }  
        while (expected > 0) {  
            int ch = -1;  
            try {  
                ch = stream, read();  
            }  
            catch (IOException e) {  
                System.err.println("StandardServer.await: read: " + e);  
                e.printStackTrace();  
                ch = -1;  
            }  
            if (ch < 32) // Control character or EOF terminates loop  
                break;  
            command.append((char) ch);  
            expected--;  
        }  
        // Close the socket now that we are done with it  
        try {  
            socket.close();  
        }  
        catch (IOException e) {  
            ;  
        }  
        // Match against our command string  
        boolean match = command.toString().equals(shutdown);  
        if (match) {  
            break;  
        }  
        else  
            System.err.println("StandardServer.await: Invalid command '" +  
                    command.toString() + "' received");  
    }  
    // Close the server socket and return  
    try {  
        serverSocket.close();  
    }  
    catch (IOException e) {  
        ;  
    }  
}

The await method creates a ServerSocket object that waits on port 8085 and then calls its accept method in a while loop. The accept method will only return if there is an incoming message on the designated port (8085). Each message will then be matched against the shutdown string. If it matches, control breaks out of the while loop and the SocketServer is closed. If it does not match, control stays in the while loop waiting for another message.

await 方法会创建一个 ServerSocket 对象,该对象在 8085 端口等待,然后在 while 循环中调用其 accept 方法。

只有在指定端口(8085)上有传入信息时,接受方法才会返回。

然后,每条信息都将与关机字符串进行匹配。

如果匹配,控制将跳出 while 循环并关闭 SocketServer

如果不匹配,控制程序将停留在 while 循环中,等待另一条消息。

Service(服务)

The org.apache.catalina.Service interface represents a service. A service can hold one container and multiple connectors. You can add as many connectors as you want and all the connectors will be associated with the container. The Service interface is given in Listing 14.6.

org.apache.catalina.Service 接口表示一个服务。

一个服务可以持有一个容器和多个连接器。

您可以添加任意数量的连接器,并且所有连接器都将与容器关联。

Service 接口如14.6节所示。

Listing 14.6: The Service interface

清单 14.6:服务接口


package org.apache.catalina;
    public interface Service {
        /**
         * Return the <code>Container</code> that handles requests for all
         * <code>Connectors</code> associated with this Service.
         */
        public Container getContainer();
        /**
         * Set the <code>Container</code> that handles requests for all
         * <code>Connectors</code> associated with this Service.
         *
         * @param container The new Container
         */
        public void setContainer(Container container);
        /**
         * Return descriptive information about this Service implementation
         * and the corresponding version number, in the format
         * <code>&lt;description&gt;/&lt;version&gt;</code>.
         */
        public String getInfo();
        /**
         * Return the name of this Service.
         */
        public String getName();
        /**
         * Set the name of this Service.
         *
         * @param name The new service name
         */
        public void setName(String name);
        /**
         * Return the <code>Server</code> with which we are associated
         * (if any).
         */
        public Server getServer();
        /**
         * Set the <code>Server</code> with which we are associated (if any).
         *
         * @param server The server that owns this Service
         */
        public void setServer(Server server);
        /**
         * Add a new Connector to the set of defined Connectors,
         * and associate it with this Service's Container.
         *
         * @param connector The Connector to be added
         */
        public void addConnector(Connector connector);
        /**
         * Find and return the set of Connectors associated with
         * this Service.
         */
        public Connector[] findConnectors();
        /**
         * Remove the specified Connector from the set associated from this
         * Service. The removed Connector will also be disassociated
         * from our Container.
         *
         * @param connector The Connector to be removed
         */
        public void removeConnector(Connector connector);
        /**
         * Invoke a pre-startup initialization. This is used to
         * allow connectors to bind to restricted ports under
         * Unix operating environments.
         *
         * @exception LifecycleException If this server was
         * already initialized.
         */
        public void initialize() throws LifecycleException;
    }

StandardService(标准服务)

The org.apache.catalina.core.StandardService class is the standard implementation of Service. The StandardService class's initialize method initializes all the connectors added to the service. StandardService implements Service as well as the org.apache.catalina.Lifecycle interface. Its start method starts the container and all the connectors.

这一章节解释了 Catalina 中的两个重要组件:服务器和服务。

服务器特别有用,因为它提供了一个温和的机制来启动和停止 Catalina 部署。

服务组件封装了一个容器和一个或多个连接器。

本章附带的应用程序展示了如何使用服务器和服务组件。

它还演示了如何在 StandardServer 类中使用停止机制。

Container and Connector(s)(容器和连接器)

A StandardService instance contains two types of components: a container and one or more connectors. Being able to have multiple connectors enable Tomcat to service multiple protocols. One connector can be used to service HTTP requests, and another for servicing HTTPS requests.

一个 StandardService 实例包含两种类型的组件:一个容器和一个或多个连接器。

能够拥有多个连接器使得Tomcat能够服务多种协议。

一个连接器可以用于处理HTTP请求,另一个用于处理 HTTPS 请求。

The StandardService class uses the container variable as an object reference for the the Container instance and the connectors array for all the connectors.

StandardService 类使用容器变量作为 Container 实例的对象引用,使用 connectors 数组来存储所有的连接器。

private Container container = null;
private Connector connectors[] = new Connector[0];

To associate a container with this service, you use the setContainer method, which is given in Listing 14.7.

要将容器与此服务关联起来,可以使用清单 14.7 中的 setContainer 方法。

Listing 14.7: The setContainer method

清单 14.7:setContainer 方法

public void setContainer(Container container) {
        Container oldContainer = this.container;
        if ((oldContainer != null) && (oldContainer instanceof Engine))
            ((Engine) oldContainer).setService(null);
        this.container = container;
        if ((this.container != null) && (this.container instanceof Engine))
            ((Engine) this.container).setService(this);
        if (started && (this.container != null) &&
                (this.container instanceof Lifecycle)) {
            try {
                ((Lifecycle) this.container).start();
            }
            catch (LifecycleException e) {
                ;
            }
        }
        synchronized (connectors) {
            for (int i = 0; i < connectors.length; i++)
                connectors[i].setContainer(this.container);
        }
        if (started && (oldContainer != null) &&
                (oldContainer instanceof Lifecycle)) {
            try {
                ((Lifecycle) oldContainer).stop();
            }
            catch (LifecycleException e) {
                ;
            }
        }
        // Report this property change to interested listeners
        support.firePropertyChange("container", oldContainer,
                this.container);
    }

The container associated with this service will be passed to the setContainer method on each Connector object that is available in this service to create the association between the container and each individual connector.

与此服务相关联的容器将被传递给该服务中可用的每个连接器对象上的setContainer方法,以创建容器与每个单独连接器之间的关联。

To add a connector to a Service object, use the addConnector method. To remove a connector, call the removeConnector method. The addConnector method is given in Listing 14.8 and the removeConnector method in Listing 14.9.

要向Service对象添加连接器,请使用 addConnector 方法。要删除连接器,请调用 removeConnector 方法。addConnector方法在清单14.8中给出,removeConnector 方法在清单14.9中给出。

Listing 14.8: The addConnector method

清单14.8:addConnector 方法

public void addConnector(Connector connector) {
        synchronized (connectors) {
            connector.setContainer(this.container);
            connector.setService(this);
            Connector results[] = new Connector[connectors.length + 1];
            System.arraycopy(connectors, 0, results, 0, connectors.length);
            results[connectors.length] = connector;
            connectors = results;
            if (initialized) {
                try {
                    connector.initialize();
                }
                catch (LifecycleException e) {
                    e.printStackTrace(System.err);
                }
            }
            if (started && (connector instanceof Lifecycle)) {
                try {
                    ((Lifecycle) connector).start();
                }
                catch (LifecycleException e) {
                    ;
                }
            }
            // Report this property change to interested listeners
            support.firePropertyChange("connector", null, connector);
        }
    }

Listing 14.9: The removeConnector method

清单 14.9: removeConnector 方法

public void removeConnector(Connector connector) {  
    synchronized (connectors) {  
        int j = -1;  
        for (int i = 0; i < connectors.length; i++) {  
            if (connector == connectors [i]) {  
                j = i;  
                break;            }  
        }  
        if (j < 0)  
            return;  
        if (started && (connectors[j] instanceof Lifecycle)) {  
            try {  
                ((Lifecycle) connectors[j]).stop();  
            }  
            catch (LifecycleException e) {  
                ;  
            }  
        }  
        connectors[j].setContainer(null);  
        connector.setService(null);  
        int k = 0;  
        Connector results[] = new Connector[connectors.length - 1];  
        for (int i = 0; i < connectors.length; i++) {  
            if (i != j)  
                results[k++] = connectors[i];  
        }  
        connectors = results;  
        // Report this property change to interested listeners  
        support.firePropertyChange("connector", connector, null);  
    }  
}

The addConnector method initializes and starts the added connector.

addConnector 方法初始化并启动添加的连接器。

Lifecycle Methods(生命周期方法)

The lifecycle methods are the start and the stop methods inherited from the Lifecycle interface plus the initialize method. The initialize method calls the initialize method of each connector in this service. This method is given in Listing 14.10.

生命周期方法是从 Lifecycle 接口继承的 startstop 方法,再加上 initialize 方法。

initialize 方法会调用该服务中每个连接器的 initialize 方法。

该方法在代码清单14.10中给出。

Listing 14.10: The initialize method of StandardService

代码清单14.10:StandardServiceinitialize 方法

 public void initialize() throws LifecycleException {
        if (initialized)
            throw new LifecycleException (
                    sm.getString("StandardService.initialize.initialized"));
        initialized = true;
        // Initialize our defined Connectors
        synchronized (connectors) {
            for (int i = 0; i < connectors.length; i++) {
                connectors[i].initialize();
            }
        }
    }

The start method starts the associated container as well as all the connector added to this service. This method is presented in Listing 14.11.

start 方法会启动关联容器以及添加到此服务的所有连接器。

该方法在清单 14.11 中列出。

Listing 14.11: The start method

清单 14.11:启动方法

public void start() throws LifecycleException {  
    // Validate and update our current component state  
    if (started) {  
        throw new LifecycleException  
                (sm.getString("standardService.start.started"));  
    }  
    // Notify our interested LifecycleListeners  
    lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);  
    System.out.println  
            (sm.getString("standardService.start.name", this.name));  
    lifecycle.fireLifecycleEvent(START_EVENT, null);  
    started = true;  
    // Start our defined Container first  
    if (container != null) {  
        synchronized (container) {  
            if (container instanceof Lifecycle) {  
                ((Lifecycle) container).start();  
            }  
        }  
    }  
    // Start our defined Connectors second  
    synchronized (connectors) {  
        for (int i = 0; i < connectors.length; i++) {  
            if (connectors[i] instanceof Lifecycle)  
                ((Lifecycle) connectors[i]).start();  
        }  
    }  
    // Notify our interested LifecycleListeners  
    lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);  
}

The stop method stops the associated container and all the connectors. The stop method is given in Listing 14.12.

stop 方法会停止关联容器和所有连接器。

清单 14.12 给出了 stop 方法。

Listing 14.12: The stop method

清单 14.12:stop 方法

 public void stop() throws LifecycleException {
        // Validate and update our current component state
        if (!started) {
            throw new LifecycleException
                    (sm.getString("standardService.stop.notStarted"));
        }
        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        System.out.println
                (sm.getString("standardService.stop.name", this.name));
        started = false;
        // Stop our defined Connectors first
        synchronized (connectors) {
            for (int i = 0; i < connectors.length; i++) {
                if (connectors[i] instanceof Lifecycle)
                    ((Lifecycle) connectors[i]).stop();
            }
        }
        // Stop our defined Container second
        if (container != null) {
            synchronized (container) {
                if (container instanceof Lifecycle) {
                    ((Lifecycle) container).stop();
                }
            }
        }
        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
    }

The Application(应用)

The application shows you how to use a server and a service. In particular, it demonstrates how to utilize the start and stop mechanisms in the StandardServer class. There are three classes in the application. The first is the SimpleContextConfig, which is a copy from the application in Chapter 13. The two other classes are Bootstrap and Stopper. The Bootstrap class is used to start the application, and the Stopper class to stop it.

该应用程序向您展示了如何使用服务器和服务。

特别是,它演示了如何利用StandardServer类中的启动和停止机制。

应用程序中有三个类。

第一个是 SimpleContextConfig,这是第13章应用程序的副本。

另外两个类是 BootstrapStopper

Bootstrap 类用于启动应用程序,

Stopper 类用于停止应用程序。

The Bootstrap Class(Bootstrap 类)

The Bootstrap class is given in Listing 14.13.

清单 14.13 中给出了 Bootstrap 类。

Listing 14.13: The Bootstrap Class

清单 14.13: Bootstrap


package ex14.pyrmont.startup;
        import ex14.pyrmont.core.SimpleContextConfig;
        import org.apache.catalina.Connector;
        import org.apache.catalina.Context;
        import org.apache.catalina.Engine;
        import org.apache.catalina.Host;
        import org.apache.catalina.Lifecycle;
        import org.apache.catalina.LifecycleException;
        import org.apache.catalina.LifecycleListener;
        import org.apache.catalina.Loader;
        import org.apache.catalina.Server;
        import org.apache.catalina.Service;
        import org.apache.catalina.Wrapper;
        import org.apache.catalina.connector.http.HttpConnector;
        import org.apache.catalina.core.StandardContext;
        import org.apache.catalina.core.StandardEngine;
        import org.apache.catalina.core.StandardHost;
        import org.apache.catalina.core.StandardServer;
        import org.apache.catalina.core.StandardService;
        import org.apache.catalina.core.StandardWrapper;
        import org.apache.catalina.loader.WebappLoader;
public final class Bootstrap {
    public static void main(String[] args) {
        System.setProperty("catalina.base",
                System.getProperty("user.dir"));
        Connector connector = new HttpConnector();
        Wrapper wrapper1 = new StandardWrapper();
        wrapper1.setName("Primitive");
        wrapper1.setServletClass("PrimitiveServlet");
        Wrapper wrapper2 = new StandardWrapper();
        wrapper2.setName("Modern");
        wrapper2.setServletClass("ModernServlet");
        Context context = new StandardContext();
        // StandardContext's start method adds a default mapper
        context.setPath("/app1");
        context.setDocBase("app1");
        context.addChild(wrapper1);
        context.addChild(wrapper2);
        LifecycleListener listener = new SimpleContextConfig();
        ((Lifecycle) context).addLifecycleListener(listener);
        Host host = new StandardHost();
        host.addChild(context);
        host.setName("localhost");
        host.setAppBase("webapps");
        Loader loader = new WebappLoader();
        context.setLoader(loader);
        // context.addServletMapping(pattern, name);
        context.addServletMapping("/Primitive", "Primitive");
        context.addServletMapping("/Modern", "Modern");
        Engine engine = new StandardEngine();
        engine.addChild(host);
        engine.setDefaultHost("localhost");
        Service service = new StandardService();
        service.setName("Stand-alone Service");
        Server server = new StandardServer();
        server.addService(service);
        service.addConnector(connector);
        // StandardService class's setContainer method calls
        // its connectors' setContainer method
        service.setContainer(engine);
        // Start the new server
        if (server instanceof Lifecycle) {
            try {
                server.initialize();
                ((Lifecycle) server).start();
                server.await();
                // the program waits until the await method returns,
                // i.e. until a shutdown command is received.
            }
            catch (LifecycleException e) {
                e.printStackTrace(System.out);
            }
        }
        // Shut down the server
        if (server instanceof Lifecycle) {
            try {
                ((Lifecycle) server).stop();
            }
            catch (LifecycleException e) {
                e.printStackTrace(System.out);
            }
        }
    }
}

The beginning part of the main method of the Bootstrap class is similar to the one in Chapter 13. It creates a connector, two wrappers, a context, a host, and an engine. It then adds the wrappers to the context, the context to the host, and the host to the engine. It does not associate the connector with the top level container (the engine), however. Instead, the main method creates a Service object, sets its name, creates a Server object, and adds the service to the server.

Bootstrap 类的主方法的开始部分与第 13 章中的类似。

它创建了一个连接器、两个包装器、一个上下文、一个主机和一个引擎。

然后将包装器添加到上下文,上下文添加到主机,主机添加到引擎。

然而,它并没有将连接器与顶层容器(引擎)关联起来。

相反,主方法创建了一个 Service 对象,设置了它的名称,创建了一个 Server 对象,并将服务添加到服务器中。

Service service = new StandardService();
 service.setName("Stand-alone Service");
 Server server = new StandardServer();
 server.addService(service);

The main method then adds the connector and the engine to the service:

然后,主方法会将连接器和引擎添加到服务中:

service.addConnector(connector);
 service.setContainer(engine);

By adding the connector to the service, the connector will be associated with the container in the service.

通过将连接器添加到服务中,连接器将与服务中的容器关联。

The main method then calls the initialize and start methods of the Server, thereby initializing the connector and starting the connector and the container:

然后,主方法调用服务器的初始化和启动方法,从而初始化连接器并启动连接器和容器。

if (server instanceof Lifecycle) {
 try {
 server.initialize();
 ((Lifecycle) server).start();

Next, it calls the server's await method that makes the server waits for a shutdown command on port 8085. Note that at this stage the connector is already running, waiting for HTTP requests on port 8080 (the default port).

接下来,它调用服务器的 await 方法,使服务器在8085端口上等待关闭命令。

请注意,在这个阶段,连接器已经在 8080 端口上运行,等待HTTP请求(默认端口)。

server.await() ;

The await method will not return until the correct shutdown command is received. When that happens, the main method calls the server's stop method, in effect stopping all other components.

await 方法在收到正确的关闭命令之前不会返回。

当这种情况发生时,主方法调用服务器的停止方法,实际上停止了所有其他组件。

Let's now review the Stopper class that you can use to stop the Server.

现在让我们来回顾一下 Stopper 类,你可以用它来停止服务器。

The Stopper Class(塞子类 )

In the application in previous chapters, you either stop the container either abruptly or by pressing a key. The Stopper class provides a more elegant way of stopping a Catalina server. It also makes sure that the stop method of all lifecycle components will be called.

在前几章的应用程序中,你要么通过突然停止容器,要么通过按下一个键来停止。

Stopper 类提供了一种更优雅的方式来停止 Catalina 服务器。

它还确保调用所有生命周期组件的 stop 方法。

The Stopper class is given in Listing 14.14.

Stopper 类的代码如下(见第14.14节):

Listing 14.14: The Stopper class

package ex14.pyrmont.startup;  
        import java.io.OutputStream;  
        import java.io.IOException;  
        import java.net.Socket;  
public class Stopper {  
    public static void main(String[] args) {  
        // the following code is taken from the Stop method of  
        // the org.apache.catalina.startup.Catalina class        int port = 8005;  
        try {  
            Socket socket = new Socket("127.0.0.1", port);  
            OutputStream stream = socket.getOutputStream();  
            String shutdown = "SHUTDOWN";  
            for (int i = 0; i < shutdown.length(); i++)  
                stream.write(shutdown.charAt(i));  
            stream.flush();  
            stream.close();  
            socket.close();  
            System.out.println("The server was successfully shut down.");  
        }  
        catch (IOException e) {  
            System.out.println("Error. The server has not been started.");  
        }  
    }  
}

The main method of the Stopper class creates a Socket object and then flushes the string SHUTDOWN, which is the correct shutdown command, to port 8085. If a Catalina server is running, it will be shut down.

Stopper 类的主要方法创建一个 Socket 对象,然后将正确的关闭命令字符串"SHUTDOWN"发送到端口 8085

如果 Catalina 服务器正在运行,它将被关闭。

Running the Applications(运行应用程序)

To run the Bootstrap class in Windows, from the working directory, type the following:

要在 Windows 中运行 Bootstrap 类,请从工作目录中输入以下命令:

java -classpath./lib/servlet.jar;./lib/commonscollections.jar;./lib/commons-digester.jar;./lib/namingfactory.jar;./lib/naming-common.jar;./ ex14.pyrmont.startup.Bootstrap

In Linux, you use a colon to separate two libraries.

在 Linux 中,使用冒号分隔两个库。

java -classpath ./lib/servlet.jar:./lib/commonscollections.jar:./lib/commons-digester.jar:./lib/namingfactory.jar:./lib/naming-common.jar:./ ex14.pyrmont.startup.Bootstrap

To invoke PrimitiveServlet, use the following URL in your browser.

要调用 PrimitiveServlet,请在浏览器中使用以下 URL

http://localhost:8080/app1/Primitive

To invoke ModernServlet, use the following URL.

要调用 ModernServlet,请使用以下 URL。

http://localhost:8080/app1/Modern

To run the stopper to stop the application in both Windows and Linux, from the working directory, type the following:

要调用 PrimitiveServlet,请在浏览器中使用以下 URL。

java ex14.pyrmont.startup.Stopper

Note that in a real Catalina deployment, the functionality offered by the Stopper class is encapsulated into the Bootstrap class.

请注意,在真正的 Catalina 部署中,Stopper 类提供的功能已封装到 Bootstrap 类中。

Summary

This chapter explains two important components in Catalina: server and service. A server is particularly useful because it provides a gentle mechanism for starting and stopping a Catalina deployment. A service component encapsulates a container and one or many connectors. The application accompanying this chapter shows how to use the server and service components. It also demonstrates how you can use the stop mechanism in the StandardServer class.

这一章节解释了 Catalina 中的两个重要组件:服务器和服务。

服务器特别有用,因为它提供了一个温和的机制来启动和停止 Catalina 部署。

服务组件封装了一个容器和一个或多个连接器。

本章附带的应用程序展示了如何使用服务器和服务组件。它还演示了如何在 StandardServer 类中使用停止机制。


Xander
198 声望51 粉丝