View Javadoc

1   /*
2       Jameleon - An automation testing tool..
3       Copyright (C) 2003 Christian W. Hargraves (engrean@hotmail.com)
4       
5       This library is free software; you can redistribute it and/or
6       modify it under the terms of the GNU Lesser General Public
7       License as published by the Free Software Foundation; either
8       version 2.1 of the License, or (at your option) any later version.
9   
10      This library is distributed in the hope that it will be useful,
11      but WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13      Lesser General Public License for more details.
14  
15      You should have received a copy of the GNU Lesser General Public
16      License along with this library; if not, write to the Free Software
17      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */
19  package net.sf.jameleon.ant;
20  
21  import java.util.LinkedList;
22  
23  import org.apache.tools.ant.BuildException;
24  import org.apache.tools.ant.DirectoryScanner;
25  import org.apache.tools.ant.ExitException;
26  import org.apache.tools.ant.Project;
27  import org.apache.tools.ant.Task;
28  import org.apache.tools.ant.taskdefs.Execute;
29  import org.apache.tools.ant.taskdefs.ExecuteJava;
30  import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
31  import org.apache.tools.ant.taskdefs.PumpStreamHandler;
32  import org.apache.tools.ant.taskdefs.LogStreamHandler;
33  import org.apache.tools.ant.types.FileSet;
34  import org.apache.tools.ant.types.Commandline;
35  import org.apache.tools.ant.types.Path;
36  import org.apache.tools.ant.types.CommandlineJava;
37  import org.apache.tools.ant.types.Reference;
38  import org.apache.tools.ant.types.Environment;
39  
40  import java.io.File;
41  import java.io.PrintStream;
42  import java.io.FileOutputStream;
43  import java.io.IOException;
44  
45  import java.util.Vector;
46  
47  
48  public class ExecuteTestCaseSeparateVMTask extends Task {
49  
50      private CommandlineJava cmdl = new CommandlineJava();
51      private Environment env = new Environment();
52      private boolean fork = true;
53      private boolean newEnvironment = false;
54      private File dir = null;
55      private File out;
56      private PrintStream outStream = null;
57      private boolean failOnError = false;
58      private boolean separateVmPerScript = false;
59      private boolean append = false;
60      private boolean printFooter = true;
61      private Long timeout = null;
62      
63      protected final LinkedList fileSets = new LinkedList();
64      protected static final String LB = new String("\n##############################################################\n");
65      protected static final String DASH = new String("\n-------------------------------------------------------------\n");
66      protected static final String US = new String("\n_______________________________________________________________\n");
67  
68      /***
69       * Jameleon's implementation of Task.execute().
70       *
71       * @exception BuildException  If anything goes wrong.
72       */
73      public final void execute() throws BuildException {
74          validateOptions();
75          cmdl.setClassname("net.sf.jameleon.ExecuteTestCase");
76          if (dir == null || !dir.exists() || !dir.isDirectory()) {
77              dir = getProject().getBaseDir();
78          }
79          if (!printFooter) {
80              cmdl.createArgument().setLine("false");
81          }
82          for ( int i = 0; i < fileSets.size(); i++ ) {
83              FileSet fs = ( FileSet ) fileSets.get( i );
84  
85              DirectoryScanner ds = fs.getDirectoryScanner( getProject() );
86              String[] files = ds.getIncludedFiles();
87              for (int j = 0; j < files.length; j++) {
88                  cmdl.createArgument().setLine(ds.getBasedir().getPath()+File.separator+files[j]);
89                  if (separateVmPerScript) {
90                      runScript();
91                      if (!printFooter) {
92                          cmdl.createArgument().setLine("false");
93                      }
94                  }
95              }
96          }
97          if (!separateVmPerScript) {
98              runScript();
99          }
100     }
101 
102     private void runScript(){
103         int err = -1;
104         try {
105             if ((err = executeJava()) != 0) { 
106                 if (failOnError) {
107                     throw new BuildException("Java returned: " + err, getLocation());
108                 } else {
109                     log("Java Result: " + err, Project.MSG_ERR);
110                 }
111             }
112         } finally {
113             cmdl.clearJavaArgs();
114         }
115     }
116 
117     public void setPrintFooter(boolean printFooter){
118         this.printFooter = printFooter;
119     }
120 
121     public void setSeparateVmPerScript(boolean separateVmPerScript){
122         this.separateVmPerScript = separateVmPerScript;
123     }
124 
125     public void setDebug(boolean debug){
126     }
127 
128     public void setBaseDir(File dir){
129         this.dir = dir;
130     }
131 
132     public void setFork(boolean fork){
133         this.fork = fork;
134     }
135     /***
136      * Ant's &lt;fileset&gt; definition. To define the files to parse.
137      *
138      * @param set  a fileset to add
139      */
140     public void addFileset( FileSet set ) {
141         fileSets.add( set );
142     }
143 
144     /***
145      * Validate required options are set.
146      *
147      * @exception BuildException  if a fileset isn't set.
148      */
149     protected void validateOptions() throws BuildException {          
150         if ( fileSets.size() == 0 ) {
151             throw new BuildException( "At least one fileset must be specified", getLocation() );
152         }
153     }
154 
155     /***
156      * Do the execution and return a return code.
157      *
158      * @return the return code from the execute java class if it was
159      * executed in a separate VM (fork = "yes").
160      */
161     public int executeJava() throws BuildException {
162         String classname = cmdl.getClassname();
163         if (classname == null && cmdl.getJar() == null) {
164             throw new BuildException("Classname must not be null.");
165         }
166 
167         if (!fork && cmdl.getJar() != null){
168             throw new BuildException("Cannot execute a jar in non-forked mode."
169                                      + " Please set fork='true'. ");
170         }
171 
172         if (fork) {
173             log(cmdl.describeCommand(), Project.MSG_VERBOSE);
174         } else {
175             if (cmdl.getVmCommand().size() > 1) {
176                 log("JVM args ignored when same JVM is used.", 
177                     Project.MSG_WARN);
178             }
179             if (dir != null) {
180                 log("Working directory ignored when same JVM is used.", 
181                     Project.MSG_WARN);
182             }
183 
184             if (newEnvironment || null != env.getVariables()) {
185                 log("Changes to environment variables are ignored when same "
186                     + "JVM is used.", Project.MSG_WARN);
187             }
188 
189             log("Running in same VM " + cmdl.describeJavaCommand(), 
190                 Project.MSG_VERBOSE);
191         }
192         
193         try {
194             if (fork) {
195                 return run(cmdl.getCommandline());
196             } else {
197                 try {
198                     run(cmdl);
199                     return 0;
200                 } catch (ExitException ex) {
201                     return ex.getStatus();
202                 }
203             }
204         } catch (BuildException e) {
205             if (failOnError) {
206                 throw e;
207             } else {
208                 log(e.getMessage(), Project.MSG_ERR);
209                 return 0;
210             }
211         } catch (Throwable t) {
212             if (failOnError) {
213                 throw new BuildException(t);
214             } else {
215                 log(t.getMessage(), Project.MSG_ERR);
216                 return 0;
217             }
218         }
219     }
220 
221     /***
222      * Set the classpath to be used when running the Java class
223      * 
224      * @param s an Ant Path object containing the classpath.
225      */
226     public void setClasspath(Path s) {
227         createClasspath().append(s);
228     }
229     
230     /***
231      * Adds a path to the classpath.
232      */
233     public Path createClasspath() {
234         return cmdl.createClasspath(getProject()).createPath();
235     }
236 
237     /***
238      * Classpath to use, by reference.
239      */
240     public void setClasspathRef(Reference r) {
241         createClasspath().setRefid(r);
242     }
243 
244     /***
245      * Sets the Java class to execute.
246      */
247     public void setClassname(String s) throws BuildException {
248         if (cmdl.getJar() != null){
249             throw new BuildException("Cannot use 'jar' and 'classname' "
250                                      + "attributes in same command");
251         }
252         cmdl.setClassname(s);
253     }
254 
255     /***
256      * Adds a command-line argument.
257      */
258     public Commandline.Argument createArg() {
259         return cmdl.createArgument();
260     }
261 
262     /***
263      * Set the command line arguments for the JVM.
264      */
265     public void setJvmargs(String s) {
266         log("The jvmargs attribute is deprecated. " +
267             "Please use nested jvmarg elements.",
268             Project.MSG_WARN);
269         cmdl.createVmArgument().setLine(s);
270     }
271         
272     /***
273      * Adds a JVM argument.
274      */
275     public Commandline.Argument createJvmarg() {
276         return cmdl.createVmArgument();
277     }
278 
279     /***
280      * Set the command used to start the VM (only if not forking).
281      */
282     public void setJvm(String s) {
283         cmdl.setVm(s);
284     }
285         
286     /***
287      * Adds a system property.
288      */
289     public void addSysproperty(Environment.Variable sysp) {
290         cmdl.addSysproperty(sysp);
291     }
292 
293     /***
294      * If true, then fail if the command exits with a
295      * returncode other than 0
296      */
297     public void setFailOnError(boolean fail) {
298         failOnError = fail;
299     }
300 
301     /***
302      * File the output of the process is redirected to.
303      */
304     public void setOutput(File out) {
305         this.out = out;
306     }
307 
308     /***
309      * Corresponds to -mx or -Xmx depending on VM version.
310      */
311     public void setMaxmemory(String max){
312         cmdl.setMaxmemory(max);
313     }
314 
315     /***
316      * Adds an environment variable.
317      *
318      * <p>Will be ignored if we are not forking a new VM.
319      *
320      * @since Ant 1.5
321      */
322     public void addEnv(Environment.Variable var) {
323         env.addVariable(var);
324     }
325 
326     /***
327      * If true, use a completely new environment.
328      *
329      * <p>Will be ignored if we are not forking a new VM.
330      *
331      * @since Ant 1.5
332      */
333     public void setNewenvironment(boolean newenv) {
334         newEnvironment = newenv;
335     }
336 
337     /***
338      * If true, append output to existing file.
339      *
340      * @since Ant 1.5
341      */
342     public void setAppend(boolean append) {
343         this.append = append;
344     }
345 
346     /***
347      * Timeout in milliseconds after which the process will be killed.
348      *
349      * @since Ant 1.5
350      */
351     public void setTimeout(Long value) {
352         timeout = value;
353     }
354 
355     /***
356      * Pass output sent to System.out to specified output file.
357      *
358      * @since Ant 1.5
359      */
360     protected void handleOutput(String line) {
361         if (outStream != null) {
362             outStream.println(line);
363         } else {
364             super.handleOutput(line);
365         }
366     }
367     
368     /***
369      * Pass output sent to System.out to specified output file.
370      *
371      * @since Ant 1.5.2
372      */
373     protected void handleFlush(String line) {
374         if (outStream != null) {
375             outStream.print(line);
376         } else {
377             super.handleFlush(line);
378         }
379     }
380     
381     /***
382      * Pass output sent to System.err to specified output file.
383      *
384      * @since Ant 1.5
385      */
386     protected void handleErrorOutput(String line) {
387         if (outStream != null) {
388             outStream.println(line);
389         } else {
390             super.handleErrorOutput(line);
391         }
392     }
393     
394     /***
395      * Pass output sent to System.err to specified output file.
396      *
397      * @since Ant 1.5.2
398      */
399     protected void handleErrorFlush(String line) {
400         if (outStream != null) {
401             outStream.println(line);
402         } else {
403             super.handleErrorOutput(line);
404         }
405     }
406     
407     /***
408      * Executes the given classname with the given arguments as it
409      * was a command line application.
410      */
411     private void run(CommandlineJava command) throws BuildException {
412         ExecuteJava exe = new ExecuteJava();
413         exe.setJavaCommand(command.getJavaCommand());
414         exe.setClasspath(command.getClasspath());
415         exe.setSystemProperties(command.getSystemProperties());
416         exe.setTimeout(timeout);
417         if (out != null) {
418             try {
419                 outStream = 
420                     new PrintStream(new FileOutputStream(out.getAbsolutePath(),
421                                                          append));
422                 exe.execute(getProject());
423                 System.out.flush();
424                 System.err.flush();
425             } catch (IOException io) {
426                 throw new BuildException(io, getLocation());
427             } finally {
428                 if (outStream != null) {
429                     outStream.close();
430                 }
431             }
432         } else {
433             exe.execute(getProject());
434         }
435     }
436 
437     /***
438      * Executes the given classname with the given arguments in a separate VM.
439      */
440     private int run(String[] command) throws BuildException {
441         FileOutputStream fos = null;
442         try {
443             Execute exe = null;
444             if (out == null) {
445                 exe = new Execute(new LogStreamHandler(this, Project.MSG_INFO,
446                                                        Project.MSG_WARN), 
447                                   createWatchdog());
448             } else {
449                 fos = new FileOutputStream(out.getAbsolutePath(), append);
450                 exe = new Execute(new PumpStreamHandler(fos),
451                                   createWatchdog());
452             }
453             
454             exe.setAntRun(getProject());
455             
456             if (dir == null) {
457                 dir = getProject().getBaseDir();
458             } else if (!dir.exists() || !dir.isDirectory()) {
459                 throw new BuildException(dir.getAbsolutePath()
460                                          + " is not a valid directory",
461                                          getLocation());
462             }
463             
464             exe.setWorkingDirectory(dir);
465             
466             String[] environment = env.getVariables();
467             if (environment != null) {
468                 for (int i = 0; i < environment.length; i++) {
469                     log("Setting environment variable: " + environment[i],
470                         Project.MSG_VERBOSE);
471                 }
472             }
473             exe.setNewenvironment(newEnvironment);
474             exe.setEnvironment(environment);
475 
476             exe.setCommandline(command);
477             try {
478                 int rc = exe.execute();
479                 if (exe.killedProcess()) {
480                     log("Timeout: killed the sub-process", Project.MSG_WARN); 
481                 }
482                 return rc;
483             } catch (IOException e) {
484                 throw new BuildException(e, getLocation());
485             }
486         } catch (IOException io) {
487             throw new BuildException(io, getLocation());
488         } finally {
489             if (fos != null) {
490                 try {fos.close();} catch (IOException io) {}
491             }
492         }
493     }
494 
495     /***
496      * Executes the given classname with the given arguments as it
497      * was a command line application.
498      */
499     protected void run(String classname, Vector args) throws BuildException {
500         CommandlineJava cmdj = new CommandlineJava();
501         cmdj.setClassname(classname);
502         for (int i = 0; i < args.size(); i++) {
503             cmdj.createArgument().setValue((String) args.elementAt(i));
504         }
505         run(cmdj);
506     }
507 
508     /***
509      * Clear out the arguments to this java task.
510      */
511     public void clearArgs() {
512         cmdl.clearJavaArgs();
513     }
514 
515     /***
516      * Create the Watchdog to kill a runaway process.
517      *
518      * @since Ant 1.5
519      */
520     protected ExecuteWatchdog createWatchdog() throws BuildException {
521         if (timeout == null) {
522             return null;
523         }
524         return new ExecuteWatchdog(timeout.longValue());
525     }
526 
527 }
528