/projects/tomcat-7.0.2/java/org/apache/catalina/core/StandardServer.java
Java | 755 lines | 351 code | 172 blank | 232 comment | 41 complexity | bbddda3e0251b727f0c388c5e0e81215 MD5 | raw file
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.catalina.core;
- import java.beans.PropertyChangeListener;
- import java.beans.PropertyChangeSupport;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.security.AccessControlException;
- import java.util.Random;
- import javax.management.ObjectName;
- import org.apache.catalina.Context;
- import org.apache.catalina.LifecycleException;
- import org.apache.catalina.LifecycleState;
- import org.apache.catalina.Engine;
- import org.apache.catalina.Server;
- import org.apache.catalina.Service;
- import org.apache.catalina.deploy.NamingResources;
- import org.apache.catalina.mbeans.MBeanFactory;
- import org.apache.catalina.mbeans.MBeanUtils;
- import org.apache.catalina.util.LifecycleBase;
- import org.apache.catalina.util.LifecycleMBeanBase;
- import org.apache.tomcat.util.res.StringManager;
- import org.apache.catalina.util.ServerInfo;
- import org.apache.juli.logging.Log;
- import org.apache.juli.logging.LogFactory;
- import org.apache.tomcat.util.buf.StringCache;
- /**
- * Standard implementation of the <b>Server</b> interface, available for use
- * (but not required) when deploying and starting Catalina.
- *
- * @author Craig R. McClanahan
- * @version $Id: StandardServer.java 981602 2010-08-02 17:05:03Z markt $
- */
- public final class StandardServer extends LifecycleMBeanBase
- implements Server {
- private static final Log log = LogFactory.getLog(StandardServer.class);
-
- // ------------------------------------------------------------ Constructor
- /**
- * Construct a default instance of this class.
- */
- public StandardServer() {
- super();
- globalNamingResources = new NamingResources();
- globalNamingResources.setContainer(this);
- if (isUseNaming()) {
- if (namingContextListener == null) {
- namingContextListener = new NamingContextListener();
- addLifecycleListener(namingContextListener);
- }
- }
- }
- // ----------------------------------------------------- Instance Variables
- /**
- * Global naming resources context.
- */
- private javax.naming.Context globalNamingContext = null;
- /**
- * Global naming resources.
- */
- private NamingResources globalNamingResources = null;
- /**
- * Descriptive information about this Server implementation.
- */
- private static final String info =
- "org.apache.catalina.core.StandardServer/1.0";
- /**
- * The naming context listener for this web application.
- */
- private NamingContextListener namingContextListener = null;
- /**
- * The port number on which we wait for shutdown commands.
- */
- private int port = 8005;
- /**
- * The address on which we wait for shutdown commands.
- */
- private String address = "localhost";
- /**
- * A random number generator that is <strong>only</strong> used if
- * the shutdown command string is longer than 1024 characters.
- */
- private Random random = null;
- /**
- * The set of Services associated with this Server.
- */
- private Service services[] = new Service[0];
- /**
- * The shutdown command string we are looking for.
- */
- private String shutdown = "SHUTDOWN";
- /**
- * The string manager for this package.
- */
- private static final StringManager sm =
- StringManager.getManager(Constants.Package);
- /**
- * The property change support for this component.
- */
- protected PropertyChangeSupport support = new PropertyChangeSupport(this);
- private boolean stopAwait = false;
- // ------------------------------------------------------------- Properties
- /**
- * Return the global naming resources context.
- */
- public javax.naming.Context getGlobalNamingContext() {
- return (this.globalNamingContext);
- }
- /**
- * Set the global naming resources context.
- *
- * @param globalNamingContext The new global naming resource context
- */
- public void setGlobalNamingContext
- (javax.naming.Context globalNamingContext) {
- this.globalNamingContext = globalNamingContext;
- }
- /**
- * Return the global naming resources.
- */
- public NamingResources getGlobalNamingResources() {
- return (this.globalNamingResources);
- }
- /**
- * Set the global naming resources.
- *
- * @param globalNamingResources The new global naming resources
- */
- public void setGlobalNamingResources
- (NamingResources globalNamingResources) {
- NamingResources oldGlobalNamingResources =
- this.globalNamingResources;
- this.globalNamingResources = globalNamingResources;
- this.globalNamingResources.setContainer(this);
- support.firePropertyChange("globalNamingResources",
- oldGlobalNamingResources,
- this.globalNamingResources);
- }
- /**
- * Return descriptive information about this Server implementation and
- * the corresponding version number, in the format
- * <code><description>/<version></code>.
- */
- public String getInfo() {
- return (info);
- }
- /**
- * Report the current Tomcat Server Release number
- * @return Tomcat release identifier
- */
- public String getServerInfo() {
- return ServerInfo.getServerInfo();
- }
- /**
- * Return the port number we listen to for shutdown commands.
- */
- public int getPort() {
- return (this.port);
- }
- /**
- * Set the port number we listen to for shutdown commands.
- *
- * @param port The new port number
- */
- public void setPort(int port) {
- this.port = port;
- }
- /**
- * Return the address on which we listen to for shutdown commands.
- */
- public String getAddress() {
- return (this.address);
- }
- /**
- * Set the address on which we listen to for shutdown commands.
- *
- * @param address The new address
- */
- public void setAddress(String address) {
- this.address = address;
- }
- /**
- * Return the shutdown command string we are waiting for.
- */
- public String getShutdown() {
- return (this.shutdown);
- }
- /**
- * Set the shutdown command we are waiting for.
- *
- * @param shutdown The new shutdown command
- */
- public void setShutdown(String shutdown) {
- this.shutdown = shutdown;
- }
- // --------------------------------------------------------- Server Methods
- /**
- * Add a new Service to the set of defined Services.
- *
- * @param service The Service to be added
- */
- public void addService(Service service) {
- service.setServer(this);
- synchronized (services) {
- Service results[] = new Service[services.length + 1];
- System.arraycopy(services, 0, results, 0, services.length);
- results[services.length] = service;
- services = results;
- if (getState().isAvailable()) {
- try {
- service.start();
- } catch (LifecycleException e) {
- // Ignore
- }
- }
- // Report this property change to interested listeners
- support.firePropertyChange("service", null, service);
- }
- }
- public void stopAwait() {
- stopAwait=true;
- }
- /**
- * Wait until a proper shutdown command is received, then return.
- * This keeps the main thread alive - the thread pool listening for http
- * connections is daemon threads.
- */
- public void await() {
- // Negative values - don't wait on port - tomcat is embedded or we just don't like ports
- if( port == -2 ) {
- // undocumented yet - for embedding apps that are around, alive.
- return;
- }
- if( port==-1 ) {
- while( true ) {
- try {
- Thread.sleep( 10000 );
- } catch( InterruptedException ex ) {
- }
- if( stopAwait ) return;
- }
- }
-
- // Set up a server socket to wait on
- ServerSocket serverSocket = null;
- try {
- serverSocket =
- new ServerSocket(port, 1,
- InetAddress.getByName(address));
- } catch (IOException e) {
- log.error("StandardServer.await: create[" + address
- + ":" + port
- + "]: ", e);
- 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) {
- log.warn("StandardServer.accept security exception: "
- + ace.getMessage(), ace);
- continue;
- } catch (IOException e) {
- log.error("StandardServer.await: accept: ", e);
- System.exit(1);
- }
- // Read a set of characters from the socket
- StringBuilder command = new StringBuilder();
- int expected = 1024; // Cut off to avoid DoS attack
- while (expected < shutdown.length()) {
- if (random == null)
- random = new Random();
- expected += (random.nextInt() % 1024);
- }
- while (expected > 0) {
- int ch = -1;
- try {
- ch = stream.read();
- } catch (IOException e) {
- log.warn("StandardServer.await: read: ", e);
- 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) {
- // Ignore
- }
- // Match against our command string
- boolean match = command.toString().equals(shutdown);
- if (match) {
- log.info(sm.getString("standardServer.shutdownViaPort"));
- break;
- } else
- log.warn("StandardServer.await: Invalid command '" +
- command.toString() + "' received");
- }
- // Close the server socket and return
- try {
- serverSocket.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- /**
- * 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) {
- if (name == null) {
- return (null);
- }
- synchronized (services) {
- for (int i = 0; i < services.length; i++) {
- if (name.equals(services[i].getName())) {
- return (services[i]);
- }
- }
- }
- return (null);
- }
- /**
- * Return the set of Services defined within this Server.
- */
- public Service[] findServices() {
- return (services);
- }
-
- /**
- * Return the JMX service names.
- */
- public ObjectName[] getServiceNames() {
- ObjectName onames[]=new ObjectName[ services.length ];
- for( int i=0; i<services.length; i++ ) {
- onames[i]=((StandardService)services[i]).getObjectName();
- }
- return onames;
- }
- /**
- * Remove the specified Service from the set associated from this
- * Server.
- *
- * @param service The Service to be removed
- */
- public void removeService(Service service) {
- synchronized (services) {
- int j = -1;
- for (int i = 0; i < services.length; i++) {
- if (service == services[i]) {
- j = i;
- break;
- }
- }
- if (j < 0)
- return;
- try {
- services[j].stop();
- } catch (LifecycleException e) {
- // Ignore
- }
- int k = 0;
- Service results[] = new Service[services.length - 1];
- for (int i = 0; i < services.length; i++) {
- if (i != j)
- results[k++] = services[i];
- }
- services = results;
- // Report this property change to interested listeners
- support.firePropertyChange("service", service, null);
- }
- }
- // --------------------------------------------------------- Public Methods
- /**
- * Add a property change listener to this component.
- *
- * @param listener The listener to add
- */
- public void addPropertyChangeListener(PropertyChangeListener listener) {
- support.addPropertyChangeListener(listener);
- }
- /**
- * Remove a property change listener from this component.
- *
- * @param listener The listener to remove
- */
- public void removePropertyChangeListener(PropertyChangeListener listener) {
- support.removePropertyChangeListener(listener);
- }
- /**
- * Return a String representation of this component.
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("StandardServer[");
- sb.append(getPort());
- sb.append("]");
- return (sb.toString());
- }
- /**
- * Write the configuration information for this entire <code>Server</code>
- * out to the server.xml configuration file.
- *
- * @exception javax.management.InstanceNotFoundException if the managed resource object
- * cannot be found
- * @exception javax.management.MBeanException if the initializer of the object throws
- * an exception, or persistence is not supported
- * @exception javax.management.RuntimeOperationsException if an exception is reported
- * by the persistence mechanism
- */
- public synchronized void storeConfig() throws Exception {
- ObjectName sname = null;
- try {
- sname = new ObjectName("Catalina:type=StoreConfig");
- if(mserver.isRegistered(sname)) {
- mserver.invoke(sname, "storeConfig", null, null);
- } else
- log.error("StoreConfig mbean not registered" + sname);
- } catch (Throwable t) {
- log.error(t);
- }
- }
- /**
- * Write the configuration information for <code>Context</code>
- * out to the specified configuration file.
- *
- * @exception javax.management.InstanceNotFoundException if the managed resource object
- * cannot be found
- * @exception javax.management.MBeanException if the initializer of the object throws
- * an exception, or persistence is not supported
- * @exception javax.management.RuntimeOperationsException if an exception is reported
- * by the persistence mechanism
- */
- public synchronized void storeContext(Context context) throws Exception {
-
- ObjectName sname = null;
- try {
- sname = new ObjectName("Catalina:type=StoreConfig");
- if(mserver.isRegistered(sname)) {
- mserver.invoke(sname, "store",
- new Object[] {context},
- new String [] { "java.lang.String"});
- } else
- log.error("StoreConfig mbean not registered" + sname);
- } catch (Throwable t) {
- log.error(t);
- }
-
- }
- /**
- * Return true if naming should be used.
- */
- private boolean isUseNaming() {
- boolean useNaming = true;
- // Reading the "catalina.useNaming" environment variable
- String useNamingProperty = System.getProperty("catalina.useNaming");
- if ((useNamingProperty != null)
- && (useNamingProperty.equals("false"))) {
- useNaming = false;
- }
- return useNaming;
- }
- /**
- * Start nested components ({@link Service}s) and implement the requirements
- * of {@link LifecycleBase#startInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
- * that prevents this component from being used
- */
- @Override
- protected void startInternal() throws LifecycleException {
- fireLifecycleEvent(CONFIGURE_START_EVENT, null);
- setState(LifecycleState.STARTING);
- // Start our defined Services
- synchronized (services) {
- for (int i = 0; i < services.length; i++) {
- services[i].start();
- }
- }
- }
- /**
- * Stop nested components ({@link Service}s) and implement the requirements
- * of {@link LifecycleBase#stopInternal()}.
- *
- * @exception LifecycleException if this component detects a fatal error
- * that needs to be reported
- */
- @Override
- protected void stopInternal() throws LifecycleException {
- setState(LifecycleState.STOPPING);
- fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
-
- // Stop our defined Services
- for (int i = 0; i < services.length; i++) {
- services[i].stop();
- }
- if (port == -1)
- stopAwait();
- }
- /**
- * Invoke a pre-startup initialization. This is used to allow connectors
- * to bind to restricted ports under Unix operating environments.
- */
- @Override
- protected void initInternal() throws LifecycleException {
-
- super.initInternal();
- // Register global String cache
- // Note although the cache is global, if there are multiple Servers
- // present in the JVM (may happen when embedding) then the same cache
- // will be registered under multiple names
- onameStringCache = register(new StringCache(), "type=StringCache");
- // Register the MBeanFactory
- onameMBeanFactory = register(new MBeanFactory(), "type=MBeanFactory");
-
- // Register the naming resources
- onameNamingResoucres = register(globalNamingResources,
- "type=NamingResources");
-
- // Initialize our defined Services
- for (int i = 0; i < services.length; i++) {
- services[i].init();
- }
- }
-
- @Override
- protected void destroyInternal() throws LifecycleException {
- // Destroy our defined Services
- for (int i = 0; i < services.length; i++) {
- services[i].destroy();
- }
- unregister(onameMBeanFactory);
-
- unregister(onameStringCache);
-
- unregister(onameNamingResoucres);
-
- super.destroyInternal();
- }
- private ObjectName onameStringCache;
- private ObjectName onameMBeanFactory;
- private ObjectName onameNamingResoucres;
-
- /**
- * Obtain the MBean domain for this server. The domain is obtained using
- * the following search order:
- * <ol>
- * <li>Name of first {@link Engine}.</li>
- * <li>Name of first {@link Service}.</li>
- * </ol>
- */
- @Override
- protected String getDomainInternal() {
-
- String domain = null;
-
- Service[] services = findServices();
- if (services.length > 0) {
- Service service = services[0];
- if (service != null) {
- domain = MBeanUtils.getDomain(service);
- }
- }
- return domain;
- }
-
- @Override
- protected final String getObjectNameKeyProperties() {
- return "type=Server";
- }
- }