blob: 9a6b4d85baa373079b43824c400ed1303827bd9a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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
* {@link 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 != null ? notify : this;
}
/**
* 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);
}
/**
* 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 the exception.
*/
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) {
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();
}
}
}
}