/* * 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.BuildException; import org.apache.tools.ant.Task; /** * 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 { final long start = System.currentTimeMillis(); final long end = start + timeout; synchronized (notify) { long now = System.currentTimeMillis(); while (!finished && now < end) { notify.wait(end - now); now = System.currentTimeMillis(); } } } /** * 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(); } } } }