View Javadoc

1   /*
2    $Id: GroovyMain.java,v 1.12 2005/01/05 09:58:22 jez Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11   statements and notices.  Redistributions must also contain a
12   copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15   above copyright notice, this list of conditions and the
16   following disclaimer in the documentation and/or other
17   materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20   products derived from this Software without prior written
21   permission of The Codehaus.  For written permission,
22   please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25   nor may "groovy" appear in their names without prior written
26   permission of The Codehaus. "groovy" is a registered
27   trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30   http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  package groovy.ui;
47  
48  import groovy.lang.GroovyShell;
49  import groovy.lang.MetaClass;
50  import groovy.lang.Script;
51  import org.apache.commons.cli.*;
52  import org.codehaus.groovy.control.CompilationFailedException;
53  import org.codehaus.groovy.control.CompilerConfiguration;
54  import org.codehaus.groovy.runtime.InvokerHelper;
55  
56  import java.io.*;
57  import java.util.Iterator;
58  import java.util.List;
59  
60  /***
61   * A Command line to execute groovy.
62   *
63   * @author Jeremy Rayner
64   * @author Yuri Schimke
65   * @version $Revision: 1.12 $
66   */
67  public class GroovyMain {
68      // arguments to the script
69      private List args;
70  
71      // is this a file on disk
72      private boolean isScriptFile;
73  
74      // filename or content of script
75      private String script;
76  
77      // process args as input files
78      private boolean processFiles;
79  
80      // edit input files in place
81      private boolean editFiles;
82  
83      // automatically output the result of each script
84      private boolean autoOutput;
85  
86      // process sockets
87      private boolean processSockets;
88  
89      // port to listen on when processing sockets
90      private int port;
91  
92      // backup input files with extension
93      private String backupExtension;
94  
95      // do you want full stack traces in script exceptions?
96      private boolean debug = false;
97  
98      // Compiler configuration, used to set the encodings of the scripts/classes
99      private CompilerConfiguration conf = new CompilerConfiguration();
100 
101     /***
102      * Main CLI interface.
103      *
104      * @param args all command line args.
105      */
106     public static void main(String args[]) {
107         MetaClass.setUseReflection(true);
108 
109         Options options = buildOptions();
110 
111         try {
112             CommandLine cmd = parseCommandLine(options, args);
113 
114             if (cmd.hasOption('h')) {
115                 HelpFormatter formatter = new HelpFormatter();
116                 formatter.printHelp("groovy", options);
117             } else if (cmd.hasOption('v')) {
118                 String version = InvokerHelper.getVersion();
119                 System.out.println("Groovy Version: " + version + " JVM: " + System.getProperty("java.vm.version"));
120             } else {
121                 // If we fail, then exit with an error so scripting frameworks can catch it
122                 if (!process(cmd)) {
123                     System.exit(1);
124                 }
125             }
126         } catch (ParseException pe) {
127             System.out.println("error: " + pe.getMessage());
128             HelpFormatter formatter = new HelpFormatter();
129             formatter.printHelp("groovy", options);
130         }
131     }
132 
133     /***
134      * Parse the command line.
135      *
136      * @param options the options parser.
137      * @param args    the command line args.
138      * @return parsed command line.
139      * @throws ParseException if there was a problem.
140      */
141     private static CommandLine parseCommandLine(Options options, String[] args) throws ParseException {
142         CommandLineParser parser = new PosixParser();
143         CommandLine cmd = parser.parse(options, args, true);
144         return cmd;
145     }
146 
147     /***
148      * Build the options parser.  Has to be synchronized because of the way Options are constructed.
149      *
150      * @return an options parser.
151      */
152     private static synchronized Options buildOptions() {
153         Options options = new Options();
154 
155         options.addOption(OptionBuilder.hasArg(false).withDescription("usage information").withLongOpt("help").create('h'));
156 
157         options.addOption(OptionBuilder.hasArg(false).withDescription("debug mode will print out full stack traces").withLongOpt("debug").create('d'));
158 
159         options.addOption(OptionBuilder.hasArg(false).withDescription("display the Groovy and JVM versions").withLongOpt("version").create('v'));
160 
161         options.addOption(OptionBuilder.withArgName("charset").hasArg().withDescription("specify the encoding of the files").withLongOpt("encoding").create('c'));
162 
163         options.addOption(OptionBuilder.withArgName("script").hasArg().withDescription("specify a command line script").create('e'));
164 
165         options.addOption(OptionBuilder.withArgName("extension").hasOptionalArg().withDescription("modify files in place").create('i'));
166 
167         options.addOption(OptionBuilder.hasArg(false).withDescription("process files line by line").create('n'));
168 
169         options.addOption(OptionBuilder.hasArg(false).withDescription("process files line by line and print result").create('p'));
170 
171         options.addOption(OptionBuilder.withArgName("port").hasOptionalArg().withDescription("listen on a port and process inbound lines").create('l'));
172         return options;
173     }
174 
175     /***
176      * Process the users request.
177      *
178      * @param line the parsed command line.
179      * @throws ParseException if invalid options are chosen
180      */
181     private static boolean process(CommandLine line) throws ParseException {
182         GroovyMain main = new GroovyMain();
183 
184         List args = line.getArgList();
185 
186         // add the ability to parse scripts with a specified encoding
187         if (line.hasOption('c')) {
188             main.conf.setSourceEncoding(line.getOptionValue("encoding"));
189         }
190 
191         main.isScriptFile = !line.hasOption('e');
192         main.debug = line.hasOption('d');
193         main.processFiles = line.hasOption('p') || line.hasOption('n');
194         main.autoOutput = line.hasOption('p');
195         main.editFiles = line.hasOption('i');
196         if (main.editFiles) {
197             main.backupExtension = line.getOptionValue('i');
198         }
199 
200         if (main.isScriptFile) {
201             if (args.isEmpty())
202                 throw new ParseException("neither -e or filename provided");
203 
204             main.script = (String) args.remove(0);
205             if (main.script.endsWith(".java"))
206                 throw new ParseException("error: cannot compile file with .java extension: " + main.script);
207         } else {
208             main.script = line.getOptionValue('e');
209         }
210 
211         main.processSockets = line.hasOption('l');
212         if (main.processSockets) {
213             String p = line.getOptionValue('l', "1960"); // default port to listen to
214             main.port = new Integer(p).intValue();
215         }
216         main.args = args;
217 
218         return main.run();
219     }
220 
221 
222     /***
223      * Run the script.
224      */
225     private boolean run() {
226         try {
227             if (processSockets) {
228                 processSockets();
229             } else if (processFiles) {
230                 processFiles();
231             } else {
232                 processOnce();
233             }
234             return true;
235         } catch (Exception e) {
236             System.err.println("Caught: " + e);
237             if (debug) {
238                 e.printStackTrace();
239             } else {
240                 StackTraceElement[] stackTrace = e.getStackTrace();
241                 for (int i = 0; i < stackTrace.length; i++) {
242                     StackTraceElement element = stackTrace[i];
243                     if (!element.getFileName().endsWith(".java")) {
244                         System.err.println("\tat " + element);
245                     }
246                 }
247             }
248             return false;
249         }
250     }
251 
252     /***
253      * Process Sockets.
254      */
255     private void processSockets() throws CompilationFailedException, IOException {
256         GroovyShell groovy = new GroovyShell(conf);
257         //check the script is currently valid before starting a server against the script
258         if (isScriptFile) {
259             groovy.parse(new FileInputStream(script));
260         } else {
261             groovy.parse(script);
262         }
263         new GroovySocketServer(groovy, isScriptFile, script, autoOutput, port);
264     }
265 
266     /***
267      * Process the input files.
268      */
269     private void processFiles() throws CompilationFailedException, IOException {
270         GroovyShell groovy = new GroovyShell(conf);
271 
272         Script s = null;
273 
274         if (isScriptFile)
275             s = groovy.parse(new File(script));
276         else
277             s = groovy.parse(script, "main");
278 
279         if (args.isEmpty()) {
280             BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
281             PrintWriter writer = new PrintWriter(System.out);
282 
283             processReader(s, reader, writer);
284         } else {
285             Iterator i = args.iterator();
286             while (i.hasNext()) {
287                 String filename = (String) i.next();
288                 File file = new File(filename);
289                 processFile(s, file);
290             }
291         }
292     }
293 
294     /***
295      * Process a single input file.
296      *
297      * @param s    the script to execute.
298      * @param file the input file.
299      */
300     private void processFile(Script s, File file) throws IOException {
301         if (!file.exists())
302             throw new FileNotFoundException(file.getName());
303 
304         if (!editFiles) {
305             BufferedReader reader = new BufferedReader(new FileReader(file));
306             try {
307                 PrintWriter writer = new PrintWriter(System.out);
308                 processReader(s, reader, writer);
309                 writer.flush();
310             } finally {
311                 reader.close();
312             }
313         } else {
314             File backup = null;
315             if (backupExtension == null) {
316                 backup = File.createTempFile("groovy_", ".tmp");
317                 backup.deleteOnExit();
318             } else {
319                 backup = new File(file.getPath() + backupExtension);
320                 backup.delete();
321             }
322             if (!file.renameTo(backup))
323                 throw new IOException("unable to rename " + file + " to " + backup);
324 
325             BufferedReader reader = new BufferedReader(new FileReader(backup));
326             try {
327                 PrintWriter writer = new PrintWriter(new FileWriter(file));
328                 try {
329                     processReader(s, reader, writer);
330                 } finally {
331                     writer.close();
332                 }
333             } finally {
334                 reader.close();
335             }
336         }
337     }
338 
339     /***
340      * Process a script against a single input file.
341      *
342      * @param s      script to execute.
343      * @param reader input file.
344      * @param pw     output sink.
345      */
346     private void processReader(Script s, BufferedReader reader, PrintWriter pw) throws IOException {
347         String line = null;
348         s.setProperty("out", pw);
349         while ((line = reader.readLine()) != null) {
350             s.setProperty("line", line);
351             Object o = s.run();
352 
353             if (autoOutput) {
354                 pw.println(o);
355             }
356         }
357     }
358 
359     /***
360      * Process the standard, single script with args.
361      */
362     private void processOnce() throws CompilationFailedException, IOException {
363         GroovyShell groovy = new GroovyShell(conf);
364 
365         if (isScriptFile)
366             groovy.run(new File(script), args);
367         else
368             groovy.run(script, "main", args);
369     }
370 }