View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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 java.io.File;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.net.URL;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.nio.file.Paths;
29  import java.util.ArrayList;
30  import java.util.Collections;
31  import java.util.HashMap;
32  import java.util.HashSet;
33  import java.util.List;
34  import java.util.ListIterator;
35  import java.util.Map;
36  import java.util.Properties;
37  import java.util.Set;
38  import java.util.StringTokenizer;
39  
40  import org.eclipse.jetty.start.Props.Prop;
41  import org.eclipse.jetty.start.config.ConfigSource;
42  import org.eclipse.jetty.start.config.ConfigSources;
43  import org.eclipse.jetty.start.config.DirConfigSource;
44  
45  import static org.eclipse.jetty.start.UsageException.ERR_BAD_ARG;
46  
47  /**
48   * The Arguments required to start Jetty.
49   */
50  public class StartArgs
51  {
52      public static final String VERSION;
53  
54      static
55      {
56          // Use command line versions
57          String ver = System.getProperty("jetty.version",null);
58          String tag = System.getProperty("jetty.tag.version","master");
59  
60          // Use META-INF/MANIFEST.MF versions
61          if (ver == null)
62          {
63              Package pkg = StartArgs.class.getPackage();
64              if ((pkg != null) && "Eclipse.org - Jetty".equals(pkg.getImplementationVendor()) && (pkg.getImplementationVersion() != null))
65              {
66                  ver = pkg.getImplementationVersion();
67                  if (tag == null)
68                  {
69                      tag = "jetty-" + ver;
70                  }
71              }
72          }
73  
74          // Use jetty-version.properties values
75          if (ver == null)
76          {
77              URL url = Thread.currentThread().getContextClassLoader().getResource("jetty-version.properties");
78              if (url != null)
79              {
80                  try (InputStream in = url.openStream())
81                  {
82                      Properties props = new Properties();
83                      props.load(in);
84                      ver = props.getProperty("jetty.version");
85                  }
86                  catch (IOException ignore)
87                  {
88                      StartLog.debug(ignore);
89                  }
90              }
91          }
92  
93          // Default values
94          if (ver == null)
95          {
96              ver = "0.0";
97              if (tag == null)
98              {
99                  tag = "master";
100             }
101         }
102 
103         // Set Tag Defaults
104         if (tag == null || tag.contains("-SNAPSHOT"))
105         {
106             tag = "master";
107         }
108 
109         VERSION = ver;
110         System.setProperty("jetty.version",VERSION);
111         System.setProperty("jetty.tag.version",tag);
112     }
113 
114     private static final String SERVER_MAIN = "org.eclipse.jetty.xml.XmlConfiguration";
115 
116     /** List of enabled modules */
117     private Set<String> modules = new HashSet<>();
118 
119     /** List of modules to skip [files] section validation */
120     private Set<String> skipFileValidationModules = new HashSet<>();
121 
122     /** Map of enabled modules to the source of where that activation occurred */
123     private Map<String, List<String>> sources = new HashMap<>();
124 
125     /** Map of properties to where that property was declared */
126     private Map<String, String> propertySource = new HashMap<>();
127 
128     /** List of all active [files] sections from enabled modules */
129     private List<FileArg> files = new ArrayList<>();
130 
131     /** List of all active [lib] sections from enabled modules */
132     private Classpath classpath;
133 
134     /** List of all active [xml] sections from enabled modules */
135     private List<Path> xmls = new ArrayList<>();
136 
137     /** JVM arguments, found via commmand line and in all active [exec] sections from enabled modules */
138     private List<String> jvmArgs = new ArrayList<>();
139 
140     /** List of all xml references found directly on command line or start.ini */
141     private List<String> xmlRefs = new ArrayList<>();
142 
143     /** List of all property references found directly on command line or start.ini */
144     private List<String> propertyFileRefs = new ArrayList<>();
145 
146     /** List of all property files */
147     private List<Path> propertyFiles = new ArrayList<>();
148 
149     private Props properties = new Props();
150     private Set<String> systemPropertyKeys = new HashSet<>();
151     private List<String> rawLibs = new ArrayList<>();
152 
153     // jetty.base - build out commands
154     /** --add-to-startd=[module,[module]] */
155     private List<String> addToStartdIni = new ArrayList<>();
156     /** --add-to-start=[module,[module]] */
157     private List<String> addToStartIni = new ArrayList<>();
158 
159     // module inspection commands
160     /** --write-module-graph=[filename] */
161     private String moduleGraphFilename;
162 
163     /** Collection of all modules */
164     private Modules allModules;
165     /** Should the server be run? */
166     private boolean run = true;
167 
168     /** Download related args */
169     private boolean download = false;
170     private boolean licenseCheckRequired = false;
171     private boolean testingMode = false;
172 
173     private boolean help = false;
174     private boolean stopCommand = false;
175     private boolean listModules = false;
176     private boolean listClasspath = false;
177     private boolean listConfig = false;
178     private boolean version = false;
179     private boolean dryRun = false;
180 
181     private boolean exec = false;
182     private String exec_properties;
183     private boolean approveAllLicenses = false;
184 
185     public StartArgs()
186     {
187         classpath = new Classpath();
188     }
189 
190     private void addFile(Module module, String uriLocation)
191     {
192         if (module.isSkipFilesValidation())
193         {
194             StartLog.debug("Not validating %s [files] for %s",module,uriLocation);
195             return;
196         }
197 
198         FileArg arg = new FileArg(module,properties.expand(uriLocation));
199         if (!files.contains(arg))
200         {
201             files.add(arg);
202         }
203     }
204 
205     public void addSystemProperty(String key, String value)
206     {
207         this.systemPropertyKeys.add(key);
208         System.setProperty(key,value);
209     }
210 
211     private void addUniqueXmlFile(String xmlRef, Path xmlfile) throws IOException
212     {
213         if (!FS.canReadFile(xmlfile))
214         {
215             throw new IOException("Cannot read file: " + xmlRef);
216         }
217         xmlfile = FS.toRealPath(xmlfile);
218         if (!xmls.contains(xmlfile))
219         {
220             xmls.add(xmlfile);
221         }
222     }
223 
224     private void addUniquePropertyFile(String propertyFileRef, Path propertyFile) throws IOException
225     {
226         if (!FS.canReadFile(propertyFile))
227         {
228             throw new IOException("Cannot read file: " + propertyFileRef);
229         }
230         propertyFile = FS.toRealPath(propertyFile);
231         if (!propertyFiles.contains(propertyFile))
232         {
233             propertyFiles.add(propertyFile);
234         }
235     }
236 
237     public void dumpActiveXmls(BaseHome baseHome)
238     {
239         System.out.println();
240         System.out.println("Jetty Active XMLs:");
241         System.out.println("------------------");
242         if (xmls.isEmpty())
243         {
244             System.out.println(" (no xml files specified)");
245             return;
246         }
247 
248         for (Path xml : xmls)
249         {
250             System.out.printf(" %s%n",baseHome.toShortForm(xml.toAbsolutePath()));
251         }
252     }
253 
254     public void dumpEnvironment(BaseHome baseHome)
255     {
256         // Java Details
257         System.out.println();
258         System.out.println("Java Environment:");
259         System.out.println("-----------------");
260         dumpSystemProperty("java.home");
261         dumpSystemProperty("java.vm.vendor");
262         dumpSystemProperty("java.vm.version");
263         dumpSystemProperty("java.vm.name");
264         dumpSystemProperty("java.vm.info");
265         dumpSystemProperty("java.runtime.name");
266         dumpSystemProperty("java.runtime.version");
267         dumpSystemProperty("java.io.tmpdir");
268         dumpSystemProperty("user.dir");
269         dumpSystemProperty("user.language");
270         dumpSystemProperty("user.country");
271 
272         // Jetty Environment
273         System.out.println();
274         System.out.println("Jetty Environment:");
275         System.out.println("-----------------");
276         dumpProperty("jetty.version");
277         dumpProperty("jetty.tag.version");
278         dumpProperty("jetty.home");
279         dumpProperty("jetty.base");
280 
281         // Jetty Configuration Environment
282         System.out.println();
283         System.out.println("Config Search Order:");
284         System.out.println("--------------------");
285         for (ConfigSource config : baseHome.getConfigSources())
286         {
287             System.out.printf(" %s",config.getId());
288             if (config instanceof DirConfigSource)
289             {
290                 DirConfigSource dirsource = (DirConfigSource)config;
291                 if (dirsource.isPropertyBased())
292                 {
293                     System.out.printf(" -> %s",dirsource.getDir());
294                 }
295             }
296             System.out.println();
297         }
298 
299         // Jetty Se
300         System.out.println();
301     }
302 
303     public void dumpJvmArgs()
304     {
305         System.out.println();
306         System.out.println("JVM Arguments:");
307         System.out.println("--------------");
308         if (jvmArgs.isEmpty())
309         {
310             System.out.println(" (no jvm args specified)");
311             return;
312         }
313 
314         for (String jvmArgKey : jvmArgs)
315         {
316             String value = System.getProperty(jvmArgKey);
317             if (value != null)
318             {
319                 System.out.printf(" %s = %s%n",jvmArgKey,value);
320             }
321             else
322             {
323                 System.out.printf(" %s%n",jvmArgKey);
324             }
325         }
326     }
327 
328     public void dumpProperties()
329     {
330         System.out.println();
331         System.out.println("Properties:");
332         System.out.println("-----------");
333 
334         List<String> sortedKeys = new ArrayList<>();
335         for (Prop prop : properties)
336         {
337             if (prop.origin.equals(Props.ORIGIN_SYSPROP))
338             {
339                 continue; // skip
340             }
341             sortedKeys.add(prop.key);
342         }
343 
344         if (sortedKeys.isEmpty())
345         {
346             System.out.println(" (no properties specified)");
347             return;
348         }
349 
350         Collections.sort(sortedKeys);
351 
352         for (String key : sortedKeys)
353         {
354             dumpProperty(key);
355         }
356     }
357 
358     private void dumpProperty(String key)
359     {
360         Prop prop = properties.getProp(key);
361         if (prop == null)
362         {
363             System.out.printf(" %s (not defined)%n",key);
364         }
365         else
366         {
367             System.out.printf(" %s = %s%n",key,properties.expand(prop.value));
368             if (StartLog.isDebugEnabled())
369             {
370                 System.out.printf("   origin: %s%n",prop.origin);
371                 while (prop.overrides != null)
372                 {
373                     prop = prop.overrides;
374                     System.out.printf("   (overrides)%n");
375                     System.out.printf("     %s = %s%n",key,properties.expand(prop.value));
376                     System.out.printf("     origin: %s%n",prop.origin);
377                 }
378             }
379         }
380     }
381 
382     public void dumpSystemProperties()
383     {
384         System.out.println();
385         System.out.println("System Properties:");
386         System.out.println("------------------");
387 
388         if (systemPropertyKeys.isEmpty())
389         {
390             System.out.println(" (no system properties specified)");
391             return;
392         }
393 
394         List<String> sortedKeys = new ArrayList<>();
395         sortedKeys.addAll(systemPropertyKeys);
396         Collections.sort(sortedKeys);
397 
398         for (String key : sortedKeys)
399         {
400             String value = System.getProperty(key);
401             System.out.printf(" %s = %s%n",key,properties.expand(value));
402         }
403     }
404 
405     private void dumpSystemProperty(String key)
406     {
407         System.out.printf(" %s = %s%n",key,System.getProperty(key));
408     }
409 
410     /**
411      * Ensure that the System Properties are set (if defined as a System property, or start.config property, or
412      * start.ini property)
413      *
414      * @param key
415      *            the key to be sure of
416      */
417     private void ensureSystemPropertySet(String key)
418     {
419         if (systemPropertyKeys.contains(key))
420         {
421             return; // done
422         }
423 
424         if (properties.containsKey(key))
425         {
426             String val = properties.expand(properties.getString(key));
427             if (val == null)
428             {
429                 return; // no value to set
430             }
431             // setup system property
432             systemPropertyKeys.add(key);
433             System.setProperty(key,val);
434         }
435     }
436 
437     /**
438      * Expand any command line added <code>--lib</code> lib references.
439      *
440      * @param baseHome
441      *            the base home in use
442      * @throws IOException
443      *             if unable to expand the libraries
444      */
445     public void expandLibs(BaseHome baseHome) throws IOException
446     {
447         StartLog.debug("Expanding Libs");
448         for (String rawlibref : rawLibs)
449         {
450             StartLog.debug("rawlibref = " + rawlibref);
451             String libref = properties.expand(rawlibref);
452             StartLog.debug("expanded = " + libref);
453 
454             // perform path escaping (needed by windows)
455             libref = libref.replaceAll("\\\\([^\\\\])","\\\\\\\\$1");
456 
457             for (Path libpath : baseHome.getPaths(libref))
458             {
459                 classpath.addComponent(libpath.toFile());
460             }
461         }
462     }
463 
464     /**
465      * Build up the Classpath and XML file references based on enabled Module list.
466      *
467      * @param baseHome
468      *            the base home in use
469      * @param activeModules
470      *            the active (selected) modules
471      * @throws IOException
472      *             if unable to expand the modules
473      */
474     public void expandModules(BaseHome baseHome, List<Module> activeModules) throws IOException
475     {
476         StartLog.debug("Expanding Modules");
477         for (Module module : activeModules)
478         {
479             // Find and Expand Libraries
480             for (String rawlibref : module.getLibs())
481             {
482                 StartLog.debug("rawlibref = " + rawlibref);
483                 String libref = properties.expand(rawlibref);
484                 StartLog.debug("expanded = " + libref);
485 
486                 for (Path libpath : baseHome.getPaths(libref))
487                 {
488                     classpath.addComponent(libpath.toFile());
489                 }
490             }
491 
492             for (String jvmArg : module.getJvmArgs())
493             {
494                 exec = true;
495                 jvmArgs.add(jvmArg);
496             }
497 
498             // Find and Expand XML files
499             for (String xmlRef : module.getXmls())
500             {
501                 // Straight Reference
502                 xmlRef = properties.expand(xmlRef);
503                 Path xmlfile = baseHome.getPath(xmlRef);
504                 addUniqueXmlFile(xmlRef,xmlfile);
505             }
506 
507             // Register Download operations
508             for (String file : module.getFiles())
509             {
510                 StartLog.debug("Adding module specified file: %s",file);
511                 addFile(module,file);
512             }
513         }
514     }
515 
516     public List<String> getAddToStartdIni()
517     {
518         return addToStartdIni;
519     }
520 
521     public List<String> getAddToStartIni()
522     {
523         return addToStartIni;
524     }
525 
526     public Modules getAllModules()
527     {
528         return allModules;
529     }
530 
531     public Classpath getClasspath()
532     {
533         return classpath;
534     }
535 
536     public Set<String> getEnabledModules()
537     {
538         return this.modules;
539     }
540 
541     public List<FileArg> getFiles()
542     {
543         return files;
544     }
545 
546     public List<String> getJvmArgs()
547     {
548         return jvmArgs;
549     }
550 
551     public CommandLineBuilder getMainArgs(BaseHome baseHome, boolean addJavaInit) throws IOException
552     {
553         CommandLineBuilder cmd = new CommandLineBuilder();
554 
555         if (addJavaInit)
556         {
557             cmd.addRawArg(CommandLineBuilder.findJavaBin());
558 
559             for (String x : jvmArgs)
560             {
561                 cmd.addRawArg(x);
562             }
563 
564             cmd.addRawArg("-Djava.io.tmpdir=" + System.getProperty("java.io.tmpdir"));
565             cmd.addRawArg("-Djetty.home=" + baseHome.getHome());
566             cmd.addRawArg("-Djetty.base=" + baseHome.getBase());
567 
568             // System Properties
569             for (String propKey : systemPropertyKeys)
570             {
571                 String value = System.getProperty(propKey);
572                 cmd.addEqualsArg("-D" + propKey,value);
573             }
574 
575             cmd.addRawArg("-cp");
576             cmd.addRawArg(classpath.toString());
577             cmd.addRawArg(getMainClassname());
578         }
579 
580         // Special Stop/Shutdown properties
581         ensureSystemPropertySet("STOP.PORT");
582         ensureSystemPropertySet("STOP.KEY");
583         ensureSystemPropertySet("STOP.WAIT");
584 
585         // pass properties as args or as a file
586         if (dryRun && exec_properties==null)
587         {
588             for (Prop p : properties)
589                 cmd.addRawArg(CommandLineBuilder.quote(p.key) + "=" + CommandLineBuilder.quote(p.value));
590         }
591         else if (properties.size() > 0)
592         {
593             Path prop_path;
594             if (exec_properties==null)
595             {
596                 prop_path=Files.createTempFile("start_", ".properties");
597                 prop_path.toFile().deleteOnExit();
598             }
599             else
600                 prop_path=new File(exec_properties).toPath();
601                 
602             try (OutputStream out = Files.newOutputStream(prop_path))
603             {
604                 properties.store(out,"start.jar properties");
605             }
606             cmd.addRawArg(prop_path.toAbsolutePath().toString());
607         }
608 
609         for (Path xml : xmls)
610         {
611             cmd.addRawArg(xml.toAbsolutePath().toString());
612         }
613 
614         for (Path propertyFile : propertyFiles)
615         {
616             cmd.addRawArg(propertyFile.toAbsolutePath().toString());
617         }
618 
619         return cmd;
620     }
621 
622     public String getMainClassname()
623     {
624         String mainclass = System.getProperty("jetty.server",SERVER_MAIN);
625         return System.getProperty("main.class",mainclass);
626     }
627 
628     public Path getMavenLocalRepoDir()
629     {
630         // Try property first
631         String localRepo = getProperties().getString("maven.local.repo");
632 
633         if (Utils.isBlank(localRepo))
634         {
635             // Try jetty specific env variable
636             localRepo = System.getenv("JETTY_MAVEN_LOCAL_REPO");
637         }
638 
639         if (Utils.isBlank(localRepo))
640         {
641             // Try generic env variable
642             localRepo = System.getenv("MAVEN_LOCAL_REPO");
643         }
644 
645         // TODO: load & use $HOME/.m2/settings.xml ?
646         // TODO: possibly use Eclipse Aether to manage it ?
647         // TODO: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=449511
648 
649         // Still blank? then its not specified
650         if (Utils.isBlank(localRepo))
651         {
652             return null;
653         }
654 
655         Path localRepoDir = new File(localRepo).toPath();
656         localRepoDir = localRepoDir.normalize().toAbsolutePath();
657         if (Files.exists(localRepoDir) && Files.isDirectory(localRepoDir))
658         {
659             return localRepoDir;
660         }
661 
662         StartLog.warn("Not a valid maven local repository directory: %s",localRepoDir);
663 
664         // Not a valid repository directory, skip it
665         return null;
666     }
667 
668     public String getModuleGraphFilename()
669     {
670         return moduleGraphFilename;
671     }
672 
673     public Props getProperties()
674     {
675         return properties;
676     }
677 
678     public Set<String> getSkipFileValidationModules()
679     {
680         return skipFileValidationModules;
681     }
682 
683     public List<String> getSources(String module)
684     {
685         return sources.get(module);
686     }
687 
688     public List<Path> getXmlFiles()
689     {
690         return xmls;
691     }
692 
693     public boolean hasJvmArgs()
694     {
695         return jvmArgs.size() > 0;
696     }
697 
698     public boolean hasSystemProperties()
699     {
700         for (String key : systemPropertyKeys)
701         {
702             // ignored keys
703             if ("jetty.home".equals(key) || "jetty.base".equals(key) || "main.class".equals(key))
704             {
705                 // skip
706                 continue;
707             }
708             return true;
709         }
710         return false;
711     }
712 
713     public boolean isApproveAllLicenses()
714     {
715         return approveAllLicenses;
716     }
717 
718     public boolean isDownload()
719     {
720         return download;
721     }
722 
723     public boolean isDryRun()
724     {
725         return dryRun;
726     }
727 
728     public boolean isExec()
729     {
730         return exec;
731     }
732 
733     public boolean isLicenseCheckRequired()
734     {
735         return licenseCheckRequired;
736     }
737 
738     public boolean isNormalMainClass()
739     {
740         return SERVER_MAIN.equals(getMainClassname());
741     }
742 
743     public boolean isHelp()
744     {
745         return help;
746     }
747 
748     public boolean isListClasspath()
749     {
750         return listClasspath;
751     }
752 
753     public boolean isListConfig()
754     {
755         return listConfig;
756     }
757 
758     public boolean isListModules()
759     {
760         return listModules;
761     }
762 
763     public boolean isRun()
764     {
765         return run;
766     }
767 
768     public boolean isStopCommand()
769     {
770         return stopCommand;
771     }
772 
773     public boolean isTestingModeEnabled()
774     {
775         return testingMode;
776     }
777 
778     public boolean isVersion()
779     {
780         return version;
781     }
782 
783     public void parse(ConfigSources sources)
784     {
785         ListIterator<ConfigSource> iter = sources.reverseListIterator();
786         while (iter.hasPrevious())
787         {
788             ConfigSource source = iter.previous();
789             for (RawArgs.Entry arg : source.getArgs())
790             {
791                 parse(arg.getLine(),arg.getOrigin());
792             }
793         }
794     }
795 
796     public void parse(final String rawarg, String source)
797     {
798         parse(rawarg,source,true);
799     }
800 
801     /**
802      * Parse a single line of argument.
803      *
804      * @param rawarg
805      *            the raw argument to parse
806      * @param source
807      *            the origin of this line of argument
808      * @param replaceProps
809      *            true if properties in this parse replace previous ones, false to not replace.
810      */
811     private void parse(final String rawarg, String source, boolean replaceProps)
812     {
813         if (rawarg == null)
814         {
815             return;
816         }
817 
818         StartLog.debug("parse(\"%s\", \"%s\", %b)",rawarg,source,replaceProps);
819 
820         final String arg = rawarg.trim();
821 
822         if (arg.length() <= 0)
823         {
824             return;
825         }
826 
827         if (arg.startsWith("#"))
828         {
829             return;
830         }
831 
832         if ("--help".equals(arg) || "-?".equals(arg))
833         {
834             help = true;
835             run = false;
836             return;
837         }
838 
839         if ("--debug".equals(arg) || arg.startsWith("--start-log-file"))
840         {
841             // valid, but handled in StartLog instead
842             return;
843         }
844 
845         if ("--testing-mode".equals(arg))
846         {
847             System.setProperty("org.eclipse.jetty.start.testing","true");
848             testingMode = true;
849             return;
850         }
851 
852         if (arg.startsWith("--include-jetty-dir="))
853         {
854             // valid, but handled in ConfigSources instead
855             return;
856         }
857 
858         if ("--stop".equals(arg))
859         {
860             stopCommand = true;
861             run = false;
862             return;
863         }
864 
865         if (arg.startsWith("--download="))
866         {
867             addFile(null,Props.getValue(arg));
868             run = false;
869             download = true;
870             return;
871         }
872 
873         if (arg.equals("--create-files"))
874         {
875             run = false;
876             download = true;
877             licenseCheckRequired = true;
878             return;
879         }
880 
881         if ("--list-classpath".equals(arg) || "--version".equals(arg) || "-v".equals(arg) || "--info".equals(arg))
882         {
883             listClasspath = true;
884             run = false;
885             return;
886         }
887 
888         if ("--list-config".equals(arg))
889         {
890             listConfig = true;
891             run = false;
892             return;
893         }
894 
895         if ("--dry-run".equals(arg) || "--exec-print".equals(arg))
896         {
897             dryRun = true;
898             run = false;
899             return;
900         }
901 
902         // Enable forked execution of Jetty server
903         if ("--exec".equals(arg))
904         {
905             exec = true;
906             return;
907         }
908         
909         // Assign a fixed name to the property file for exec
910         if (arg.startsWith("--exec-properties="))
911         {
912             exec_properties=Props.getValue(arg);
913             if (!exec_properties.endsWith(".properties"))
914                 throw new UsageException(ERR_BAD_ARG,"--exec-properties filename must have .properties suffix: %s",exec_properties);
915             return;
916         }
917 
918         // Enable forked execution of Jetty server
919         if ("--approve-all-licenses".equals(arg))
920         {
921             approveAllLicenses = true;
922             return;
923         }
924 
925         // Arbitrary Libraries
926         if (arg.startsWith("--lib="))
927         {
928             String cp = Props.getValue(arg);
929 
930             if (cp != null)
931             {
932                 StringTokenizer t = new StringTokenizer(cp,File.pathSeparator);
933                 while (t.hasMoreTokens())
934                 {
935                     rawLibs.add(t.nextToken());
936                 }
937             }
938             return;
939         }
940 
941         // Module Management
942         if ("--list-modules".equals(arg))
943         {
944             listModules = true;
945             run = false;
946             return;
947         }
948 
949         // jetty.base build-out : add to ${jetty.base}/start.d/
950         if (arg.startsWith("--add-to-startd="))
951         {
952             List<String> moduleNames = Props.getValues(arg);
953             addToStartdIni.addAll(moduleNames);
954             run = false;
955             download = true;
956             licenseCheckRequired = true;
957             return;
958         }
959 
960         // jetty.base build-out : add to ${jetty.base}/start.ini
961         if (arg.startsWith("--add-to-start="))
962         {
963             List<String> moduleNames = Props.getValues(arg);
964             addToStartIni.addAll(moduleNames);
965             run = false;
966             download = true;
967             licenseCheckRequired = true;
968             return;
969         }
970 
971         // Enable a module
972         if (arg.startsWith("--module="))
973         {
974             List<String> moduleNames = Props.getValues(arg);
975             enableModules(source,moduleNames);
976             return;
977         }
978 
979         // Skip [files] validation on a module
980         if (arg.startsWith("--skip-file-validation="))
981         {
982             List<String> moduleNames = Props.getValues(arg);
983             for (String moduleName : moduleNames)
984             {
985                 skipFileValidationModules.add(moduleName);
986             }
987             return;
988         }
989 
990         // Create graphviz output of module graph
991         if (arg.startsWith("--write-module-graph="))
992         {
993             this.moduleGraphFilename = Props.getValue(arg);
994             run = false;
995             return;
996         }
997 
998         // Start property (syntax similar to System property)
999         if (arg.startsWith("-D"))
1000         {
1001             String[] assign = arg.substring(2).split("=",2);
1002             systemPropertyKeys.add(assign[0]);
1003             switch (assign.length)
1004             {
1005                 case 2:
1006                     System.setProperty(assign[0],assign[1]);
1007                     setProperty(assign[0],assign[1],source,replaceProps);
1008                     break;
1009                 case 1:
1010                     System.setProperty(assign[0],"");
1011                     setProperty(assign[0],"",source,replaceProps);
1012                     break;
1013                 default:
1014                     break;
1015             }
1016             return;
1017         }
1018 
1019         // Anything else with a "-" is considered a JVM argument
1020         if (arg.startsWith("-"))
1021         {
1022             // Only add non-duplicates
1023             if (!jvmArgs.contains(arg))
1024             {
1025                 jvmArgs.add(arg);
1026             }
1027             return;
1028         }
1029 
1030         // Is this a raw property declaration?
1031         int idx = arg.indexOf('=');
1032         if (idx >= 0)
1033         {
1034             String key = arg.substring(0,idx);
1035             String value = arg.substring(idx + 1);
1036 
1037             if (replaceProps)
1038             {
1039                 if (propertySource.containsKey(key))
1040                 {
1041                     StartLog.warn("Property %s in %s already set in %s",key,source,propertySource.get(key));
1042                 }
1043                 propertySource.put(key,source);
1044             }
1045 
1046             if ("OPTION".equals(key) || "OPTIONS".equals(key))
1047             {
1048                 StringBuilder warn = new StringBuilder();
1049                 warn.append("The behavior of the argument ");
1050                 warn.append(arg).append(" (seen in ").append(source);
1051                 warn.append(") has changed, and is now considered a normal property.  ");
1052                 warn.append(key).append(" no longer controls what libraries are on your classpath,");
1053                 warn.append(" use --module instead. See --help for details.");
1054                 StartLog.warn(warn.toString());
1055             }
1056 
1057             setProperty(key,value,source,replaceProps);
1058             return;
1059         }
1060 
1061         // Is this an xml file?
1062         if (FS.isXml(arg))
1063         {
1064             // only add non-duplicates
1065             if (!xmlRefs.contains(arg))
1066             {
1067                 xmlRefs.add(arg);
1068             }
1069             return;
1070         }
1071 
1072         if (FS.isPropertyFile(arg))
1073         {
1074             // only add non-duplicates
1075             if (!propertyFileRefs.contains(arg))
1076             {
1077                 propertyFileRefs.add(arg);
1078             }
1079             return;
1080         }
1081 
1082         // Anything else is unrecognized
1083         throw new UsageException(ERR_BAD_ARG,"Unrecognized argument: \"%s\" in %s",arg,source);
1084     }
1085 
1086     private void enableModules(String source, List<String> moduleNames)
1087     {
1088         for (String moduleName : moduleNames)
1089         {
1090             modules.add(moduleName);
1091             List<String> list = sources.get(moduleName);
1092             if (list == null)
1093             {
1094                 list = new ArrayList<String>();
1095                 sources.put(moduleName,list);
1096             }
1097             list.add(source);
1098         }
1099     }
1100 
1101     public void parseModule(Module module)
1102     {
1103         if (module.hasDefaultConfig())
1104         {
1105             for (String line : module.getDefaultConfig())
1106             {
1107                 parse(line,module.getFilesystemRef(),false);
1108             }
1109         }
1110     }
1111 
1112     public void resolveExtraXmls(BaseHome baseHome) throws IOException
1113     {
1114         // Find and Expand XML files
1115         for (String xmlRef : xmlRefs)
1116         {
1117             // Straight Reference
1118             Path xmlfile = baseHome.getPath(xmlRef);
1119             if (!FS.exists(xmlfile))
1120             {
1121                 xmlfile = baseHome.getPath("etc/" + xmlRef);
1122             }
1123             addUniqueXmlFile(xmlRef,xmlfile);
1124         }
1125     }
1126 
1127     public void resolvePropertyFiles(BaseHome baseHome) throws IOException
1128     {
1129         // Find and Expand property files
1130         for (String propertyFileRef : propertyFileRefs)
1131         {
1132             // Straight Reference
1133             Path propertyFile = baseHome.getPath(propertyFileRef);
1134             if (!FS.exists(propertyFile))
1135             {
1136                 propertyFile = baseHome.getPath("etc/" + propertyFileRef);
1137             }
1138             addUniquePropertyFile(propertyFileRef,propertyFile);
1139         }
1140     }
1141 
1142     public void setAllModules(Modules allModules)
1143     {
1144         this.allModules = allModules;
1145     }
1146 
1147     public void setProperty(String key, String value, String source, boolean replaceProp)
1148     {
1149         // Special / Prevent override from start.ini's
1150         if (key.equals("jetty.home"))
1151         {
1152             properties.setProperty("jetty.home",System.getProperty("jetty.home"),source);
1153             return;
1154         }
1155 
1156         // Special / Prevent override from start.ini's
1157         if (key.equals("jetty.base"))
1158         {
1159             properties.setProperty("jetty.base",System.getProperty("jetty.base"),source);
1160             return;
1161         }
1162 
1163         if (replaceProp || (!properties.containsKey(key)))
1164         {
1165             properties.setProperty(key,value,source);
1166             if(key.equals("java.version"))
1167             {
1168                 Version ver = new Version(value);
1169 
1170                 properties.setProperty("java.version",ver.toShortString(),source);
1171                 properties.setProperty("java.version.major",Integer.toString(ver.getLegacyMajor()),source);
1172                 properties.setProperty("java.version.minor",Integer.toString(ver.getMajor()),source);
1173                 properties.setProperty("java.version.revision",Integer.toString(ver.getRevision()),source);
1174                 properties.setProperty("java.version.update",Integer.toString(ver.getUpdate()),source);
1175             }
1176         }
1177     }
1178 
1179     public void setRun(boolean run)
1180     {
1181         this.run = run;
1182     }
1183 
1184     @Override
1185     public String toString()
1186     {
1187         StringBuilder builder = new StringBuilder();
1188         builder.append("StartArgs [enabledModules=");
1189         builder.append(modules);
1190         builder.append(", xmlRefs=");
1191         builder.append(xmlRefs);
1192         builder.append(", properties=");
1193         builder.append(properties);
1194         builder.append(", jvmArgs=");
1195         builder.append(jvmArgs);
1196         builder.append("]");
1197         return builder.toString();
1198     }
1199 }