1
2
3
4
5
6
7
8
9
10
11
12
13 package org.eclipse.jetty.start;
14
15 import java.io.BufferedReader;
16 import java.io.File;
17 import java.io.FileFilter;
18 import java.io.FileInputStream;
19 import java.io.FileNotFoundException;
20 import java.io.FileReader;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.OutputStream;
25 import java.io.PrintStream;
26 import java.io.Reader;
27 import java.lang.reflect.InvocationTargetException;
28 import java.lang.reflect.Method;
29 import java.net.ConnectException;
30 import java.net.InetAddress;
31 import java.net.Socket;
32 import java.security.Policy;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Set;
39 import java.util.TimeZone;
40
41 import org.eclipse.jetty.start.log.RedirectedStreamLogger;
42
43
44
45
46
47
48
49
50
51
52
53
54
55 public class Main
56 {
57 private boolean _showUsage = false;
58 private boolean _dumpVersions = false;
59 private boolean _listOptions = false;
60 private boolean _dryRun = false;
61 private boolean _exec = false;
62 private boolean _secure = false;
63 private boolean _fromDaemon = false;
64 private final Config _config = new Config();
65 private Set<String> _sysProps = new HashSet<String>();
66 private List<String> _xArgs = new ArrayList<String>();
67
68 private String _jettyHome;
69
70 public static void main(String[] args)
71 {
72 Main main = new Main();
73 try
74 {
75 main.parseCommandLine(args);
76 }
77 catch (Throwable t)
78 {
79 t.printStackTrace(System.err);
80 }
81 }
82
83 public void parseCommandLine(String[] args)
84 {
85 try
86 {
87 List<String> arguments = new ArrayList<String>();
88
89 arguments.addAll(loadStartIni());
90 if (args.length>0)
91 arguments.addAll(Arrays.asList(args));
92
93
94 List<String> xmls = new ArrayList<String>();
95
96 for (String arg : arguments)
97 {
98 if ("--help".equals(arg))
99 {
100 _showUsage = true;
101 continue;
102 }
103
104 if ("--stop".equals(arg))
105 {
106 int port = Integer.parseInt(System.getProperty("STOP.PORT","-1"));
107 String key = System.getProperty("STOP.KEY",null);
108 stop(port,key);
109 return;
110 }
111
112 if ("--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
113 {
114 _dumpVersions = true;
115 continue;
116 }
117
118 if ("--list-modes".equals(arg) || "--list-options".equals(arg))
119 {
120 _listOptions = true;
121 continue;
122 }
123
124 if ("--exec-print".equals(arg)||"--dry-run".equals(arg))
125 {
126 _dryRun = true;
127 continue;
128 }
129
130 if ("--exec".equals(arg))
131 {
132 _exec = true;
133 continue;
134 }
135
136
137 if ("--fromDaemon".equals(arg))
138 {
139 _fromDaemon = true;
140 PrintStream logger = new PrintStream(new RedirectedStreamLogger("daemon_yyyy_mm_dd.log",false,90,TimeZone.getTimeZone("GMT")));
141 System.setOut(logger);
142 System.setErr(logger);
143 continue;
144 }
145
146 if ("--secure".equals(arg))
147 {
148 _secure = true;
149 continue;
150 }
151
152 if (arg.startsWith("-X"))
153 {
154 _xArgs.add(arg);
155 continue;
156 }
157
158 if (arg.startsWith("-D"))
159 {
160 String[] assign = arg.substring(2).split("=",2);
161 _sysProps.add(assign[0]);
162 switch(assign.length)
163 {
164 case 2:
165 System.setProperty(assign[0],assign[1]);
166 break;
167 case 1:
168 System.setProperty(assign[0],"");
169 break;
170 default:
171 break;
172 }
173 continue;
174 }
175
176
177 else if (arg.indexOf('=') >= 0)
178 {
179 String[] assign = arg.split("=",2);
180
181 switch(assign.length)
182 {
183 case 2:
184 this._config.setProperty(assign[0],assign[1]);
185 break;
186 case 1:
187 this._config.setProperty(assign[0],null);
188 break;
189 default:
190 break;
191 }
192 continue;
193 }
194
195
196 xmls.add(arg);
197 }
198
199
200 String options = _config.getProperty("OPTIONS");
201 if (options!=null)
202 {
203 String ids[] = options.split(",");
204 for (String id : ids)
205 _config.addActiveOption(id);
206 }
207
208 start(xmls);
209 }
210 catch (Throwable t)
211 {
212 t.printStackTrace(System.err);
213 usage();
214 }
215 }
216
217
218
219
220 private List<String> loadStartIni()
221 {
222 String jettyHome=System.getProperty("jetty.home");
223 File startIniFile = (jettyHome!=null)? new File(jettyHome,"start.ini"):new File("start.ini");
224 if (!startIniFile.exists() || !startIniFile.canRead())
225 {
226
227 return Collections.emptyList();
228 }
229
230 List<String> args = new ArrayList<String>();
231
232 FileReader reader = null;
233 BufferedReader buf = null;
234 try
235 {
236 reader = new FileReader(startIniFile);
237 buf = new BufferedReader(reader);
238
239 String arg;
240 while ((arg = buf.readLine()) != null)
241 {
242 arg=arg.trim();
243 if (arg.length()==0 || arg.startsWith("#"))
244 continue;
245 args.add(arg);
246 }
247 }
248 catch (IOException e)
249 {
250 e.printStackTrace();
251 }
252 finally
253 {
254 close(buf);
255 close(reader);
256 }
257
258 return args;
259 }
260
261 private void usage()
262 {
263 String usageResource = "org/eclipse/jetty/start/usage.txt";
264 InputStream usageStream = getClass().getClassLoader().getResourceAsStream(usageResource);
265
266 if (usageStream == null)
267 {
268 System.err.println("Usage: java -jar start.jar [options] [properties] [configs]");
269 System.err.println("ERROR: detailed usage resource unavailable");
270 System.exit(1);
271 }
272
273 BufferedReader buf = null;
274 try
275 {
276 buf = new BufferedReader(new InputStreamReader(usageStream));
277 String line;
278
279 while ((line = buf.readLine()) != null)
280 {
281 if (line.startsWith("@OPTIONS@"))
282 {
283 List<String> sortedOptions = new ArrayList<String>();
284 sortedOptions.addAll(_config.getSectionIds());
285 Collections.sort(sortedOptions);
286
287 System.err.println(" Available OPTIONS: ");
288
289 for (String option : sortedOptions)
290 {
291 System.err.println(" [" + option + "]");
292 }
293 }
294 else if (line.startsWith("@CONFIGS@"))
295 {
296 System.err.println(" Configurations Available in ${jetty.home}/etc/: ");
297 File etc = new File(System.getProperty("jetty.home","."),"etc");
298 if (!etc.exists())
299 {
300 System.err.println(" Unable to find " + etc);
301 continue;
302 }
303
304 if (!etc.isDirectory())
305 {
306 System.err.println(" Unable list dir " + etc);
307 continue;
308 }
309
310 File configs[] = etc.listFiles(new FileFilter()
311 {
312 public boolean accept(File path)
313 {
314 if (!path.isFile())
315 {
316 return false;
317 }
318
319 String name = path.getName().toLowerCase();
320 return (name.startsWith("jetty") && name.endsWith(".xml"));
321 }
322 });
323
324 List<File> configFiles = new ArrayList<File>();
325 configFiles.addAll(Arrays.asList(configs));
326 Collections.sort(configFiles);
327
328 for (File configFile : configFiles)
329 {
330 System.err.println(" etc/" + configFile.getName());
331 }
332 }
333 else
334 {
335 System.err.println(line);
336 }
337 }
338 }
339 catch (IOException e)
340 {
341 e.printStackTrace(System.err);
342 }
343 finally
344 {
345 if (buf != null)
346 {
347 try
348 {
349 buf.close();
350 }
351 catch (IOException ignore)
352 {
353
354 }
355 }
356 }
357 System.exit(1);
358 }
359
360 public void invokeMain(ClassLoader classloader, String classname, List<String> args) throws IllegalAccessException, InvocationTargetException,
361 NoSuchMethodException, ClassNotFoundException
362 {
363 Class<?> invoked_class = null;
364
365 try
366 {
367 invoked_class = classloader.loadClass(classname);
368 }
369 catch (ClassNotFoundException e)
370 {
371 e.printStackTrace();
372 }
373
374 if (Config.isDebug() || invoked_class == null)
375 {
376 if (invoked_class == null)
377 System.err.println("ClassNotFound: " + classname);
378 else
379 System.err.println(classname + " " + invoked_class.getPackage().getImplementationVersion());
380
381 if (invoked_class == null)
382 {
383 usage();
384 return;
385 }
386 }
387
388 String argArray[] = args.toArray(new String[0]);
389
390 Class<?>[] method_param_types = new Class[] { argArray.getClass() };
391
392 Method main = invoked_class.getDeclaredMethod("main",method_param_types);
393 Object[] method_params = new Object[] { argArray };
394 main.invoke(null,method_params);
395 }
396
397
398 public static void close(Reader reader)
399 {
400 if (reader == null)
401 {
402 return;
403 }
404 try
405 {
406 reader.close();
407 }
408 catch (IOException e)
409 {
410 e.printStackTrace();
411 }
412 }
413
414
415 public static void close(InputStream stream)
416 {
417 if (stream == null)
418 {
419 return;
420 }
421 try
422 {
423 stream.close();
424 }
425 catch (IOException e)
426 {
427 e.printStackTrace();
428 }
429 }
430
431
432 public void start(List<String> xmls) throws FileNotFoundException,IOException,InterruptedException
433 {
434
435 startMonitor();
436
437
438 if (xmls.isEmpty())
439 {
440
441
442 if (_fromDaemon)
443 {
444 xmls.add("etc/jetty-logging.xml");
445 }
446 xmls.add("etc/jetty.xml");
447 }
448
449
450 initConfig(xmls);
451
452
453 if (_secure)
454 {
455 _config.addActiveOption("policy");
456 _config.addActiveOption("security");
457 }
458
459
460 xmls = resolveXmlConfigs(xmls);
461
462
463 Classpath classpath = _config.getActiveClasspath();
464
465 System.setProperty("java.class.path",classpath.toString());
466 ClassLoader cl = classpath.getClassLoader();
467 if (Config.isDebug())
468 {
469 System.err.println("java.class.path=" + System.getProperty("java.class.path"));
470 System.err.println("jetty.home=" + System.getProperty("jetty.home"));
471 System.err.println("java.home=" + System.getProperty("java.home"));
472 System.err.println("java.io.tmpdir=" + System.getProperty("java.io.tmpdir"));
473 System.err.println("java.class.path=" + classpath);
474 System.err.println("classloader=" + cl);
475 System.err.println("classloader.parent=" + cl.getParent());
476 }
477
478
479 if (_showUsage)
480 {
481 usage();
482 return;
483 }
484
485
486 if (_dumpVersions)
487 {
488 showClasspathWithVersions(classpath);
489 showActiveSecurityPolicies(cl);
490 return;
491 }
492
493
494 if (_listOptions)
495 {
496 showAllOptionsWithVersions(classpath);
497 return;
498 }
499
500
501 if (_dryRun)
502 {
503 System.out.println(buildCommandLine(classpath,xmls));
504 return;
505 }
506
507
508 if (_exec)
509 {
510 String cmd = buildCommandLine(classpath,xmls);
511 Process process = Runtime.getRuntime().exec(cmd);
512 copyInThread(process.getErrorStream(),System.err);
513 copyInThread(process.getInputStream(),System.out);
514 copyInThread(System.in,process.getOutputStream());
515 process.waitFor();
516 return;
517 }
518
519 if (_xArgs.size()>0 || _sysProps.size()>0)
520 System.err.println("WARNING: System properties and/or JVM args set. Consider using --dry-run or --exec");
521
522
523 Thread.currentThread().setContextClassLoader(cl);
524
525
526 initSecurity(cl);
527
528
529 try
530 {
531
532 String classname = _config.getMainClassname();
533
534
535 String mainClass = System.getProperty("jetty.server");
536 if (mainClass != null)
537 classname = mainClass;
538
539
540 mainClass = System.getProperty("main.class");
541 if (mainClass != null)
542 classname = mainClass;
543
544 Config.debug("main.class=" + classname);
545
546 invokeMain(cl,classname,xmls);
547 }
548 catch (Exception e)
549 {
550 e.printStackTrace();
551 }
552 }
553
554 private void copyInThread(final InputStream in,final OutputStream out)
555 {
556 new Thread(new Runnable()
557 {
558 public void run()
559 {
560 try
561 {
562 byte[] buf=new byte[1024];
563 int len=in.read(buf);
564 while(len>0)
565 {
566 out.write(buf,0,len);
567 len=in.read(buf);
568 }
569 }
570 catch(IOException e)
571 {
572 e.printStackTrace();
573 }
574 }
575
576 }).start();
577 }
578
579 private String resolveXmlConfig(String xmlFilename) throws FileNotFoundException
580 {
581 File xml = new File(xmlFilename);
582 if (xml.exists() && xml.isFile() && xml.isAbsolute())
583 {
584 return xml.getAbsolutePath();
585 }
586
587 xml = new File(_jettyHome,fixPath(xmlFilename));
588 if (xml.exists() && xml.isFile())
589 {
590 return xml.getAbsolutePath();
591 }
592
593 xml = new File(_jettyHome,fixPath("etc/" + xmlFilename));
594 if (xml.exists() && xml.isFile())
595 {
596 return xml.getAbsolutePath();
597 }
598
599 throw new FileNotFoundException("Unable to find XML Config: " + xmlFilename);
600 }
601
602 private String buildCommandLine(Classpath classpath, List<String> xmls)
603 {
604 StringBuilder cmd = new StringBuilder();
605 cmd.append(findJavaBin());
606 for (String x:_xArgs)
607 cmd.append(' ').append(x);
608 cmd.append(" -Djetty.home=").append(_jettyHome);
609 for (String p:_sysProps)
610 {
611 cmd.append(" -D").append(p);
612 String v=System.getProperty(p);
613 if (v!=null)
614 cmd.append('=').append(v);
615 }
616 cmd.append(" -cp ").append(classpath.toString());
617 cmd.append(' ').append(_config.getMainClassname());
618 for (String xml : xmls)
619 {
620 cmd.append(' ').append(xml);
621 }
622
623 return cmd.toString();
624 }
625
626 private String findJavaBin()
627 {
628 File javaHome = new File(System.getProperty("java.home"));
629 if (!javaHome.exists())
630 {
631 return null;
632 }
633
634 File javabin = findExecutable(javaHome,"bin/java");
635 if (javabin != null)
636 {
637 return javabin.getAbsolutePath();
638 }
639
640 javabin = findExecutable(javaHome,"bin/java.exe");
641 if (javabin != null)
642 {
643 return javabin.getAbsolutePath();
644 }
645
646 return "java";
647 }
648
649 private File findExecutable(File root, String path)
650 {
651 String npath = path.replace('/',File.separatorChar);
652 File exe = new File(root,npath);
653 if (!exe.exists())
654 {
655 return null;
656 }
657 return exe;
658 }
659
660 private void showAllOptionsWithVersions(Classpath classpath)
661 {
662 Set<String> sectionIds = _config.getSectionIds();
663
664 StringBuffer msg = new StringBuffer();
665 msg.append("There ");
666 if (sectionIds.size() > 1)
667 {
668 msg.append("are ");
669 }
670 else
671 {
672 msg.append("is ");
673 }
674 msg.append(String.valueOf(sectionIds.size()));
675 msg.append(" OPTION");
676 if (sectionIds.size() > 1)
677 {
678 msg.append("s");
679 }
680 msg.append(" available to use.");
681 System.out.println(msg);
682 System.out.println("Each option is listed along with associated available classpath entries, in the order that they would appear from that mode.");
683 System.out.println("Note: If using multiple options (eg: 'Server,servlet,webapp,jms,jmx') "
684 + "then overlapping entries will not be repeated in the eventual classpath.");
685 System.out.println();
686 System.out.printf("${jetty.home} = %s%n",_jettyHome);
687 System.out.println();
688
689 for (String sectionId : sectionIds)
690 {
691 if (Config.DEFAULT_SECTION.equals(sectionId))
692 {
693 System.out.println("GLOBAL option (Prepended Entries)");
694 }
695 else if ("*".equals(sectionId))
696 {
697 System.out.println("GLOBAL option (Appended Entries) (*)");
698 }
699 else
700 {
701 System.out.printf("Option [%s]",sectionId);
702 if (Character.isUpperCase(sectionId.charAt(0)))
703 {
704 System.out.print(" (Aggregate)");
705 }
706 System.out.println();
707 }
708 System.out.println("-------------------------------------------------------------");
709
710 Classpath sectionCP = _config.getSectionClasspath(sectionId);
711
712 if (sectionCP.isEmpty())
713 {
714 System.out.println("Empty option, no classpath entries active.");
715 System.out.println();
716 continue;
717 }
718
719 int i = 0;
720 for (File element : sectionCP.getElements())
721 {
722 String elementPath = element.getAbsolutePath();
723 if (elementPath.startsWith(_jettyHome))
724 {
725 elementPath = "${jetty.home}" + elementPath.substring(_jettyHome.length());
726 }
727 System.out.printf("%2d: %20s | %s\n",i++,getVersion(element),elementPath);
728 }
729
730 System.out.println();
731 }
732 }
733
734 private void showClasspathWithVersions(Classpath classpath)
735 {
736
737
738
739 System.out.println("Active Options: " + _config.getActiveOptions());
740
741 if (classpath.count() == 0)
742 {
743 System.out.println("No version information available show.");
744 return;
745 }
746
747 System.out.println("Version Information on " + classpath.count() + " entr" + ((classpath.count() > 1)?"ies":"y") + " in the classpath.");
748 System.out.println("Note: order presented here is how they would appear on the classpath.");
749 System.out.println(" changes to the OPTIONS=[option,option,...] command line option will be reflected here.");
750
751 int i = 0;
752 for (File element : classpath.getElements())
753 {
754 String elementPath = element.getAbsolutePath();
755 if (elementPath.startsWith(_jettyHome))
756 {
757 elementPath = "${jetty.home}" + elementPath.substring(_jettyHome.length());
758 }
759 System.out.printf("%2d: %20s | %s\n",i++,getVersion(element),elementPath);
760 }
761 }
762
763 private void showActiveSecurityPolicies(ClassLoader cl)
764 {
765
766 initSecurity(cl);
767
768 Policy policy = Policy.getPolicy();
769
770 if (policy != null && policy.getClass().getName().contains("JettyPolicy"))
771 {
772 System.out.println("Active Security Policies: ");
773
774 try
775 {
776 Method m = policy.getClass().getMethod("dump",new Class[]{ PrintStream.class });
777 m.invoke(policy,new Object[]
778 { System.out });
779 }
780 catch (SecurityException e)
781 {
782
783 e.printStackTrace();
784 }
785 catch (NoSuchMethodException e)
786 {
787
788 e.printStackTrace();
789 }
790 catch (IllegalArgumentException e)
791 {
792
793 e.printStackTrace();
794 }
795 catch (IllegalAccessException e)
796 {
797
798 e.printStackTrace();
799 }
800 catch (InvocationTargetException e)
801 {
802
803 e.printStackTrace();
804 }
805 }
806 }
807
808 private String fixPath(String path)
809 {
810 return path.replace('/',File.separatorChar);
811 }
812
813 private String getVersion(File element)
814 {
815 if (element.isDirectory())
816 {
817 return "(dir)";
818 }
819
820 if (element.isFile())
821 {
822 String name = element.getName().toLowerCase();
823 if (name.endsWith(".jar"))
824 {
825 return JarVersion.getVersion(element);
826 }
827
828 if (name.endsWith(".zip"))
829 {
830 return getZipVersion(element);
831 }
832 }
833
834 return "";
835 }
836
837 private String getZipVersion(File element)
838 {
839
840 return "";
841 }
842
843 private void initSecurity(ClassLoader cl)
844 {
845
846 try
847 {
848 if (_secure)
849 {
850 Policy.setPolicy(_config.getPolicyInstance(cl));
851 System.setSecurityManager(new SecurityManager());
852
853 }
854 else
855 {
856 Policy policy = Policy.getPolicy();
857 if (policy != null)
858 policy.refresh();
859 }
860 }
861 catch (Exception e)
862 {
863 e.printStackTrace();
864 }
865 }
866
867 private List<String> resolveXmlConfigs(List<String> xmls) throws FileNotFoundException
868 {
869 List<String> ret = new ArrayList<String>();
870 for (String xml : xmls)
871 {
872 ret.add(resolveXmlConfig(xml));
873 }
874
875 return ret;
876 }
877
878 private void initConfig(List<String> xmls)
879 {
880 InputStream cfgstream = null;
881 try
882 {
883 _config.setArgCount(xmls.size());
884
885
886 String cfgName = System.getProperty("START","org/eclipse/jetty/start/start.config");
887 Config.debug("config=" + cfgName);
888
889
890 cfgstream = getClass().getClassLoader().getResourceAsStream(cfgName);
891
892
893 if (cfgstream == null)
894 cfgstream = new FileInputStream(cfgName);
895
896
897 _config.parse(cfgstream);
898
899 _jettyHome = _config.getProperty("jetty.home");
900 if (_jettyHome != null)
901 {
902 _jettyHome = new File(_jettyHome).getCanonicalPath();
903 System.setProperty("jetty.home",_jettyHome);
904 }
905 }
906 catch (Exception e)
907 {
908 e.printStackTrace();
909 System.exit(1);
910 }
911 finally
912 {
913 close(cfgstream);
914 }
915 }
916
917 private void startMonitor()
918 {
919 int port = Integer.parseInt(System.getProperty("STOP.PORT","-1"));
920 String key = System.getProperty("STOP.KEY",null);
921
922 Monitor.monitor(port,key);
923 }
924
925
926
927
928 public void stop(int port, String key)
929 {
930 int _port = port;
931 String _key = key;
932
933 try
934 {
935 if (_port <= 0)
936 System.err.println("STOP.PORT system property must be specified");
937 if (_key == null)
938 {
939 _key = "";
940 System.err.println("STOP.KEY system property must be specified");
941 System.err.println("Using empty key");
942 }
943
944 Socket s = new Socket(InetAddress.getByName("127.0.0.1"),_port);
945 OutputStream out = s.getOutputStream();
946 out.write((_key + "\r\nstop\r\n").getBytes());
947 out.flush();
948 s.close();
949 }
950 catch (ConnectException e)
951 {
952 System.err.println("ERROR: Not running!");
953 }
954 catch (Exception e)
955 {
956 e.printStackTrace();
957 }
958 }
959 }