1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.start;
20
21 import static org.eclipse.jetty.start.UsageException.ERR_BAD_GRAPH;
22 import static org.eclipse.jetty.start.UsageException.ERR_INVOKE_MAIN;
23 import static org.eclipse.jetty.start.UsageException.ERR_NOT_STOPPED;
24 import static org.eclipse.jetty.start.UsageException.ERR_UNKNOWN;
25
26 import java.io.BufferedReader;
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.LineNumberReader;
32 import java.io.OutputStream;
33 import java.lang.reflect.InvocationTargetException;
34 import java.lang.reflect.Method;
35 import java.net.ConnectException;
36 import java.net.InetAddress;
37 import java.net.Socket;
38 import java.net.SocketTimeoutException;
39 import java.nio.file.Path;
40 import java.util.List;
41 import java.util.Locale;
42
43 import org.eclipse.jetty.start.config.CommandLineConfigSource;
44 import org.eclipse.jetty.start.graph.GraphException;
45 import org.eclipse.jetty.start.graph.Selection;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public class Main
66 {
67 private static final int EXIT_USAGE = 1;
68
69 public static void main(String[] args)
70 {
71 try
72 {
73 Main main = new Main();
74 StartArgs startArgs = main.processCommandLine(args);
75 main.start(startArgs);
76 }
77 catch (UsageException e)
78 {
79 System.err.println(e.getMessage());
80 usageExit(e.getCause(),e.getExitCode());
81 }
82 catch (Throwable e)
83 {
84 usageExit(e,UsageException.ERR_UNKNOWN);
85 }
86 }
87
88 static void usageExit(int exit)
89 {
90 usageExit(null,exit);
91 }
92
93 static void usageExit(Throwable t, int exit)
94 {
95 if (t != null)
96 {
97 t.printStackTrace(System.err);
98 }
99 System.err.println();
100 System.err.println("Usage: java -jar start.jar [options] [properties] [configs]");
101 System.err.println(" java -jar start.jar --help # for more information");
102 System.exit(exit);
103 }
104
105 private BaseHome baseHome;
106 private StartArgs startupArgs;
107
108 public Main() throws IOException
109 {
110 }
111
112 private void copyInThread(final InputStream in, final OutputStream out)
113 {
114 new Thread(new Runnable()
115 {
116 @Override
117 public void run()
118 {
119 try
120 {
121 byte[] buf = new byte[1024];
122 int len = in.read(buf);
123 while (len > 0)
124 {
125 out.write(buf,0,len);
126 len = in.read(buf);
127 }
128 }
129 catch (IOException e)
130 {
131
132 }
133 }
134
135 }).start();
136 }
137
138 private void dumpClasspathWithVersions(Classpath classpath)
139 {
140 StartLog.endStartLog();
141 System.out.println();
142 System.out.println("Jetty Server Classpath:");
143 System.out.println("-----------------------");
144 if (classpath.count() == 0)
145 {
146 System.out.println("No classpath entries and/or version information available show.");
147 return;
148 }
149
150 System.out.println("Version Information on " + classpath.count() + " entr" + ((classpath.count() > 1)?"ies":"y") + " in the classpath.");
151 System.out.println("Note: order presented here is how they would appear on the classpath.");
152 System.out.println(" changes to the --module=name command line options will be reflected here.");
153
154 int i = 0;
155 for (File element : classpath.getElements())
156 {
157 System.out.printf("%2d: %24s | %s\n",i++,getVersion(element),baseHome.toShortForm(element));
158 }
159 }
160
161 public BaseHome getBaseHome()
162 {
163 return baseHome;
164 }
165
166 private String getVersion(File element)
167 {
168 if (element.isDirectory())
169 {
170 return "(dir)";
171 }
172
173 if (element.isFile())
174 {
175 String name = element.getName().toLowerCase(Locale.ENGLISH);
176 if (name.endsWith(".jar"))
177 {
178 return JarVersion.getVersion(element);
179 }
180 }
181
182 return "";
183 }
184
185 public void invokeMain(ClassLoader classloader, StartArgs args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException, IOException
186 {
187 Class<?> invoked_class = null;
188 String mainclass = args.getMainClassname();
189
190 try
191 {
192 invoked_class = classloader.loadClass(mainclass);
193 }
194 catch (ClassNotFoundException e)
195 {
196 System.out.println("WARNING: Nothing to start, exiting ...");
197 StartLog.debug(e);
198 usageExit(ERR_INVOKE_MAIN);
199 return;
200 }
201
202 StartLog.debug("%s - %s",invoked_class,invoked_class.getPackage().getImplementationVersion());
203
204 CommandLineBuilder cmd = args.getMainArgs(baseHome,false);
205 String argArray[] = cmd.getArgs().toArray(new String[0]);
206 StartLog.debug("Command Line Args: %s",cmd.toString());
207
208 Class<?>[] method_param_types = new Class[]
209 { argArray.getClass() };
210
211 Method main = invoked_class.getDeclaredMethod("main",method_param_types);
212 Object[] method_params = new Object[] { argArray };
213 StartLog.endStartLog();
214 main.invoke(null,method_params);
215 }
216
217 public void listConfig(StartArgs args)
218 {
219 StartLog.endStartLog();
220
221
222 args.dumpEnvironment(baseHome);
223
224
225 args.dumpJvmArgs();
226
227
228 args.dumpSystemProperties();
229
230
231 args.dumpProperties();
232
233
234 dumpClasspathWithVersions(args.getClasspath());
235
236
237 args.dumpActiveXmls(baseHome);
238 }
239
240 public void listModules(StartArgs args)
241 {
242 StartLog.endStartLog();
243 System.out.println();
244 System.out.println("Jetty All Available Modules:");
245 System.out.println("----------------------------");
246 args.getAllModules().dump();
247
248
249 System.out.println();
250 System.out.println("Jetty Selected Module Ordering:");
251 System.out.println("-------------------------------");
252 Modules modules = args.getAllModules();
253 modules.dumpSelected();
254 }
255
256
257
258
259
260
261
262 public StartArgs processCommandLine(List<String> cmdLine) throws Exception
263 {
264 return this.processCommandLine(cmdLine.toArray(new String[cmdLine.size()]));
265 }
266
267 public StartArgs processCommandLine(String[] cmdLine) throws Exception
268 {
269
270
271
272 CommandLineConfigSource cmdLineSource = new CommandLineConfigSource(cmdLine);
273 baseHome = new BaseHome(cmdLineSource);
274
275 StartLog.debug("jetty.home=%s",baseHome.getHome());
276 StartLog.debug("jetty.base=%s",baseHome.getBase());
277
278
279
280
281
282
283 StartLog.debug("Parsing collected arguments");
284 StartArgs args = new StartArgs();
285 args.parse(baseHome.getConfigSources());
286
287 try
288 {
289
290
291 Modules modules = new Modules(baseHome,args);
292 StartLog.debug("Registering all modules");
293 modules.registerAll();
294
295
296
297 for (String enabledModule : args.getEnabledModules())
298 {
299 for (String source : args.getSources(enabledModule))
300 {
301 String shortForm = baseHome.toShortForm(source);
302 modules.selectNode(enabledModule,new Selection(shortForm));
303 }
304 }
305
306 StartLog.debug("Building Module Graph");
307 modules.buildGraph();
308
309 args.setAllModules(modules);
310 List<Module> activeModules = modules.getSelected();
311
312 final Version START_VERSION = new Version(StartArgs.VERSION);
313
314 for(Module enabled: activeModules)
315 {
316 if(enabled.getVersion().isNewerThan(START_VERSION))
317 {
318 throw new UsageException(UsageException.ERR_BAD_GRAPH, "Module [" + enabled.getName() + "] specifies jetty version [" + enabled.getVersion()
319 + "] which is newer than this version of jetty [" + START_VERSION + "]");
320 }
321 }
322
323 for(String name: args.getSkipFileValidationModules())
324 {
325 Module module = modules.get(name);
326 module.setSkipFilesValidation(true);
327 }
328
329
330
331 args.expandLibs(baseHome);
332 args.expandModules(baseHome,activeModules);
333
334 }
335 catch (GraphException e)
336 {
337 throw new UsageException(ERR_BAD_GRAPH,e);
338 }
339
340
341
342 args.resolveExtraXmls(baseHome);
343
344
345
346 args.resolvePropertyFiles(baseHome);
347
348 return args;
349 }
350
351 public void start(StartArgs args) throws IOException, InterruptedException
352 {
353 StartLog.debug("StartArgs: %s",args);
354
355
356 Classpath classpath = args.getClasspath();
357
358 System.setProperty("java.class.path",classpath.toString());
359
360
361 if (args.isHelp())
362 {
363 usage(true);
364 }
365
366
367 if (args.isListClasspath())
368 {
369 dumpClasspathWithVersions(classpath);
370 }
371
372
373 if (args.isListConfig())
374 {
375 listConfig(args);
376 }
377
378
379 if (args.isListModules())
380 {
381 listModules(args);
382 }
383
384
385 if (args.getModuleGraphFilename() != null)
386 {
387 Path outputFile = baseHome.getBasePath(args.getModuleGraphFilename());
388 System.out.printf("Generating GraphViz Graph of Jetty Modules at %s%n",baseHome.toShortForm(outputFile));
389 ModuleGraphWriter writer = new ModuleGraphWriter();
390 writer.config(args.getProperties());
391 writer.write(args.getAllModules(),outputFile);
392 }
393
394
395 if (args.isDryRun())
396 {
397 CommandLineBuilder cmd = args.getMainArgs(baseHome,true);
398 System.out.println(cmd.toString(StartLog.isDebugEnabled()?" \\\n":" "));
399 }
400
401 if (args.isStopCommand())
402 {
403 doStop(args);
404 }
405
406 BaseBuilder baseBuilder = new BaseBuilder(baseHome,args);
407 if(baseBuilder.build())
408 {
409
410 StartLog.info("Base directory was modified");
411 return;
412 }
413
414
415 if (!args.isRun())
416 {
417 return;
418 }
419
420
421 if (args.isExec())
422 {
423 CommandLineBuilder cmd = args.getMainArgs(baseHome,true);
424 cmd.debug();
425 ProcessBuilder pbuilder = new ProcessBuilder(cmd.getArgs());
426 StartLog.endStartLog();
427 final Process process = pbuilder.start();
428 Runtime.getRuntime().addShutdownHook(new Thread()
429 {
430 @Override
431 public void run()
432 {
433 StartLog.debug("Destroying " + process);
434 process.destroy();
435 }
436 });
437
438 copyInThread(process.getErrorStream(),System.err);
439 copyInThread(process.getInputStream(),System.out);
440 copyInThread(System.in,process.getOutputStream());
441 process.waitFor();
442 System.exit(0);
443 return;
444 }
445
446 if (args.hasJvmArgs() || args.hasSystemProperties())
447 {
448 System.err.println("WARNING: System properties and/or JVM args set. Consider using --dry-run or --exec");
449 }
450
451 ClassLoader cl = classpath.getClassLoader();
452 Thread.currentThread().setContextClassLoader(cl);
453
454
455 try
456 {
457 invokeMain(cl, args);
458 }
459 catch (Exception e)
460 {
461 usageExit(e,ERR_INVOKE_MAIN);
462 }
463 }
464
465 private void doStop(StartArgs args)
466 {
467 String stopHost = args.getProperties().getString("STOP.HOST");
468 int stopPort = Integer.parseInt(args.getProperties().getString("STOP.PORT"));
469 String stopKey = args.getProperties().getString("STOP.KEY");
470
471 if (args.getProperties().getString("STOP.WAIT") != null)
472 {
473 int stopWait = Integer.parseInt(args.getProperties().getString("STOP.WAIT"));
474
475 stop(stopHost,stopPort,stopKey,stopWait);
476 }
477 else
478 {
479 stop(stopHost,stopPort,stopKey);
480 }
481 }
482
483
484
485
486
487
488
489 public void stop(String host, int port, String key)
490 {
491 stop(host,port,key,0);
492 }
493
494 public void stop(String host, int port, String key, int timeout)
495 {
496 if (host==null || host.length()==0)
497 host="127.0.0.1";
498
499 try
500 {
501 if (port <= 0)
502 {
503 System.err.println("STOP.PORT system property must be specified");
504 }
505 if (key == null)
506 {
507 key = "";
508 System.err.println("STOP.KEY system property must be specified");
509 System.err.println("Using empty key");
510 }
511
512 try (Socket s = new Socket(InetAddress.getByName(host),port))
513 {
514 if (timeout > 0)
515 {
516 s.setSoTimeout(timeout * 1000);
517 }
518
519 try (OutputStream out = s.getOutputStream())
520 {
521 out.write((key + "\r\nstop\r\n").getBytes());
522 out.flush();
523
524 if (timeout > 0)
525 {
526 System.err.printf("Waiting %,d seconds for jetty to stop%n",timeout);
527 LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream()));
528 String response;
529 while ((response = lin.readLine()) != null)
530 {
531 StartLog.debug("Received \"%s\"",response);
532 if ("Stopped".equals(response))
533 {
534 StartLog.warn("Server reports itself as Stopped");
535 }
536 }
537 }
538 }
539 }
540 }
541 catch (SocketTimeoutException e)
542 {
543 System.err.println("Timed out waiting for stop confirmation");
544 System.exit(ERR_UNKNOWN);
545 }
546 catch (ConnectException e)
547 {
548 usageExit(e,ERR_NOT_STOPPED);
549 }
550 catch (Exception e)
551 {
552 usageExit(e,ERR_UNKNOWN);
553 }
554 }
555
556 public void usage(boolean exit)
557 {
558 StartLog.endStartLog();
559 if(!printTextResource("org/eclipse/jetty/start/usage.txt"))
560 {
561 System.err.println("ERROR: detailed usage resource unavailable");
562 }
563 if (exit)
564 {
565 System.exit(EXIT_USAGE);
566 }
567 }
568
569 public static boolean printTextResource(String resourceName)
570 {
571 boolean resourcePrinted = false;
572 try (InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName))
573 {
574 if (stream != null)
575 {
576 try (InputStreamReader reader = new InputStreamReader(stream); BufferedReader buf = new BufferedReader(reader))
577 {
578 resourcePrinted = true;
579 String line;
580 while ((line = buf.readLine()) != null)
581 {
582 System.out.println(line);
583 }
584 }
585 }
586 else
587 {
588 System.out.println("Unable to find resource: " + resourceName);
589 }
590 }
591 catch (IOException e)
592 {
593 StartLog.warn(e);
594 }
595
596 return resourcePrinted;
597 }
598
599
600
601 public void init(String[] args) throws Exception
602 {
603 try
604 {
605 startupArgs = processCommandLine(args);
606 }
607 catch (UsageException e)
608 {
609 System.err.println(e.getMessage());
610 usageExit(e.getCause(),e.getExitCode());
611 }
612 catch (Throwable e)
613 {
614 usageExit(e,UsageException.ERR_UNKNOWN);
615 }
616 }
617
618 public void start() throws Exception
619 {
620 start(startupArgs);
621 }
622
623 public void stop() throws Exception
624 {
625 doStop(startupArgs);
626 }
627
628 public void destroy()
629 {
630 }
631 }