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