/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. * * * This file incorporates work covered by the following copyright and * permission notice: * * 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.startup; import org.apache.catalina.Container; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.Server; import org.apache.catalina.core.StandardServer; import org.apache.tomcat.util.digester.Digester; import org.apache.tomcat.util.digester.Rule; import org.glassfish.logging.annotation.LogMessageInfo; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import java.io.*; import java.net.Socket; import java.text.MessageFormat; import java.util.logging.Level; import java.util.logging.Logger; /** * 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><b>-help</b> - Display usage information. * <li><b>-stop</b> - Stop the currently running instance of Catalina. * </u> * * Should do the same thing as Embedded, but using a server.xml file. * * @author Craig R. McClanahan * @author Remy Maucherat * @version $Revision: 1.5 $ $Date: 2006/11/10 18:12:35 $ */ public class Catalina extends Embedded { private static final ClassLoader standardServerClassLoader = StandardServer.class.getClassLoader(); @LogMessageInfo( message = "Error processing command line arguments", level = "WARNING" ) public static final String ERROR_PROCESSING_COMMAND_LINE_EXCEPTION = "AS-WEB-CORE-00399"; @LogMessageInfo( message = "Catalina.stop: ", level = "SEVERE", cause = "Could not stop server", action = "Verify if the input file exist or if there are any I/O exceptions, parsing exceptions" ) public static final String CATALINA_STOP_EXCEPTION = "AS-WEB-CORE-00400"; @LogMessageInfo( message = "Can't load server.xml from {0}", level = "WARNING" ) public static final String CANNOT_LOAD_SERVER_XML_EXCEPTION = "AS-WEB-CORE-00401"; @LogMessageInfo( message = "Catalina.start: ", level = "WARNING" ) public static final String CATALINA_START_WARNING_EXCEPTION = "AS-WEB-CORE-00402"; @LogMessageInfo( message = "Catalina.start: ", level = "SEVERE", cause = "Could not initialize the server", action = "Verify if the server has already been initialized" ) public static final String CATALINA_START_SEVERE_EXCEPTION = "AS-WEB-CORE-00403"; @LogMessageInfo( message = "Initialization processed in {0} ms", level = "INFO" ) public static final String INIT_PROCESSED_EXCEPTION = "AS-WEB-CORE-00404"; @LogMessageInfo( message = "Error loading configuration", level = "WARNING" ) public static final String ERROR_LOADING_CONFIGURATION_EXCEPTION = "AS-WEB-CORE-00405"; @LogMessageInfo( message = "Server startup in {0} ms", level = "WARNING" ) public static final String SERVER_STARTUP_INFO = "AS-WEB-CORE-00406"; // --------------------------------------------------- Instance Variables /** * Pathname to the server configuration file. */ protected String configFile = "conf/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; /** * Are we starting a new server? */ protected boolean starting = false; /** * Are we stopping an existing server? */ protected boolean stopping = false; /** * Shutdown hook. */ protected Thread shutdownHook = new CatalinaShutdownHook(); // ----------------------------------------------------------- Properties public void setConfig(String file) { configFile = file; } public void setConfigFile(String file) { configFile = file; } public String getConfigFile() { return configFile; } /** * Set the shared extensions class loader. * * @param parentClassLoader The shared extensions class loader. */ public void setParentClassLoader(ClassLoader parentClassLoader) { this.parentClassLoader = parentClassLoader; } /** * Set the server instance we are configuring. * * @param server The new server */ public void setServer(Server server) { this.server = server; } // --------------------------------------------------------- Main Program /** * The application main program. * * @param args Command line arguments */ public static void main(String args[]) { (new Catalina()).process(args); } /** * The instance main program. * * @param args Command line arguments */ public void process(String args[]) { setAwait(true); setCatalinaHome(); setCatalinaBase(); try { if (arguments(args)) { if (starting) { load(args); start(); } else if (stopping) { stopServer(); } } } catch (Exception e) { log.log(Level.WARNING, ERROR_PROCESSING_COMMAND_LINE_EXCEPTION, e); } } // ------------------------------------------------------ Protected Methods /** * Process the specified command line arguments, and return * <code>true</code> if we should continue processing; otherwise * return <code>false</code>. * * @param args Command line arguments to process */ protected boolean arguments(String args[]) { boolean isConfig = false; if (args.length < 1) { usage(); return (false); } for (int i = 0; i < args.length; i++) { if (isConfig) { configFile = args[i]; isConfig = false; } else if (args[i].equals("-config")) { isConfig = true; } else if (args[i].equals("-debug")) { debug = 1; } else if (args[i].equals("-nonaming")) { setUseNaming( false ); } else if (args[i].equals("-help")) { usage(); return (false); } else if (args[i].equals("start")) { starting = true; stopping = false; } else if (args[i].equals("stop")) { starting = false; stopping = true; } else { usage(); return (false); } } return (true); } /** * Return a File object representing our configuration file. */ protected File configFile() { File file = new File(configFile); if (!file.isAbsolute()) file = new File(System.getProperty("catalina.base"), configFile); return (file); } /** * Create and configure the Digester we will be using for startup. */ protected Digester createStartDigester() { long t1=System.currentTimeMillis(); // Initialize the digester Digester digester = new Digester(); if (debug>0) digester.setDebug(debug); digester.setValidating(false); digester.setClassLoader(standardServerClassLoader); // 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.NamingResources"); digester.addSetProperties("Server/GlobalNamingResources"); digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResources"); digester.addObjectCreate("Server/Listener", null, // MUST be specified in the element "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"); digester.addObjectCreate("Server/Service/Connector", "org.apache.catalina.connector.CoyoteConnector", "className"); digester.addRule("Server/Service/Connector", new SetAllPropertiesRule()); digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.Connector"); digester.addObjectCreate("Server/Service/Connector/Factory", "org.apache.catalina.connector.CoyoteServerSocketFactory", "className"); digester.addSetProperties("Server/Service/Connector/Factory"); digester.addSetNext("Server/Service/Connector/Factory", "setFactory", "org.apache.catalina.net.ServerSocketFactory"); 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"); // 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/Default")); digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/DefaultContext/")); digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/Default")); digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/DefaultContext/")); digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/")); digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/")); // When the 'engine' is found, set the parentClassLoader. digester.addRule("Server/Service/Engine", new SetParentClassLoaderRule(digester, parentClassLoader)); long t2=System.currentTimeMillis(); if (log.isLoggable(Level.FINE)) log.log(Level.FINE, "Digester for server.xml created " + ( t2-t1 )); return (digester); } /** * Create and configure the Digester we will be using for shutdown. */ protected Digester createStopDigester() { // Initialize the digester Digester digester = new Digester(); if (debug>0) digester.setDebug(debug); // 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); } public void stopServer() { if( server == null ) { // Create and execute our Digester Digester digester = createStopDigester(); digester.setClassLoader(Thread.currentThread().getContextClassLoader()); File file = configFile(); FileInputStream fis = null; try { InputSource is = new InputSource("file://" + file.getAbsolutePath()); fis = new FileInputStream(file); is.setByteStream(fis); digester.push(this); digester.parse(is); } catch (Exception e) { log.log(Level.SEVERE, CATALINA_STOP_EXCEPTION, e); try { if (fis != null) { fis.close(); } } catch (IOException ioe) {} System.exit(1); } finally { try { if (fis != null) { fis.close(); } } catch (IOException ioe) {} } } // Stop the existing server Socket socket = null; OutputStream stream = null; try { socket = new Socket("127.0.0.1", server.getPort()); stream = socket.getOutputStream(); String shutdown = server.getShutdown(); for (int i = 0; i < shutdown.length(); i++) stream.write(shutdown.charAt(i)); stream.flush(); } catch (IOException e) { log.log(Level.SEVERE, CATALINA_STOP_EXCEPTION, e); System.exit(1); } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // Ignore } } if (socket != null) { try { socket.close(); } catch (IOException e) { // Ignore } } } } /** * Set the <code>catalina.base</code> System property to the current * working directory if it has not been set. * @deprecated Use initDirs() */ public void setCatalinaBase() { initDirs(); } /** * Set the <code>catalina.home</code> System property to the current * working directory if it has not been set. * @deprecated Use initDirs() */ public void setCatalinaHome() { initDirs(); } /** * Start a new server instance. */ public void load() { initDirs(); // Before digester - it may be needed initNaming(); // Create and execute our Digester Digester digester = createStartDigester(); long t1 = System.currentTimeMillis(); Exception ex = null; InputSource inputSource = null; InputStream inputStream = null; File file = null; try { file = configFile(); inputStream = new FileInputStream(file); inputSource = new InputSource("file://" + file.getAbsolutePath()); } catch (Exception e) { ; } if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream(getConfigFile()); inputSource = new InputSource (getClass().getClassLoader() .getResource(getConfigFile()).toString()); } catch (Exception e) { ; } } // This should be included in catalina.jar // Alternative: don't bother with xml, just create it manually. if( inputStream==null ) { try { inputStream = getClass().getClassLoader() .getResourceAsStream("server-embed.xml"); inputSource = new InputSource (getClass().getClassLoader() .getResource("server-embed.xml").toString()); } catch (Exception e) { ; } } if (inputStream == null && file != null) { String msg = MessageFormat.format(rb.getString(CANNOT_LOAD_SERVER_XML_EXCEPTION), file.getAbsolutePath()); log.log(Level.WARNING, msg); return; } if (inputStream != null) { try { inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource); } catch (Exception e) { log.log(Level.WARNING, CATALINA_START_WARNING_EXCEPTION, e); return; } finally { try { inputStream.close(); } catch (IOException ioe) {} } } // Start the new server if (server instanceof Lifecycle) { try { server.initialize(); } catch (LifecycleException e) { log.log(Level.SEVERE, CATALINA_START_SEVERE_EXCEPTION, e); } } if (log.isLoggable(Level.INFO)) { long t2 = System.currentTimeMillis(); String msg = MessageFormat.format(rb.getString(INIT_PROCESSED_EXCEPTION), (t2-t1)); log.log(Level.INFO, msg); } } /* * Load using arguments */ public void load(String args[]) { setCatalinaHome(); setCatalinaBase(); try { if (arguments(args)) load(); } catch (Exception e) { log.log(Level.WARNING, ERROR_LOADING_CONFIGURATION_EXCEPTION, e); } } public void create() { } public void destroy() { } /** * Start a new server instance. */ public void start() { if (server == null) { load(); } long t1 = System.currentTimeMillis(); // Start the new server if (server instanceof Lifecycle) { try { ((Lifecycle) server).start(); } catch (LifecycleException e) { log.log(Level.SEVERE, CATALINA_START_SEVERE_EXCEPTION, e); } } if (log.isLoggable(Level.INFO)) { long t2 = System.currentTimeMillis(); String msg = MessageFormat.format(rb.getString(SERVER_STARTUP_INFO), (t2 - t1)); log.log(Level.INFO, msg); } try { // Register shutdown hook Runtime.getRuntime().addShutdownHook(shutdownHook); } catch (Throwable t) { // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } 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 Runtime.getRuntime().removeShutdownHook(shutdownHook); } catch (Throwable t) { // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } // Shut down the server if (server instanceof Lifecycle) { try { ((Lifecycle) server).stop(); } catch (LifecycleException e) { log.log(Level.SEVERE, CATALINA_STOP_EXCEPTION, e); } } } /** * Await and shutdown. */ public void await() { server.await(); } /** * Print usage information for this application. */ protected void usage() { System.out.println ("usage: java org.apache.catalina.startup.Catalina" + " [ -config {pathname} ] [ -debug ]" + " [ -nonaming ] { start | stop }"); } // --------------------------------------- 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 { public void run() { if (server != null) { Catalina.this.stop(); } } } } // ------------------------------------------------------------ Private Classes /** * 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 { public SetParentClassLoaderRule(Digester digester, ClassLoader parentClassLoader) { super(digester); this.parentClassLoader = parentClassLoader; } ClassLoader parentClassLoader = null; public void begin(Attributes attributes) throws Exception { if (digester.getDebug() >= 1) digester.log("Setting parent class loader"); Container top = (Container) digester.peek(); top.setParentClassLoader(parentClassLoader); } }