Author: stevel
Date: Mon Oct 29 10:44:21 2007
New Revision: 589767

URL: http://svn.apache.org/viewvc?rev=589767&view=rev
Log:
new test tasks, not declared yet -so they dont exist-

<funtest> for functional testing
<blockfor> is waitfor that throws a BuildTimeoutException when it times out

Added:
    ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/
    
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java
    
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java
    
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java
    ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java
Modified:
    ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java

Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java?rev=589767&r1=589766&r2=589767&view=diff
==============================================================================
--- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java 
(original)
+++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/WaitFor.java Mon Oct 
29 10:44:21 2007
@@ -52,20 +52,24 @@
  * @ant.task category="control"
  */
 public class WaitFor extends ConditionBase {
-    private static final long ONE_SECOND = 1000L;
-    private static final long ONE_MINUTE = ONE_SECOND * 60L;
-    private static final long ONE_HOUR   = ONE_MINUTE * 60L;
-    private static final long ONE_DAY    = ONE_HOUR * 24L;
-    private static final long ONE_WEEK   = ONE_DAY * 7L;
-
-    private static final long DEFAULT_MAX_WAIT_MILLIS = ONE_MINUTE * 3L;
-    private static final long DEFAULT_CHECK_MILLIS = 500L;
-
-    /** default max wait time */
-    private long maxWaitMillis = DEFAULT_MAX_WAIT_MILLIS;
-    private long maxWaitMultiplier = 1L;
-    private long checkEveryMillis = DEFAULT_CHECK_MILLIS;
-    private long checkEveryMultiplier = 1L;
+    public static final long ONE_MILLISECOND = 1L;
+    public static final long ONE_SECOND = 1000L;
+    public static final long ONE_MINUTE = ONE_SECOND * 60L;
+    public static final long ONE_HOUR   = ONE_MINUTE * 60L;
+    public static final long ONE_DAY    = ONE_HOUR * 24L;
+    public static final long ONE_WEEK   = ONE_DAY * 7L;
+
+    public static final long DEFAULT_MAX_WAIT_MILLIS = ONE_MINUTE * 3L;
+    public static final long DEFAULT_CHECK_MILLIS = 500L;
+
+    /** default max wait time in the current unit*/
+    private long maxWait = DEFAULT_MAX_WAIT_MILLIS;
+    private long maxWaitMultiplier = ONE_MILLISECOND;
+    /**
+     * check time in the current unit
+     */
+    private long checkEvery = DEFAULT_CHECK_MILLIS;
+    private long checkEveryMultiplier = ONE_MILLISECOND;
     private String timeoutProperty;
 
     /**
@@ -75,14 +79,26 @@
         super("waitfor");
     }
 
+
+    /**
+     * Constructor that takes the name of the task in the task name.
+     *
+     * @param taskName the name of the task.
+     * @since Ant 1.8
+     */
+    public WaitFor(String taskName) {
+        super(taskName);
+    }
+
     /**
      * Set the maximum length of time to wait.
      * @param time a <code>long</code> value
      */
     public void setMaxWait(long time) {
-        maxWaitMillis = time;
+        maxWait = time;
     }
 
+
     /**
      * Set the max wait time unit
      * @param unit an enumerated <code>Unit</code> value
@@ -91,12 +107,14 @@
         maxWaitMultiplier = unit.getMultiplier();
     }
 
+
+
     /**
      * Set the time between each check
      * @param time a <code>long</code> value
      */
     public void setCheckEvery(long time) {
-        checkEveryMillis = time;
+        checkEvery = time;
     }
 
     /**
@@ -131,32 +149,42 @@
                                      + getTaskName());
         }
         Condition c = (Condition) getConditions().nextElement();
-
-        long savedMaxWaitMillis = maxWaitMillis;
-        long savedCheckEveryMillis = checkEveryMillis;
         try {
-            try {
-                maxWaitMillis *= maxWaitMultiplier;
-                checkEveryMillis *= checkEveryMultiplier;
-                long start = System.currentTimeMillis();
-                long end = start + maxWaitMillis;
-
-                while (System.currentTimeMillis() < end) {
-                    if (c.eval()) {
-                        processSuccess();
-                        return;
-                    }
-                    Thread.sleep(checkEveryMillis);
+            long maxWaitMillis = calculateMaxWaitMillis();
+            long checkEveryMillis = calculateCheckEveryMillis();
+            long start = System.currentTimeMillis();
+            long end = start + maxWaitMillis;
+
+            while (System.currentTimeMillis() < end) {
+                if (c.eval()) {
+                    processSuccess();
+                    return;
                 }
-            } catch (InterruptedException e) {
-                log("Task " + getTaskName()
-                        + " interrupted, treating as timed out.");
+                Thread.sleep(checkEveryMillis);
             }
-            processTimeout();
-        } finally {
-            maxWaitMillis = savedMaxWaitMillis;
-            checkEveryMillis = savedCheckEveryMillis;
+        } catch (InterruptedException e) {
+            log("Task " + getTaskName()
+                    + " interrupted, treating as timed out.");
         }
+        processTimeout();
+    }
+
+    /**
+     * Get the check wait time, in milliseconds.
+     * @since Ant 1.8
+     * @return how long to wait between checks
+     */
+    public long calculateCheckEveryMillis() {
+        return checkEvery * checkEveryMultiplier;
+    }
+
+    /**
+     * Get the maxiumum wait time, in milliseconds.
+     * @since Ant 1.8
+     * @return how long to wait before timing out
+     */
+    public long calculateMaxWaitMillis() {
+        return maxWait * maxWaitMultiplier;
     }
 
     /**

Added: 
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java?rev=589767&view=auto
==============================================================================
--- 
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java
 (added)
+++ 
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BlockFor.java
 Mon Oct 29 10:44:21 2007
@@ -0,0 +1,73 @@
+/*
+ * Copyright  2007 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.testing;
+
+import org.apache.tools.ant.taskdefs.WaitFor;
+
+/**
+ *
+ * Created 29-Oct-2007 12:28:28
+ * @since Ant 1.8
+ */
+
+public class BlockFor extends WaitFor {
+
+    /**
+     * Text to include in a message
+     */
+    private String text;
+
+
+    /**
+     * Constructor that takes the name of the task in the task name.
+     *
+     */
+    public BlockFor() {
+        super("blockfor");
+        text=getTaskName()+" timed out";
+    }
+
+    /**
+     * Constructor that takes the name of the task in the task name.
+     *
+     * @param taskName the name of the task.
+     */
+    public BlockFor(String taskName) {
+        super(taskName);
+    }
+
+    /**
+     * If the wait fails, a BuildException is thrown. All the superclasses 
actions are called first.
+     * @throws BuildTimeoutException on timeout, using the text in [EMAIL 
PROTECTED] #text}
+     *
+     */
+    protected void processTimeout() throws BuildTimeoutException {
+        super.processTimeout();
+        throw new BuildTimeoutException(text,getLocation());
+    }
+
+    /**
+     * Set the error text; all properties are expanded in the message.
+     *
+     * @param message the text to use in a failure message
+     */
+    public void addText(String message) {
+        text = getProject().replaceProperties(message);
+    }
+
+
+}

Added: 
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java?rev=589767&view=auto
==============================================================================
--- 
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java
 (added)
+++ 
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/BuildTimeoutException.java
 Mon Oct 29 10:44:21 2007
@@ -0,0 +1,111 @@
+/*
+ * Copyright  2007 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.testing;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+
+/**
+ *
+ * This exception is used to indicate timeouts.
+ * @since Ant1.8
+ *
+ */
+
+public class BuildTimeoutException extends BuildException {
+
+
+    /**
+     * Constructs a build exception with no descriptive information.
+     */
+    public BuildTimeoutException() {
+    }
+
+    /**
+     * Constructs an exception with the given descriptive message.
+     *
+     * @param message A description of or information about the exception.
+     *            Should not be <code>null</code>.
+     */
+    public BuildTimeoutException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs an exception with the given message and exception as
+     * a root cause.
+     *
+     * @param message A description of or information about the exception.
+     *            Should not be <code>null</code> unless a cause is specified.
+     * @param cause The exception that might have caused this one.
+     *              May be <code>null</code>.
+     */
+    public BuildTimeoutException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs an exception with the given message and exception as
+     * a root cause and a location in a file.
+     *
+     * @param msg A description of or information about the exception.
+     *            Should not be <code>null</code> unless a cause is specified.
+     * @param cause The exception that might have caused this one.
+     *              May be <code>null</code>.
+     * @param location The location in the project file where the error
+     *                 occurred. Must not be <code>null</code>.
+     */
+    public BuildTimeoutException(String msg, Throwable cause, Location 
location) {
+        super(msg, cause, location);
+    }
+
+    /**
+     * Constructs an exception with the given exception as a root cause.
+     *
+     * @param cause The exception that might have caused this one.
+     *              Should not be <code>null</code>.
+     */
+    public BuildTimeoutException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs an exception with the given descriptive message and a
+     * location in a file.
+     *
+     * @param message A description of or information about the exception.
+     *            Should not be <code>null</code>.
+     * @param location The location in the project file where the error
+     *                 occurred. Must not be <code>null</code>.
+     */
+    public BuildTimeoutException(String message, Location location) {
+        super(message, location);
+    }
+
+    /**
+     * Constructs an exception with the given exception as
+     * a root cause and a location in a file.
+     *
+     * @param cause The exception that might have caused this one.
+     *              Should not be <code>null</code>.
+     * @param location The location in the project file where the error
+     *                 occurred. Must not be <code>null</code>.
+     */
+    public BuildTimeoutException(Throwable cause, Location location) {
+        super(cause, location);
+    }
+}

Added: 
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java?rev=589767&view=auto
==============================================================================
--- 
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java
 (added)
+++ 
ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/optional/testing/Funtest.java
 Mon Oct 29 10:44:21 2007
@@ -0,0 +1,457 @@
+/*
+ * Copyright  2007 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.testing;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.TaskAdapter;
+import org.apache.tools.ant.util.WorkerAnt;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.Parallel;
+import org.apache.tools.ant.taskdefs.Sequential;
+import org.apache.tools.ant.taskdefs.WaitFor;
+
+/**
+ * Task to provide functional testing under Ant, with a fairly complex worflow 
of:
+ *
+ * <ul>
+ * <li>Conditional execution</li>
+ * <li>Application to start</li>
+ * <li>A probe to "waitfor" before running tests</li>
+ * <li>A tests sequence</li>
+ * <li>A reporting sequence that runs after the tests have finished</li>
+ * <li>A "teardown" clause that runs after the rest.</li>
+ * <li>Automated termination of the program it executes, if a timeout is not 
met</li>
+ * <li>Checking of a failure property and automatic raising of a fault (with 
the text in failureText)
+ * if test shutdown and reporting succeeded</li>
+ *  </ul>
+ *
+ * The task is designed to be framework neutral; it will work with JUnit, 
TestNG and other test frameworks That can be
+ * executed from Ant. It bears a resemblance to the FunctionalTest task from 
SmartFrog, as the attribute names were
+ * chosen to make migration easier. However, this task benefits from the 
ability to tweak Ant's internals, and so
+ * simplify the workflow, and from the experience of using the SmartFrog task. 
No code has been shared.
+ *
+ * @since Ant 1.8
+ */
+
+public class Funtest extends Task {
+
+    /**
+     * A condition that must be true before the tests are run. This makes it 
easier to define complex tests that only
+     * run if certain conditions are met, such as OS or network state.
+     */
+    private Condition condition;
+
+
+    /**
+     * Used internally to set the workflow up
+     */
+    private Parallel timedTests;
+
+    /**
+     * Setup runs if the condition is met. Once setup is complete, teardown 
will be run when the task finishes
+     */
+    private Sequential setup;
+
+    /**
+     * The application to run
+     */
+    private Sequential application;
+
+    /**
+     * A block that halts the tests until met.
+     */
+    private BlockFor block;
+
+    /**
+     * Tests to run
+     */
+    private Sequential tests;
+
+    /**
+     * Reporting only runs if the tests were executed. If the block stopped 
them, reporting is skipped.
+     */
+    private Sequential reporting;
+
+    /**
+     * Any teardown operations.
+     */
+    private Sequential teardown;
+
+    /**
+     * time for the tests to time out
+     */
+    private long timeout;
+
+    private long timeoutUnitMultiplier= WaitFor.ONE_MILLISECOND;
+
+    /**
+     * time for the execution to time out.
+     */
+    private long shutdownTime = 10*WaitFor.ONE_SECOND;
+
+    private long shutdownUnitMultiplier = WaitFor.ONE_MILLISECOND;
+
+    /**
+     * Name of a property to look for
+     */
+    private String failureProperty;
+
+    /**
+     * Message to send when tests failed
+     */
+    private String failureMessage="Tests failed";
+
+    /**
+     * Flag to set to true if you don't care about any shutdown errors.
+     * <p/>
+     * In that situation, errors raised during teardown are logged but not
+     * turned into BuildFault events. Similar to catching and ignoring
+     * <code>finally {}</code> clauses in Java/
+     */
+    private boolean failOnTeardownErrors=true;
+
+
+    /**
+     * What was thrown in the test run (including reporting)
+     */
+    private BuildException testException;
+    /**
+     * What got thrown during teardown
+     */
+    private BuildException teardownException;
+
+    /**
+     * Did the application throw an exception
+     */
+    private BuildException applicationException;
+
+    /**
+     * Did the task throw an exception
+     */
+    private BuildException taskException;
+
+    /** [EMAIL PROTECTED] */
+    public static final String WARN_OVERRIDING = "Overriding previous 
definition of ";
+    /** [EMAIL PROTECTED] */
+    public static final String APPLICATION_FORCIBLY_SHUT_DOWN = "Application 
forcibly shut down";
+    /** [EMAIL PROTECTED] */
+    public static final String SHUTDOWN_INTERRUPTED = "Shutdown interrupted";
+    public static final String SKIPPING_TESTS = "Condition failed -skipping 
tests";
+
+    /**
+     * Log if the definition is overriding something
+     *
+     * @param name       what is being defined
+     * @param definition what should be null if you don't want a warning
+     */
+    private void logOverride(String name, Object definition) {
+        if (definition != null) {
+            log(WARN_OVERRIDING + '<' + name + '>', Project.MSG_WARN);
+        }
+    }
+
+    public void addCondition(Condition newCondition) {
+        logOverride("condition", condition);
+        condition = newCondition;
+    }
+
+    public void addApplication(Sequential sequence) {
+        logOverride("application", application);
+        application = sequence;
+    }
+
+    public void addSetup(Sequential sequence) {
+        logOverride("setup", setup);
+        setup = sequence;
+    }
+
+    public void addBlock(BlockFor sequence) {
+        logOverride("block", block);
+        block = sequence;
+    }
+
+    public void addTests(Sequential sequence) {
+        logOverride("tests", tests);
+        tests = sequence;
+    }
+
+    public void addReporting(Sequential sequence) {
+        logOverride("reporting", reporting);
+        reporting = sequence;
+    }
+
+    public void addTeardown(Sequential sequence) {
+        logOverride("teardown", teardown);
+        teardown = sequence;
+    }
+
+
+    public void setFailOnTeardownErrors(boolean failOnTeardownErrors) {
+        this.failOnTeardownErrors = failOnTeardownErrors;
+    }
+
+    public void setFailureMessage(String failureMessage) {
+        this.failureMessage = failureMessage;
+    }
+
+    public void setFailureProperty(String failureProperty) {
+        this.failureProperty = failureProperty;
+    }
+
+    public void setShutdownTime(long shutdownTime) {
+        this.shutdownTime = shutdownTime;
+    }
+
+    public void setTimeout(long timeout) {
+        this.timeout = timeout;
+    }
+
+    public void setTimeoutUnit(WaitFor.Unit unit) {
+        timeoutUnitMultiplier=unit.getMultiplier();
+    }
+
+    public void setShutdownUnit(WaitFor.Unit unit) {
+        shutdownUnitMultiplier = unit.getMultiplier();
+    }
+
+
+    public BuildException getApplicationException() {
+        return applicationException;
+    }
+
+    public BuildException getTeardownException() {
+        return teardownException;
+    }
+
+    public BuildException getTestException() {
+        return testException;
+    }
+
+    public BuildException getTaskException() {
+        return taskException;
+    }
+
+    /**
+     * Bind and initialise a task
+     * @param task task to bind
+     */
+    private void bind(Task task) {
+        task.bindToOwner(this);
+        task.init();
+    }
+
+    /**
+     * Create a newly bound parallel instance
+     * @param parallelTimeout timeout
+     * @return a bound and initialised parallel instance.
+     */
+    private Parallel newParallel(long parallelTimeout) {
+        Parallel par=new Parallel();
+        bind(par);
+        par.setFailOnAny(true);
+        par.setTimeout(parallelTimeout);
+        return par;
+    }
+
+    /**
+     * Create a newly bound parallel instance with one child
+     * @param parallelTimeout timeout
+     * @return a bound and initialised parallel instance.
+     */
+    private Parallel newParallel(long parallelTimeout,Task child) {
+        Parallel par = newParallel(parallelTimeout);
+        par.addTask(child);
+        return par;
+    }
+
+    /**
+     * Run the functional test sequence.
+     * <p/>
+     * This is a fairly complex workflow -what is going on is that we try to 
clean up
+     * no matter how the run ended, and to retain the innermost exception that 
got thrown
+     * during cleanup. That is, if teardown fails after the tests themselves 
failed, it is the
+     * test failing that is more important.
+     * @throws BuildException if something was caught during the run or 
teardown.
+     */
+    public void execute() throws BuildException {
+
+        //before anything else, check the condition
+        //and bail out if it is defined but not true
+        if (condition != null && !condition.eval()) {
+            //we are skipping the test
+            log(SKIPPING_TESTS);
+            return;
+        }
+
+        long timeoutMillis = timeout * timeoutUnitMultiplier;
+
+        //set up the application to run in a separate thread
+        Parallel applicationRun = newParallel(timeoutMillis);
+        //with a worker which we can use to manage it
+        WorkerAnt worker = new WorkerAnt(applicationRun, null);
+        if (application != null) {
+            applicationRun.addTask(application);
+        }
+
+        //The test run consists of the block followed by the tests.
+        long testRunTimeout = 0;
+        Sequential testRun = new Sequential();
+        bind(testRun);
+        if (block != null) {
+            //waitfor is not a task, it needs to be adapted
+            testRun.addTask(new TaskAdapter(block));
+            //add the block time to the total test run timeout
+            testRunTimeout = block.calculateMaxWaitMillis();
+        }
+
+        //add the tests and more delay
+        if (tests != null) {
+            testRun.addTask(tests);
+            testRunTimeout += timeoutMillis;
+        }
+        //add the reporting and more delay
+        if (reporting != null) {
+            testRun.addTask(reporting);
+            testRunTimeout += timeoutMillis;
+        }
+
+        //wrap this in a parallel purely to set up timeouts for the
+        //test run
+        timedTests = newParallel(testRunTimeout, testRun);
+
+        try {
+            //run any setup task
+            if (setup != null) {
+                Parallel setupRun = newParallel(timeoutMillis, setup);
+                setupRun.execute();
+            }
+            //start the worker thread and leave it running
+            worker.start();
+            //start the probe+test sequence
+            timedTests.execute();
+        } catch (BuildException e) {
+            //Record the exception and continue
+            testException = e;
+        } finally {
+            //teardown always runs; its faults are filed away
+            if (teardown != null) {
+                try {
+                    Parallel teardownRun = newParallel(timeoutMillis, 
teardown);
+                    teardownRun.execute();
+                } catch (BuildException e) {
+                    teardownException = e;
+                }
+            }
+        }
+
+        //we get here whether or not the tests/teardown have thrown a 
BuildException.
+        //do a forced shutdown of the running application, before processing 
the faults
+
+        try {
+            //wait for the worker to have finished
+            long shutdownTimeMillis = shutdownTime * shutdownUnitMultiplier;
+            worker.waitUntilFinished(shutdownTimeMillis);
+            if (worker.isAlive()) {
+                //then, if it is still running, interrupt it a second time.
+                log(APPLICATION_FORCIBLY_SHUT_DOWN, Project.MSG_WARN);
+                worker.interrupt();
+                worker.waitUntilFinished(shutdownTimeMillis);
+            }
+        } catch (InterruptedException e) {
+            //success, something interrupted the shutdown. There may be a 
leaked
+            //worker;
+            log(SHUTDOWN_INTERRUPTED, e, Project.MSG_VERBOSE);
+        }
+        applicationException = worker.getBuildException();
+
+        /**Now faults are analysed
+            the priority is
+          -testexceptions, except those indicating a build timeout when the 
application itself
+            failed.
+            (because often it is the application fault that is more 
interesting than the probe
+             failure, which is usually triggered by the application not 
starting
+          -application exceptions (above test timeout exceptions)
+          -teardown exceptions -except when they are being ignored
+          -any
+        */
+
+        processExceptions();
+    }
+
+    /**
+     * Now faults are analysed.
+     * <p> The priority is
+     * <ol>
+     * <li>testexceptions, except those indicating a build timeout when the 
application itself
+     failed.<br>
+     (because often it is the application fault that is more interesting than 
the probe
+     failure, which is usually triggered by the application not starting
+     </li><li>
+     Application exceptions (above test timeout exceptions)
+     </li><li>
+     Teardown exceptions -except when they are being ignored
+     </li><li>
+     Test failures as indicated by the failure property
+     </li></ol>
+
+     */
+    protected void processExceptions() {
+        taskException = testException;
+
+        //look for an application fault
+        if (applicationException != null) {
+            if (taskException == null || taskException instanceof 
BuildTimeoutException) {
+                taskException = applicationException;
+            } else {
+                log("Application Exception:" + applicationException.toString(),
+                        applicationException,
+                        Project.MSG_WARN);
+            }
+        }
+
+        //now look for teardown faults, which may be ignored
+        if (teardownException != null) {
+            if (taskException == null && failOnTeardownErrors) {
+                taskException = teardownException;
+            } else {
+                //don't let the cleanup exception get in the way of any other 
failure
+                log("teardown exception" + teardownException.toString(),
+                        teardownException,
+                        Project.MSG_WARN);
+            }
+        }
+
+        //now, analyse the tests
+        if (failureProperty != null
+             && getProject().getProperty(failureProperty) != null) {
+            //we've failed
+            log(failureMessage);
+            if(taskException == null) {
+                taskException = new BuildException(failureMessage);
+            }
+        }
+
+        //at this point taskException is null or not.
+        //if not, throw the exception
+        if (taskException != null) {
+            throw taskException;
+        }
+    }
+}

Added: ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java
URL: 
http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java?rev=589767&view=auto
==============================================================================
--- ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java (added)
+++ ant/core/trunk/src/main/org/apache/tools/ant/util/WorkerAnt.java Mon Oct 29 
10:44:21 2007
@@ -0,0 +1,170 @@
+/*
+ * Copyright  2007 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * A worker ant executes a single task in a background thread.
+ * After the run, any exception thrown is turned into a buildexception, which 
can be
+ * rethrown, the finished attribute is set, then notifyAll() is called,
+ * so that anyone waiting on the same notify object gets woken up.
+ * </p>
+ * This class is effectively a superset of
+ * [EMAIL PROTECTED] org.apache.tools.ant.taskdefs.Parallel.TaskRunnable}
+ *
+ * @since Ant 1.8
+ */
+
+public class WorkerAnt extends Thread {
+
+    private Task task;
+    private Object notify;
+    private volatile boolean finished=false;
+    private volatile BuildException buildException;
+    private volatile Throwable exception;
+
+    /**
+     * Error message if invoked with no task
+     */
+    public static final String ERROR_NO_TASK = "No task defined";
+
+
+    /**
+     * Create the worker.
+     * <p/>
+     * This does not start the thread, merely configures it.
+     * @param task the task
+     * @param notify what to notify
+     */
+    public WorkerAnt(Task task, Object notify) {
+        this.task = task;
+        this.notify = notify;
+    }
+
+    /**
+     * Create the worker, using the worker as the notification point.
+     * <p/>
+     * This does not start the thread, merely configures it.
+     * @param task the task
+     */
+    public WorkerAnt(Task task) {
+        this(task,null);
+        notify = this;
+    }
+
+    /**
+     * Get any build exception.
+     * This would seem to be oversynchronised, but know that Java pre-1.5 can 
reorder volatile access.
+     * The synchronized attribute is to force an ordering.
+     *
+     * @return the exception or null
+     */
+    public synchronized BuildException getBuildException() {
+        return buildException;
+    }
+
+    /**
+     * Get whatever was thrown, which may or may not be a buildException.
+     * Assertion: getException() instanceof BuildException <=> 
getBuildException()==getException()
+     * @return
+     */
+    public synchronized Throwable getException() {
+        return exception;
+    }
+
+
+    /**
+     * Get the task
+     * @return the task
+     */
+    public Task getTask() {
+        return task;
+    }
+
+
+    /**
+     * Query the task/thread for being finished.
+     * This would seem to be oversynchronised, but know that Java pre-1.5 can 
reorder volatile access.
+     * The synchronized attribute is to force an ordering.
+     * @return true if the task is finished.
+     */
+    public synchronized boolean isFinished() {
+        return finished;
+    }
+
+    /**
+     * Block on the notify object and so wait until the thread is finished.
+     * @param timeout timeout in milliseconds
+     * @throws InterruptedException if the execution was interrupted
+     */
+    public void waitUntilFinished(long timeout) throws InterruptedException {
+        synchronized(notify) {
+            if(finished) {
+                return;
+            }
+            notify.wait(timeout);
+        }
+    }
+
+    /**
+     * Raise an exception if one was caught
+     *
+     * @throws BuildException if one has been picked up
+     */
+    public void rethrowAnyBuildException() {
+        BuildException ex = getBuildException();
+        if (ex != null) {
+            throw ex;
+        }
+    }
+
+
+    /**
+     * Handle a caught exception, by recording it and possibly wrapping it
+     * in a BuildException for later rethrowing.
+     * @param thrown what was caught earlier
+     */
+    private synchronized void caught(Throwable thrown) {
+        exception = thrown;
+        buildException = (thrown instanceof BuildException)?
+                (BuildException)thrown
+                :new BuildException(thrown);
+    }
+
+    /**
+     * Run the task, which is skipped if null.
+     * When invoked again, the task is re-run.
+     */
+    public void run() {
+        try {
+            if (task != null) {
+                task.execute();
+            }
+        } catch (Throwable thrown) {
+            caught(thrown);
+        } finally {
+            synchronized (notify) {
+                finished=true;
+                //reset the task.
+                //wake up our owner, if it is waiting
+                notify.notifyAll();
+            }
+        }
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to