1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.startup;
20
21
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.net.InetAddress;
28 import java.net.Socket;
29 import java.util.ArrayList;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.logging.LogManager;
33
34 import org.apache.catalina.Container;
35 import org.apache.catalina.Lifecycle;
36 import org.apache.catalina.LifecycleException;
37 import org.apache.catalina.core.StandardServer;
38 import org.apache.juli.ClassLoaderLogManager;
39 import org.apache.tomcat.util.digester.Digester;
40 import org.apache.tomcat.util.digester.Rule;
41 import org.xml.sax.Attributes;
42 import org.xml.sax.InputSource;
43
44
45 /**
46 * Startup/Shutdown shell program for Catalina. The following command line
47 * options are recognized:
48 * <ul>
49 * <li><b>-config {pathname}</b> - Set the pathname of the configuration file
50 * to be processed. If a relative path is specified, it will be
51 * interpreted as relative to the directory pathname specified by the
52 * "catalina.base" system property. [conf/server.xml]
53 * <li><b>-help</b> - Display usage information.
54 * <li><b>-nonaming</b> - Disable naming support.
55 * <li><b>start</b> - Start an instance of Catalina.
56 * <li><b>stop</b> - Stop the currently running instance of Catalina.
57 * </u>
58 *
59 * Should do the same thing as Embedded, but using a server.xml file.
60 *
61 * @author Craig R. McClanahan
62 * @author Remy Maucherat
63 * @version $Revision: 919742 $ $Date: 2010-03-06 12:15:07 +0100 (Sat, 06 Mar 2010) $
64 */
65
66 public class Catalina extends Embedded {
67
68
69 // ----------------------------------------------------- Instance Variables
70
71
72 /**
73 * Pathname to the server configuration file.
74 */
75 protected String configFile = "conf/server.xml";
76
77 // XXX Should be moved to embedded
78 /**
79 * The shared extensions class loader for this server.
80 */
81 protected ClassLoader parentClassLoader =
82 Catalina.class.getClassLoader();
83
84
85 /**
86 * Are we starting a new server?
87 */
88 protected boolean starting = false;
89
90
91 /**
92 * Are we stopping an existing server?
93 */
94 protected boolean stopping = false;
95
96
97 /**
98 * Use shutdown hook flag.
99 */
100 protected boolean useShutdownHook = true;
101
102
103 /**
104 * Shutdown hook.
105 */
106 protected Thread shutdownHook = null;
107
108
109 // ------------------------------------------------------------- Properties
110
111
112 public void setConfig(String file) {
113 configFile = file;
114 }
115
116
117 public void setConfigFile(String file) {
118 configFile = file;
119 }
120
121
122 public String getConfigFile() {
123 return configFile;
124 }
125
126
127 public void setUseShutdownHook(boolean useShutdownHook) {
128 this.useShutdownHook = useShutdownHook;
129 }
130
131
132 public boolean getUseShutdownHook() {
133 return useShutdownHook;
134 }
135
136
137 /**
138 * Set the shared extensions class loader.
139 *
140 * @param parentClassLoader The shared extensions class loader.
141 */
142 public void setParentClassLoader(ClassLoader parentClassLoader) {
143
144 this.parentClassLoader = parentClassLoader;
145
146 }
147
148
149 // ----------------------------------------------------------- Main Program
150
151 /**
152 * The application main program.
153 *
154 * @param args Command line arguments
155 */
156 public static void main(String args[]) {
157 (new Catalina()).process(args);
158 }
159
160
161 /**
162 * The instance main program.
163 *
164 * @param args Command line arguments
165 */
166 public void process(String args[]) {
167
168 setAwait(true);
169 setCatalinaHome();
170 setCatalinaBase();
171 try {
172 if (arguments(args)) {
173 if (starting) {
174 load(args);
175 start();
176 } else if (stopping) {
177 stopServer();
178 }
179 }
180 } catch (Exception e) {
181 e.printStackTrace(System.out);
182 }
183 }
184
185
186 // ------------------------------------------------------ Protected Methods
187
188
189 /**
190 * Process the specified command line arguments, and return
191 * <code>true</code> if we should continue processing; otherwise
192 * return <code>false</code>.
193 *
194 * @param args Command line arguments to process
195 */
196 protected boolean arguments(String args[]) {
197
198 boolean isConfig = false;
199
200 if (args.length < 1) {
201 usage();
202 return (false);
203 }
204
205 for (int i = 0; i < args.length; i++) {
206 if (isConfig) {
207 configFile = args[i];
208 isConfig = false;
209 } else if (args[i].equals("-config")) {
210 isConfig = true;
211 } else if (args[i].equals("-nonaming")) {
212 setUseNaming( false );
213 } else if (args[i].equals("-help")) {
214 usage();
215 return (false);
216 } else if (args[i].equals("start")) {
217 starting = true;
218 stopping = false;
219 } else if (args[i].equals("stop")) {
220 starting = false;
221 stopping = true;
222 } else {
223 usage();
224 return (false);
225 }
226 }
227
228 return (true);
229
230 }
231
232
233 /**
234 * Return a File object representing our configuration file.
235 */
236 protected File configFile() {
237
238 File file = new File(configFile);
239 if (!file.isAbsolute())
240 file = new File(System.getProperty("catalina.base"), configFile);
241 return (file);
242
243 }
244
245
246 /**
247 * Create and configure the Digester we will be using for startup.
248 */
249 protected Digester createStartDigester() {
250 long t1=System.currentTimeMillis();
251 // Initialize the digester
252 Digester digester = new Digester();
253 digester.setValidating(false);
254 digester.setRulesValidation(true);
255 HashMap<Class, List<String>> fakeAttributes = new HashMap<Class, List<String>>();
256 ArrayList<String> attrs = new ArrayList<String>();
257 attrs.add("className");
258 fakeAttributes.put(Object.class, attrs);
259 digester.setFakeAttributes(fakeAttributes);
260 digester.setClassLoader(StandardServer.class.getClassLoader());
261
262 // Configure the actions we will be using
263 digester.addObjectCreate("Server",
264 "org.apache.catalina.core.StandardServer",
265 "className");
266 digester.addSetProperties("Server");
267 digester.addSetNext("Server",
268 "setServer",
269 "org.apache.catalina.Server");
270
271 digester.addObjectCreate("Server/GlobalNamingResources",
272 "org.apache.catalina.deploy.NamingResources");
273 digester.addSetProperties("Server/GlobalNamingResources");
274 digester.addSetNext("Server/GlobalNamingResources",
275 "setGlobalNamingResources",
276 "org.apache.catalina.deploy.NamingResources");
277
278 digester.addObjectCreate("Server/Listener",
279 null, // MUST be specified in the element
280 "className");
281 digester.addSetProperties("Server/Listener");
282 digester.addSetNext("Server/Listener",
283 "addLifecycleListener",
284 "org.apache.catalina.LifecycleListener");
285
286 digester.addObjectCreate("Server/Service",
287 "org.apache.catalina.core.StandardService",
288 "className");
289 digester.addSetProperties("Server/Service");
290 digester.addSetNext("Server/Service",
291 "addService",
292 "org.apache.catalina.Service");
293
294 digester.addObjectCreate("Server/Service/Listener",
295 null, // MUST be specified in the element
296 "className");
297 digester.addSetProperties("Server/Service/Listener");
298 digester.addSetNext("Server/Service/Listener",
299 "addLifecycleListener",
300 "org.apache.catalina.LifecycleListener");
301
302 //Executor
303 digester.addObjectCreate("Server/Service/Executor",
304 "org.apache.catalina.core.StandardThreadExecutor",
305 "className");
306 digester.addSetProperties("Server/Service/Executor");
307
308 digester.addSetNext("Server/Service/Executor",
309 "addExecutor",
310 "org.apache.catalina.Executor");
311
312
313 digester.addRule("Server/Service/Connector",
314 new ConnectorCreateRule());
315 digester.addRule("Server/Service/Connector",
316 new SetAllPropertiesRule(new String[]{"executor"}));
317 digester.addSetNext("Server/Service/Connector",
318 "addConnector",
319 "org.apache.catalina.connector.Connector");
320
321
322
323
324 digester.addObjectCreate("Server/Service/Connector/Listener",
325 null, // MUST be specified in the element
326 "className");
327 digester.addSetProperties("Server/Service/Connector/Listener");
328 digester.addSetNext("Server/Service/Connector/Listener",
329 "addLifecycleListener",
330 "org.apache.catalina.LifecycleListener");
331
332 // Add RuleSets for nested elements
333 digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
334 digester.addRuleSet(new EngineRuleSet("Server/Service/"));
335 digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
336 digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
337 digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Host/Cluster/"));
338 digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
339
340 // When the 'engine' is found, set the parentClassLoader.
341 digester.addRule("Server/Service/Engine",
342 new SetParentClassLoaderRule(parentClassLoader));
343 digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Cluster/"));
344
345 long t2=System.currentTimeMillis();
346 if (log.isDebugEnabled())
347 log.debug("Digester for server.xml created " + ( t2-t1 ));
348 return (digester);
349
350 }
351
352
353 /**
354 * Create and configure the Digester we will be using for shutdown.
355 */
356 protected Digester createStopDigester() {
357
358 // Initialize the digester
359 Digester digester = new Digester();
360
361 // Configure the rules we need for shutting down
362 digester.addObjectCreate("Server",
363 "org.apache.catalina.core.StandardServer",
364 "className");
365 digester.addSetProperties("Server");
366 digester.addSetNext("Server",
367 "setServer",
368 "org.apache.catalina.Server");
369
370 return (digester);
371
372 }
373
374
375 public void stopServer() {
376 stopServer(null);
377 }
378
379 public void stopServer(String[] arguments) {
380
381 if (arguments != null) {
382 arguments(arguments);
383 }
384
385 if( getServer() == null ) {
386 // Create and execute our Digester
387 Digester digester = createStopDigester();
388 digester.setClassLoader(Thread.currentThread().getContextClassLoader());
389 File file = configFile();
390 try {
391 InputSource is =
392 new InputSource("file://" + file.getAbsolutePath());
393 FileInputStream fis = new FileInputStream(file);
394 is.setByteStream(fis);
395 digester.push(this);
396 digester.parse(is);
397 fis.close();
398 } catch (Exception e) {
399 log.error("Catalina.stop: ", e);
400 System.exit(1);
401 }
402 }
403
404 // Stop the existing server
405 try {
406 if (getServer().getPort()>0) {
407 String hostAddress = InetAddress.getByName("localhost").getHostAddress();
408 Socket socket = new Socket(hostAddress, getServer().getPort());
409 OutputStream stream = socket.getOutputStream();
410 String shutdown = getServer().getShutdown();
411 for (int i = 0; i < shutdown.length(); i++)
412 stream.write(shutdown.charAt(i));
413 stream.flush();
414 stream.close();
415 socket.close();
416 } else {
417 log.error(sm.getString("catalina.stopServer"));
418 System.exit(1);
419 }
420 } catch (IOException e) {
421 log.error("Catalina.stop: ", e);
422 System.exit(1);
423 }
424
425 }
426
427
428 /**
429 * Set the <code>catalina.base</code> System property to the current
430 * working directory if it has not been set.
431 * @deprecated Use initDirs()
432 */
433 public void setCatalinaBase() {
434 initDirs();
435 }
436
437 /**
438 * Set the <code>catalina.home</code> System property to the current
439 * working directory if it has not been set.
440 * @deprecated Use initDirs()
441 */
442 public void setCatalinaHome() {
443 initDirs();
444 }
445
446 /**
447 * Start a new server instance.
448 */
449 public void load() {
450
451 long t1 = System.nanoTime();
452
453 initDirs();
454
455 // Before digester - it may be needed
456
457 initNaming();
458
459 // Create and execute our Digester
460 Digester digester = createStartDigester();
461
462 InputSource inputSource = null;
463 InputStream inputStream = null;
464 File file = null;
465 try {
466 file = configFile();
467 inputStream = new FileInputStream(file);
468 inputSource = new InputSource("file://" + file.getAbsolutePath());
469 } catch (Exception e) {
470 ;
471 }
472 if (inputStream == null) {
473 try {
474 inputStream = getClass().getClassLoader()
475 .getResourceAsStream(getConfigFile());
476 inputSource = new InputSource
477 (getClass().getClassLoader()
478 .getResource(getConfigFile()).toString());
479 } catch (Exception e) {
480 ;
481 }
482 }
483
484 // This should be included in catalina.jar
485 // Alternative: don't bother with xml, just create it manually.
486 if( inputStream==null ) {
487 try {
488 inputStream = getClass().getClassLoader()
489 .getResourceAsStream("server-embed.xml");
490 inputSource = new InputSource
491 (getClass().getClassLoader()
492 .getResource("server-embed.xml").toString());
493 } catch (Exception e) {
494 ;
495 }
496 }
497
498
499 if ((inputStream == null) && (file != null)) {
500 log.warn("Can't load server.xml from " + file.getAbsolutePath());
501 if (file.exists() && !file.canRead()) {
502 log.warn("Permissions incorrect, read permission is not allowed on the file.");
503 }
504 return;
505 }
506
507 try {
508 inputSource.setByteStream(inputStream);
509 digester.push(this);
510 digester.parse(inputSource);
511 inputStream.close();
512 } catch (Exception e) {
513 log.warn("Catalina.start using "
514 + getConfigFile() + ": " , e);
515 return;
516 }
517
518 // Stream redirection
519 initStreams();
520
521 // Start the new server
522 if (getServer() instanceof Lifecycle) {
523 try {
524 getServer().initialize();
525 } catch (LifecycleException e) {
526 if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
527 throw new java.lang.Error(e);
528 else
529 log.error("Catalina.start", e);
530
531 }
532 }
533
534 long t2 = System.nanoTime();
535 if(log.isInfoEnabled())
536 log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
537
538 }
539
540
541 /*
542 * Load using arguments
543 */
544 public void load(String args[]) {
545
546 try {
547 if (arguments(args))
548 load();
549 } catch (Exception e) {
550 e.printStackTrace(System.out);
551 }
552 }
553
554 public void create() {
555
556 }
557
558 public void destroy() {
559
560 }
561
562 /**
563 * Start a new server instance.
564 */
565 public void start() {
566
567 if (getServer() == null) {
568 load();
569 }
570
571 if (getServer() == null) {
572 log.fatal("Cannot start server. Server instance is not configured.");
573 return;
574 }
575
576 long t1 = System.nanoTime();
577
578 // Start the new server
579 if (getServer() instanceof Lifecycle) {
580 try {
581 ((Lifecycle) getServer()).start();
582 } catch (LifecycleException e) {
583 log.error("Catalina.start: ", e);
584 }
585 }
586
587 long t2 = System.nanoTime();
588 if(log.isInfoEnabled())
589 log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
590
591 try {
592 // Register shutdown hook
593 if (useShutdownHook) {
594 if (shutdownHook == null) {
595 shutdownHook = new CatalinaShutdownHook();
596 }
597 Runtime.getRuntime().addShutdownHook(shutdownHook);
598
599 // If JULI is being used, disable JULI's shutdown hook since
600 // shutdown hooks run in parallel and log messages may be lost
601 // if JULI's hook completes before the CatalinaShutdownHook()
602 LogManager logManager = LogManager.getLogManager();
603 if (logManager instanceof ClassLoaderLogManager) {
604 ((ClassLoaderLogManager) logManager).setUseShutdownHook(
605 false);
606 }
607 }
608 } catch (Throwable t) {
609 // This will fail on JDK 1.2. Ignoring, as Tomcat can run
610 // fine without the shutdown hook.
611 }
612
613 if (await) {
614 await();
615 stop();
616 }
617
618 }
619
620
621 /**
622 * Stop an existing server instance.
623 */
624 public void stop() {
625
626 try {
627 // Remove the ShutdownHook first so that server.stop()
628 // doesn't get invoked twice
629 if (useShutdownHook) {
630 Runtime.getRuntime().removeShutdownHook(shutdownHook);
631
632 // If JULI is being used, re-enable JULI's shutdown to ensure
633 // log messages are not lost
634 LogManager logManager = LogManager.getLogManager();
635 if (logManager instanceof ClassLoaderLogManager) {
636 ((ClassLoaderLogManager) logManager).setUseShutdownHook(
637 true);
638 }
639 }
640 } catch (Throwable t) {
641 // This will fail on JDK 1.2. Ignoring, as Tomcat can run
642 // fine without the shutdown hook.
643 }
644
645 // Shut down the server
646 if (getServer() instanceof Lifecycle) {
647 try {
648 ((Lifecycle) getServer()).stop();
649 } catch (LifecycleException e) {
650 log.error("Catalina.stop", e);
651 }
652 }
653
654 }
655
656
657 /**
658 * Await and shutdown.
659 */
660 public void await() {
661
662 getServer().await();
663
664 }
665
666
667 /**
668 * Print usage information for this application.
669 */
670 protected void usage() {
671
672 System.out.println
673 ("usage: java org.apache.catalina.startup.Catalina"
674 + " [ -config {pathname} ]"
675 + " [ -nonaming ] "
676 + " { -help | start | stop }");
677
678 }
679
680
681 // --------------------------------------- CatalinaShutdownHook Inner Class
682
683 // XXX Should be moved to embedded !
684 /**
685 * Shutdown hook which will perform a clean shutdown of Catalina if needed.
686 */
687 protected class CatalinaShutdownHook extends Thread {
688
689 public void run() {
690
691 if (getServer() != null) {
692 Catalina.this.stop();
693 }
694
695 // If JULI is used, shut JULI down *after* the server shuts down
696 // so log messages aren't lost
697 LogManager logManager = LogManager.getLogManager();
698 if (logManager instanceof ClassLoaderLogManager) {
699 ((ClassLoaderLogManager) logManager).shutdown();
700 }
701
702 }
703
704 }
705
706
707 private static org.apache.juli.logging.Log log=
708 org.apache.juli.logging.LogFactory.getLog( Catalina.class );
709
710 }
711
712
713 // ------------------------------------------------------------ Private Classes
714
715
716 /**
717 * Rule that sets the parent class loader for the top object on the stack,
718 * which must be a <code>Container</code>.
719 */
720
721 final class SetParentClassLoaderRule extends Rule {
722
723 public SetParentClassLoaderRule(ClassLoader parentClassLoader) {
724
725 this.parentClassLoader = parentClassLoader;
726
727 }
728
729 ClassLoader parentClassLoader = null;
730
731 public void begin(String namespace, String name, Attributes attributes)
732 throws Exception {
733
734 if (digester.getLogger().isDebugEnabled())
735 digester.getLogger().debug("Setting parent class loader");
736
737 Container top = (Container) digester.peek();
738 top.setParentClassLoader(parentClassLoader);
739
740 }
741
742
743 }