View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.start;
20  
21  import static org.eclipse.jetty.start.UsageException.ERR_BAD_ARG;
22  
23  import java.io.File;
24  import java.io.FileFilter;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collections;
30  import java.util.Enumeration;
31  import java.util.HashMap;
32  import java.util.HashSet;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Properties;
36  import java.util.Set;
37  
38  /**
39   * The Arguments required to start Jetty.
40   */
41  public class StartArgs
42  {
43      public static final String CMD_LINE_SOURCE = "<cmd-line>";
44      public static final String VERSION;
45  
46      static
47      {
48          String ver = System.getProperty("jetty.version",null);
49  
50          if (ver == null)
51          {
52              Package pkg = StartArgs.class.getPackage();
53              if ((pkg != null) && "Eclipse.org - Jetty".equals(pkg.getImplementationVendor()) && (pkg.getImplementationVersion() != null))
54              {
55                  ver = pkg.getImplementationVersion();
56              }
57          }
58  
59          if (ver == null)
60          {
61              ver = "TEST";
62          }
63  
64          VERSION = ver;
65          System.setProperty("jetty.version",VERSION);
66      }
67  
68      private static final String SERVER_MAIN = "org.eclipse.jetty.xml.XmlConfiguration";
69  
70      private List<String> commandLine = new ArrayList<>();
71      private Set<String> modules = new HashSet<>();
72      private Map<String, List<String>> sources = new HashMap<>();
73      private List<FileArg> files = new ArrayList<>();
74      private Classpath classpath;
75      private List<String> xmlRefs = new ArrayList<>();
76      private List<File> xmls = new ArrayList<>();
77      private Properties properties = new Properties();
78      private Set<String> systemPropertyKeys = new HashSet<>();
79      private List<String> jvmArgs = new ArrayList<>();
80      private List<String> moduleStartdIni = new ArrayList<>();
81      private List<String> moduleStartIni = new ArrayList<>();
82      private Map<String, String> propertySource = new HashMap<>();
83      private String moduleGraphFilename;
84  
85      private Modules allModules;
86      // Should the server be run?
87      private boolean run = true;
88      private boolean help = false;
89      private boolean stopCommand = false;
90      private boolean listModules = false;
91      private boolean listClasspath = false;
92      private boolean listConfig = false;
93      private boolean version = false;
94      private boolean dryRun = false;
95  
96      private boolean exec = false;
97  
98      public StartArgs(String[] commandLineArgs)
99      {
100         commandLine.addAll(Arrays.asList(commandLineArgs));
101         classpath = new Classpath();
102     }
103 
104     private void addFile(String uriLocation)
105     {
106         FileArg arg = new FileArg(uriLocation);
107         if (!files.contains(arg))
108         {
109             files.add(arg);
110         }
111     }
112 
113     public void addSystemProperty(String key, String value)
114     {
115         this.systemPropertyKeys.add(key);
116         System.setProperty(key,value);
117     }
118 
119     private void addUniqueXmlFile(String xmlRef, File xmlfile) throws IOException
120     {
121         if (!FS.canReadFile(xmlfile))
122         {
123             throw new IOException("Cannot read file: " + xmlRef);
124         }
125         xmlfile = xmlfile.getCanonicalFile();
126         if (!xmls.contains(xmlfile))
127         {
128             xmls.add(xmlfile);
129         }
130     }
131 
132     public void dumpActiveXmls(BaseHome baseHome)
133     {
134         System.out.println();
135         System.out.println("Jetty Active XMLs:");
136         System.out.println("------------------");
137         if (xmls.isEmpty())
138         {
139             System.out.println(" (no xml files specified)");
140             return;
141         }
142 
143         for (File xml : xmls)
144         {
145             System.out.printf(" %s%n",baseHome.toShortForm(xml.getAbsolutePath()));
146         }
147     }
148 
149     public void dumpEnvironment()
150     {
151         // Java Details
152         System.out.println();
153         System.out.println("Java Environment:");
154         System.out.println("-----------------");
155         dumpSystemProperty("java.home");
156         dumpSystemProperty("java.vm.vendor");
157         dumpSystemProperty("java.vm.version");
158         dumpSystemProperty("java.vm.name");
159         dumpSystemProperty("java.vm.info");
160         dumpSystemProperty("java.runtime.name");
161         dumpSystemProperty("java.runtime.version");
162         dumpSystemProperty("java.io.tmpdir");
163 
164         // Jetty Environment
165         System.out.println();
166         System.out.println("Jetty Environment:");
167         System.out.println("-----------------");
168 
169         dumpSystemProperty("jetty.home");
170         dumpSystemProperty("jetty.base");
171         dumpSystemProperty("jetty.version");
172     }
173 
174     public void dumpJvmArgs()
175     {
176         System.out.println();
177         System.out.println("JVM Arguments:");
178         System.out.println("--------------");
179         if (jvmArgs.isEmpty())
180         {
181             System.out.println(" (no jvm args specified)");
182             return;
183         }
184 
185         for (String jvmArgKey : jvmArgs)
186         {
187             String value = System.getProperty(jvmArgKey);
188             if (value != null)
189             {
190                 System.out.printf(" %s = %s%n",jvmArgKey,value);
191             }
192             else
193             {
194                 System.out.printf(" %s%n",jvmArgKey);
195             }
196         }
197     }
198 
199     public void dumpProperties()
200     {
201         System.out.println();
202         System.out.println("Properties:");
203         System.out.println("-----------");
204 
205         if (properties.isEmpty())
206         {
207             System.out.println(" (no properties specified)");
208             return;
209         }
210 
211         List<String> sortedKeys = new ArrayList<>();
212         @SuppressWarnings("unchecked")
213         Enumeration<String> keyEnum = (Enumeration<String>)properties.propertyNames();
214         while (keyEnum.hasMoreElements())
215         {
216             sortedKeys.add(keyEnum.nextElement());
217         }
218 
219         Collections.sort(sortedKeys);
220 
221         for (String key : sortedKeys)
222         {
223             String value = properties.getProperty(key);
224             System.out.printf(" %s = %s%n",key,value);
225         }
226     }
227 
228     public void dumpSystemProperties()
229     {
230         System.out.println();
231         System.out.println("System Properties:");
232         System.out.println("------------------");
233 
234         if (systemPropertyKeys.isEmpty())
235         {
236             System.out.println(" (no system properties specified)");
237             return;
238         }
239 
240         List<String> sortedKeys = new ArrayList<>();
241         sortedKeys.addAll(systemPropertyKeys);
242         Collections.sort(sortedKeys);
243 
244         for (String key : sortedKeys)
245         {
246             String value = System.getProperty(key);
247             System.out.printf(" %s = %s%n",key,value);
248         }
249     }
250 
251     private void dumpSystemProperty(String key)
252     {
253         System.out.printf(" %s=%s%n",key,System.getProperty(key));
254     }
255 
256     /**
257      * Ensure that the System Properties are set (if defined as a System property, or start.config property, or start.ini property)
258      * 
259      * @param key
260      *            the key to be sure of
261      */
262     private void ensureSystemPropertySet(String key)
263     {
264         if (systemPropertyKeys.contains(key))
265         {
266             return; // done
267         }
268 
269         if (properties.containsKey(key))
270         {
271             String val = properties.getProperty(key,null);
272             if (val == null)
273             {
274                 return; // no value to set
275             }
276             // setup system property
277             systemPropertyKeys.add(key);
278             System.setProperty(key,val);
279         }
280     }
281 
282     /**
283      * Build up the Classpath and XML file references based on enabled Module list.
284      * 
285      * @param baseHome
286      * @param activeModules
287      * @throws IOException
288      */
289     public void expandModules(BaseHome baseHome, List<Module> activeModules) throws IOException
290     {
291         for (Module module : activeModules)
292         {
293             // Find and Expand Libraries
294             for (String rawlibref : module.getLibs())
295             {
296                 String libref = rawlibref.replace("${jetty.version}",VERSION);
297                 libref = FS.separators(libref);
298 
299                 if (libref.contains("*"))
300                 {
301                     // Glob Reference
302                     int idx = libref.lastIndexOf(File.separatorChar);
303 
304                     String relativePath = "/";
305                     String filenameRef = libref;
306                     if (idx >= 0)
307                     {
308                         relativePath = libref.substring(0,idx);
309                         filenameRef = libref.substring(idx + 1);
310                     }
311 
312                     StringBuilder regex = new StringBuilder();
313                     regex.append('^');
314                     for (char c : filenameRef.toCharArray())
315                     {
316                         switch (c)
317                         {
318                             case '*':
319                                 regex.append(".*");
320                                 break;
321                             case '.':
322                                 regex.append("\\.");
323                                 break;
324                             default:
325                                 regex.append(c);
326                         }
327                     }
328                     regex.append('$');
329 
330                     FileFilter filter = new FS.FilenameRegexFilter(regex.toString());
331 
332                     for (File libfile : baseHome.listFiles(relativePath,filter))
333                     {
334                         classpath.addComponent(libfile);
335                     }
336                 }
337                 else
338                 {
339                     // Straight Reference
340                     File libfile = baseHome.getFile(libref);
341                     classpath.addComponent(libfile);
342                 }
343             }
344 
345             // Find and Expand XML files
346             for (String xmlRef : module.getXmls())
347             {
348                 // Straight Reference
349                 File xmlfile = baseHome.getFile(xmlRef);
350                 addUniqueXmlFile(xmlRef,xmlfile);
351             }
352 
353             // Register Download operations
354             for (String file : module.getFiles())
355             {
356                 StartLog.debug("Adding module specified file: %s",file);
357                 addFile(file);
358             }
359         }
360     }
361 
362     public Modules getAllModules()
363     {
364         return allModules;
365     }
366 
367     public Classpath getClasspath()
368     {
369         return classpath;
370     }
371 
372     public List<String> getCommandLine()
373     {
374         return this.commandLine;
375     }
376 
377     public List<FileArg> getFiles()
378     {
379         return files;
380     }
381 
382     public Set<String> getEnabledModules()
383     {
384         return this.modules;
385     }
386 
387     public List<String> getJvmArgs()
388     {
389         return jvmArgs;
390     }
391 
392     public CommandLineBuilder getMainArgs(BaseHome baseHome, boolean addJavaInit) throws IOException
393     {
394         CommandLineBuilder cmd = new CommandLineBuilder();
395 
396         if (addJavaInit)
397         {
398             cmd.addArg(CommandLineBuilder.findJavaBin());
399 
400             for (String x : jvmArgs)
401             {
402                 cmd.addArg(x);
403             }
404 
405             cmd.addRawArg("-Djetty.home=" + baseHome.getHome());
406             cmd.addRawArg("-Djetty.base=" + baseHome.getBase());
407 
408             // System Properties
409             for (String propKey : systemPropertyKeys)
410             {
411                 String value = System.getProperty(propKey);
412                 cmd.addEqualsArg("-D" + propKey,value);
413             }
414 
415             cmd.addArg("-cp");
416             cmd.addRawArg(classpath.toString());
417             cmd.addRawArg(getMainClassname());
418         }
419 
420         // Special Stop/Shutdown properties
421         ensureSystemPropertySet("STOP.PORT");
422         ensureSystemPropertySet("STOP.KEY");
423         ensureSystemPropertySet("STOP.WAIT");
424 
425         // Check if we need to pass properties as a file
426         if (properties.size() > 0)
427         {
428             File prop_file = File.createTempFile("start",".properties");
429             if (!dryRun)
430             {
431                 prop_file.deleteOnExit();
432             }
433             try (FileOutputStream out = new FileOutputStream(prop_file))
434             {
435                 properties.store(out,"start.jar properties");
436             }
437             cmd.addArg(prop_file.getAbsolutePath());
438         }
439 
440         for (File xml : xmls)
441         {
442             cmd.addRawArg(xml.getAbsolutePath());
443         }
444 
445         return cmd;
446     }
447 
448     public String getMainClassname()
449     {
450         String mainclass = System.getProperty("jetty.server",SERVER_MAIN);
451         return System.getProperty("main.class",mainclass);
452     }
453 
454     public String getModuleGraphFilename()
455     {
456         return moduleGraphFilename;
457     }
458 
459     public List<String> getModuleStartdIni()
460     {
461         return moduleStartdIni;
462     }
463 
464     public List<String> getModuleStartIni()
465     {
466         return moduleStartIni;
467     }
468 
469     public Properties getProperties()
470     {
471         return properties;
472     }
473 
474     public List<String> getSources(String module)
475     {
476         return sources.get(module);
477     }
478 
479     private String getValue(String arg)
480     {
481         int idx = arg.indexOf('=');
482         if (idx == (-1))
483         {
484             throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
485         }
486         String value = arg.substring(idx + 1).trim();
487         if (value.length() <= 0)
488         {
489             throw new UsageException(ERR_BAD_ARG,"Argument is missing a required value: %s",arg);
490         }
491         return value;
492     }
493 
494     private List<String> getValues(String arg)
495     {
496         String v = getValue(arg);
497         ArrayList<String> l = new ArrayList<>();
498         for (String s : v.split(","))
499         {
500             if (s != null)
501             {
502                 s = s.trim();
503                 if (s.length() > 0)
504                 {
505                     l.add(s);
506                 }
507             }
508         }
509         return l;
510     }
511 
512     public List<File> getXmlFiles()
513     {
514         return xmls;
515     }
516 
517     public boolean hasJvmArgs()
518     {
519         return jvmArgs.size() > 0;
520     }
521 
522     public boolean hasSystemProperties()
523     {
524         for (String key : systemPropertyKeys)
525         {
526             // ignored keys
527             if ("jetty.home".equals(key) || "jetty.base".equals(key))
528             {
529                 // skip
530                 continue;
531             }
532             return true;
533         }
534         return false;
535     }
536 
537     public boolean isDryRun()
538     {
539         return dryRun;
540     }
541 
542     public boolean isExec()
543     {
544         return exec;
545     }
546 
547     public boolean isHelp()
548     {
549         return help;
550     }
551 
552     public boolean isListClasspath()
553     {
554         return listClasspath;
555     }
556 
557     public boolean isListConfig()
558     {
559         return listConfig;
560     }
561 
562     public boolean isListModules()
563     {
564         return listModules;
565     }
566 
567     public boolean isRun()
568     {
569         return run;
570     }
571 
572     public boolean isStopCommand()
573     {
574         return stopCommand;
575     }
576 
577     public boolean isVersion()
578     {
579         return version;
580     }
581 
582     public void parse(BaseHome baseHome, TextFile file)
583     {
584         String source;
585         try
586         {
587             source = baseHome.toShortForm(file.getFile());
588         }
589         catch (Exception e)
590         {
591             throw new UsageException(ERR_BAD_ARG,"Bad file: %s",file);
592         }
593         for (String line : file)
594         {
595             parse(line,source);
596         }
597     }
598 
599     public void parse(final String rawarg, String source)
600     {
601         if (rawarg == null)
602         {
603             return;
604         }
605 
606         final String arg = rawarg.trim();
607 
608         if (arg.length() <= 0)
609         {
610             return;
611         }
612 
613         if (arg.startsWith("#"))
614         {
615             return;
616         }
617 
618         if ("--help".equals(arg) || "-?".equals(arg))
619         {
620             if (!CMD_LINE_SOURCE.equals(source))
621             {
622                 throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
623             }
624 
625             help = true;
626             run = false;
627             return;
628         }
629 
630         if ("--debug".equals(arg))
631         {
632             // valid, but handled in StartLog instead
633             return;
634         }
635 
636         if ("--stop".equals(arg))
637         {
638             if (!CMD_LINE_SOURCE.equals(source))
639             {
640                 throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
641             }
642             stopCommand = true;
643             run = false;
644             return;
645         }
646 
647         if (arg.startsWith("--download="))
648         {
649             addFile(getValue(arg));
650             return;
651         }
652 
653         if ("--list-classpath".equals(arg) || "--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
654         {
655             listClasspath = true;
656             run = false;
657             return;
658         }
659 
660         if ("--list-config".equals(arg))
661         {
662             listConfig = true;
663             run = false;
664             return;
665         }
666 
667         if ("--dry-run".equals(arg) || "--exec-print".equals(arg))
668         {
669             if (!CMD_LINE_SOURCE.equals(source))
670             {
671                 throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
672             }
673             dryRun = true;
674             run = false;
675             return;
676         }
677 
678         if ("--exec".equals(arg))
679         {
680             exec = true;
681             return;
682         }
683 
684         // Arbitrary Libraries
685 
686         if (arg.startsWith("--lib="))
687         {
688             String cp = getValue(arg);
689             classpath.addClasspath(cp);
690             return;
691         }
692 
693         // Module Management
694         if ("--list-modules".equals(arg))
695         {
696             listModules = true;
697             run = false;
698             return;
699         }
700 
701         if (arg.startsWith("--add-to-startd"))
702         {
703             if (!CMD_LINE_SOURCE.equals(source))
704             {
705                 throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
706             }
707             moduleStartdIni.addAll(getValues(arg));
708             run = false;
709             return;
710         }
711 
712         if (arg.startsWith("--add-to-start"))
713         {
714             if (!CMD_LINE_SOURCE.equals(source))
715             {
716                 throw new UsageException(ERR_BAD_ARG,"%s not allowed in %s",arg,source);
717             }
718             moduleStartIni.addAll(getValues(arg));
719             run = false;
720             return;
721         }
722 
723         if (arg.startsWith("--module="))
724         {
725             for (String moduleName : getValues(arg))
726             {
727                 modules.add(moduleName);
728                 List<String> list = sources.get(moduleName);
729                 if (list == null)
730                 {
731                     list = new ArrayList<String>();
732                     sources.put(moduleName,list);
733                 }
734                 list.add(source);
735             }
736             return;
737         }
738 
739         if (arg.startsWith("--write-module-graph="))
740         {
741             this.moduleGraphFilename = getValue(arg);
742             run = false;
743             return;
744         }
745 
746         // Start property (syntax similar to System property)
747         if (arg.startsWith("-D"))
748         {
749             String[] assign = arg.substring(2).split("=",2);
750             systemPropertyKeys.add(assign[0]);
751             switch (assign.length)
752             {
753                 case 2:
754                     System.setProperty(assign[0],assign[1]);
755                     break;
756                 case 1:
757                     System.setProperty(assign[0],"");
758                     break;
759                 default:
760                     break;
761             }
762             return;
763         }
764 
765         // Anything else with a "-" is considered a JVM argument
766         if (arg.startsWith("-"))
767         {
768             // Only add non-duplicates
769             if (!jvmArgs.contains(arg))
770             {
771                 jvmArgs.add(arg);
772             }
773             return;
774         }
775 
776         // Is this a raw property declaration?
777         int idx = arg.indexOf('=');
778         if (idx >= 0)
779         {
780             String key = arg.substring(0,idx);
781             String value = arg.substring(idx + 1);
782 
783             if (source != CMD_LINE_SOURCE)
784             {
785                 if (propertySource.containsKey(key))
786                 {
787                     throw new UsageException(ERR_BAD_ARG,"Property %s in %s already set in %s",key,source,propertySource.get(key));
788                 }
789                 propertySource.put(key,source);
790             }
791 
792             if ("OPTION".equals(key) || "OPTIONS".equals(key))
793             {
794                 StringBuilder warn = new StringBuilder();
795                 warn.append("The behavior of the argument ");
796                 warn.append(arg).append(" (seen in ").append(source);
797                 warn.append(") has changed, and is now considered a normal property.  ");
798                 warn.append(key).append(" no longer controls what libraries are on your classpath,");
799                 warn.append(" use --module instead. See --help for details.");
800                 StartLog.warn(warn.toString());
801             }
802 
803             properties.setProperty(key,value);
804             return;
805         }
806 
807         // Is this an xml file?
808         if (FS.isXml(arg))
809         {
810             // only add non-duplicates
811             if (!xmlRefs.contains(arg))
812             {
813                 xmlRefs.add(arg);
814             }
815             return;
816         }
817 
818         // Anything else is unrecognized
819         throw new UsageException(ERR_BAD_ARG,"Unrecognized argument: \"%s\" in %s",arg,source);
820     }
821 
822     public void parseCommandLine()
823     {
824         for (String line : commandLine)
825         {
826             parse(line,StartArgs.CMD_LINE_SOURCE);
827         }
828     }
829 
830     public void resolveExtraXmls(BaseHome baseHome) throws IOException
831     {
832         // Find and Expand XML files
833         for (String xmlRef : xmlRefs)
834         {
835             // Straight Reference
836             File xmlfile = baseHome.getFile(xmlRef);
837             if (!xmlfile.exists())
838             {
839                 xmlfile = baseHome.getFile("etc/" + xmlRef);
840             }
841             addUniqueXmlFile(xmlRef,xmlfile);
842         }
843     }
844 
845     public void setAllModules(Modules allModules)
846     {
847         this.allModules = allModules;
848     }
849 
850     @Override
851     public String toString()
852     {
853         StringBuilder builder = new StringBuilder();
854         builder.append("StartArgs [commandLine=");
855         builder.append(commandLine);
856         builder.append(", enabledModules=");
857         builder.append(modules);
858         builder.append(", xmlRefs=");
859         builder.append(xmlRefs);
860         builder.append(", properties=");
861         builder.append(properties);
862         builder.append(", jvmArgs=");
863         builder.append(jvmArgs);
864         builder.append("]");
865         return builder.toString();
866     }
867 }