Catalina.java
/*
* 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.startup;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.ConnectException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.LogManager;
import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Server;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.security.SecurityConfig;
import org.apache.juli.ClassLoaderLogManager;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.digester.Rule;
import org.apache.tomcat.util.digester.RuleSet;
import org.apache.tomcat.util.file.ConfigFileLoader;
import org.apache.tomcat.util.file.ConfigurationSource;
import org.apache.tomcat.util.log.SystemLogHandler;
import org.apache.tomcat.util.res.StringManager;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
/**
* Startup/Shutdown shell program for Catalina. The following command line options are recognized:
* <ul>
* <li><b>-config {pathname}</b> - Set the pathname of the configuration file to be processed. If a relative path is
* specified, it will be interpreted as relative to the directory pathname specified by the "catalina.base" system
* property. [conf/server.xml]</li>
* <li><b>-help</b> - Display usage information.</li>
* <li><b>-nonaming</b> - Disable naming support.</li>
* <li><b>configtest</b> - Try to test the config</li>
* <li><b>start</b> - Start an instance of Catalina.</li>
* <li><b>stop</b> - Stop the currently running instance of Catalina.</li>
* </ul>
*
* @author Craig R. McClanahan
* @author Remy Maucherat
*/
public class Catalina {
/**
* The string manager for this package.
*/
protected static final StringManager sm = StringManager.getManager(Constants.Package);
public static final String SERVER_XML = "conf/server.xml";
// ----------------------------------------------------- Instance Variables
/**
* Use await.
*/
protected boolean await = false;
/**
* Pathname to the server configuration file.
*/
protected String configFile = SERVER_XML;
// XXX Should be moved to embedded
/**
* The shared extensions class loader for this server.
*/
protected ClassLoader parentClassLoader = Catalina.class.getClassLoader();
/**
* The server component we are starting or stopping.
*/
protected Server server = null;
/**
* Use shutdown hook flag.
*/
protected boolean useShutdownHook = true;
/**
* Shutdown hook.
*/
protected Thread shutdownHook = null;
/**
* Is naming enabled ?
*/
protected boolean useNaming = true;
/**
* Prevent duplicate loads.
*/
protected boolean loaded = false;
/**
* Generate Tomcat embedded code from configuration files.
*/
protected boolean generateCode = false;
/**
* Location of generated sources.
*/
protected File generatedCodeLocation = null;
/**
* Value of the argument.
*/
protected String generatedCodeLocationParameter = null;
/**
* Top package name for generated source.
*/
protected String generatedCodePackage = "catalinaembedded";
/**
* Use generated code as a replacement for configuration files.
*/
protected boolean useGeneratedCode = false;
// ----------------------------------------------------------- Constructors
public Catalina() {
setSecurityProtection();
ExceptionUtils.preload();
}
// ------------------------------------------------------------- Properties
public void setConfigFile(String file) {
configFile = file;
}
public String getConfigFile() {
return configFile;
}
public void setUseShutdownHook(boolean useShutdownHook) {
this.useShutdownHook = useShutdownHook;
}
public boolean getUseShutdownHook() {
return useShutdownHook;
}
public boolean getGenerateCode() {
return this.generateCode;
}
public void setGenerateCode(boolean generateCode) {
this.generateCode = generateCode;
}
public boolean getUseGeneratedCode() {
return this.useGeneratedCode;
}
public void setUseGeneratedCode(boolean useGeneratedCode) {
this.useGeneratedCode = useGeneratedCode;
}
public File getGeneratedCodeLocation() {
return this.generatedCodeLocation;
}
public void setGeneratedCodeLocation(File generatedCodeLocation) {
this.generatedCodeLocation = generatedCodeLocation;
}
public String getGeneratedCodePackage() {
return this.generatedCodePackage;
}
public void setGeneratedCodePackage(String generatedCodePackage) {
this.generatedCodePackage = generatedCodePackage;
}
/**
* Set the shared extensions class loader.
*
* @param parentClassLoader The shared extensions class loader.
*/
public void setParentClassLoader(ClassLoader parentClassLoader) {
this.parentClassLoader = parentClassLoader;
}
public ClassLoader getParentClassLoader() {
if (parentClassLoader != null) {
return parentClassLoader;
}
return ClassLoader.getSystemClassLoader();
}
public void setServer(Server server) {
this.server = server;
}
public Server getServer() {
return server;
}
/**
* @return <code>true</code> if naming is enabled.
*/
public boolean isUseNaming() {
return this.useNaming;
}
/**
* Enables or disables naming support.
*
* @param useNaming The new use naming value
*/
public void setUseNaming(boolean useNaming) {
this.useNaming = useNaming;
}
public void setAwait(boolean b) {
await = b;
}
public boolean isAwait() {
return await;
}
// ------------------------------------------------------ Protected Methods
/**
* Process the specified command line arguments.
*
* @param args Command line arguments to process
*
* @return <code>true</code> if we should continue processing
*/
protected boolean arguments(String args[]) {
boolean isConfig = false;
boolean isGenerateCode = false;
if (args.length < 1) {
usage();
return false;
}
for (String arg : args) {
if (isConfig) {
configFile = arg;
isConfig = false;
} else if (arg.equals("-config")) {
isConfig = true;
} else if (arg.equals("-generateCode")) {
setGenerateCode(true);
isGenerateCode = true;
} else if (arg.equals("-useGeneratedCode")) {
setUseGeneratedCode(true);
isGenerateCode = false;
} else if (arg.equals("-nonaming")) {
setUseNaming(false);
isGenerateCode = false;
} else if (arg.equals("-help")) {
usage();
return false;
} else if (arg.equals("start")) {
isGenerateCode = false;
// NOOP
} else if (arg.equals("configtest")) {
isGenerateCode = false;
// NOOP
} else if (arg.equals("stop")) {
isGenerateCode = false;
// NOOP
} else if (isGenerateCode) {
generatedCodeLocationParameter = arg;
isGenerateCode = false;
} else {
usage();
return false;
}
}
return true;
}
/**
* Return a File object representing our configuration file.
*
* @return the main configuration file
*/
protected File configFile() {
File file = new File(configFile);
if (!file.isAbsolute()) {
file = new File(Bootstrap.getCatalinaBase(), configFile);
}
return file;
}
/**
* Create and configure the Digester we will be using for startup.
*
* @return the main digester to parse server.xml
*/
protected Digester createStartDigester() {
// Initialize the digester
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
Map<Class<?>,List<String>> fakeAttributes = new HashMap<>();
// Ignore className on all elements
List<String> objectAttrs = new ArrayList<>();
objectAttrs.add("className");
fakeAttributes.put(Object.class, objectAttrs);
// Ignore attribute added by Eclipse for its internal tracking
List<String> contextAttrs = new ArrayList<>();
contextAttrs.add("source");
fakeAttributes.put(StandardContext.class, contextAttrs);
// Ignore Connector attribute used internally but set on Server
List<String> connectorAttrs = new ArrayList<>();
connectorAttrs.add("portOffset");
fakeAttributes.put(Connector.class, connectorAttrs);
digester.setFakeAttributes(fakeAttributes);
digester.setUseContextClassLoader(true);
// Configure the actions we will be using
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
digester.addRule("Server/Listener", new ListenerCreateRule(null, "className"));
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");
digester.addObjectCreate("Server/Service/Listener", null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
// Executor
digester.addObjectCreate("Server/Service/Executor", "org.apache.catalina.core.StandardThreadExecutor",
"className");
digester.addSetProperties("Server/Service/Executor");
digester.addSetNext("Server/Service/Executor", "addExecutor", "org.apache.catalina.Executor");
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
digester.addSetProperties("Server/Service/Connector",
new String[] { "executor", "sslImplementationName", "protocol" });
digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector");
digester.addRule("Server/Service/Connector", new AddPortOffsetRule());
digester.addObjectCreate("Server/Service/Connector/SSLHostConfig", "org.apache.tomcat.util.net.SSLHostConfig");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
digester.addSetNext("Server/Service/Connector/SSLHostConfig", "addSslHostConfig",
"org.apache.tomcat.util.net.SSLHostConfig");
digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate", new CertificateCreateRule());
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/Certificate", new String[] { "type" });
digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate", "addCertificate",
"org.apache.tomcat.util.net.SSLHostConfigCertificate");
digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
"org.apache.tomcat.util.net.openssl.OpenSSLConf");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf", "setOpenSslConf",
"org.apache.tomcat.util.net.openssl.OpenSSLConf");
digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
"org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd", "addCmd",
"org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
digester.addObjectCreate("Server/Service/Connector/Listener", null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener", "addLifecycleListener",
"org.apache.catalina.LifecycleListener");
digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol", null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
digester.addSetNext("Server/Service/Connector/UpgradeProtocol", "addUpgradeProtocol",
"org.apache.coyote.UpgradeProtocol");
// Add RuleSets for nested elements
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
// When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine", new SetParentClassLoaderRule(parentClassLoader));
addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
return digester;
}
/**
* Cluster support is optional. The JARs may have been removed.
*/
private void addClusterRuleSet(Digester digester, String prefix) {
Class<?> clazz = null;
Constructor<?> constructor = null;
try {
clazz = Class.forName("org.apache.catalina.ha.ClusterRuleSet");
constructor = clazz.getConstructor(String.class);
RuleSet ruleSet = (RuleSet) constructor.newInstance(prefix);
digester.addRuleSet(ruleSet);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.noCluster", e.getClass().getName() + ": " + e.getMessage()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("catalina.noCluster", e.getClass().getName() + ": " + e.getMessage()));
}
}
}
/**
* Create and configure the Digester we will be using for shutdown.
*
* @return the digester to process the stop operation
*/
protected Digester createStopDigester() {
// Initialize the digester
Digester digester = new Digester();
digester.setUseContextClassLoader(true);
// Configure the rules we need for shutting down
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
return digester;
}
protected void parseServerXml(boolean start) {
// Set configuration source
ConfigFileLoader
.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
File file = configFile();
if (useGeneratedCode && !Digester.isGeneratedCodeLoaderSet()) {
// Load loader
String loaderClassName = generatedCodePackage + ".DigesterGeneratedCodeLoader";
try {
Digester.GeneratedCodeLoader loader = (Digester.GeneratedCodeLoader) Catalina.class.getClassLoader()
.loadClass(loaderClassName).getDeclaredConstructor().newInstance();
Digester.setGeneratedCodeLoader(loader);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.info(sm.getString("catalina.noLoader", loaderClassName), e);
} else {
log.info(sm.getString("catalina.noLoader", loaderClassName));
}
// No loader so don't use generated code
useGeneratedCode = false;
}
}
// Init source location
File serverXmlLocation = null;
String xmlClassName = null;
if (generateCode || useGeneratedCode) {
xmlClassName = start ? generatedCodePackage + ".ServerXml" : generatedCodePackage + ".ServerXmlStop";
}
if (generateCode) {
if (generatedCodeLocationParameter != null) {
generatedCodeLocation = new File(generatedCodeLocationParameter);
if (!generatedCodeLocation.isAbsolute()) {
generatedCodeLocation = new File(Bootstrap.getCatalinaHomeFile(), generatedCodeLocationParameter);
}
} else {
generatedCodeLocation = new File(Bootstrap.getCatalinaHomeFile(), "work");
}
serverXmlLocation = new File(generatedCodeLocation, generatedCodePackage);
if (!serverXmlLocation.isDirectory() && !serverXmlLocation.mkdirs()) {
log.warn(sm.getString("catalina.generatedCodeLocationError", generatedCodeLocation.getAbsolutePath()));
// Disable code generation
generateCode = false;
}
}
ServerXml serverXml = null;
if (useGeneratedCode) {
serverXml = (ServerXml) Digester.loadGeneratedClass(xmlClassName);
}
if (serverXml != null) {
try {
serverXml.load(this);
} catch (Exception e) {
log.warn(sm.getString("catalina.configFail", "GeneratedCode"), e);
}
} else {
try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
// Create and execute our Digester
Digester digester = start ? createStartDigester() : createStopDigester();
InputStream inputStream = resource.getInputStream();
InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
inputSource.setByteStream(inputStream);
digester.push(this);
if (generateCode) {
digester.startGeneratingCode();
generateClassHeader(digester, start);
}
digester.parse(inputSource);
if (generateCode) {
generateClassFooter(digester);
try (FileWriter writer = new FileWriter(
new File(serverXmlLocation, start ? "ServerXml.java" : "ServerXmlStop.java"))) {
writer.write(digester.getGeneratedCode().toString());
}
digester.endGeneratingCode();
Digester.addGeneratedClass(xmlClassName);
}
} catch (Exception e) {
log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
if (file.exists() && !file.canRead()) {
log.warn(sm.getString("catalina.incorrectPermissions"));
}
}
}
}
public void stopServer() {
stopServer(null);
}
public void stopServer(String[] arguments) {
if (arguments != null) {
arguments(arguments);
}
Server s = getServer();
if (s == null) {
parseServerXml(false);
if (getServer() == null) {
log.error(sm.getString("catalina.stopError"));
System.exit(1);
}
} else {
// Server object already present. Must be running as a service
try {
s.stop();
s.destroy();
} catch (LifecycleException e) {
log.error(sm.getString("catalina.stopError"), e);
}
return;
}
// Stop the existing server
s = getServer();
if (s.getPortWithOffset() > 0) {
try (Socket socket = new Socket(s.getAddress(), s.getPortWithOffset());
OutputStream stream = socket.getOutputStream()) {
String shutdown = s.getShutdown();
for (int i = 0; i < shutdown.length(); i++) {
stream.write(shutdown.charAt(i));
}
stream.flush();
} catch (ConnectException ce) {
log.error(sm.getString("catalina.stopServer.connectException", s.getAddress(),
String.valueOf(s.getPortWithOffset()), String.valueOf(s.getPort()),
String.valueOf(s.getPortOffset())));
log.error(sm.getString("catalina.stopError"), ce);
System.exit(1);
} catch (IOException e) {
log.error(sm.getString("catalina.stopError"), e);
System.exit(1);
}
} else {
log.error(sm.getString("catalina.stopServer"));
System.exit(1);
}
}
/**
* Start a new server instance.
*/
public void load() {
if (loaded) {
return;
}
loaded = true;
long t1 = System.nanoTime();
initDirs();
// Before digester - it may be needed
initNaming();
// Parse main server.xml
parseServerXml(true);
Server s = getServer();
if (s == null) {
return;
}
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
// Stream redirection
initStreams();
// Start the new server
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new Error(e);
} else {
log.error(sm.getString("catalina.initError"), e);
}
}
if (log.isInfoEnabled()) {
log.info(sm.getString("catalina.init",
Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
}
}
/*
* Load using arguments
*/
public void load(String args[]) {
try {
if (arguments(args)) {
load();
}
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
/**
* Start a new server instance.
*/
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal(sm.getString("catalina.noServer"));
return;
}
long t1 = System.nanoTime();
// Start the new server
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug(sm.getString("catalina.destroyFail"), e1);
}
return;
}
if (log.isInfoEnabled()) {
log.info(sm.getString("catalina.startup",
Long.toString(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1))));
}
if (generateCode) {
// Generate loader which will load all generated classes
generateLoader();
}
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(false);
}
}
if (await) {
await();
stop();
}
}
/**
* Stop an existing server instance.
*/
public void stop() {
try {
// Remove the ShutdownHook first so that server.stop()
// doesn't get invoked twice
if (useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
// If JULI is being used, re-enable JULI's shutdown to ensure
// log messages are not lost
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(true);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
// Shut down the server
try {
Server s = getServer();
LifecycleState state = s.getState();
if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0 && LifecycleState.DESTROYED.compareTo(state) >= 0) {
// Nothing to do. stop() was already called
} else {
s.stop();
s.destroy();
}
} catch (LifecycleException e) {
log.error(sm.getString("catalina.stopError"), e);
}
}
/**
* Await and shutdown.
*/
public void await() {
getServer().await();
}
/**
* Print usage information for this application.
*/
protected void usage() {
System.out.println(sm.getString("catalina.usage"));
}
/**
* @deprecated unused. Will be removed in Tomcat 10 onwards.
*/
@Deprecated
protected void initDirs() {
}
protected void initStreams() {
// Replace System.out and System.err with a custom PrintStream
System.setOut(new SystemLogHandler(System.out));
System.setErr(new SystemLogHandler(System.err));
}
protected void initNaming() {
// Setting additional variables
if (!useNaming) {
log.info(sm.getString("catalina.noNaming"));
System.setProperty("catalina.useNaming", "false");
} else {
System.setProperty("catalina.useNaming", "true");
String value = "org.apache.naming";
String oldValue = System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
if (oldValue != null) {
value = value + ":" + oldValue;
}
System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.namingPrefix", value));
}
value = System.getProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY);
if (value == null) {
System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
} else {
log.debug(sm.getString("catalina.initialContextFactory", value));
}
}
}
/**
* Set the security package access/protection.
*/
protected void setSecurityProtection() {
SecurityConfig securityConfig = SecurityConfig.newInstance();
securityConfig.setPackageDefinition();
securityConfig.setPackageAccess();
}
protected void generateLoader() {
String loaderClassName = "DigesterGeneratedCodeLoader";
StringBuilder code = new StringBuilder();
code.append("package ").append(generatedCodePackage).append(';').append(System.lineSeparator());
code.append("public class ").append(loaderClassName);
code.append(" implements org.apache.tomcat.util.digester.Digester.GeneratedCodeLoader {")
.append(System.lineSeparator());
code.append("public Object loadGeneratedCode(String className) {").append(System.lineSeparator());
code.append("switch (className) {").append(System.lineSeparator());
for (String generatedClassName : Digester.getGeneratedClasses()) {
code.append("case \"").append(generatedClassName).append("\" : return new ").append(generatedClassName);
code.append("();").append(System.lineSeparator());
}
code.append("default: return null; }").append(System.lineSeparator());
code.append("}}").append(System.lineSeparator());
File loaderLocation = new File(generatedCodeLocation, generatedCodePackage);
try (FileWriter writer = new FileWriter(new File(loaderLocation, loaderClassName + ".java"))) {
writer.write(code.toString());
} catch (IOException e) {
// Should not happen
log.debug(sm.getString("catalina.loaderWriteFail"), e);
}
}
protected void generateClassHeader(Digester digester, boolean start) {
StringBuilder code = digester.getGeneratedCode();
code.append("package ").append(generatedCodePackage).append(';').append(System.lineSeparator());
code.append("public class ServerXml");
if (!start) {
code.append("Stop");
}
code.append(" implements ");
code.append(ServerXml.class.getName().replace('$', '.')).append(" {").append(System.lineSeparator());
code.append("public void load(").append(Catalina.class.getName());
code.append(' ').append(digester.toVariableName(this)).append(") throws Exception {").append(System.lineSeparator());
}
protected void generateClassFooter(Digester digester) {
StringBuilder code = digester.getGeneratedCode();
code.append('}').append(System.lineSeparator());
code.append('}').append(System.lineSeparator());
}
public interface ServerXml {
void load(Catalina catalina) throws Exception;
}
// --------------------------------------- CatalinaShutdownHook Inner Class
// XXX Should be moved to embedded !
/**
* Shutdown hook which will perform a clean shutdown of Catalina if needed.
*/
protected class CatalinaShutdownHook extends Thread {
@Override
public void run() {
try {
if (getServer() != null) {
Catalina.this.stop();
}
} catch (Throwable ex) {
ExceptionUtils.handleThrowable(ex);
log.error(sm.getString("catalina.shutdownHookFail"), ex);
} finally {
// If JULI is used, shut JULI down *after* the server shuts down
// so log messages aren't lost
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).shutdown();
}
}
}
}
private static final Log log = LogFactory.getLog(Catalina.class);
/**
* Rule that sets the parent class loader for the top object on the stack, which must be a <code>Container</code>.
*/
final class SetParentClassLoaderRule extends Rule {
SetParentClassLoaderRule(ClassLoader parentClassLoader) {
this.parentClassLoader = parentClassLoader;
}
ClassLoader parentClassLoader = null;
@Override
public void begin(String namespace, String name, Attributes attributes) throws Exception {
if (digester.getLogger().isTraceEnabled()) {
digester.getLogger().trace("Setting parent class loader");
}
Container top = (Container) digester.peek();
top.setParentClassLoader(parentClassLoader);
StringBuilder code = digester.getGeneratedCode();
if (code != null) {
code.append(digester.toVariableName(top)).append(".setParentClassLoader(");
code.append(digester.toVariableName(Catalina.this)).append(".getParentClassLoader());");
code.append(System.lineSeparator());
}
}
}
}