/**********************************************************************
* Copyright (c) 2005-2009 ant4eclipse project team.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nils Hartmann, Daniel Kasmeroglu, Gerd Wuetherich
**********************************************************************/
package org.ant4eclipse.ant.platform;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import org.ant4eclipse.ant.platform.core.MacroExecutionComponent;
import org.ant4eclipse.ant.platform.core.MacroExecutionValues;
import org.ant4eclipse.ant.platform.core.ProjectReferenceAwareComponent;
import org.ant4eclipse.ant.platform.core.ScopedMacroDefinition;
import org.ant4eclipse.ant.platform.core.SubElementAndAttributesComponent;
import org.ant4eclipse.ant.platform.core.delegate.MacroExecutionDelegate;
import org.ant4eclipse.ant.platform.core.delegate.MacroExecutionValuesProvider;
import org.ant4eclipse.ant.platform.core.delegate.ProjectReferenceAwareDelegate;
import org.ant4eclipse.ant.platform.core.delegate.SubElementAndAttributesDelegate;
import org.ant4eclipse.ant.platform.core.task.AbstractProjectSetPathBasedTask;
import org.ant4eclipse.lib.core.logging.A4ELogging;
import org.ant4eclipse.lib.core.service.ServiceRegistryAccess;
import org.ant4eclipse.lib.core.util.StopWatchService;
import org.ant4eclipse.lib.platform.model.resource.EclipseProject;
import org.ant4eclipse.lib.platform.tools.BuildOrderResolver;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.MacroDef;
import org.apache.tools.ant.taskdefs.MacroDef.NestedSequential;
/**
* @author Gerd Wuetherich (gerd@gerd-wuetherich.de)
*/
public class ExecuteProjectSetTask extends AbstractProjectSetPathBasedTask implements MacroExecutionComponent<Scope>,
SubElementAndAttributesComponent, ProjectReferenceAwareComponent {
/** the {@link MacroExecutionDelegate} */
private MacroExecutionDelegate<Scope> _macroExecutionDelegate;
/** the {@link SubElementAndAttributesDelegate} */
private SubElementAndAttributesDelegate _subElementAndAttributeDelegate;
/** the {@link ProjectReferenceAwareDelegate} */
private ProjectReferenceAwareDelegate _projectReferenceAwareDelegate;
/** the {@link PlatformExecutorValuesProvider} */
private PlatformExecutorValuesProvider _platformExecutorValuesProvider;
/** indicates if the build order should be resolved */
private boolean _resolveBuildOrder = true;
/** indicates the number of concurrent threads */
private int _threadCount = 1;
/**
* <p>
* Creates a new instance of type {@link ExecuteProjectSetTask}.
* </p>
*/
public ExecuteProjectSetTask() {
// create the MacroExecutionDelegate
this._macroExecutionDelegate = new MacroExecutionDelegate<Scope>(this, "executeProjectSet");
this._subElementAndAttributeDelegate = new SubElementAndAttributesDelegate(this);
this._projectReferenceAwareDelegate = new ProjectReferenceAwareDelegate();
this._platformExecutorValuesProvider = new PlatformExecutorValuesProvider(getPathDelegate());
}
/**
* <p>
* Returns is the build order should be resolved or not.
* </p>
*
* @return the resolveBuildOrder
*/
public boolean isResolveBuildOrder() {
return this._resolveBuildOrder;
}
/**
* <p>
* Sets if the build order should be resolved or not.
* </p>
*
* @param resolveBuildOrder
* the resolveBuildOrder to set
*/
public void setResolveBuildOrder(boolean resolveBuildOrder) {
this._resolveBuildOrder = resolveBuildOrder;
}
/**
* {@inheritDoc}
*/
public String[] getProjectReferenceTypes() {
return this._projectReferenceAwareDelegate.getProjectReferenceTypes();
}
/**
* {@inheritDoc}
*/
public boolean isProjectReferenceTypesSet() {
return this._projectReferenceAwareDelegate.isProjectReferenceTypesSet();
}
/**
* {@inheritDoc}
*/
public void requireProjectReferenceTypesSet() {
this._projectReferenceAwareDelegate.requireProjectReferenceTypesSet();
}
/**
* {@inheritDoc}
*/
public void setProjectReferenceTypes(String referenceTypes) {
this._projectReferenceAwareDelegate.setProjectReferenceTypes(referenceTypes);
}
/**
* {@inheritDoc}
*/
public String getPrefix() {
return this._macroExecutionDelegate.getPrefix();
}
/**
* {@inheritDoc}
*/
public void setPrefix(String prefix) {
this._macroExecutionDelegate.setPrefix(prefix);
}
/**
* {@inheritDoc}
*/
public NestedSequential createScopedMacroDefinition(Scope scope) {
return this._macroExecutionDelegate.createScopedMacroDefinition(scope);
}
/**
* {@inheritDoc}
*/
public void executeMacroInstance(MacroDef macroDef, MacroExecutionValuesProvider provider) {
this._macroExecutionDelegate.executeMacroInstance(macroDef, provider);
}
/**
* {@inheritDoc}
*/
public List<ScopedMacroDefinition<Scope>> getScopedMacroDefinitions() {
return this._macroExecutionDelegate.getScopedMacroDefinitions();
}
public int getThreadCount() {
return this._threadCount;
}
public void setThreadCount(int threads) {
this._threadCount = threads;
}
@Override
protected void preconditions() throws BuildException {
super.preconditions();
if (this._resolveBuildOrder && this._threadCount > 1) {
throw new BuildException("In parallel mode (threadCount>1) build order can not be resolved");
}
if (this._threadCount < 1) {
throw new BuildException("ThreadCount must at least be 1");
}
}
/**
* {@inheritDoc}
*/
@Override
protected void doExecute() {
StopWatchService stopWatchService = ServiceRegistryAccess.instance().getService(StopWatchService.class);
stopWatchService.getOrCreateStopWatch("executeProjectSet").start();
// check required attributes
requireAllWorkspaceProjectsOrProjectSetOrProjectNamesSet();
requireWorkspaceDirectoryOrWorkspaceIdSet();
// get all eclipse projects and calculate the build order if necessary
List<EclipseProject> projects = null;
if (this._resolveBuildOrder) {
// resolve the build order
projects = BuildOrderResolver.resolveBuildOrder(getWorkspace(), getProjectNames(),
this._projectReferenceAwareDelegate.getProjectReferenceTypes(),
this._subElementAndAttributeDelegate.getSubElements());
} else {
// only get the specified projects
projects = Arrays.asList(getWorkspace().getProjects(getProjectNames(), false));
}
final BuildCallable[] buildCallables = new BuildCallable[this._threadCount];
for (int i = 0; i < buildCallables.length; i++) {
buildCallables[i] = new BuildCallable();
}
int callableIndex = 0;
for (final EclipseProject eclipseProject : projects) {
buildCallables[callableIndex].addProject(eclipseProject);
callableIndex++;
if (callableIndex >= buildCallables.length) {
callableIndex = 0;
}
}
// execute the macro definitions
for (ScopedMacroDefinition<Scope> scopedMacroDefinition : getScopedMacroDefinitions()) {
//
for (BuildCallable buildCallable : buildCallables) {
buildCallable.setScopedMacroDefinition(scopedMacroDefinition);
}
if (buildCallables.length > 1) {
// create the future tasks
@SuppressWarnings("unchecked")
FutureTask<Void>[] futureTasks = new FutureTask[this._threadCount];
for (int i = 0; i < futureTasks.length; i++) {
futureTasks[i] = new FutureTask<Void>(buildCallables[i]);
Thread thread = new Thread(futureTasks[i]);
thread.setName("A4E-" + thread.getName());
thread.start();
}
// collect the result
BuildException buildException = null;
for (FutureTask<Void> futureTask : futureTasks) {
if (buildException != null) {
// At least one task crashed: cancel all others
try {
A4ELogging.debug("One task crashed, canceling task " + futureTask);
futureTask.cancel(true);
} catch (Exception ex) {
// ignore
}
continue;
}
// Wait for task to complete
try {
futureTask.get();
} catch (Exception e) {
Throwable t = e;
if (e instanceof ExecutionException) {
t = ((ExecutionException) e).getCause();
}
if (t instanceof BuildException) {
buildException = (BuildException) t;
}
buildException = new BuildException(e);
}
}
if (buildException != null) {
throw buildException;
}
} else {
try {
buildCallables[0].call();
} catch (Exception e) {
Throwable t = e;
if (e instanceof ExecutionException) {
t = ((ExecutionException) e).getCause();
}
if (t instanceof BuildException) {
throw (BuildException) t;
}
throw new BuildException(e);
}
}
}
stopWatchService.getOrCreateStopWatch("executeProjectSet").stop();
}
class BuildCallable implements Callable<Void> {
private final List<EclipseProject> _projects = new LinkedList<EclipseProject>();
private ScopedMacroDefinition<Scope> _scopedMacroDefinition;
public void addProject(EclipseProject project) {
this._projects.add(project);
}
public void setScopedMacroDefinition(ScopedMacroDefinition<Scope> scopedMacroDefinition) {
this._scopedMacroDefinition = scopedMacroDefinition;
}
public Void call() throws Exception {
// System.out.println(String.format("ExecuteProjectSetTask[%s] 1: %s", Thread.currentThread(), this._projects));
for (final EclipseProject eclipseProject : this._projects) {
// System.out.println(String.format("ExecuteProjectSetTask[%s] 2: %s", Thread.currentThread(),
// eclipseProject.getSpecifiedName()));
// execute macro instance
ExecuteProjectSetTask.this._macroExecutionDelegate.executeMacroInstance(
this._scopedMacroDefinition.getMacroDef(), new MacroExecutionValuesProvider() {
public MacroExecutionValues provideMacroExecutionValues(MacroExecutionValues values) {
// set the values
ExecuteProjectSetTask.this._platformExecutorValuesProvider
.provideExecutorValues(eclipseProject, values);
// System.out.println(String.format("ExecuteProjectSetTask[%s] 3: %s", Thread.currentThread(),
// values.getProperties()));
// return result
return values;
}
});
}
return null;
}
}
/**
* <p>
* Creates a new {@link MacroDef} for each <forEachProject> element of the {@link ExecuteProjectSetTask}.
* </p>
*
* @return the {@link NestedSequential}
*/
public final Object createForEachProject() {
return createScopedMacroDefinition(Scope.PROJECT);
}
/**
* {@inheritDoc}
*/
public Object createDynamicElement(String name) throws BuildException {
return this._subElementAndAttributeDelegate.createDynamicElement(name);
}
/**
* {@inheritDoc}
*/
public List<Object> getSubElements() {
return this._subElementAndAttributeDelegate.getSubElements();
}
/**
* {@inheritDoc}
*/
public Map<String, String> getSubAttributes() {
return this._subElementAndAttributeDelegate.getSubAttributes();
}
/**
* {@inheritDoc}
*/
public void setDynamicAttribute(String name, String value) throws BuildException {
this._subElementAndAttributeDelegate.setDynamicAttribute(name, value);
}
}
/**
* <p>
* Within the ExecuteProjectSetTask, we only have the PROJECT scope.
* </p>
*
* @author Gerd Wütherich (gerd@gerd-wuetherich.de)
*/
enum Scope {
PROJECT;
}