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 }