001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.startup;
019:
020: import java.io.File;
021: import java.io.FileInputStream;
022: import java.io.InputStream;
023: import java.io.IOException;
024: import java.io.OutputStream;
025: import java.net.Socket;
026: import org.apache.catalina.Container;
027: import org.apache.catalina.Lifecycle;
028: import org.apache.catalina.LifecycleException;
029: import org.apache.catalina.Server;
030: import org.apache.catalina.core.StandardServer;
031: import org.apache.tomcat.util.digester.Digester;
032: import org.apache.tomcat.util.digester.Rule;
033: import org.xml.sax.Attributes;
034: import org.xml.sax.InputSource;
035:
036: /**
037: * Startup/Shutdown shell program for Catalina. The following command line
038: * options are recognized:
039: * <ul>
040: * <li><b>-config {pathname}</b> - Set the pathname of the configuration file
041: * to be processed. If a relative path is specified, it will be
042: * interpreted as relative to the directory pathname specified by the
043: * "catalina.base" system property. [conf/server.xml]
044: * <li><b>-help</b> - Display usage information.
045: * <li><b>-stop</b> - Stop the currently running instance of Catalina.
046: * </u>
047: *
048: * Should do the same thing as Embedded, but using a server.xml file.
049: *
050: * @author Craig R. McClanahan
051: * @author Remy Maucherat
052: * @version $Revision: 523327 $ $Date: 2007-03-28 15:52:08 +0200 (mer., 28 mars 2007) $
053: */
054:
055: public class Catalina extends Embedded {
056:
057: // ----------------------------------------------------- Instance Variables
058:
059: /**
060: * Pathname to the server configuration file.
061: */
062: protected String configFile = "conf/server.xml";
063:
064: // XXX Should be moved to embedded
065: /**
066: * The shared extensions class loader for this server.
067: */
068: protected ClassLoader parentClassLoader = Catalina.class
069: .getClassLoader();
070:
071: /**
072: * The server component we are starting or stopping
073: */
074: protected Server server = null;
075:
076: /**
077: * Are we starting a new server?
078: */
079: protected boolean starting = false;
080:
081: /**
082: * Are we stopping an existing server?
083: */
084: protected boolean stopping = false;
085:
086: /**
087: * Use shutdown hook flag.
088: */
089: protected boolean useShutdownHook = true;
090:
091: /**
092: * Shutdown hook.
093: */
094: protected Thread shutdownHook = null;
095:
096: // ------------------------------------------------------------- Properties
097:
098: public void setConfig(String file) {
099: configFile = file;
100: }
101:
102: public void setConfigFile(String file) {
103: configFile = file;
104: }
105:
106: public String getConfigFile() {
107: return configFile;
108: }
109:
110: public void setUseShutdownHook(boolean useShutdownHook) {
111: this .useShutdownHook = useShutdownHook;
112: }
113:
114: public boolean getUseShutdownHook() {
115: return useShutdownHook;
116: }
117:
118: /**
119: * Set the shared extensions class loader.
120: *
121: * @param parentClassLoader The shared extensions class loader.
122: */
123: public void setParentClassLoader(ClassLoader parentClassLoader) {
124:
125: this .parentClassLoader = parentClassLoader;
126:
127: }
128:
129: /**
130: * Set the server instance we are configuring.
131: *
132: * @param server The new server
133: */
134: public void setServer(Server server) {
135:
136: this .server = server;
137:
138: }
139:
140: // ----------------------------------------------------------- Main Program
141:
142: /**
143: * The application main program.
144: *
145: * @param args Command line arguments
146: */
147: public static void main(String args[]) {
148: (new Catalina()).process(args);
149: }
150:
151: /**
152: * The instance main program.
153: *
154: * @param args Command line arguments
155: */
156: public void process(String args[]) {
157:
158: setAwait(true);
159: setCatalinaHome();
160: setCatalinaBase();
161: try {
162: if (arguments(args)) {
163: if (starting) {
164: load(args);
165: start();
166: } else if (stopping) {
167: stopServer();
168: }
169: }
170: } catch (Exception e) {
171: e.printStackTrace(System.out);
172: }
173: }
174:
175: // ------------------------------------------------------ Protected Methods
176:
177: /**
178: * Process the specified command line arguments, and return
179: * <code>true</code> if we should continue processing; otherwise
180: * return <code>false</code>.
181: *
182: * @param args Command line arguments to process
183: */
184: protected boolean arguments(String args[]) {
185:
186: boolean isConfig = false;
187:
188: if (args.length < 1) {
189: usage();
190: return (false);
191: }
192:
193: for (int i = 0; i < args.length; i++) {
194: if (isConfig) {
195: configFile = args[i];
196: isConfig = false;
197: } else if (args[i].equals("-config")) {
198: isConfig = true;
199: } else if (args[i].equals("-nonaming")) {
200: setUseNaming(false);
201: } else if (args[i].equals("-help")) {
202: usage();
203: return (false);
204: } else if (args[i].equals("start")) {
205: starting = true;
206: stopping = false;
207: } else if (args[i].equals("stop")) {
208: starting = false;
209: stopping = true;
210: } else {
211: usage();
212: return (false);
213: }
214: }
215:
216: return (true);
217:
218: }
219:
220: /**
221: * Return a File object representing our configuration file.
222: */
223: protected File configFile() {
224:
225: File file = new File(configFile);
226: if (!file.isAbsolute())
227: file = new File(System.getProperty("catalina.base"),
228: configFile);
229: return (file);
230:
231: }
232:
233: /**
234: * Create and configure the Digester we will be using for startup.
235: */
236: protected Digester createStartDigester() {
237: long t1 = System.currentTimeMillis();
238: // Initialize the digester
239: Digester digester = new Digester();
240: digester.setValidating(false);
241: digester.setClassLoader(StandardServer.class.getClassLoader());
242:
243: // Configure the actions we will be using
244: digester.addObjectCreate("Server",
245: "org.apache.catalina.core.StandardServer", "className");
246: digester.addSetProperties("Server");
247: digester.addSetNext("Server", "setServer",
248: "org.apache.catalina.Server");
249:
250: digester.addObjectCreate("Server/GlobalNamingResources",
251: "org.apache.catalina.deploy.NamingResources");
252: digester.addSetProperties("Server/GlobalNamingResources");
253: digester.addSetNext("Server/GlobalNamingResources",
254: "setGlobalNamingResources",
255: "org.apache.catalina.deploy.NamingResources");
256:
257: digester.addObjectCreate("Server/Listener", null, // MUST be specified in the element
258: "className");
259: digester.addSetProperties("Server/Listener");
260: digester.addSetNext("Server/Listener", "addLifecycleListener",
261: "org.apache.catalina.LifecycleListener");
262:
263: digester
264: .addObjectCreate("Server/Service",
265: "org.apache.catalina.core.StandardService",
266: "className");
267: digester.addSetProperties("Server/Service");
268: digester.addSetNext("Server/Service", "addService",
269: "org.apache.catalina.Service");
270:
271: digester.addObjectCreate("Server/Service/Listener", null, // MUST be specified in the element
272: "className");
273: digester.addSetProperties("Server/Service/Listener");
274: digester.addSetNext("Server/Service/Listener",
275: "addLifecycleListener",
276: "org.apache.catalina.LifecycleListener");
277:
278: //Executor
279: digester.addObjectCreate("Server/Service/Executor",
280: "org.apache.catalina.core.StandardThreadExecutor",
281: "className");
282: digester.addSetProperties("Server/Service/Executor");
283:
284: digester.addSetNext("Server/Service/Executor", "addExecutor",
285: "org.apache.catalina.Executor");
286:
287: digester.addRule("Server/Service/Connector",
288: new ConnectorCreateRule());
289: digester.addRule("Server/Service/Connector",
290: new SetAllPropertiesRule(new String[] { "executor" }));
291: digester.addSetNext("Server/Service/Connector", "addConnector",
292: "org.apache.catalina.connector.Connector");
293:
294: digester.addObjectCreate("Server/Service/Connector/Listener",
295: null, // MUST be specified in the element
296: "className");
297: digester.addSetProperties("Server/Service/Connector/Listener");
298: digester.addSetNext("Server/Service/Connector/Listener",
299: "addLifecycleListener",
300: "org.apache.catalina.LifecycleListener");
301:
302: // Add RuleSets for nested elements
303: digester.addRuleSet(new NamingRuleSet(
304: "Server/GlobalNamingResources/"));
305: digester.addRuleSet(new EngineRuleSet("Server/Service/"));
306: digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
307: digester.addRuleSet(new ContextRuleSet(
308: "Server/Service/Engine/Host/"));
309: digester
310: .addRuleSet(ClusterRuleSetFactory
311: .getClusterRuleSet("Server/Service/Engine/Host/Cluster/"));
312: digester.addRuleSet(new NamingRuleSet(
313: "Server/Service/Engine/Host/Context/"));
314:
315: // When the 'engine' is found, set the parentClassLoader.
316: digester.addRule("Server/Service/Engine",
317: new SetParentClassLoaderRule(parentClassLoader));
318: digester.addRuleSet(ClusterRuleSetFactory
319: .getClusterRuleSet("Server/Service/Engine/Cluster/"));
320:
321: long t2 = System.currentTimeMillis();
322: if (log.isDebugEnabled())
323: log.debug("Digester for server.xml created " + (t2 - t1));
324: return (digester);
325:
326: }
327:
328: /**
329: * Create and configure the Digester we will be using for shutdown.
330: */
331: protected Digester createStopDigester() {
332:
333: // Initialize the digester
334: Digester digester = new Digester();
335:
336: // Configure the rules we need for shutting down
337: digester.addObjectCreate("Server",
338: "org.apache.catalina.core.StandardServer", "className");
339: digester.addSetProperties("Server");
340: digester.addSetNext("Server", "setServer",
341: "org.apache.catalina.Server");
342:
343: return (digester);
344:
345: }
346:
347: public void stopServer() {
348: stopServer(null);
349: }
350:
351: public void stopServer(String[] arguments) {
352:
353: if (arguments != null) {
354: arguments(arguments);
355: }
356:
357: if (server == null) {
358: // Create and execute our Digester
359: Digester digester = createStopDigester();
360: digester.setClassLoader(Thread.currentThread()
361: .getContextClassLoader());
362: File file = configFile();
363: try {
364: InputSource is = new InputSource("file://"
365: + file.getAbsolutePath());
366: FileInputStream fis = new FileInputStream(file);
367: is.setByteStream(fis);
368: digester.push(this );
369: digester.parse(is);
370: fis.close();
371: } catch (Exception e) {
372: log.error("Catalina.stop: ", e);
373: System.exit(1);
374: }
375: }
376:
377: // Stop the existing server
378: try {
379: Socket socket = new Socket("127.0.0.1", server.getPort());
380: OutputStream stream = socket.getOutputStream();
381: String shutdown = server.getShutdown();
382: for (int i = 0; i < shutdown.length(); i++)
383: stream.write(shutdown.charAt(i));
384: stream.flush();
385: stream.close();
386: socket.close();
387: } catch (IOException e) {
388: log.error("Catalina.stop: ", e);
389: System.exit(1);
390: }
391:
392: }
393:
394: /**
395: * Set the <code>catalina.base</code> System property to the current
396: * working directory if it has not been set.
397: * @deprecated Use initDirs()
398: */
399: public void setCatalinaBase() {
400: initDirs();
401: }
402:
403: /**
404: * Set the <code>catalina.home</code> System property to the current
405: * working directory if it has not been set.
406: * @deprecated Use initDirs()
407: */
408: public void setCatalinaHome() {
409: initDirs();
410: }
411:
412: /**
413: * Start a new server instance.
414: */
415: public void load() {
416:
417: long t1 = System.nanoTime();
418:
419: initDirs();
420:
421: // Before digester - it may be needed
422:
423: initNaming();
424:
425: // Create and execute our Digester
426: Digester digester = createStartDigester();
427:
428: InputSource inputSource = null;
429: InputStream inputStream = null;
430: File file = null;
431: try {
432: file = configFile();
433: inputStream = new FileInputStream(file);
434: inputSource = new InputSource("file://"
435: + file.getAbsolutePath());
436: } catch (Exception e) {
437: ;
438: }
439: if (inputStream == null) {
440: try {
441: inputStream = getClass().getClassLoader()
442: .getResourceAsStream(getConfigFile());
443: inputSource = new InputSource(getClass()
444: .getClassLoader().getResource(getConfigFile())
445: .toString());
446: } catch (Exception e) {
447: ;
448: }
449: }
450:
451: // This should be included in catalina.jar
452: // Alternative: don't bother with xml, just create it manually.
453: if (inputStream == null) {
454: try {
455: inputStream = getClass().getClassLoader()
456: .getResourceAsStream("server-embed.xml");
457: inputSource = new InputSource(getClass()
458: .getClassLoader().getResource(
459: "server-embed.xml").toString());
460: } catch (Exception e) {
461: ;
462: }
463: }
464:
465: if ((inputStream == null) && (file != null)) {
466: log.warn("Can't load server.xml from "
467: + file.getAbsolutePath());
468: return;
469: }
470:
471: try {
472: inputSource.setByteStream(inputStream);
473: digester.push(this );
474: digester.parse(inputSource);
475: inputStream.close();
476: } catch (Exception e) {
477: log.warn("Catalina.start using " + getConfigFile() + ": ",
478: e);
479: return;
480: }
481:
482: // Stream redirection
483: initStreams();
484:
485: // Start the new server
486: if (server instanceof Lifecycle) {
487: try {
488: server.initialize();
489: } catch (LifecycleException e) {
490: log.error("Catalina.start", e);
491: }
492: }
493:
494: long t2 = System.nanoTime();
495: if (log.isInfoEnabled())
496: log.info("Initialization processed in "
497: + ((t2 - t1) / 1000000) + " ms");
498:
499: }
500:
501: /*
502: * Load using arguments
503: */
504: public void load(String args[]) {
505:
506: try {
507: if (arguments(args))
508: load();
509: } catch (Exception e) {
510: e.printStackTrace(System.out);
511: }
512: }
513:
514: public void create() {
515:
516: }
517:
518: public void destroy() {
519:
520: }
521:
522: /**
523: * Start a new server instance.
524: */
525: public void start() {
526:
527: if (server == null) {
528: load();
529: }
530:
531: long t1 = System.nanoTime();
532:
533: // Start the new server
534: if (server instanceof Lifecycle) {
535: try {
536: ((Lifecycle) server).start();
537: } catch (LifecycleException e) {
538: log.error("Catalina.start: ", e);
539: }
540: }
541:
542: long t2 = System.nanoTime();
543: if (log.isInfoEnabled())
544: log.info("Server startup in " + ((t2 - t1) / 1000000)
545: + " ms");
546:
547: try {
548: // Register shutdown hook
549: if (useShutdownHook) {
550: if (shutdownHook == null) {
551: shutdownHook = new CatalinaShutdownHook();
552: }
553: Runtime.getRuntime().addShutdownHook(shutdownHook);
554: }
555: } catch (Throwable t) {
556: // This will fail on JDK 1.2. Ignoring, as Tomcat can run
557: // fine without the shutdown hook.
558: }
559:
560: if (await) {
561: await();
562: stop();
563: }
564:
565: }
566:
567: /**
568: * Stop an existing server instance.
569: */
570: public void stop() {
571:
572: try {
573: // Remove the ShutdownHook first so that server.stop()
574: // doesn't get invoked twice
575: if (useShutdownHook) {
576: Runtime.getRuntime().removeShutdownHook(shutdownHook);
577: }
578: } catch (Throwable t) {
579: // This will fail on JDK 1.2. Ignoring, as Tomcat can run
580: // fine without the shutdown hook.
581: }
582:
583: // Shut down the server
584: if (server instanceof Lifecycle) {
585: try {
586: ((Lifecycle) server).stop();
587: } catch (LifecycleException e) {
588: log.error("Catalina.stop", e);
589: }
590: }
591:
592: }
593:
594: /**
595: * Await and shutdown.
596: */
597: public void await() {
598:
599: server.await();
600:
601: }
602:
603: /**
604: * Print usage information for this application.
605: */
606: protected void usage() {
607:
608: System.out
609: .println("usage: java org.apache.catalina.startup.Catalina"
610: + " [ -config {pathname} ]"
611: + " [ -nonaming ] { start | stop }");
612:
613: }
614:
615: // --------------------------------------- CatalinaShutdownHook Inner Class
616:
617: // XXX Should be moved to embedded !
618: /**
619: * Shutdown hook which will perform a clean shutdown of Catalina if needed.
620: */
621: protected class CatalinaShutdownHook extends Thread {
622:
623: public void run() {
624:
625: if (server != null) {
626: Catalina.this .stop();
627: }
628:
629: }
630:
631: }
632:
633: private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
634: .getLog(Catalina.class);
635:
636: }
637:
638: // ------------------------------------------------------------ Private Classes
639:
640: /**
641: * Rule that sets the parent class loader for the top object on the stack,
642: * which must be a <code>Container</code>.
643: */
644:
645: final class SetParentClassLoaderRule extends Rule {
646:
647: public SetParentClassLoaderRule(ClassLoader parentClassLoader) {
648:
649: this .parentClassLoader = parentClassLoader;
650:
651: }
652:
653: ClassLoader parentClassLoader = null;
654:
655: public void begin(String namespace, String name,
656: Attributes attributes) throws Exception {
657:
658: if (digester.getLogger().isDebugEnabled())
659: digester.getLogger().debug("Setting parent class loader");
660:
661: Container top = (Container) digester.peek();
662: top.setParentClassLoader(parentClassLoader);
663:
664: }
665:
666: }
|