1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 <fileset> 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