| /* |
| * Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. |
| * Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed 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 org.apache.catalina.*; |
| import org.apache.catalina.deploy.NamingResources; |
| import org.apache.catalina.util.LifecycleSupport; |
| import static com.sun.logging.LogCleanerUtil.neutralizeForLog; |
| |
| import javax.management.ObjectName; |
| 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.security.SecureRandom; |
| import java.util.List; |
| import java.util.ResourceBundle; |
| import java.text.MessageFormat; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| |
| /** |
| * Standard implementation of the <b>Server</b> interface, available for use |
| * (but not required) when deploying and starting Catalina. |
| * |
| * @author Craig R. McClanahan |
| * @version $Revision: 1.5 $ $Date: 2007/02/20 20:16:56 $ |
| */ |
| |
| public final class StandardServer |
| implements Lifecycle, Server |
| { |
| private static final Logger log = LogFacade.getLogger(); |
| private static final ResourceBundle rb = log.getResourceBundle(); |
| |
| //-------------------------------------------------------------- |
| |
| |
| // -------------------------------------------------------------- Constants |
| |
| |
| /** |
| * The set of class/property combinations that should <strong>NOT</strong> |
| * be persisted because they are automatically calculated. |
| */ |
| private static String exceptions[][] = { |
| { "org.apache.catalina.core.StandardEngine", "domain" }, |
| { "org.apache.catalina.core.StandardHost", "domain" }, |
| { "org.apache.catalina.core.StandardContext", "available" }, |
| { "org.apache.catalina.core.StandardContext", "configFile" }, |
| { "org.apache.catalina.core.StandardContext", "configured" }, |
| { "org.apache.catalina.core.StandardContext", "distributable" }, |
| { "org.apache.catalina.core.StandardContext", "domain" }, |
| { "org.apache.catalina.core.StandardContext", "engineName" }, |
| { "org.apache.catalina.core.StandardContext", "name" }, |
| { "org.apache.catalina.core.StandardContext", "override" }, |
| { "org.apache.catalina.core.StandardContext", "publicId" }, |
| { "org.apache.catalina.core.StandardContext", "replaceWelcomeFiles" }, |
| { "org.apache.catalina.core.StandardContext", "sessionTimeout" }, |
| { "org.apache.catalina.core.StandardContext", "startupTime" }, |
| { "org.apache.catalina.core.StandardContext", "tldScanTime" }, |
| { "org.apache.catalina.core.StandardContext", "workDir" }, |
| { "org.apache.catalina.session.StandardManager", "distributable" }, |
| { "org.apache.catalina.session.StandardManager", "entropy" }, |
| }; |
| |
| |
| /** |
| * The set of classes that represent persistable properties. |
| */ |
| private static Class persistables[] = { |
| String.class, |
| Integer.class, Integer.TYPE, |
| Boolean.class, Boolean.TYPE, |
| Byte.class, Byte.TYPE, |
| Character.class, Character.TYPE, |
| Double.class, Double.TYPE, |
| Float.class, Float.TYPE, |
| Long.class, Long.TYPE, |
| Short.class, Short.TYPE, |
| }; |
| |
| |
| /** |
| * The set of class names that should be skipped when persisting state, |
| * because the corresponding listeners, valves, etc. are configured |
| * automatically at startup time. |
| */ |
| private static String skippables[] = { |
| "org.apache.catalina.authenticator.BasicAuthenticator", |
| "org.apache.catalina.authenticator.DigestAuthenticator", |
| "org.apache.catalina.authenticator.FormAuthenticator", |
| "org.apache.catalina.authenticator.NonLoginAuthenticator", |
| "org.apache.catalina.authenticator.SSLAuthenticator", |
| "org.apache.catalina.core.NamingContextListener", |
| "org.apache.catalina.core.StandardContextValve", |
| "org.apache.catalina.core.StandardEngineValve", |
| "org.apache.catalina.core.StandardHostValve", |
| "org.apache.catalina.startup.ContextConfig", |
| "org.apache.catalina.startup.EngineConfig", |
| "org.apache.catalina.startup.HostConfig", |
| "org.apache.catalina.valves.CertificatesValve", |
| "org.apache.catalina.valves.ErrorReportValve", |
| "org.apache.catalina.valves.RequestListenerValve", |
| }; |
| |
| |
| /** |
| * The set of class names that are the standard implementations of |
| * components, and hence should not be persisted. |
| */ |
| private static String standardImplementations[] = { |
| "org.apache.catalina.core.StandardServer", |
| "org.apache.catalina.core.StandardService", |
| "org.apache.catalina.connector.CoyoteConnector", |
| "org.apache.catalina.core.StandardEngine", |
| "org.apache.catalina.core.StandardHost", |
| "org.apache.catalina.core.StandardContext" |
| }; |
| |
| |
| // ------------------------------------------------------------ Constructor |
| |
| |
| /** |
| * Construct a default instance of this class. |
| */ |
| public StandardServer() { |
| |
| super(); |
| ServerFactory.setServer(this); |
| |
| globalNamingResources = new NamingResources(); |
| globalNamingResources.setContainer(this); |
| |
| if (isUseNaming()) { |
| namingContextListener = new NamingContextListener(); |
| namingContextListener.setDebug(getDebug()); |
| addLifecycleListener(namingContextListener); |
| } |
| |
| } |
| |
| |
| // ----------------------------------------------------- Instance Variables |
| |
| |
| /** |
| * Debugging detail level. |
| */ |
| private int debug = 0; |
| |
| |
| /** |
| * 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 lifecycle event support for this component. |
| */ |
| private LifecycleSupport lifecycle = new LifecycleSupport(this); |
| |
| |
| /** |
| * 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; |
| |
| |
| /** |
| * A random number generator that is <strong>only</strong> used if |
| * the shutdown command string is longer than 1024 characters. |
| */ |
| private SecureRandom random = null; |
| |
| |
| /** |
| * The set of Services associated with this Server. |
| */ |
| private Service services[] = new Service[0]; |
| |
| private final Object servicesMonitor = new Object(); |
| |
| |
| /** |
| * The shutdown command string we are looking for. |
| */ |
| private String shutdown = "SHUTDOWN"; |
| |
| /** |
| * Has this component been started? |
| */ |
| private boolean started = false; |
| |
| |
| /** |
| * Has this component been initialized? |
| */ |
| private boolean initialized = false; |
| |
| |
| /** |
| * The property change support for this component. |
| */ |
| private PropertyChangeSupport support = new PropertyChangeSupport(this); |
| |
| |
| // ------------------------------------------------------------- Properties |
| |
| |
| /** |
| * Return the debugging detail level. |
| */ |
| public int getDebug() { |
| |
| return (this.debug); |
| |
| } |
| |
| |
| /** |
| * Set the debugging detail level. |
| * |
| * @param debug The new debugging detail level |
| */ |
| public void setDebug(int debug) { |
| |
| this.debug = debug; |
| |
| } |
| |
| |
| /** |
| * 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); |
| |
| } |
| |
| |
| /** |
| * 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 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 (servicesMonitor) { |
| Service results[] = new Service[services.length + 1]; |
| System.arraycopy(services, 0, results, 0, services.length); |
| results[services.length] = service; |
| services = results; |
| |
| if (initialized) { |
| try { |
| service.initialize(); |
| } catch (LifecycleException e) { |
| String msg = MessageFormat.format(rb.getString( |
| LogFacade.LIFECYCLE_EXCEPTION_DURING_SERVICE_INIT), e.toString()); |
| log.log(Level.SEVERE, msg, e); |
| } |
| } |
| |
| if (started && (service instanceof Lifecycle)) { |
| try { |
| ((Lifecycle) service).start(); |
| } catch (LifecycleException e) { |
| // Ignore |
| } |
| } |
| |
| // Report this property change to interested listeners |
| support.firePropertyChange("service", null, service); |
| } |
| |
| } |
| |
| |
| /** |
| * 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) { |
| String msg = MessageFormat.format( |
| rb.getString(LogFacade.STANDARD_SERVER_AWAIT_CREATE_EXCEPTION), port); |
| log.log(Level.SEVERE, msg, 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) { |
| String msg = MessageFormat.format( |
| rb.getString(LogFacade.STANDARD_SERVER_ACCEPT_SECURITY_EXCEPTION), |
| ace.getMessage()); |
| log.log(Level.WARNING, msg, ace); |
| continue; |
| } catch (IOException e) { |
| String msg = MessageFormat.format( |
| rb.getString(LogFacade.STANDARD_SERVER_AWAIT_ACCEPT_EXCEPTION), |
| e.toString()); |
| log.log(Level.SEVERE, msg, 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 SecureRandom();//use self seeding |
| expected += random.nextInt(1024); |
| } |
| while (expected > 0) { |
| int ch = -1; |
| try { |
| ch = stream.read(); |
| } catch (IOException e) { |
| String msg = MessageFormat.format( |
| rb.getString(LogFacade.STANDARD_SERVER_AWAIT_READ_EXCEPTION), |
| e.toString()); |
| log.log(Level.WARNING, msg, 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) { |
| break; |
| } else { |
| log.log(Level.WARNING, LogFacade.STANDARD_SERVER_AWAIT_INVALID_COMMAND_RECEIVED_EXCEPTION, |
| neutralizeForLog(command.toString())); |
| } |
| } |
| |
| // 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 (servicesMonitor) { |
| 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 object names of all registered Service instances |
| */ |
| 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 (servicesMonitor) { |
| int j = -1; |
| for (int i = 0; i < services.length; i++) { |
| if (service == services[i]) { |
| j = i; |
| break; |
| } |
| } |
| if (j < 0) |
| return; |
| if (services[j] instanceof Lifecycle) { |
| try { |
| ((Lifecycle) 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. |
| */ |
| public String toString() { |
| |
| StringBuilder sb = new StringBuilder("StandardServer["); |
| sb.append(getPort()); |
| sb.append("]"); |
| return (sb.toString()); |
| |
| } |
| |
| |
| // -------------------------------------------------------- Private Methods |
| |
| |
| /** |
| * 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; |
| } |
| |
| |
| // ---------------------------------------------------- Lifecycle Methods |
| |
| |
| /** |
| * Add a LifecycleEvent listener to this component. |
| * |
| * @param listener The listener to add |
| */ |
| public void addLifecycleListener(LifecycleListener listener) { |
| lifecycle.addLifecycleListener(listener); |
| } |
| |
| |
| /** |
| * Gets the (possibly empty) list of lifecycle listeners |
| * associated with this StandardServer. |
| */ |
| public List<LifecycleListener> findLifecycleListeners() { |
| return lifecycle.findLifecycleListeners(); |
| } |
| |
| |
| /** |
| * Remove a LifecycleEvent listener from this component. |
| * |
| * @param listener The listener to remove |
| */ |
| public void removeLifecycleListener(LifecycleListener listener) { |
| lifecycle.removeLifecycleListener(listener); |
| } |
| |
| |
| /** |
| * Prepare for the beginning of active use of the public methods of this |
| * component. This method should be called before any of the public |
| * methods of this component are utilized. It should also send a |
| * LifecycleEvent of type START_EVENT to any registered listeners. |
| * |
| * @exception LifecycleException if this component detects a fatal error |
| * that prevents this component from being used |
| */ |
| public void start() throws LifecycleException { |
| |
| // Validate and update our current component state |
| if (started) { |
| if (log.isLoggable(Level.FINE)) { |
| log.log(Level.FINE, "This server has already been started"); |
| } |
| return; |
| } |
| |
| // Notify our interested LifecycleListeners |
| lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); |
| |
| lifecycle.fireLifecycleEvent(START_EVENT, null); |
| started = true; |
| |
| // Start our defined Services |
| synchronized (servicesMonitor) { |
| 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); |
| |
| } |
| |
| |
| /** |
| * Gracefully terminate the active use of the public methods of this |
| * component. This method should be the last one called on a given |
| * instance of this component. It should also send a LifecycleEvent |
| * of type STOP_EVENT to any registered listeners. |
| * |
| * @exception LifecycleException if this component detects a fatal error |
| * that needs to be reported |
| */ |
| public void stop() throws LifecycleException { |
| |
| // Validate and update our current component state |
| if (!started) |
| return; |
| |
| // 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); |
| |
| } |
| |
| public void init() throws Exception { |
| initialize(); |
| } |
| |
| /** |
| * Invoke a pre-startup initialization. This is used to allow connectors |
| * to bind to restricted ports under Unix operating environments. |
| */ |
| public void initialize() |
| throws LifecycleException |
| { |
| if (initialized) { |
| log.log(Level.INFO, LogFacade.STANDARD_SERVER_INITIALIZE_INITIALIZED); |
| return; |
| } |
| // START GlassFish 2439 |
| lifecycle.fireLifecycleEvent(INIT_EVENT, null); |
| // END GlassFish 2439 |
| initialized = true; |
| |
| if( oname==null ) { |
| try { |
| oname=new ObjectName( "Catalina:type=Server"); |
| } catch (Exception e) { |
| String msg = MessageFormat.format(rb.getString(LogFacade.ERROR_REGISTERING), e.toString()); |
| log.log(Level.SEVERE, msg, e); |
| } |
| } |
| |
| // Initialize our defined Services |
| for (int i = 0; i < services.length; i++) { |
| services[i].initialize(); |
| } |
| } |
| |
| private String type; |
| private String domain; |
| private String suffix; |
| private ObjectName oname; |
| |
| public ObjectName getObjectName() { |
| return oname; |
| } |
| |
| public String getDomain() { |
| return domain; |
| } |
| |
| } |