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