blob: 718ef2631184fd0ce97489be1f18478c37872f30 [file] [log] [blame]
/*******************************************************************************
* 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.ofbiz.catalina.container;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.apache.catalina.Cluster;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Globals;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Manager;
import org.apache.catalina.connector.Connector;
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.filters.RequestDumperFilter;
import org.apache.catalina.ha.tcp.ReplicationValve;
import org.apache.catalina.ha.tcp.SimpleTcpCluster;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.ContextConfig;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.tribes.group.GroupChannel;
import org.apache.catalina.tribes.membership.McastService;
import org.apache.catalina.tribes.transport.MultiPointSender;
import org.apache.catalina.tribes.transport.ReplicationTransmitter;
import org.apache.catalina.tribes.transport.nio.NioReceiver;
import org.apache.catalina.util.ServerInfo;
import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.util.IntrospectionUtils;
import org.apache.tomcat.util.descriptor.web.FilterDef;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.apache.ofbiz.base.component.ComponentConfig;
import org.apache.ofbiz.base.concurrent.ExecutionPool;
import org.apache.ofbiz.base.container.Container;
import org.apache.ofbiz.base.container.ContainerConfig;
import org.apache.ofbiz.base.container.ContainerConfig.Configuration.Property;
import org.apache.ofbiz.base.container.ContainerException;
import org.apache.ofbiz.base.location.FlexibleLocation;
import org.apache.ofbiz.base.start.Start;
import org.apache.ofbiz.base.start.StartupCommand;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.SSLUtil;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.base.util.UtilXml;
import org.w3c.dom.Document;
/*
* --- Access Log Pattern Information - From Tomcat 5 AccessLogValve.java
* <p>Patterns for the logged message may include constant text or any of the
* following replacement strings, for which the corresponding information
* from the specified Response is substituted:</p>
* <ul>
* <li><b>%a</b> - Remote IP address
* <li><b>%A</b> - Local IP address
* <li><b>%b</b> - Bytes sent, excluding HTTP headers, or '-' if no bytes
* were sent
* <li><b>%B</b> - Bytes sent, excluding HTTP headers
* <li><b>%h</b> - Remote host name
* <li><b>%H</b> - Request protocol
* <li><b>%l</b> - Remote logical username from identd (always returns '-')
* <li><b>%m</b> - Request method
* <li><b>%p</b> - Local port
* <li><b>%q</b> - Query string (prepended with a '?' if it exists, otherwise
* an empty string
* <li><b>%r</b> - First line of the request
* <li><b>%s</b> - HTTP status code of the response
* <li><b>%S</b> - User session ID
* <li><b>%t</b> - Date and time, in Common Log Format format
* <li><b>%u</b> - Remote user that was authenticated
* <li><b>%U</b> - Requested URL path
* <li><b>%v</b> - Local server name
* <li><b>%D</b> - Time taken to process the request, in millis
* <li><b>%T</b> - Time taken to process the request, in seconds
* </ul>
* <p>In addition, the caller can specify one of the following aliases for
* commonly utilized patterns:</p>
* <ul>
* <li><b>common</b> - <code>%h %l %u %t "%r" %s %b</code>
* <li><b>combined</b> -
* <code>%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"</code>
* </ul>
*
* <p>
* There is also support to write information from the cookie, incoming
* header, the Session or something else in the ServletRequest.<br/>
* It is modeled after the apache syntax:
* <ul>
* <li><code>%{xxx}i</code> for incoming headers
* <li><code>%{xxx}c</code> for a specific cookie
* <li><code>%{xxx}r</code> xxx is an attribute in the ServletRequest
* <li><code>%{xxx}s</code> xxx is an attribute in the HttpSession
* </ul>
* </p>
*/
/**
* CatalinaContainer - Tomcat
*
*/
public class CatalinaContainer implements Container {
public static final String CATALINA_HOSTS_HOME = System.getProperty("ofbiz.home") + "/framework/catalina/hosts";
public static final String J2EE_SERVER = "OFBiz Container 3.1";
public static final String J2EE_APP = "OFBiz";
public static final String module = CatalinaContainer.class.getName();
private static final ThreadGroup CATALINA_THREAD_GROUP = new ThreadGroup("CatalinaContainer");
// load the JSSE properties (set the trust store)
static {
SSLUtil.loadJsseProperties();
}
private Tomcat tomcat = null;
protected Map<String, ContainerConfig.Configuration.Property> clusterConfig = new HashMap<String, ContainerConfig.Configuration.Property>();
protected boolean contextReloadable = false;
protected boolean crossContext = false;
protected boolean distribute = false;
protected String catalinaRuntimeHome;
private String name;
@Override
public void init(List<StartupCommand> ofbizCommands, String name, String configFile) throws ContainerException {
this.name = name;
// get the container config
ContainerConfig.Configuration cc = ContainerConfig.getConfiguration(name, configFile);
if (cc == null) {
throw new ContainerException("No catalina-container configuration found in container config!");
}
// embedded properties
boolean useNaming = ContainerConfig.getPropertyValue(cc, "use-naming", false);
//int debug = ContainerConfig.getPropertyValue(cc, "debug", 0);
// grab some global context settings
this.contextReloadable = ContainerConfig.getPropertyValue(cc, "apps-context-reloadable", false);
this.crossContext = ContainerConfig.getPropertyValue(cc, "apps-cross-context", true);
this.distribute = ContainerConfig.getPropertyValue(cc, "apps-distributable", true);
this.catalinaRuntimeHome = ContainerConfig.getPropertyValue(cc, "catalina-runtime-home", "runtime/catalina");
// set catalina_home
System.setProperty(Globals.CATALINA_HOME_PROP, System.getProperty("ofbiz.home") + "/" + this.catalinaRuntimeHome);
System.setProperty(Globals.CATALINA_BASE_PROP, System.getProperty(Globals.CATALINA_HOME_PROP));
// create the instance of embedded Tomcat
System.setProperty("catalina.useNaming", String.valueOf(useNaming));
tomcat = new Tomcat();
tomcat.setBaseDir(System.getProperty("ofbiz.home"));
// configure JNDI in the StandardServer
StandardServer server = (StandardServer) tomcat.getServer();
if (useNaming) {
tomcat.enableNaming();
}
try {
server.setGlobalNamingContext(new InitialContext());
} catch (NamingException e) {
throw new ContainerException(e);
}
// create the engine
List<ContainerConfig.Configuration.Property> engineProps = cc.getPropertiesWithValue("engine");
if (UtilValidate.isEmpty(engineProps)) {
throw new ContainerException("Cannot load CatalinaContainer; no engines defined.");
}
if (engineProps.size() > 1) {
throw new ContainerException("Cannot load CatalinaContainer; more than one engine configuration found; only one is supported.");
}
createEngine(engineProps.get(0));
// create the connectors
List<ContainerConfig.Configuration.Property> connectorProps = cc.getPropertiesWithValue("connector");
if (UtilValidate.isEmpty(connectorProps)) {
throw new ContainerException("Cannot load CatalinaContainer; no connectors defined!");
}
for (ContainerConfig.Configuration.Property connectorProp: connectorProps) {
createConnector(connectorProp);
}
}
public boolean start() throws ContainerException {
// load the web applications
loadComponents();
// Start the Tomcat server
try {
tomcat.getServer().start();
} catch (LifecycleException e) {
throw new ContainerException(e);
}
for (Connector con: tomcat.getService().findConnectors()) {
Debug.logInfo("Connector " + con.getProtocol() + " @ " + con.getPort() + " - " +
(con.getSecure() ? "secure" : "not-secure") + " [" + con.getProtocolHandlerClassName() + "] started.", module);
}
Debug.logInfo("Started " + ServerInfo.getServerInfo(), module);
return true;
}
private Engine createEngine(ContainerConfig.Configuration.Property engineConfig) throws ContainerException {
if (tomcat == null) {
throw new ContainerException("Cannot create Engine without Tomcat instance!");
}
ContainerConfig.Configuration.Property defaultHostProp = engineConfig.getProperty("default-host");
if (defaultHostProp == null) {
throw new ContainerException("default-host element of server property is required for catalina!");
}
String engineName = engineConfig.name;
String hostName = defaultHostProp.value;
tomcat.setHostname(hostName);
Engine engine = tomcat.getEngine();
engine.setName(engineName);
// set the JVM Route property (JK/JK2)
String jvmRoute = ContainerConfig.getPropertyValue(engineConfig, "jvm-route", null);
if (jvmRoute != null) {
engine.setJvmRoute(jvmRoute);
}
// create a default virtual host; others will be created as needed
Host host = tomcat.getHost();
configureHost(host);
// configure clustering
List<ContainerConfig.Configuration.Property> clusterProps = engineConfig.getPropertiesWithValue("cluster");
if (clusterProps != null && clusterProps.size() > 1) {
throw new ContainerException("Only one cluster configuration allowed per engine");
}
if (UtilValidate.isNotEmpty(clusterProps)) {
ContainerConfig.Configuration.Property clusterProp = clusterProps.get(0);
createCluster(clusterProp, host);
clusterConfig.put(engineName, clusterProp);
}
// configure the CrossSubdomainSessionValve
boolean enableSessionValve = ContainerConfig.getPropertyValue(engineConfig, "enable-cross-subdomain-sessions", false);
if (enableSessionValve) {
CrossSubdomainSessionValve sessionValve = new CrossSubdomainSessionValve();
((StandardEngine)engine).addValve(sessionValve);
}
// configure the access log valve
String logDir = ContainerConfig.getPropertyValue(engineConfig, "access-log-dir", null);
AccessLogValve al = null;
if (logDir != null) {
al = new AccessLogValve();
if (!logDir.startsWith("/")) {
logDir = System.getProperty("ofbiz.home") + "/" + logDir;
}
File logFile = new File(logDir);
if (!logFile.isDirectory()) {
throw new ContainerException("Log directory [" + logDir + "] is not available; make sure the directory is created");
}
al.setDirectory(logFile.getAbsolutePath());
}
// configure the SslAcceleratorValve
String sslAcceleratorPortStr = ContainerConfig.getPropertyValue(engineConfig, "ssl-accelerator-port", null);
if (UtilValidate.isNotEmpty(sslAcceleratorPortStr)) {
Integer sslAcceleratorPort = Integer.valueOf(sslAcceleratorPortStr);
SslAcceleratorValve sslAcceleratorValve = new SslAcceleratorValve();
sslAcceleratorValve.setSslAcceleratorPort(sslAcceleratorPort);
((StandardEngine)engine).addValve(sslAcceleratorValve);
}
String alp2 = ContainerConfig.getPropertyValue(engineConfig, "access-log-pattern", null);
if (al != null && UtilValidate.isNotEmpty(alp2)) {
al.setPattern(alp2);
}
String alp3 = ContainerConfig.getPropertyValue(engineConfig, "access-log-prefix", null);
if (al != null && UtilValidate.isNotEmpty(alp3)) {
al.setPrefix(alp3);
}
boolean alp5 = ContainerConfig.getPropertyValue(engineConfig, "access-log-rotate", false);
if (al != null) {
al.setRotatable(alp5);
}
if (al != null) {
((StandardEngine)engine).addValve(al);
}
return engine;
}
private static Host createHost(String hostName) {
Host host = new StandardHost();
host.setName(hostName);
configureHost(host);
return host;
}
private static void configureHost(Host host) {
host.setAppBase(CATALINA_HOSTS_HOME);
host.setDeployOnStartup(false);
host.setBackgroundProcessorDelay(5);
host.setAutoDeploy(false);
((StandardHost)host).setWorkDir(new File(System.getProperty(Globals.CATALINA_HOME_PROP), "work" + File.separator + host.getName()).getAbsolutePath());
}
protected Cluster createCluster(ContainerConfig.Configuration.Property clusterProps, Host host) throws ContainerException {
String defaultValveFilter = ".*\\.gif;.*\\.js;.*\\.jpg;.*\\.htm;.*\\.html;.*\\.txt;.*\\.png;.*\\.css;.*\\.ico;.*\\.htc;";
ReplicationValve clusterValve = new ReplicationValve();
clusterValve.setFilter(ContainerConfig.getPropertyValue(clusterProps, "rep-valve-filter", defaultValveFilter));
String mcb = ContainerConfig.getPropertyValue(clusterProps, "mcast-bind-addr", null);
String mca = ContainerConfig.getPropertyValue(clusterProps, "mcast-addr", null);
int mcp = ContainerConfig.getPropertyValue(clusterProps, "mcast-port", -1);
int mcf = ContainerConfig.getPropertyValue(clusterProps, "mcast-freq", 500);
int mcd = ContainerConfig.getPropertyValue(clusterProps, "mcast-drop-time", 3000);
if (mca == null || mcp == -1) {
throw new ContainerException("Cluster configuration requires mcast-addr and mcast-port properties");
}
McastService mcast = new McastService();
if (mcb != null) {
mcast.setMcastBindAddress(mcb);
}
mcast.setAddress(mca);
mcast.setPort(mcp);
mcast.setMcastDropTime(mcd);
mcast.setFrequency(mcf);
String tla = ContainerConfig.getPropertyValue(clusterProps, "tcp-listen-host", "auto");
int tlp = ContainerConfig.getPropertyValue(clusterProps, "tcp-listen-port", 4001);
int tlt = ContainerConfig.getPropertyValue(clusterProps, "tcp-sector-timeout", 100);
int tlc = ContainerConfig.getPropertyValue(clusterProps, "tcp-thread-count", 6);
//String tls = getPropertyValue(clusterProps, "", "");
if (tlp == -1) {
throw new ContainerException("Cluster configuration requires tcp-listen-port property");
}
NioReceiver listener = new NioReceiver();
listener.setAddress(tla);
listener.setPort(tlp);
listener.setSelectorTimeout(tlt);
listener.setMaxThreads(tlc);
listener.setMinThreads(tlc);
//listener.setIsSenderSynchronized(false);
ReplicationTransmitter trans = new ReplicationTransmitter();
try {
MultiPointSender mps = (MultiPointSender)Class.forName(ContainerConfig.getPropertyValue(clusterProps, "replication-mode", "org.apache.catalina.tribes.transport.bio.PooledMultiSender")).newInstance();
trans.setTransport(mps);
} catch (Exception exc) {
throw new ContainerException("Cluster configuration requires a valid replication-mode property: " + exc.getMessage());
}
String mgrClassName = ContainerConfig.getPropertyValue(clusterProps, "manager-class", "org.apache.catalina.ha.session.DeltaManager");
//int debug = ContainerConfig.getPropertyValue(clusterProps, "debug", 0);
// removed since 5.5.9? boolean expireSession = ContainerConfig.getPropertyValue(clusterProps, "expire-session", false);
// removed since 5.5.9? boolean useDirty = ContainerConfig.getPropertyValue(clusterProps, "use-dirty", true);
SimpleTcpCluster cluster = new SimpleTcpCluster();
cluster.setClusterName(clusterProps.name);
Manager manager = null;
try {
manager = (Manager)Class.forName(mgrClassName).newInstance();
} catch (Exception exc) {
throw new ContainerException("Cluster configuration requires a valid manager-class property: " + exc.getMessage());
}
//cluster.setManagerClassName(mgrClassName);
//host.setManager(manager);
//cluster.registerManager(manager);
cluster.setManagerTemplate((org.apache.catalina.ha.ClusterManager)manager);
//cluster.setDebug(debug);
// removed since 5.5.9? cluster.setExpireSessionsOnShutdown(expireSession);
// removed since 5.5.9? cluster.setUseDirtyFlag(useDirty);
GroupChannel channel = new GroupChannel();
channel.setChannelReceiver(listener);
channel.setChannelSender(trans);
channel.setMembershipService(mcast);
cluster.setChannel(channel);
cluster.addValve(clusterValve);
// removed since 5.5.9? cluster.setPrintToScreen(true);
// set the cluster to the host
host.setCluster(cluster);
Debug.logInfo("Catalina Cluster [" + cluster.getClusterName() + "] configured for host - " + host.getName(), module);
return cluster;
}
protected Connector createConnector(ContainerConfig.Configuration.Property connectorProp) throws ContainerException {
if (tomcat == null) {
throw new ContainerException("Cannot create Connector without Tomcat instance!");
}
Connector connector = null;
if (UtilValidate.isNotEmpty(connectorProp.properties)) {
String protocol = ContainerConfig.getPropertyValue(connectorProp, "protocol", "HTTP/1.1");
int port = ContainerConfig.getPropertyValue(connectorProp, "port", 0) + Start.getInstance().getConfig().portOffset;
// set the protocol and the port first
connector = new Connector(protocol);
connector.setPort(port);
// then set all the other parameters
for (ContainerConfig.Configuration.Property prop: connectorProp.properties.values()) {
if ("protocol".equals(prop.name) || "port".equals(prop.name)) {
// protocol and port are already set
continue;
}
if (IntrospectionUtils.setProperty(connector, prop.name, prop.value)) {
if (prop.name.indexOf("Pass") != -1) {
// this property may be a password, do not include its value in the logs
Debug.logInfo("Tomcat " + connector + ": set " + prop.name, module);
} else {
Debug.logInfo("Tomcat " + connector + ": set " + prop.name + "=" + prop.value, module);
}
} else {
Debug.logWarning("Tomcat " + connector + ": ignored parameter " + prop.name, module);
}
}
tomcat.getService().addConnector(connector);
}
return connector;
}
private Callable<Context> createContext(final ComponentConfig.WebappInfo appInfo) throws ContainerException {
Debug.logInfo("Creating context [" + appInfo.name + "]", module);
final Engine engine = tomcat.getEngine();
List<String> virtualHosts = appInfo.getVirtualHosts();
final Host host;
if (UtilValidate.isEmpty(virtualHosts)) {
host = tomcat.getHost();
} else {
// assume that the first virtual-host will be the default; additional virtual-hosts will be aliases
Iterator<String> vhi = virtualHosts.iterator();
String hostName = vhi.next();
org.apache.catalina.Container childContainer = engine.findChild(hostName);
if (childContainer instanceof Host) {
host = (Host)childContainer;
} else {
host = createHost(hostName);
engine.addChild(host);
}
while (vhi.hasNext()) {
host.addAlias(vhi.next());
}
}
return new Callable<Context>() {
public Context call() throws ContainerException, LifecycleException {
StandardContext context = configureContext(engine, host, appInfo);
host.addChild(context);
return context;
}
};
}
private StandardContext configureContext(Engine engine, Host host, ComponentConfig.WebappInfo appInfo) throws ContainerException {
// webapp settings
Map<String, String> initParameters = appInfo.getInitParameters();
// set the root location (make sure we set the paths correctly)
String location = appInfo.componentConfig.getRootLocation() + appInfo.location;
location = location.replace('\\', '/');
if (location.endsWith("/")) {
location = location.substring(0, location.length() - 1);
}
// get the mount point
String mount = appInfo.mountPoint;
if (mount.endsWith("/*")) {
mount = mount.substring(0, mount.length() - 2);
}
final String webXmlFilePath = new StringBuilder().append("file:///").append(location).append("/WEB-INF/web.xml").toString();
boolean appIsDistributable = distribute;
URL webXmlUrl = null;
try {
webXmlUrl = FlexibleLocation.resolveLocation(webXmlFilePath);
} catch (MalformedURLException e) {
throw new ContainerException(e);
}
File webXmlFile = new File(webXmlUrl.getFile());
if (webXmlFile.exists()) {
Document webXmlDoc = null;
try {
webXmlDoc = UtilXml.readXmlDocument(webXmlUrl);
} catch (Exception e) {
throw new ContainerException(e);
}
appIsDistributable = webXmlDoc.getElementsByTagName("distributable").getLength() > 0;
} else {
Debug.logInfo(webXmlFilePath + " not found.", module);
}
final boolean contextIsDistributable = distribute && appIsDistributable;
// create the web application context
StandardContext context = new StandardContext();
context.setParent(host);
context.setDocBase(location);
context.setPath(mount);
context.addLifecycleListener(new ContextConfig());
Tomcat.initWebappDefaults(context);
// configure persistent sessions
// important: the call to context.setManager(...) must be done after Tomcat.initWebappDefaults(...)
Property clusterProp = clusterConfig.get(engine.getName());
if (clusterProp != null && contextIsDistributable) {
Manager sessionMgr = null;
String mgrClassName = ContainerConfig.getPropertyValue(clusterProp, "manager-class", "org.apache.catalina.ha.session.DeltaManager");
try {
sessionMgr = (Manager)Class.forName(mgrClassName).newInstance();
} catch (Exception exc) {
throw new ContainerException("Cluster configuration requires a valid manager-class property: " + exc.getMessage());
}
context.setManager(sessionMgr);
}
JarScanner jarScanner = context.getJarScanner();
if (jarScanner instanceof StandardJarScanner) {
StandardJarScanner standardJarScanner = (StandardJarScanner) jarScanner;
standardJarScanner.setJarScanFilter(new FilterJars());
standardJarScanner.setScanClassPath(true);
}
context.setJ2EEApplication(J2EE_APP);
context.setJ2EEServer(J2EE_SERVER);
context.setLoader(new WebappLoader(Thread.currentThread().getContextClassLoader()));
context.setDisplayName(appInfo.name);
context.setDocBase(location);
StandardRoot resources = new StandardRoot(context);
resources.setAllowLinking(true);
context.setResources(resources);
context.setReloadable(contextReloadable);
context.setDistributable(contextIsDistributable);
context.setCrossContext(crossContext);
context.setPrivileged(appInfo.privileged);
context.getServletContext().setAttribute("_serverId", appInfo.server);
context.getServletContext().setAttribute("componentName", appInfo.componentConfig.getComponentName());
// request dumper filter
String enableRequestDump = initParameters.get("enableRequestDump");
if ("true".equals(enableRequestDump)) {
// create the Requester Dumper Filter instance
FilterDef requestDumperFilterDef = new FilterDef();
requestDumperFilterDef.setFilterClass(RequestDumperFilter.class.getName());
requestDumperFilterDef.setFilterName("RequestDumper");
FilterMap requestDumperFilterMap = new FilterMap();
requestDumperFilterMap.setFilterName("RequestDumper");
requestDumperFilterMap.addURLPattern("*");
context.addFilterMap(requestDumperFilterMap);
}
// set the init parameters
for (Map.Entry<String, String> entry: initParameters.entrySet()) {
context.addParameter(entry.getKey(), entry.getValue());
}
return context;
}
protected void loadComponents() throws ContainerException {
if (tomcat == null) {
throw new ContainerException("Cannot load web applications without Tomcat instance!");
}
// load the applications
List<ComponentConfig.WebappInfo> webResourceInfos = ComponentConfig.getAllWebappResourceInfos();
List<String> loadedMounts = new ArrayList<String>();
if (webResourceInfos == null) {
return;
}
ScheduledExecutorService executor = ExecutionPool.getScheduledExecutor(CATALINA_THREAD_GROUP, "catalina-startup", Runtime.getRuntime().availableProcessors(), 0, true);
try {
List<Future<Context>> futures = new ArrayList<Future<Context>>();
for (int i = webResourceInfos.size(); i > 0; i--) {
ComponentConfig.WebappInfo appInfo = webResourceInfos.get(i - 1);
String engineName = appInfo.server;
List<String> virtualHosts = appInfo.getVirtualHosts();
String mount = appInfo.getContextRoot();
List<String> keys = new ArrayList<String>();
if (virtualHosts.isEmpty()) {
keys.add(engineName + ":DEFAULT:" + mount);
} else {
for (String virtualHost: virtualHosts) {
keys.add(engineName + ":" + virtualHost + ":" + mount);
}
}
if (!keys.removeAll(loadedMounts)) {
// nothing was removed from the new list of keys; this
// means there are no existing loaded entries that overlap
// with the new set
if (!appInfo.location.isEmpty()) {
futures.add(executor.submit(createContext(appInfo)));
}
loadedMounts.addAll(keys);
} else {
appInfo.setAppBarDisplay(false); // disable app bar display on overridden apps
Debug.logInfo("Duplicate webapp mount; not loading : " + appInfo.getName() + " / " + appInfo.getLocation(), module);
}
}
ExecutionPool.getAllFutures(futures);
} finally {
executor.shutdown();
}
}
public void stop() throws ContainerException {
try {
tomcat.stop();
} catch (LifecycleException e) {
// don't throw this; or it will kill the rest of the shutdown process
Debug.logVerbose(e, module); // happens usually when running tests, disabled unless in verbose
}
}
public String getName() {
return name;
}
}