You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pig.apache.org by pr...@apache.org on 2010/05/06 02:05:27 UTC

svn commit: r941551 - in /hadoop/pig/trunk: CHANGES.txt src/org/apache/pig/Main.java src/org/apache/pig/tools/grunt/Grunt.java src/org/apache/pig/tools/grunt/GruntParser.java test/org/apache/pig/test/TestGrunt.java test/org/apache/pig/test/Util.java

Author: pradeepkth
Date: Thu May  6 00:05:26 2010
New Revision: 941551

URL: http://svn.apache.org/viewvc?rev=941551&view=rev
Log:
PIG-1211: Pig script runs half way after which it reports syntax error (pradeepkth)

Modified:
    hadoop/pig/trunk/CHANGES.txt
    hadoop/pig/trunk/src/org/apache/pig/Main.java
    hadoop/pig/trunk/src/org/apache/pig/tools/grunt/Grunt.java
    hadoop/pig/trunk/src/org/apache/pig/tools/grunt/GruntParser.java
    hadoop/pig/trunk/test/org/apache/pig/test/TestGrunt.java
    hadoop/pig/trunk/test/org/apache/pig/test/Util.java

Modified: hadoop/pig/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/pig/trunk/CHANGES.txt?rev=941551&r1=941550&r2=941551&view=diff
==============================================================================
--- hadoop/pig/trunk/CHANGES.txt (original)
+++ hadoop/pig/trunk/CHANGES.txt Thu May  6 00:05:26 2010
@@ -54,6 +54,9 @@ PIG-1309: Map-side Cogroup (ashutoshc)
 
 BUG FIXES
 
+PIG-1211: Pig script runs half way after which it reports syntax error
+(pradeepkth)
+
 PIG-1401: "explain -script <script file>" executes grunt commands like
 run/dump/copy etc - explain -script should not execute any grunt command and
 only explain the query plans (pradeepkth)

Modified: hadoop/pig/trunk/src/org/apache/pig/Main.java
URL: http://svn.apache.org/viewvc/hadoop/pig/trunk/src/org/apache/pig/Main.java?rev=941551&r1=941550&r2=941551&view=diff
==============================================================================
--- hadoop/pig/trunk/src/org/apache/pig/Main.java (original)
+++ hadoop/pig/trunk/src/org/apache/pig/Main.java Thu May  6 00:05:26 2010
@@ -80,7 +80,9 @@ public class Main
     private static final String JAR = "jar";
     private static final String VERBOSE = "verbose";
     
-    private enum ExecMode {STRING, FILE, SHELL, UNKNOWN};
+    private enum ExecMode {STRING, FILE, SHELL, UNKNOWN}
+
+    private static boolean checkScriptOnly = false;
                 
 /**
  * The Main-Class for the Pig Jar that will provide a shell and setup a classpath appropriate
@@ -114,7 +116,7 @@ public static void main(String args[])
         CmdLineParser opts = new CmdLineParser(args);
         opts.registerOpt('4', "log4jconf", CmdLineParser.ValueExpected.REQUIRED);
         opts.registerOpt('b', "brief", CmdLineParser.ValueExpected.NOT_ACCEPTED);
-        opts.registerOpt('c', "cluster", CmdLineParser.ValueExpected.REQUIRED);
+        opts.registerOpt('c', "check", CmdLineParser.ValueExpected.NOT_ACCEPTED);
         opts.registerOpt('d', "debug", CmdLineParser.ValueExpected.REQUIRED);
         opts.registerOpt('e', "execute", CmdLineParser.ValueExpected.NOT_ACCEPTED);
         opts.registerOpt('f', "file", CmdLineParser.ValueExpected.REQUIRED);
@@ -139,11 +141,6 @@ public static void main(String args[])
         if(execTypeString!=null && execTypeString.length()>0){
             execType = PigServer.parseExecType(execTypeString);
         }
-        String cluster = "local";
-        String clusterConfigured = properties.getProperty("cluster");
-        if(clusterConfigured != null && clusterConfigured.length() > 0){
-            cluster = clusterConfigured;
-        }
         
         //by default warning aggregation is on
         properties.setProperty("aggregate.warning", ""+true);
@@ -172,12 +169,7 @@ public static void main(String args[])
                 break;
 
             case 'c': 
-                // Needed away to specify the cluster to run the MR job on
-                // Bug 831708 - fixed
-                String clusterParameter = opts.getValStr();
-                if (clusterParameter != null && clusterParameter.length() > 0) {
-                    cluster = clusterParameter;
-                }
+                checkScriptOnly = true;
                 break;
 
             case 'd':
@@ -314,7 +306,7 @@ public static void main(String args[])
 
             // run parameter substitution preprocessor first
             substFile = file + ".substituted";
-            pin = runParamPreprocessor(in, params, paramFiles, substFile, debug || dryrun);
+            pin = runParamPreprocessor(in, params, paramFiles, substFile, debug || dryrun || checkScriptOnly);
             if (dryrun) {
                 log.info("Dry run completed. Substituted pig script is at " + substFile);
                 return;
@@ -334,12 +326,27 @@ public static void main(String args[])
             
             grunt = new Grunt(pin, pigContext);
             gruntCalled = true;
-            int results[] = grunt.exec();
-            rc = getReturnCodeForStats(results);
+            
+            if(checkScriptOnly) {
+                grunt.checkScript(substFile);
+                System.err.println(file + " syntax OK");
+                rc = 0;
+            } else {
+                int results[] = grunt.exec();
+                rc = getReturnCodeForStats(results);
+            }
+            
             return;
         }
 
         case STRING: {
+            if(checkScriptOnly) {
+                System.err.println("ERROR:" +
+                        "-c (-check) option is only valid " +
+                        "when executing pig with a pig script file)");
+                rc = 2; // failure
+                return;
+            }
             // Gather up all the remaining arguments into a string and pass them into
             // grunt.
             StringBuffer sb = new StringBuffer();
@@ -366,6 +373,13 @@ public static void main(String args[])
         // run grunt interactive.
         String remainders[] = opts.getRemainingArgs();
         if (remainders == null) {
+            if(checkScriptOnly) {
+                System.err.println("ERROR:" +
+                        "-c (-check) option is only valid " +
+                        "when executing pig with a pig script file)");
+                rc = 2; // failure
+                return;
+            }
             // Interactive
             mode = ExecMode.SHELL;
             ConsoleReader reader = new ConsoleReader(System.in, new OutputStreamWriter(System.out));
@@ -383,15 +397,14 @@ public static void main(String args[])
         } else {
             // They have a pig script they want us to run.
             if (remainders.length > 1) {
-                   throw new RuntimeException("You can only run one pig script "
-                    + "at a time from the command line.");
+                   throw new RuntimeException("Encountered unexpected arguments on command line - please check the command line.");
             }
             mode = ExecMode.FILE;
             in = new BufferedReader(new FileReader(remainders[0]));
 
             // run parameter substitution preprocessor first
             substFile = remainders[0] + ".substituted";
-            pin = runParamPreprocessor(in, params, paramFiles, substFile, debug || dryrun);
+            pin = runParamPreprocessor(in, params, paramFiles, substFile, debug || dryrun || checkScriptOnly);
             if (dryrun){
                 log.info("Dry run completed. Substituted pig script is at " + substFile);
                 return;
@@ -411,8 +424,15 @@ public static void main(String args[])
 
             grunt = new Grunt(pin, pigContext);
             gruntCalled = true;
-            int[] results = grunt.exec();
-            rc = getReturnCodeForStats(results);
+            
+            if(checkScriptOnly) {
+                grunt.checkScript(substFile);
+                System.err.println(remainders[0] + " syntax OK");
+                rc = 0;
+            } else {
+                int results[] = grunt.exec();
+                rc = getReturnCodeForStats(results);
+            }
             return;
         }
 

Modified: hadoop/pig/trunk/src/org/apache/pig/tools/grunt/Grunt.java
URL: http://svn.apache.org/viewvc/hadoop/pig/trunk/src/org/apache/pig/tools/grunt/Grunt.java?rev=941551&r1=941550&r2=941551&view=diff
==============================================================================
--- hadoop/pig/trunk/src/org/apache/pig/tools/grunt/Grunt.java (original)
+++ hadoop/pig/trunk/src/org/apache/pig/tools/grunt/Grunt.java Thu May  6 00:05:26 2010
@@ -23,6 +23,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.io.IOException;
 import java.io.FileOutputStream;
+import java.util.ArrayList;
 
 import jline.ConsoleReader;
 import jline.Completor;
@@ -93,5 +94,20 @@ public class Grunt 
             throw (t);
         }
     }
+    
+    public void checkScript(String scriptFile) throws Throwable {
+        boolean verbose = "true".equalsIgnoreCase(pig.getPigContext().getProperties().getProperty("verbose"));
+        try {
+            parser.setInteractive(false);
+            boolean dontPrintOutput = true;
+            parser.processExplain(null, scriptFile, false, "text", null, 
+                    new ArrayList<String>(), new ArrayList<String>(),
+                    dontPrintOutput);
+        } catch (Throwable t) {
+            LogUtils.writeLog(t, pig.getPigContext().getProperties().getProperty("pig.logfile"), 
+                    log, verbose, "Pig Stack Trace");
+            throw (t);
+        }
+    }
 
 }

Modified: hadoop/pig/trunk/src/org/apache/pig/tools/grunt/GruntParser.java
URL: http://svn.apache.org/viewvc/hadoop/pig/trunk/src/org/apache/pig/tools/grunt/GruntParser.java?rev=941551&r1=941550&r2=941551&view=diff
==============================================================================
--- hadoop/pig/trunk/src/org/apache/pig/tools/grunt/GruntParser.java (original)
+++ hadoop/pig/trunk/src/org/apache/pig/tools/grunt/GruntParser.java Thu May  6 00:05:26 2010
@@ -31,6 +31,7 @@ import java.io.PrintStream;
 import java.io.Reader;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
@@ -230,7 +231,7 @@ public class GruntParser extends PigScri
             }
             mPigServer.dumpSchema(alias);
         } else {
-            log.warn("'describe' statement is ignored while processing explain");
+            log.warn("'describe' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
 
@@ -238,6 +239,15 @@ public class GruntParser extends PigScri
     protected void processExplain(String alias, String script, boolean isVerbose, 
                                   String format, String target, 
                                   List<String> params, List<String> files) 
+    throws IOException, ParseException {
+        processExplain(alias, script, isVerbose, format, target, params, files, 
+                false);
+    }
+
+    protected void processExplain(String alias, String script, boolean isVerbose, 
+                                  String format, String target, 
+                                  List<String> params, List<String> files,
+                                  boolean dontPrintOutput) 
         throws IOException, ParseException {
         
         if (null != mExplain) {
@@ -267,7 +277,7 @@ public class GruntParser extends PigScri
             }
 
             mExplain.mLast = true;
-            explainCurrentBatch();
+            explainCurrentBatch(dontPrintOutput);
 
         } finally {
             if (script != null) {
@@ -278,9 +288,30 @@ public class GruntParser extends PigScri
     }
 
     protected void explainCurrentBatch() throws IOException {
-        PrintStream lp = System.out;
-        PrintStream pp = System.out;
-        PrintStream ep = System.out;
+        explainCurrentBatch(false);
+    }
+
+    /**
+     * A {@link PrintStream} implementation which does not write anything 
+     * Used with '-check' command line option to pig Main 
+     * (through {@link GruntParser#explainCurrentBatch(boolean) } )
+     */
+    static class NullPrintStream extends PrintStream {
+        public NullPrintStream(String fileName) throws FileNotFoundException {
+            super(fileName);
+        }
+        @Override
+        public void write(byte[] buf, int off, int len) {}
+        @Override
+        public void write(int b) {}
+        @Override
+        public void write(byte [] b) {}
+    }
+    
+    protected void explainCurrentBatch(boolean dontPrintOutput) throws IOException {
+        PrintStream lp = (dontPrintOutput) ? new NullPrintStream("dummy") : System.out;
+        PrintStream pp = (dontPrintOutput) ? new NullPrintStream("dummy") : System.out;
+        PrintStream ep = (dontPrintOutput) ? new NullPrintStream("dummy") : System.out;
         
         if (!(mExplain.mLast && mExplain.mCount == 0)) {
             if (mPigServer.isBatchEmpty()) {
@@ -324,7 +355,7 @@ public class GruntParser extends PigScri
         if(mExplain == null) { // process only if not in "explain" mode
             mPigServer.printAliases();
         } else {
-            log.warn("'aliases' statement is ignored while processing explain");
+            log.warn("'aliases' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
     
@@ -376,7 +407,7 @@ public class GruntParser extends PigScri
                 loadScript(script, false, mLoadOnly, params, files);
             }
         } else {
-            log.warn("'run/exec' statement is ignored while processing explain");
+            log.warn("'run/exec' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
 
@@ -519,7 +550,7 @@ public class GruntParser extends PigScri
                 throw new IOException("Failed to Cat: " + path, e);
             }
         } else {
-            log.warn("'cat' statement is ignored while processing explain");
+            log.warn("'cat' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
 
@@ -554,7 +585,7 @@ public class GruntParser extends PigScri
                                                          : (path)), e);
             }
         } else {
-            log.warn("'cd' statement is ignored while processing explain");
+            log.warn("'cd' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
 
@@ -569,7 +600,7 @@ public class GruntParser extends PigScri
                 System.out.println(TupleFormat.format(t));
             }
         } else {
-            log.warn("'dump' statement is ignored while processing explain");
+            log.warn("'dump' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
     
@@ -579,7 +610,7 @@ public class GruntParser extends PigScri
         if(mExplain == null) { // process only if not in "explain" mode
             mPigServer.getExamples(alias);
         } else {
-            log.warn("'illustrate' statement is ignored while processing explain");
+            log.warn("'illustrate' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
 
@@ -639,7 +670,7 @@ public class GruntParser extends PigScri
                 throw new IOException("Failed to LS on " + path, e);
             }
         } else {
-            log.warn("'ls' statement is ignored while processing explain");
+            log.warn("'ls' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
 
@@ -660,7 +691,7 @@ public class GruntParser extends PigScri
         if(mExplain == null) { // process only if not in "explain" mode
             System.out.println(mDfs.getActiveContainer().toString());
         } else {
-            log.warn("'pwd' statement is ignored while processing explain");
+            log.warn("'pwd' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
 
@@ -704,7 +735,7 @@ public class GruntParser extends PigScri
                 throw new IOException("Failed to move " + src + " to " + dst, e);
             }
         } else {
-            log.warn("'mv' statement is ignored while processing explain");
+            log.warn("'mv' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
     
@@ -725,7 +756,7 @@ public class GruntParser extends PigScri
                 throw new IOException("Failed to copy " + src + " to " + dst, e);
             }
         } else {
-            log.warn("'cp' statement is ignored while processing explain");
+            log.warn("'cp' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
     
@@ -746,7 +777,7 @@ public class GruntParser extends PigScri
                 throw new IOException("Failed to copy " + src + "to (locally) " + dst, e);
             }
         } else {
-            log.warn("'copyToLocal' statement is ignored while processing explain");
+            log.warn("'copyToLocal' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
 
@@ -767,7 +798,7 @@ public class GruntParser extends PigScri
                 throw new IOException("Failed to copy (loally) " + src + "to " + dst, e);
             }
         } else {
-            log.warn("'copyFromLocal' statement is ignored while processing explain");
+            log.warn("'copyFromLocal' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
     
@@ -778,7 +809,7 @@ public class GruntParser extends PigScri
             ContainerDescriptor dirDescriptor = mDfs.asContainer(dir);
             dirDescriptor.create();
         } else {
-            log.warn("'mkdir' statement is ignored while processing explain");
+            log.warn("'mkdir' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
     
@@ -816,7 +847,7 @@ public class GruntParser extends PigScri
                 dfsPath.delete();
             }
         } else {
-            log.warn("'rm/rmf' statement is ignored while processing explain");
+            log.warn("'rm/rmf' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
 
@@ -832,7 +863,7 @@ public class GruntParser extends PigScri
                 throw new IOException(e);
             }
         } else {
-            log.warn("'fs' statement is ignored while processing explain");
+            log.warn("'fs' statement is ignored while processing 'explain -script' or '-check'");
         }
     }
     

Modified: hadoop/pig/trunk/test/org/apache/pig/test/TestGrunt.java
URL: http://svn.apache.org/viewvc/hadoop/pig/trunk/test/org/apache/pig/test/TestGrunt.java?rev=941551&r1=941550&r2=941551&view=diff
==============================================================================
--- hadoop/pig/trunk/test/org/apache/pig/test/TestGrunt.java (original)
+++ hadoop/pig/trunk/test/org/apache/pig/test/TestGrunt.java Thu May  6 00:05:26 2010
@@ -29,6 +29,7 @@ import org.apache.pig.PigException;
 import org.apache.pig.PigServer;
 import org.apache.pig.backend.executionengine.ExecException;
 import org.apache.pig.impl.PigContext;
+import org.apache.pig.test.Util.ProcessReturnInfo;
 import org.apache.pig.tools.grunt.Grunt;
 import org.apache.pig.tools.parameters.ParameterSubstitutionPreprocessor;
 import org.apache.pig.tools.pigscript.parser.ParseException;
@@ -41,6 +42,7 @@ import java.io.InputStreamReader;
 import java.io.BufferedReader;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.util.ArrayList;
 
 public class TestGrunt extends TestCase {
     MiniCluster cluster = MiniCluster.buildCluster();
@@ -494,7 +496,8 @@ public class TestGrunt extends TestCase 
                     "'mkdir'", "'illustrate'", "'run/exec'", "'fs'", "'aliases'",
                     "'mv'", "'dump'" };
             for (String c : cmds) {
-                String expected = c + " statement is ignored while processing explain";
+                String expected = c + " statement is ignored while processing " +
+                		"'explain -script' or '-check'";
                 assertTrue("Checking if " + gruntLoggingContents + " contains " + 
                         expected, gruntLoggingContents.contains(expected));
             }
@@ -934,4 +937,90 @@ public class TestGrunt extends TestCase 
             assertTrue(e.getMessage().contains("Encountered: \"^\" (94), after : \"\\\"\""));
         }
     }
+    
+    public void testCheckScript() throws Throwable {
+        // a query which has grunt commands intermixed with pig statements - this
+        // should pass through successfully with the check and all the grunt commands
+        // should be ignored during the check.
+        String query = "rmf input-copy.txt; cat 'foo'; a = load '1.txt' ; " +
+        		"aliases;illustrate a; copyFromLocal foo bar; copyToLocal foo bar; " +
+        		"describe a; mkdir foo; run bar.pig; exec bar.pig; cp foo bar; " +
+        		"explain a;cd 'bar'; pwd; ls ; fs -ls ; fs -rmr foo; mv foo bar; " +
+        		"dump a;store a into 'input-copy.txt' ; a = load '2.txt' as (b);" +
+        		"explain a; rm foo; store a into 'bar';";
+        
+        String[] cmds = new String[] { "'rm/rmf'", "'cp'", "'cat'", "'cd'", "'pwd'", 
+                "'copyFromLocal'", "'copyToLocal'", "'describe'", "'ls'", 
+                "'mkdir'", "'illustrate'", "'run/exec'", "'fs'", "'aliases'",
+                "'mv'", "'dump'" };
+        ArrayList<String> msgs = new ArrayList<String>();
+        for (String c : cmds) {
+            msgs.add(c + " statement is ignored while processing " +
+            		"'explain -script' or '-check'");
+        }
+        validate(query, true, msgs.toArray(new String[0]));
+    }
+    
+    public void testCheckScriptSyntaxErr() throws Throwable {
+        // a query which has grunt commands intermixed with pig statements - this
+        // should fail with the -check option with a syntax error
+        
+        // the query has a typo - chararay instead of chararray
+        String query = "a = load '1.txt' ;  fs -rmr foo; mv foo bar; dump a;" +
+        		"store a into 'input-copy.txt' ; dump a; a = load '2.txt' as " +
+        		"(b:chararay);explain a; rm foo; store a into 'bar';";
+        
+        String[] cmds = new String[] { "'fs'", "'mv'", "'dump'" };
+        ArrayList<String> msgs = new ArrayList<String>();
+        for (String c : cmds) {
+            msgs.add(c + " statement is ignored while processing " +
+                    "'explain -script' or '-check'");
+        }
+        msgs.add("Error during parsing");
+        validate(query, false, msgs.toArray(new String[0]));
+    }
+    
+    public void testCheckScriptTypeCheckErr() throws Throwable {
+        // a query which has grunt commands intermixed with pig statements - this
+        // should fail with the -check option with a type checking error
+        
+        // the query has incompatible types in bincond
+        String query = "a = load 'foo.pig' as (s:chararray); dump a; explain a; " +
+        		"store a into 'foobar'; b = foreach a generate " +
+        		"(s == 2 ? 1 : 2.0); store b into 'bar';";
+
+        String[] cmds = new String[] { "'dump'" };
+        ArrayList<String> msgs = new ArrayList<String>();
+        for (String c : cmds) {
+            msgs.add(c + " statement is ignored while processing " +
+                    "'explain -script' or '-check'");
+        }
+        msgs.add("In alias b, incompatible types in EqualTo Operator");
+        validate(query, false, msgs.toArray(new String[0]));
+    }
+    
+    private void validate(String query, boolean syntaxOk, 
+            String[] logMessagesToCheck) throws Throwable {
+        File scriptFile = Util.createFile(new String[] { query});
+        String scriptFileName = scriptFile.getAbsolutePath();
+        String cmd = "java -cp " + System.getProperty("java.class.path") + 
+        " org.apache.pig.Main -x local -c " + scriptFileName;
+            
+        ProcessReturnInfo  pri  = Util.executeJavaCommandAndReturnInfo(cmd);
+        for (String msg : logMessagesToCheck) {
+            assertTrue("Checking if " + pri.stderrContents + " contains " + 
+                    msg, pri.stderrContents.contains(msg));
+        }
+        if(syntaxOk) {
+            assertTrue("Checking that the syntax OK message was printed on " +
+            		"stderr <" + pri.stderrContents + ">",
+                    pri.stderrContents.contains("syntax OK"));
+        } else {
+            assertFalse("Checking that the syntax OK message was NOT printed on " +
+                    "stderr <" + pri.stderrContents + ">",
+                    pri.stderrContents.contains("syntax OK"));
+        }
+    }
+    
+    
 }

Modified: hadoop/pig/trunk/test/org/apache/pig/test/Util.java
URL: http://svn.apache.org/viewvc/hadoop/pig/trunk/test/org/apache/pig/test/Util.java?rev=941551&r1=941550&r2=941551&view=diff
==============================================================================
--- hadoop/pig/trunk/test/org/apache/pig/test/Util.java (original)
+++ hadoop/pig/trunk/test/org/apache/pig/test/Util.java Thu May  6 00:05:26 2010
@@ -27,6 +27,7 @@ import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
@@ -483,17 +484,49 @@ public class Util {
     }
     
     public static int executeJavaCommand(String cmd) throws Exception {
+        return executeJavaCommandAndReturnInfo(cmd).exitCode;
+    }
+    
+    
+    public static ProcessReturnInfo executeJavaCommandAndReturnInfo(String cmd) 
+    throws Exception {
         String javaHome = System.getenv("JAVA_HOME");
         if(javaHome != null) {
             String fileSeparator = System.getProperty("file.separator");
             cmd = javaHome + fileSeparator + "bin" + fileSeparator + cmd;
         }
         Process cmdProc = Runtime.getRuntime().exec(cmd);
-        
+        ProcessReturnInfo pri = new ProcessReturnInfo();
+        pri.stdoutContents = getContents(cmdProc.getInputStream());
+        pri.stderrContents = getContents(cmdProc.getErrorStream());
         cmdProc.waitFor();
+        pri.exitCode = cmdProc.exitValue();
+        return pri;
+    }
+    
+    private static String getContents(InputStream istr) throws IOException {
+        BufferedReader br = new BufferedReader(
+                new InputStreamReader(istr));
+        String s = "";
+        String line;
+        while ( (line = br.readLine()) != null) {
+            s += line + "\n";
+        }
+        return s;
         
-        return cmdProc.exitValue();
     }
+    public static class ProcessReturnInfo {
+        public int exitCode;
+        public String stderrContents;
+        public String stdoutContents;
+        
+        @Override
+        public String toString() {
+            return "[Exit code: " + exitCode + ", stdout: <" + stdoutContents + ">, " +
+            		"stderr: <" + stderrContents + ">"; 
+        }
+    }
+    
     static public boolean deleteDirectory(File path) {
         if(path.exists()) {
             File[] files = path.listFiles();