/*
 * 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.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.catalina.AccessLog;
import org.apache.catalina.Container;
import org.apache.catalina.ContainerEvent;
import org.apache.catalina.ContainerListener;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Realm;
import org.apache.catalina.Server;
import org.apache.catalina.Service;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.realm.NullRealm;
import org.apache.catalina.util.ServerInfo;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

Standard implementation of the Engine interface. Each child container must be a Host implementation to process the specific fully qualified host name of that virtual host.
You can set the jvmRoute direct or with the System.property jvmRoute.
Author:Craig R. McClanahan
/** * Standard implementation of the <b>Engine</b> interface. Each * child container must be a Host implementation to process the specific * fully qualified host name of that virtual host. <br> * You can set the jvmRoute direct or with the System.property <b>jvmRoute</b>. * * @author Craig R. McClanahan */
public class StandardEngine extends ContainerBase implements Engine { private static final Log log = LogFactory.getLog(StandardEngine.class); // ----------------------------------------------------------- Constructors
Create a new StandardEngine component with the default basic Valve.
/** * Create a new StandardEngine component with the default basic Valve. */
public StandardEngine() { super(); pipeline.setBasic(new StandardEngineValve()); /* Set the jmvRoute using the system property jvmRoute */ try { setJvmRoute(System.getProperty("jvmRoute")); } catch(Exception ex) { log.warn(sm.getString("standardEngine.jvmRouteFail")); } // By default, the engine will hold the reloading thread backgroundProcessorDelay = 10; } // ----------------------------------------------------- Instance Variables
Host name to use when no server host, or an unknown host, is specified in the request.
/** * Host name to use when no server host, or an unknown host, * is specified in the request. */
private String defaultHost = null;
The Service that owns this Engine, if any.
/** * The <code>Service</code> that owns this Engine, if any. */
private Service service = null;
The JVM Route ID for this Tomcat instance. All Route ID's must be unique across the cluster.
/** * The JVM Route ID for this Tomcat instance. All Route ID's must be unique * across the cluster. */
private String jvmRouteId;
Default access log to use for request/response pairs where we can't ID the intended host and context.
/** * Default access log to use for request/response pairs where we can't ID * the intended host and context. */
private final AtomicReference<AccessLog> defaultAccessLog = new AtomicReference<>(); // ------------------------------------------------------------- Properties
Obtain the configured Realm and provide a default Realm implementation when no explicit configuration is set.
Returns:configured realm, or a NullRealm by default
/** * Obtain the configured Realm and provide a default Realm implementation * when no explicit configuration is set. * * @return configured realm, or a {@link NullRealm} by default */
@Override public Realm getRealm() { Realm configured = super.getRealm(); // If no set realm has been called - default to NullRealm // This can be overridden at engine, context and host level if (configured == null) { configured = new NullRealm(); this.setRealm(configured); } return configured; }
Return the default host.
/** * Return the default host. */
@Override public String getDefaultHost() { return defaultHost; }
Set the default host.
Params:
  • host – The new default host
/** * Set the default host. * * @param host The new default host */
@Override public void setDefaultHost(String host) { String oldDefaultHost = this.defaultHost; if (host == null) { this.defaultHost = null; } else { this.defaultHost = host.toLowerCase(Locale.ENGLISH); } if (getState().isAvailable()) { service.getMapper().setDefaultHostName(host); } support.firePropertyChange("defaultHost", oldDefaultHost, this.defaultHost); }
Set the cluster-wide unique identifier for this Engine. This value is only useful in a load-balancing scenario.

This property should not be changed once it is set.

/** * Set the cluster-wide unique identifier for this Engine. * This value is only useful in a load-balancing scenario. * <p> * This property should not be changed once it is set. */
@Override public void setJvmRoute(String routeId) { jvmRouteId = routeId; }
Retrieve the cluster-wide unique identifier for this Engine. This value is only useful in a load-balancing scenario.
/** * Retrieve the cluster-wide unique identifier for this Engine. * This value is only useful in a load-balancing scenario. */
@Override public String getJvmRoute() { return jvmRouteId; }
Return the Service with which we are associated (if any).
/** * Return the <code>Service</code> with which we are associated (if any). */
@Override public Service getService() { return this.service; }
Set the Service with which we are associated (if any).
Params:
  • service – The service that owns this Engine
/** * Set the <code>Service</code> with which we are associated (if any). * * @param service The service that owns this Engine */
@Override public void setService(Service service) { this.service = service; } // --------------------------------------------------------- Public Methods
Add a child Container, only if the proposed child is an implementation of Host.
Params:
  • child – Child container to be added
/** * Add a child Container, only if the proposed child is an implementation * of Host. * * @param child Child container to be added */
@Override public void addChild(Container child) { if (!(child instanceof Host)) throw new IllegalArgumentException (sm.getString("standardEngine.notHost")); super.addChild(child); }
Disallow any attempt to set a parent for this Container, since an Engine is supposed to be at the top of the Container hierarchy.
Params:
  • container – Proposed parent Container
/** * Disallow any attempt to set a parent for this Container, since an * Engine is supposed to be at the top of the Container hierarchy. * * @param container Proposed parent Container */
@Override public void setParent(Container container) { throw new IllegalArgumentException (sm.getString("standardEngine.notParent")); } @Override protected void initInternal() throws LifecycleException { // Ensure that a Realm is present before any attempt is made to start // one. This will create the default NullRealm if necessary. getRealm(); super.initInternal(); }
Start this component and implement the requirements of LifecycleBase.startInternal().
Throws:
  • LifecycleException – if this component detects a fatal error that prevents this component from being used
/** * Start this component and implement the requirements * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */
@Override protected synchronized void startInternal() throws LifecycleException { // Log our server identification information if (log.isInfoEnabled()) { log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo())); } // Standard container startup super.startInternal(); }
Override the default implementation. If no access log is defined for the Engine, look for one in the Engine's default host and then the default host's ROOT context. If still none is found, return the default NoOp access log.
/** * Override the default implementation. If no access log is defined for the * Engine, look for one in the Engine's default host and then the default * host's ROOT context. If still none is found, return the default NoOp * access log. */
@Override public void logAccess(Request request, Response response, long time, boolean useDefault) { boolean logged = false; if (getAccessLog() != null) { accessLog.log(request, response, time); logged = true; } if (!logged && useDefault) { AccessLog newDefaultAccessLog = defaultAccessLog.get(); if (newDefaultAccessLog == null) { // If we reached this point, this Engine can't have an AccessLog // Look in the defaultHost Host host = (Host) findChild(getDefaultHost()); Context context = null; if (host != null && host.getState().isAvailable()) { newDefaultAccessLog = host.getAccessLog(); if (newDefaultAccessLog != null) { if (defaultAccessLog.compareAndSet(null, newDefaultAccessLog)) { AccessLogListener l = new AccessLogListener(this, host, null); l.install(); } } else { // Try the ROOT context of default host context = (Context) host.findChild(""); if (context != null && context.getState().isAvailable()) { newDefaultAccessLog = context.getAccessLog(); if (newDefaultAccessLog != null) { if (defaultAccessLog.compareAndSet(null, newDefaultAccessLog)) { AccessLogListener l = new AccessLogListener( this, null, context); l.install(); } } } } } if (newDefaultAccessLog == null) { newDefaultAccessLog = new NoopAccessLog(); if (defaultAccessLog.compareAndSet(null, newDefaultAccessLog)) { AccessLogListener l = new AccessLogListener(this, host, context); l.install(); } } } newDefaultAccessLog.log(request, response, time); } }
Return the parent class loader for this component.
/** * Return the parent class loader for this component. */
@Override public ClassLoader getParentClassLoader() { if (parentClassLoader != null) return parentClassLoader; if (service != null) { return service.getParentClassLoader(); } return ClassLoader.getSystemClassLoader(); } @Override public File getCatalinaBase() { if (service != null) { Server s = service.getServer(); if (s != null) { File base = s.getCatalinaBase(); if (base != null) { return base; } } } // Fall-back return super.getCatalinaBase(); } @Override public File getCatalinaHome() { if (service != null) { Server s = service.getServer(); if (s != null) { File base = s.getCatalinaHome(); if (base != null) { return base; } } } // Fall-back return super.getCatalinaHome(); } // -------------------- JMX registration -------------------- @Override protected String getObjectNameKeyProperties() { return "type=Engine"; } @Override protected String getDomainInternal() { return getName(); } // ----------------------------------------------------------- Inner classes protected static final class NoopAccessLog implements AccessLog { @Override public void log(Request request, Response response, long time) { // NOOP } @Override public void setRequestAttributesEnabled( boolean requestAttributesEnabled) { // NOOP } @Override public boolean getRequestAttributesEnabled() { // NOOP return false; } } protected static final class AccessLogListener implements PropertyChangeListener, LifecycleListener, ContainerListener { private final StandardEngine engine; private final Host host; private final Context context; private volatile boolean disabled = false; public AccessLogListener(StandardEngine engine, Host host, Context context) { this.engine = engine; this.host = host; this.context = context; } public void install() { engine.addPropertyChangeListener(this); if (host != null) { host.addContainerListener(this); host.addLifecycleListener(this); } if (context != null) { context.addLifecycleListener(this); } } private void uninstall() { disabled = true; if (context != null) { context.removeLifecycleListener(this); } if (host != null) { host.removeLifecycleListener(this); host.removeContainerListener(this); } engine.removePropertyChangeListener(this); } @Override public void lifecycleEvent(LifecycleEvent event) { if (disabled) return; String type = event.getType(); if (Lifecycle.AFTER_START_EVENT.equals(type) || Lifecycle.BEFORE_STOP_EVENT.equals(type) || Lifecycle.BEFORE_DESTROY_EVENT.equals(type)) { // Container is being started/stopped/removed // Force re-calculation and disable listener since it won't // be re-used engine.defaultAccessLog.set(null); uninstall(); } } @Override public void propertyChange(PropertyChangeEvent evt) { if (disabled) return; if ("defaultHost".equals(evt.getPropertyName())) { // Force re-calculation and disable listener since it won't // be re-used engine.defaultAccessLog.set(null); uninstall(); } } @Override public void containerEvent(ContainerEvent event) { // Only useful for hosts if (disabled) return; if (Container.ADD_CHILD_EVENT.equals(event.getType())) { Context context = (Context) event.getData(); if (context.getPath().isEmpty()) { // Force re-calculation and disable listener since it won't // be re-used engine.defaultAccessLog.set(null); uninstall(); } } } } }