View Javadoc

1   /*
2    * Copyright 2002,2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.commons.jelly.util;
18  
19  import java.io.File;
20  import java.io.FileWriter;
21  import java.net.URL;
22  import java.util.Arrays;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Properties;
26  
27  import org.apache.commons.cli.CommandLine;
28  import org.apache.commons.cli.Options;
29  import org.apache.commons.cli.ParseException;
30  import org.apache.commons.cli.Parser;
31  import org.apache.commons.cli.HelpFormatter;
32  import org.apache.commons.jelly.Jelly;
33  import org.apache.commons.jelly.JellyContext;
34  import org.apache.commons.jelly.JellyException;
35  import org.apache.commons.jelly.Script;
36  import org.apache.commons.jelly.XMLOutput;
37  
38  /***
39   * Utility class to parse command line options using CLI.
40   * Using a separate class allows us to run Jelly without
41   * CLI in the classpath when the command line interface
42   * is not in use.
43   *
44   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
45   * @author Morgan Delagrange
46   * @version $Revision: 155420 $
47   */
48  public class CommandLineParser {
49  
50      protected static CommandLineParser _instance = new CommandLineParser();
51      
52      private Options cmdLineOptions = null;
53  
54      public static CommandLineParser getInstance() {
55          return _instance;
56      }
57  
58      /***
59       * Parse out the command line options and configure
60       * the give Jelly instance.
61       *
62       * @param args   options from the command line
63       * @exception JellyException
64       *                   if the command line could not be parsed
65       */
66      public void invokeCommandLineJelly(String[] args) throws JellyException {
67          CommandLine cmdLine = null;
68          try {
69              cmdLine = parseCommandLineOptions(args);
70          } catch (ParseException e) {
71              throw new JellyException(e);
72          }
73          
74          // check for -h or -v
75          if (cmdLine.hasOption("h")) {
76              new HelpFormatter().printHelp("jelly [scriptFile] [-script scriptFile] [-o outputFile] [-Dsysprop=syspropval] [-awt]",
77                  cmdLineOptions);
78              System.exit(1);
79          }
80          if (cmdLine.hasOption("v")) {
81              System.err.println("Jelly " + Jelly.getJellyVersion());
82              System.err.println(" compiled: " + Jelly.getJellyBuildDate());
83              System.err.println("");
84              System.exit(1);
85          }
86  
87          // get the -script option. If there isn't one then use args[0]
88          String scriptFile = null;
89          if (cmdLine.hasOption("script")) {
90              scriptFile = cmdLine.getOptionValue("script");
91          } else {
92              scriptFile = args[0];
93          }
94          
95          // check the -awt option.
96          boolean runInSwingThread = cmdLine.hasOption("awt") || cmdLine.hasOption("swing");
97  
98          //
99          // Use classloader to find file
100         //
101         URL url = ClassLoaderUtils.getClassLoader(getClass()).getResource(scriptFile);
102         // check if the script file exists
103         if (url == null && !(new File(scriptFile)).exists()) {
104             throw new JellyException("Script file " + scriptFile + " not found");
105         }
106 
107         try {
108             // extract the -o option for the output file to use
109             final XMLOutput output = cmdLine.hasOption("o") ?
110                     XMLOutput.createXMLOutput(new FileWriter(cmdLine.getOptionValue("o"))) :
111                     XMLOutput.createXMLOutput(System.out);
112 
113             Jelly jelly = new Jelly();
114             jelly.setScript(scriptFile);
115 
116             final Script script = jelly.compileScript();
117 
118             // add the system properties and the command line arguments
119             final JellyContext context = jelly.getJellyContext();
120             context.setVariable("args", args);
121             context.setVariable("commandLine", cmdLine);
122             if (runInSwingThread) {
123                 javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() {
124                     try {
125                         script.run(context, output);
126                     } catch (Exception ex) {
127                         ex.printStackTrace();
128                     }
129             } } ); } else {
130                 script.run(context, output);
131             }
132 
133             // now lets wait for all threads to close
134             Runtime.getRuntime().addShutdownHook(new Thread() {
135                     public void run() {
136                         try {
137                             output.close();
138                         }
139                         catch (Exception e) {
140                             // ignore errors
141                         }
142                     }
143                 }
144             );
145 
146         } catch (Exception e) {
147             throw new JellyException(e);
148         }
149 
150     }
151 
152     /***
153      * Parse the command line using CLI. -o and -script are reserved for Jelly.
154      * -Dsysprop=sysval is support on the command line as well.
155      */
156     public CommandLine parseCommandLineOptions(String[] args) throws ParseException {
157         // create the expected options
158         cmdLineOptions = new Options();
159         cmdLineOptions.addOption("o", true, "Output file");
160         cmdLineOptions.addOption("script", true, "Jelly script to run");
161         cmdLineOptions.addOption("h","help", false, "Give this help message");
162         cmdLineOptions.addOption("v","version", false, "prints Jelly's version and exits");
163         cmdLineOptions.addOption("script", true, "Jelly script to run");
164         cmdLineOptions.addOption("awt", false, "Wether to run in the AWT thread.");
165         cmdLineOptions.addOption("swing", false, "Synonym of \"-awt\".");
166         List builtinOptionNames = Arrays.asList(new String[]{
167             "-o","-script","-h","--help","-v","--version","-awt","-swing"});
168         
169         // -D options will be added to the system properties
170         Properties sysProps = System.getProperties();
171 
172         // filter the system property setting from the arg list
173         // before passing it to the CLI parser
174         ArrayList filteredArgList = new ArrayList();
175 
176         for (int i=0;i<args.length;i++) {
177             String arg = args[i];
178 
179             // if this is a -D property parse it and add it to the system properties.
180             // -D args will not be copied into the filteredArgList.
181             if (arg.startsWith("-D") && (arg.length() > 2)) {
182                 arg = arg.substring(2);
183                 int ePos = arg.indexOf("=");
184                 if(ePos==-1 || ePos==0 || ePos==arg.length()-1)
185                     System.err.println("Invalid system property: \"" + arg + "\".");
186                 sysProps.setProperty(arg.substring(0,ePos), arg.substring(ePos+1));
187             } else {
188                 // add this to the filtered list of arguments
189                 filteredArgList.add(arg);
190 
191                 // add additional "-?" options to the options object. if this is not done
192                 // the only options allowed would be the builtin-ones.
193                 if (arg.startsWith("-") && arg.length() > 1) {
194                     if (!(builtinOptionNames.contains(arg))) {
195                         cmdLineOptions.addOption(arg.substring(1, arg.length()), true, "dynamic option");
196                     }
197                 }
198             }
199         }
200 
201         // make the filteredArgList into an array
202         String[] filterArgs = new String[filteredArgList.size()];
203         filteredArgList.toArray(filterArgs);
204 
205         // parse the command line
206         Parser parser = new org.apache.commons.cli.GnuParser();
207         return parser.parse(cmdLineOptions, filterArgs);
208     }
209 
210 }