View Javadoc

1   // ========================================================================
2   // Copyright (c) 2003-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  package org.eclipse.jetty.start;
14  
15  import java.io.BufferedReader;
16  import java.io.Closeable;
17  import java.io.File;
18  import java.io.FileFilter;
19  import java.io.FileInputStream;
20  import java.io.FileNotFoundException;
21  import java.io.FileOutputStream;
22  import java.io.FileReader;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.io.OutputStream;
27  import java.io.PrintStream;
28  import java.lang.reflect.InvocationTargetException;
29  import java.lang.reflect.Method;
30  import java.net.ConnectException;
31  import java.net.InetAddress;
32  import java.net.Socket;
33  import java.security.Policy;
34  import java.util.ArrayList;
35  import java.util.Arrays;
36  import java.util.Collections;
37  import java.util.Date;
38  import java.util.HashSet;
39  import java.util.List;
40  import java.util.Properties;
41  import java.util.Set;
42  
43  
44  /*-------------------------------------------*/
45  /**
46   * <p>
47   * Main start class. This class is intended to be the main class listed in the MANIFEST.MF of the start.jar archive. It
48   * allows an application to be started with the command "java -jar start.jar".
49   * </p>
50   * 
51   * <p>
52   * The behaviour of Main is controlled by the parsing of the {@link Config} "org/eclipse/start/start.config" file
53   * obtained as a resource or file.
54   * </p>
55   */
56  public class Main
57  {
58      private static final int EXIT_USAGE = 1;
59      private static final int ERR_LOGGING = -1;
60      private static final int ERR_INVOKE_MAIN = -2;
61      private static final int ERR_SECURITY = -3;
62      private static final int ERR_NOT_STOPPED = -4;
63      private static final int ERR_UNKNOWN = -5;
64      private boolean _showUsage = false;
65      private boolean _dumpVersions = false;
66      private boolean _listConfig = false;
67      private boolean _listOptions = false;
68      private boolean _dryRun = false;
69      private boolean _exec = false;
70      private boolean _secure = false;
71      private final Config _config = new Config();
72      private Set<String> _sysProps = new HashSet<String>();
73      private List<String> _jvmArgs = new ArrayList<String>();
74      private String _startConfig = null;
75  
76      private String _jettyHome;
77  
78      public static void main(String[] args)
79      {
80          Main main = new Main();
81          main.parseCommandLine(args);
82      }
83  
84      public void parseCommandLine(String[] args)
85      {
86          try
87          {
88              List<String> arguments = new ArrayList<String>();
89  
90              // add the command line args and look for start.ini args
91              boolean ini=false;
92              for (String arg : args)
93              {
94                  if (arg.startsWith("--ini=")||arg.equals("--ini"))
95                  {
96                      ini=true;
97                      if (arg.length()>6)
98                      {
99                          arguments.addAll(loadStartIni(arg.substring(6)));
100                         continue;
101                     }
102                 }
103                 else if (arg.startsWith("--config="))
104                 {
105                     _startConfig=arg.substring(9);
106                 }
107                 else
108                 {
109                     arguments.add(arg);
110                 }
111             }
112             
113             // if no non-option inis, add the start.ini
114             if (!ini)
115             {
116                 arguments.addAll(0,loadStartIni(null));
117             }
118             
119             // The XML Configuration Files to initialize with
120             List<String> xmls = new ArrayList<String>();
121 
122             // Process the arguments
123             int startup=0;
124             for (String arg : arguments)
125             {
126                 if ("--help".equals(arg) || "-?".equals(arg))
127                 {
128                     _showUsage = true;
129                     continue;
130                 }
131 
132                 if ("--stop".equals(arg))
133                 {
134                     int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
135                     String key = Config.getProperty("STOP.KEY",null);
136                     stop(port,key);
137                     return;
138                 }
139 
140                 if ("--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
141                 {
142                     _dumpVersions = true;
143                     continue;
144                 }
145 
146                 if ("--list-modes".equals(arg) || "--list-options".equals(arg))
147                 {
148                     _listOptions = true;
149                     continue;
150                 }
151 
152                 if ("--list-config".equals(arg))
153                 {
154                     _listConfig=true;
155                     continue;
156                 }
157 
158                 if ("--exec-print".equals(arg)||"--dry-run".equals(arg))
159                 {
160                     _dryRun = true;
161                     continue;
162                 }
163 
164                 if ("--exec".equals(arg))
165                 {
166                     _exec = true;
167                     continue;
168                 }
169 
170                 // Special internal indicator that jetty was started by the jetty.sh Daemon
171                 if ("--daemon".equals(arg))
172                 {
173                     File startDir = new File(System.getProperty("jetty.logs","logs"));
174                     if (!startDir.exists() || !startDir.canWrite() )
175                         startDir = new File(".");
176                     File startLog = new File(startDir,"start.log");
177                     if (!startLog.exists() && !startLog.createNewFile())
178                     {
179                         // Output about error is lost in majority of cases.
180                         System.err.println("Unable to create: " + startLog.getAbsolutePath());
181                         // Toss a unique exit code indicating this failure.
182                         usageExit(ERR_LOGGING);
183                     }
184 
185                     if (!startLog.canWrite())
186                     {
187                         // Output about error is lost in majority of cases.
188                         System.err.println("Unable to write to: " + startLog.getAbsolutePath());
189                         // Toss a unique exit code indicating this failure.
190                         usageExit(ERR_LOGGING);
191                     }
192                     PrintStream logger = new PrintStream(new FileOutputStream(startLog,false));
193                     System.setOut(logger);
194                     System.setErr(logger);
195                     System.out.println("Establishing start.log on " + new Date());
196                     continue;
197                 }
198 
199                 if ("--secure".equals(arg))
200                 {
201                     _secure = true;
202                     continue;
203                 }
204 
205                 if (arg.startsWith("--pre="))
206                 {
207                     xmls.add(startup++,arg.substring(6));
208                     continue;
209                 }
210                 
211                 if (arg.startsWith("-D"))
212                 {
213                     String[] assign = arg.substring(2).split("=",2);
214                     _sysProps.add(assign[0]);
215                     switch(assign.length)
216                     {
217                         case 2:
218                             System.setProperty(assign[0],assign[1]);
219                             break;
220                         case 1:
221                             System.setProperty(assign[0],"");
222                             break;
223                         default:
224                             break;
225                     }
226                     continue;
227                 }
228 
229                 if (arg.startsWith("-"))
230                 {
231                     _jvmArgs.add(arg);
232                     continue;
233                 }
234 
235                 // Is this a Property?
236                 if (arg.indexOf('=') >= 0)
237                 {
238                     String[] assign = arg.split("=",2);
239 
240                     switch(assign.length)
241                     {
242                         case 2:
243                             if ("OPTIONS".equals(assign[0]))
244                             {
245                                 String opts[] = assign[1].split(",");
246                                 for (String opt : opts)
247                                     _config.addActiveOption(opt);
248                             }
249                             else
250                             {
251                                 this._config.setProperty(assign[0],assign[1]);
252                             }
253                             break;
254                         case 1:
255                             this._config.setProperty(assign[0],null);
256                             break;
257                         default:
258                             break;
259                     }
260                     
261                     continue;
262                 }
263                 
264                 // Anything else is considered an XML file.
265                 if (xmls.contains(arg))
266                 {
267                     System.out.println("WARN: Argument '"+arg+"' specified multiple times. Check start.ini?");
268                     System.out.println("Use \"java -jar start.jar --help\" for more information.");
269                 }
270                 xmls.add(arg);
271             }
272             
273             start(xmls);
274         }
275         catch (Throwable t)
276         {
277             usageExit(t,ERR_UNKNOWN);
278         }
279     }
280 
281     /**
282      * If a start.ini is present in the CWD, then load it into the argument list.
283      */
284     private List<String> loadStartIni(String ini)
285     {
286         String jettyHome=System.getProperty("jetty.home");
287         File startIniFile = ini==null?((jettyHome!=null)?  new File(jettyHome,"start.ini"):new File("start.ini")):new File(ini);
288         if (!startIniFile.exists())
289         {
290             if (ini != null)
291             {
292                 System.err.println("Warning - can't find ini file: " + ini);
293             }
294             // No start.ini found, skip load.
295             return Collections.emptyList();
296         }
297 
298         List<String> args = new ArrayList<String>();
299 
300         FileReader reader = null;
301         BufferedReader buf = null;
302         try
303         {
304             reader = new FileReader(startIniFile);
305             buf = new BufferedReader(reader);
306 
307             String arg;
308             while ((arg = buf.readLine()) != null)
309             {
310                 arg = arg.trim();
311                 if (arg.length() == 0 || arg.startsWith("#"))
312                 {
313                     continue;
314                 }
315                 args.add(arg);
316             }
317         }
318         catch (IOException e)
319         {
320             usageExit(e,ERR_UNKNOWN);
321         }
322         finally
323         {
324             close(buf);
325             close(reader);
326         }
327 
328         return args;
329     }
330 
331     private void usage()
332     {
333         String usageResource = "org/eclipse/jetty/start/usage.txt";
334         InputStream usageStream = getClass().getClassLoader().getResourceAsStream(usageResource);
335 
336         if (usageStream == null)
337         {
338             System.err.println("ERROR: detailed usage resource unavailable");
339             usageExit(EXIT_USAGE);
340         }
341 
342         BufferedReader buf = null;
343         try
344         {
345             buf = new BufferedReader(new InputStreamReader(usageStream));
346             String line;
347 
348             while ((line = buf.readLine()) != null)
349             {
350                 if (line.endsWith("@") && line.indexOf('@')!=line.lastIndexOf('@'))
351                 {
352                     String indent=line.substring(0,line.indexOf("@"));
353                     String info=line.substring(line.indexOf('@'),line.lastIndexOf('@'));
354 
355                     if (info.equals("@OPTIONS"))
356                     {
357                         List<String> sortedOptions = new ArrayList<String>();
358                         sortedOptions.addAll(_config.getSectionIds());
359                         Collections.sort(sortedOptions);
360 
361                         for (String option : sortedOptions)
362                         {
363                             if ("*".equals(option) || option.trim().length()==0)
364                                 continue;
365                             System.out.print(indent);
366                             System.out.println(option);
367                         }
368                     }
369                     else if (info.equals("@CONFIGS"))
370                     {
371                         File etc = new File(System.getProperty("jetty.home","."),"etc");
372                         if (!etc.exists() || !etc.isDirectory())
373                         {
374                             System.out.print(indent);
375                             System.out.println("Unable to find/list " + etc);
376                             continue;
377                         }
378 
379                         File configs[] = etc.listFiles(new FileFilter()
380                         {
381                             public boolean accept(File path)
382                             {
383                                 if (!path.isFile())
384                                 {
385                                     return false;
386                                 }
387 
388                                 String name = path.getName().toLowerCase();
389                                 return (name.startsWith("jetty") && name.endsWith(".xml"));
390                             }
391                         });
392 
393                         List<File> configFiles = new ArrayList<File>();
394                         configFiles.addAll(Arrays.asList(configs));
395                         Collections.sort(configFiles);
396 
397                         for (File configFile : configFiles)
398                         {
399                             System.out.print(indent);
400                             System.out.print("etc/");
401                             System.out.println(configFile.getName());
402                         }
403                     }
404                     else if (info.equals("@STARTINI"))
405                     {
406                         List<String> ini = loadStartIni(null);
407                         if (ini!=null && ini.size()>0)
408                         {
409                             for (String a : ini)
410                             {
411                                 System.out.print(indent);
412                                 System.out.println(a);
413                             }
414                         }
415                         else
416                         {
417                             System.out.print(indent);
418                             System.out.println("none");
419                         }
420                     }
421                 }
422                 else
423                 {
424                     System.out.println(line);
425                 }
426             }
427         }
428         catch (IOException e)
429         {
430             usageExit(e,EXIT_USAGE);
431         }
432         finally
433         {
434             close(buf);
435         }
436         System.exit(EXIT_USAGE);
437     }
438 
439     public void invokeMain(ClassLoader classloader, String classname, List<String> args) throws IllegalAccessException, InvocationTargetException,
440     NoSuchMethodException, ClassNotFoundException
441     {
442         Class<?> invoked_class = null;
443 
444         try
445         {
446             invoked_class = classloader.loadClass(classname);
447         }
448         catch (ClassNotFoundException e)
449         {
450             e.printStackTrace();
451         }
452 
453         if (Config.isDebug() || invoked_class == null)
454         {
455             if (invoked_class == null)
456             {
457                 System.err.println("ClassNotFound: " + classname);
458             }
459             else
460             {
461                 System.err.println(classname + " " + invoked_class.getPackage().getImplementationVersion());
462             }
463 
464             if (invoked_class == null)
465             {
466                 usageExit(ERR_INVOKE_MAIN);
467                 return;
468             }
469         }
470 
471         String argArray[] = args.toArray(new String[0]);
472 
473         Class<?>[] method_param_types = new Class[] { argArray.getClass() };
474 
475         Method main = invoked_class.getDeclaredMethod("main",method_param_types);
476         Object[] method_params = new Object[] { argArray };
477         main.invoke(null,method_params);
478     }
479 
480     /* ------------------------------------------------------------ */
481     public static void close(Closeable c)
482     {
483         if (c == null)
484         {
485             return;
486         }
487         try
488         {
489             c.close();
490         }
491         catch (IOException e)
492         {
493             e.printStackTrace(System.err);
494         }
495     }
496 
497     /* ------------------------------------------------------------ */
498     public void start(List<String> xmls) throws FileNotFoundException, IOException, InterruptedException
499     {
500         // Setup Start / Stop Monitoring
501         int port = Integer.parseInt(Config.getProperty("STOP.PORT","-1"));
502         String key = Config.getProperty("STOP.KEY",null);
503         Monitor monitor=new Monitor(port,key);
504         
505 
506         // Load potential Config (start.config)
507         List<String> configuredXmls = loadConfig(xmls);
508 
509         // No XML defined in start.config or command line.  Can't execute.
510         if (configuredXmls.isEmpty())
511         {
512             throw new FileNotFoundException("No XML configuration files specified in start.config or command line.");
513         }
514 
515         // Add mandatory options for secure mode
516         if (_secure)
517         {
518             _config.addActiveOption("policy");
519             _config.addActiveOption("security");
520         }
521 
522         // Normalize the XML config options passed on the command line.
523         configuredXmls = resolveXmlConfigs(configuredXmls);
524 
525         // Get Desired Classpath based on user provided Active Options.
526         Classpath classpath = _config.getActiveClasspath();
527 
528         System.setProperty("java.class.path",classpath.toString());
529         ClassLoader cl = classpath.getClassLoader();
530         if (Config.isDebug())
531         {
532             System.err.println("java.class.path=" + System.getProperty("java.class.path"));
533             System.err.println("jetty.home=" + System.getProperty("jetty.home"));
534             System.err.println("java.home=" + System.getProperty("java.home"));
535             System.err.println("java.io.tmpdir=" + System.getProperty("java.io.tmpdir"));
536             System.err.println("java.class.path=" + classpath);
537             System.err.println("classloader=" + cl);
538             System.err.println("classloader.parent=" + cl.getParent());
539             System.err.println("properties=" + Config.getProperties());
540         }
541         
542         // Show the usage information and return
543         if (_showUsage)
544         {
545             usage();
546             return;
547         }
548 
549         // Show the version information and return
550         if (_dumpVersions)
551         {
552             showClasspathWithVersions(classpath);
553             showActiveSecurityPolicies(cl);
554             return;
555         }
556 
557         // Show all options with version information
558         if (_listOptions)
559         {
560             showAllOptionsWithVersions(classpath);
561             return;
562         }
563         
564         if (_listConfig)
565         {
566             listConfig();
567             return;
568         }
569 
570         // Show Command Line to execute Jetty
571         if (_dryRun)
572         {
573             System.out.println(buildCommandLine(classpath,configuredXmls));
574             return;
575         }
576         
577         // execute Jetty in another JVM
578         if (_exec)
579         {
580             String cmd = buildCommandLine(classpath,configuredXmls);
581             Process process = Runtime.getRuntime().exec(cmd);
582             copyInThread(process.getErrorStream(),System.err);
583             copyInThread(process.getInputStream(),System.out);
584             copyInThread(System.in,process.getOutputStream());
585             monitor.setProcess(process);
586             process.waitFor();
587             return;
588         }
589         
590         if (_jvmArgs.size() > 0 || _sysProps.size() > 0)
591         {
592             System.err.println("WARNING: System properties and/or JVM args set.  Consider using --dry-run or --exec");
593         }
594 
595         // Set current context class loader to what is selected.
596         Thread.currentThread().setContextClassLoader(cl);
597 
598         // Initialize the Security
599         initSecurity(cl);
600 
601         // Invoke the Main Class
602         try
603         {
604             // Get main class as defined in start.config
605             String classname = _config.getMainClassname();
606 
607             // Check for override of start class (via "jetty.server" property)
608             String mainClass = System.getProperty("jetty.server");
609             if (mainClass != null)
610             {
611                 classname = mainClass;
612             }
613 
614             // Check for override of start class (via "main.class" property)
615             mainClass = System.getProperty("main.class");
616             if (mainClass != null)
617             {
618                 classname = mainClass;
619             }
620 
621             Config.debug("main.class=" + classname);
622 
623             invokeMain(cl,classname,configuredXmls);
624         }
625         catch (Exception e)
626         {
627             usageExit(e,ERR_INVOKE_MAIN);
628         }
629     }
630 
631     private void copyInThread(final InputStream in,final OutputStream out)
632     {
633         new Thread(new Runnable()
634         {
635             public void run()
636             {
637                 try
638                 {
639                     byte[] buf=new byte[1024];
640                     int len=in.read(buf);
641                     while(len>0)
642                     {
643                         out.write(buf,0,len);
644                         len=in.read(buf);
645                     }
646                 }
647                 catch(IOException e)
648                 {
649                     // e.printStackTrace();
650                 }
651             }
652             
653         }).start();
654     }
655     
656     private String resolveXmlConfig(String xmlFilename) throws FileNotFoundException
657     {
658         if (!xmlFilename.toLowerCase().endsWith(".xml"))
659         {
660             // Nothing to resolve.
661             return xmlFilename;
662         }
663 
664         File xml = new File(xmlFilename);
665         if (xml.exists() && xml.isFile() && xml.isAbsolute())
666         {
667             return xml.getAbsolutePath();
668         }
669 
670         xml = new File(_jettyHome,fixPath(xmlFilename));
671         if (xml.exists() && xml.isFile())
672         {
673             return xml.getAbsolutePath();
674         }
675 
676         xml = new File(_jettyHome,fixPath("etc/" + xmlFilename));
677         if (xml.exists() && xml.isFile())
678         {
679             return xml.getAbsolutePath();
680         }
681 
682         throw new FileNotFoundException("Unable to find XML Config: " + xmlFilename);
683     }
684 
685     private String buildCommandLine(Classpath classpath, List<String> xmls) throws IOException
686     {
687         StringBuilder cmd = new StringBuilder();
688         cmd.append(findJavaBin());
689         for (String x:_jvmArgs)
690             cmd.append(' ').append(x);
691         cmd.append(" -Djetty.home=").append(_jettyHome);
692         for (String p:_sysProps)
693         {
694             cmd.append("   -D").append(p);
695             String v=System.getProperty(p);
696             if (v!=null && v.length()>0)
697                 cmd.append('=').append(v);
698         }
699         cmd.append("   -cp ").append(classpath.toString());
700         cmd.append("   ").append(_config.getMainClassname());
701         
702         // Check if we need to pass properties as a file
703         Properties properties = Config.getProperties();
704         if (properties.size()>0) 
705         {
706             File prop_file = File.createTempFile("start",".properties");
707             if (!_dryRun)
708                 prop_file.deleteOnExit();
709             properties.store(new FileOutputStream(prop_file),"start.jar properties");
710             cmd.append(" ").append(prop_file.getAbsolutePath());
711         }
712         
713         for (String xml : xmls)
714         {
715             cmd.append(' ').append(xml);
716         }
717 
718         return cmd.toString();
719     }
720 
721     private String findJavaBin()
722     {
723         File javaHome = new File(System.getProperty("java.home"));
724         if (!javaHome.exists())
725         {
726             return null;
727         }
728 
729         File javabin = findExecutable(javaHome,"bin/java");
730         if (javabin != null)
731         {
732             return javabin.getAbsolutePath();
733         }
734 
735         javabin = findExecutable(javaHome,"bin/java.exe");
736         if (javabin != null)
737         {
738             return javabin.getAbsolutePath();
739         }
740 
741         return "java";
742     }
743 
744     private File findExecutable(File root, String path)
745     {
746         String npath = path.replace('/',File.separatorChar);
747         File exe = new File(root,npath);
748         if (!exe.exists())
749         {
750             return null;
751         }
752         return exe;
753     }
754 
755     private void showAllOptionsWithVersions(Classpath classpath)
756     {
757         Set<String> sectionIds = _config.getSectionIds();
758 
759         StringBuffer msg = new StringBuffer();
760         msg.append("There ");
761         if (sectionIds.size() > 1)
762         {
763             msg.append("are ");
764         }
765         else
766         {
767             msg.append("is ");
768         }
769         msg.append(String.valueOf(sectionIds.size()));
770         msg.append(" OPTION");
771         if (sectionIds.size() > 1)
772         {
773             msg.append("s");
774         }
775         msg.append(" available to use.");
776         System.out.println(msg);
777         System.out.println("Each option is listed along with associated available classpath entries,  in the order that they would appear from that mode.");
778         System.out.println("Note: If using multiple options (eg: 'Server,servlet,webapp,jms,jmx') "
779                 + "then overlapping entries will not be repeated in the eventual classpath.");
780         System.out.println();
781         System.out.printf("${jetty.home} = %s%n",_jettyHome);
782         System.out.println();
783 
784         for (String sectionId : sectionIds)
785         {
786             if (Config.DEFAULT_SECTION.equals(sectionId))
787             {
788                 System.out.println("GLOBAL option (Prepended Entries)");
789             }
790             else if ("*".equals(sectionId))
791             {
792                 System.out.println("GLOBAL option (Appended Entries) (*)");
793             }
794             else
795             {
796                 System.out.printf("Option [%s]",sectionId);
797                 if (Character.isUpperCase(sectionId.charAt(0)))
798                 {
799                     System.out.print(" (Aggregate)");
800                 }
801                 System.out.println();
802             }
803             System.out.println("-------------------------------------------------------------");
804 
805             Classpath sectionCP = _config.getSectionClasspath(sectionId);
806 
807             if (sectionCP.isEmpty())
808             {
809                 System.out.println("Empty option, no classpath entries active.");
810                 System.out.println();
811                 continue;
812             }
813 
814             int i = 0;
815             for (File element : sectionCP.getElements())
816             {
817                 String elementPath = element.getAbsolutePath();
818                 if (elementPath.startsWith(_jettyHome))
819                 {
820                     elementPath = "${jetty.home}" + elementPath.substring(_jettyHome.length());
821                 }
822                 System.out.printf("%2d: %20s | %s\n",i++,getVersion(element),elementPath);
823             }
824 
825             System.out.println();
826         }
827     }
828 
829     private void showClasspathWithVersions(Classpath classpath)
830     {
831         // Iterate through active classpath, and fetch Implementation Version from each entry (if present)
832         // to dump to end user.
833 
834         System.out.println("Active Options: " + _config.getActiveOptions());
835 
836         if (classpath.count() == 0)
837         {
838             System.out.println("No version information available show.");
839             return;
840         }
841 
842         System.out.println("Version Information on " + classpath.count() + " entr" + ((classpath.count() > 1)?"ies":"y") + " in the classpath.");
843         System.out.println("Note: order presented here is how they would appear on the classpath.");
844         System.out.println("      changes to the OPTIONS=[option,option,...] command line option will be reflected here.");
845 
846         int i = 0;
847         for (File element : classpath.getElements())
848         {
849             String elementPath = element.getAbsolutePath();
850             if (elementPath.startsWith(_jettyHome))
851             {
852                 elementPath = "${jetty.home}" + elementPath.substring(_jettyHome.length());
853             }
854             System.out.printf("%2d: %20s | %s\n",i++,getVersion(element),elementPath);
855         }
856     }
857 
858     private void showActiveSecurityPolicies(ClassLoader cl)
859     {
860 
861         initSecurity(cl);
862 
863         Policy policy = Policy.getPolicy();
864 
865         if (policy != null && policy.getClass().getName().contains("JettyPolicy"))
866         {
867             System.out.println("Active Security Policies: ");
868 
869             try
870             {
871                 Method m = policy.getClass().getMethod("dump",new Class[]{ PrintStream.class });
872                 m.invoke(policy,new Object[]
873                 { System.out });
874             }
875             catch (SecurityException e)
876             {
877                 e.printStackTrace(System.err);
878             }
879             catch (NoSuchMethodException e)
880             {
881                 e.printStackTrace(System.err);
882             }
883             catch (IllegalArgumentException e)
884             {
885                 e.printStackTrace(System.err);
886             }
887             catch (IllegalAccessException e)
888             {
889                 e.printStackTrace(System.err);
890             }
891             catch (InvocationTargetException e)
892             {
893                 e.printStackTrace(System.err);
894             }
895         }
896     }
897 
898     private String fixPath(String path)
899     {
900         return path.replace('/',File.separatorChar);
901     }
902 
903     private String getVersion(File element)
904     {
905         if (element.isDirectory())
906         {
907             return "(dir)";
908         }
909 
910         if (element.isFile())
911         {
912             String name = element.getName().toLowerCase();
913             if (name.endsWith(".jar"))
914             {
915                 return JarVersion.getVersion(element);
916             }
917 
918             if (name.endsWith(".zip"))
919             {
920                 return getZipVersion(element);
921             }
922         }
923 
924         return "";
925     }
926 
927     private String getZipVersion(File element)
928     {
929         // TODO - find version in zip file.  Look for META-INF/MANIFEST.MF ?
930         return "";
931     }
932 
933     private void initSecurity(ClassLoader cl)
934     {
935         // Init the Security Policies
936         try
937         {
938             if (_secure)
939             {
940                 Policy.setPolicy(_config.getPolicyInstance(cl));
941                 System.setSecurityManager(new SecurityManager());
942                 // Policy.getPolicy().refresh();
943             }
944             else
945             {
946                 Policy policy = Policy.getPolicy();
947                 if (policy != null)
948                 {
949                     policy.refresh();
950                 }
951             }
952         }
953         catch (Exception e)
954         {
955             usageExit(e,ERR_SECURITY);
956         }
957     }
958 
959     private List<String> resolveXmlConfigs(List<String> xmls) throws FileNotFoundException
960     {
961         List<String> ret = new ArrayList<String>();
962         for (String xml : xmls)
963         {
964             ret.add(resolveXmlConfig(xml));
965         }
966 
967         return ret;
968     }
969 
970     private void listConfig()
971     {
972         InputStream cfgstream = null;
973         try
974         {
975             cfgstream=getConfigStream();
976             byte[] buf=new byte[4096];
977             
978             int len=0;
979             
980             while (len>=0)
981             {
982                 len=cfgstream.read(buf);
983                 if (len>0)
984                     System.out.write(buf,0,len);
985             }
986         }
987         catch (Exception e)
988         {
989             usageExit(e,ERR_UNKNOWN);
990         }
991         finally
992         {
993             close(cfgstream);
994         }
995     }
996     
997     /**
998      * Load Configuration.
999      * 
1000      * No specific configuration is real until a {@link Config#getCombinedClasspath(java.util.Collection)} is used to
1001      * execute the {@link Class} specified by {@link Config#getMainClassname()} is executed.
1002      * 
1003      * @param xmls
1004      *            the command line specified xml configuration options.
1005      * @return the list of xml configurations arriving via command line and start.config choices.
1006      */
1007     private List<String> loadConfig(List<String> xmls)
1008     {
1009         InputStream cfgstream = null;
1010         try
1011         {
1012             // Pass in xmls.size into Config so that conditions based on "nargs" work.
1013             _config.setArgCount(xmls.size());
1014             
1015             cfgstream=getConfigStream();
1016                 
1017             // parse the config
1018             _config.parse(cfgstream);
1019             
1020             _jettyHome = Config.getProperty("jetty.home");
1021             if (_jettyHome != null)
1022             {
1023                 _jettyHome = new File(_jettyHome).getCanonicalPath();
1024                 System.setProperty("jetty.home",_jettyHome);
1025             }
1026 
1027             // Collect the configured xml configurations.
1028             List<String> ret = new ArrayList<String>();
1029             ret.addAll(xmls); // add command line provided xmls first.
1030             for (String xmlconfig : _config.getXmlConfigs())
1031             {
1032                 // add xmlconfigs arriving via start.config
1033                 if (!ret.contains(xmlconfig))
1034                 {
1035                     ret.add(xmlconfig);
1036                 }
1037             }
1038 
1039             return ret;
1040         }
1041         catch (Exception e)
1042         {
1043             usageExit(e,ERR_UNKNOWN);
1044             return null; // never executed (just here to satisfy javac compiler)
1045         }
1046         finally
1047         {
1048             close(cfgstream);
1049         }
1050     }
1051 
1052     private InputStream getConfigStream() throws FileNotFoundException
1053     {
1054         String config=_startConfig;
1055         if (config == null || config.length() == 0)
1056         {
1057             config = System.getProperty("START","org/eclipse/jetty/start/start.config");
1058         }
1059         
1060         Config.debug("config=" + config);
1061 
1062         // Look up config as resource first.
1063         InputStream cfgstream = getClass().getClassLoader().getResourceAsStream(config);
1064 
1065         // resource not found, try filesystem next
1066         if (cfgstream == null)
1067         {
1068             cfgstream = new FileInputStream(config);
1069         }
1070         
1071         return cfgstream;
1072     }
1073 
1074 
1075     /**
1076      * Stop a running jetty instance.
1077      */
1078     public void stop(int port, String key)
1079     {
1080         int _port = port;
1081         String _key = key;
1082 
1083         try
1084         {
1085             if (_port <= 0)
1086             {
1087                 System.err.println("STOP.PORT system property must be specified");
1088             }
1089             if (_key == null)
1090             {
1091                 _key = "";
1092                 System.err.println("STOP.KEY system property must be specified");
1093                 System.err.println("Using empty key");
1094             }
1095 
1096             Socket s = new Socket(InetAddress.getByName("127.0.0.1"),_port);
1097             try
1098             {
1099                 OutputStream out = s.getOutputStream();
1100                 out.write((_key + "\r\nstop\r\n").getBytes());
1101                 out.flush();
1102             }
1103             finally
1104             {
1105                 s.close();
1106             }
1107         }
1108         catch (ConnectException e)
1109         {
1110             usageExit(e,ERR_NOT_STOPPED);
1111         }
1112         catch (Exception e)
1113         {
1114             usageExit(e,ERR_UNKNOWN);
1115         }
1116     }
1117     
1118 
1119     static void usageExit(Throwable t, int exit)
1120     {
1121         t.printStackTrace(System.err);
1122         System.err.println();
1123         System.err.println("Usage: java -jar start.jar [options] [properties] [configs]");
1124         System.err.println("       java -jar start.jar --help  # for more information");
1125         System.exit(exit);
1126     }
1127     static void usageExit(int exit)
1128     {
1129         System.err.println();
1130         System.err.println("Usage: java -jar start.jar [options] [properties] [configs]");
1131         System.err.println("       java -jar start.jar --help  # for more information");
1132         System.exit(exit);
1133     }
1134 }