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