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