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