@@ -55,6 +55,7 @@ package org.apache.ant.core.execution;
import org.apache.ant.core.model.*;
import org.apache.ant.core.support.*;
import org.apache.ant.core.types.*;
import java.util.*;
import java.net.*;
@@ -67,6 +68,13 @@ import java.net.*;
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
*/
public class ExecutionFrame {
/** State used in dependency analysis when a target's dependencies are being
examined. */
private static final String VISITING = "VISITING";
/** State used in dependency analysis to indicate a target has been examined */
private static final String VISITED = "VISITED";
/** The Project that this execiton frame is processing */
private Project project = null;
@@ -74,9 +82,6 @@ public class ExecutionFrame {
Project's source URL and it's base attribute. */
private URL baseURL = null;
/** The task defs that this frame will use to process tasks */
private Map taskDefs = new HashMap();
/** The imported frames of this frame. For each project imported by this frame's
project, a corresponding ExecutionFrame is created. */
private Map importedFrames = new HashMap();
@@ -88,22 +93,79 @@ public class ExecutionFrame {
created by tasks that have been executed */
private Map dataValues = new HashMap();
/** Introspector objects used to configure Execution Tasks from the Task models.*/
/** Introspector objects used to configure Tasks from the Task models.*/
private Map introspectors = new HashMap();
/** The task defs that this frame will use to process tasks */
private Map taskDefs = new HashMap();
/** Type converters for this executionFrame. Converters are used when configuring
Tasks to handle special type conversions. */
private Map converters = new HashMap();
/** The aspect handler active in this frame */
private Map aspectHandlers = new HashMap();
/** The namespace under which this execution frame lives in the hierarchical
project namespace - null for the root namespace */
private String namespace;
public ExecutionFrame(Project project, Map taskDefs, Map converterDefs,
String namespace) throws ConfigException {
this.project = project;
this.taskDefs = taskDefs;
/**
* Construct an execution frame to process the given project model with
* the configuration represented by the libraries.
*
* @param project the model of the project to be built.
* @param libraries an Array of AntLibrary objects containing the
* configuration of Ant for this build.
*
* @throws ConfigException when the project cannot be setup with the
* given configuration
*/
public ExecutionFrame(Project project, AntLibrary[] libraries)
throws ConfigException {
this.namespace = null;
setupFrame(project, libraries);
}
/**
* Construct a subframe for managing a project imported into the main project.
* @param project the model of the project to be built.
* @param libraries an Array of AntLibrary objects containing the
* configuration of Ant for this build.
* @param namespace the location of this project within the overall import
* namespace.
*
* @throws ConfigException when the project cannot be setup with the
* given configuration
*/
private ExecutionFrame(Project project, AntLibrary[] libraries, String namespace)
throws ConfigException {
this.namespace = namespace;
setupFrame(project, libraries);
}
/**
* Set up the execution frame.
*
* This method examines the project model and constructs the required
* subframes to handle imported projects.
* @param project the model of the project to be built.
* @param libraries an Array of AntLibrary objects containing the
* configuration of Ant for this build.
*
* @throws ConfigException when the project cannot be setup with the
* given configuration
*/
private void setupFrame(Project project, AntLibrary[] libraries)
throws ConfigException {
this.project = project;
for (int i = 0; i < libraries.length; ++i) {
addLibrary(libraries[i]);
}
try {
String base = project.getBase();
@@ -123,74 +185,149 @@ public class ExecutionFrame {
+ "\" is not valid", e, project.getLocation());
}
// We create a set of converters from the converter definitions we
// have been given and initialise them. They should be AntConverters
setupConverters(converterDefs);
for (Iterator i = project.getImportedProjectNames(); i.hasNext();) {
String importName = (String)i.next();
Project importedProject = project.getImportedProject(importName);
String importNamespace
= namespace == null ? importName : namespace + ":" + importName;
ExecutionFrame importedFrame
= new ExecutionFrame(importedProject, taskDefs, converterDef s, importNamespace);
= new ExecutionFrame(importedProject, librarie s, importNamespace);
importedFrames.put(importName, importedFrame);
}
}
public URL getBaseURL() {
return baseURL;
/**
* Add a configuration library to this execution frame. The library
* will contain task definitions, converters, apsect handler definitions,
* etc.
*
* @param library the configuration library to add to this frame.
*
* @throws ConfigException if the items in the library cannot be configured.
*/
public void addLibrary(AntLibrary library) throws ConfigException {
for (Iterator i = library.getTaskDefinitions(); i.hasNext(); ) {
TaskDefinition taskDefinition = (TaskDefinition)i.next();
addTaskDefinition(taskDefinition);
}
for (Iterator i = library.getConverterDefinitions(); i.hasNext(); ) {
ConverterDefinition converterDef = (ConverterDefinition)i.next();
addConverterDefinition(converterDef);
}
for (Iterator i = library.getAspectDefinitions(); i.hasNext(); ) {
AspectDefinition aspectDef = (AspectDefinition)i.next();
addAspectHandler(aspectDef);
}
}
private void setupConverters(Map converterDefs) throws ConfigException {
converters = new HashMap();
for (Iterator i = converterDefs.values().iterator(); i.hasNext(); ) {
ConverterDefinition converterDef = (ConverterDefinition)i.next();
boolean targetLoaded = false;
try {
Class targetClass = converterDef.getTargetClass();
targetLoaded = false;
Class converterClass = converterDef.getConverterClass();
Converter converter = (AntConverter)converterClass.newInstance();
if (converter instanceof AntConverter) {
((AntConverter)converter).init(this);
}
converters.put(targetClass, converter);
}
catch (ClassNotFoundException e) {
if (targetLoaded) {
throw new ConfigException("Unable to load converter class for "
+ converterDef.getConverterClassName()
+ " in converter from " + converterDef.getLibraryURL()
, e);
}
else {
throw new ConfigException("Unable to load target class "
+ converterDef.getTargetClassName()
+ " in converter from " + converterDef.getLibraryURL()
, e);
}
/**
* Add a task definition to this execution frame
*
* @param taskDefinition the TaskDefinition to be added to the project.
*/
public void addTaskDefinition(TaskDefinition taskDefinition) {
String taskName = taskDefinition.getName();
taskDefs.put(taskName, taskDefinition);
}
/**
* Add a aspect handler definition to this execution frame
*
* @param taskDefinition the TaskDefinition to be added to the project.
*
* @throws ConfigException if the aspect handler cannot be created or configured.
*/
public void addAspectHandler(AspectDefinition aspectDefinition)
throws ConfigException {
String aspectPrefix = aspectDefinition.getAspectPrefix();
try {
Class aspectHandlerClass = aspectDefinition.getAspectHandlerClass();
aspectHandlers.put(aspectPrefix, aspectHandlerClass);
}
catch (ClassNotFoundException e) {
throw new ConfigException("Unable to load aspect handler class for "
+ aspectDefinition.getAspectHandlerClassName()
+ " in converter from " + aspectDefinition.getLibraryURL(),
e);
}
}
/**
* Add a converter definition to this library.
*
* The converter is created immediately to handle conversions
* when items are being configured. If the converter is an instance of
* an AntConverter, the converter is configured with this execution
* frame giving it the context it needs to resolve items relative to the
* project's base, etc.
*
* @param converterDef the converter definition to load
*
* @throws ConfigException if the converter cannot be created or configured.
*/
public void addConverterDefinition(ConverterDefinition converterDef) throws ConfigException {
boolean targetLoaded = false;
try {
Class targetClass = converterDef.getTargetClass();
targetLoaded = false;
Class converterClass = converterDef.getConverterClass();
Converter converter = (AntConverter)converterClass.newInstance();
if (converter instanceof AntConverter) {
((AntConverter)converter).init(this);
}
catch (InstantiationException e) {
throw new ConfigException("Unable to instantiate converter class "
+ converterDef.getTargetClassName()
+ " in converter from " + converterDef.getLibraryURL()
, e);
converters.put(targetClass, converter);
}
catch (ClassNotFoundException e) {
if (targetLoaded) {
throw new ConfigException("Unable to load converter class for "
+ converterDef.getConverterClassName()
+ " in converter from " + converterDef.getLibraryURL(),
e);
}
catch (IllegalAccessException e) {
throw new ConfigException("Unable to access converter class "
else {
throw new ConfigException("Unable to load target class "
+ converterDef.getTargetClassName()
+ " in converter from " + converterDef.getLibraryURL()
, e);
+ " in converter from " + converterDef.getLibraryURL(),
e);
}
}
catch (InstantiationException e) {
throw new ConfigException("Unable to instantiate converter class "
+ converterDef.getTargetClassName()
+ " in converter from " + converterDef.getLibraryURL(),
e);
}
catch (IllegalAccessException e) {
throw new ConfigException("Unable to access converter class "
+ converterDef.getTargetClassName()
+ " in converter from " + converterDef.getLibraryURL(),
e);
}
}
/**
* Get the bae URL of this frame. This will either be specified by the project's
* base attribute or be derived implicitly from the project's location.
*/
public URL getBaseURL() {
return baseURL;
}
public void addBuildListener(BuildListener listener) {
for (Iterator i = getImportedFrames(); i.hasNext(); ) {
ExecutionFrame subFrame = (ExecutionFrame)i.next();
subFrame.addBuildListener(listener);
}
eventSupport.addBuildListener(listener);
}
public void removeBuildListener(BuildListener listener) {
for (Iterator i = getImportedFrames(); i.hasNext(); ) {
ExecutionFrame subFrame = (ExecutionFrame)i.next();
subFrame.removeBuildListener(listener);
}
eventSupport.removeBuildListener(listener);
}
@@ -257,26 +394,6 @@ public class ExecutionFrame {
return namespace == null ? name : namespace + ":" + name;
}
/**
* Get the relative name of something with respect to this
* execution frame.
*
* @param fullname the fully qualified name.
*
* @return the relative version of the given name
*/
public String getRelativeName(String fullname) {
if (namespace == null) {
return fullname;
}
int index = fullname.indexOf(namespace);
if (index != 0) {
return fullname;
}
return fullname.substring(namespace.length() + 1);
}
/**
* Execute the given target's tasks
*
@@ -297,32 +414,140 @@ public class ExecutionFrame {
}
/**
* Initialis e the frame by executing the project level tasks if any
* Initializ e the frame by executing the project level tasks if any
*/
public void initialise() throws ExecutionException, ConfigException {
public void initialize() throws ExecutionException, ConfigException {
for (Iterator i = getImportedFrames(); i.hasNext(); ) {
ExecutionFrame subFrame = (ExecutionFrame)i.next();
subFrame.initialize();
}
Iterator taskIterator = project.getTasks();
executeTasks(taskIterator);
}
public void fillinDependencyOrder(String targetName, List dependencyOrder,
Map state, Stack visiting) throws ConfigException {
String fullTargetName = getQualifiedName(targetName);
if (state.get(fullTargetName) == VISITED) {
return;
}
Target target = getProject().getTarget(targetName);
if (target == null) {
StringBuffer sb = new StringBuffer("Target `");
sb.append(targetName);
sb.append("' does not exist in this project. ");
if (!visiting.empty()) {
String parent = (String)visiting.peek();
sb.append("It is used from target `");
sb.append(parent);
sb.append("'.");
}
throw new ConfigException(new String(sb), getProject().getLocation());
}
state.put(fullTargetName, VISITING);
visiting.push(fullTargetName);
for (Iterator i = target.getDependencies(); i.hasNext(); ) {
String dependency = (String)i.next();
try {
ExecutionFrame dependencyFrame = getRelativeFrame(dependency);
if (dependencyFrame == null) {
StringBuffer sb = new StringBuffer("Target `");
sb.append(dependency);
sb.append("' does not exist in this project. ");
throw new ConfigException(new String(sb), target.getLocation());
}
String fullyQualifiedName = getQualifiedName(dependency);
String dependencyState = (String)state.get(fullyQualifiedName);
if (dependencyState == null) {
dependencyFrame.fillinDependencyOrder(getNameInFrame(dependency), dependencyOrder,
state, visiting);
}
else if (dependencyState == VISITING) {
String circleDescription
= getCircularDesc(dependency, visiting);
throw new ConfigException(circleDescription, target.getLocation());
}
}
catch (ExecutionException e) {
throw new ConfigException(e.getMessage(), e, target.getLocation());
}
}
state.put(fullTargetName, VISITED);
String poppedNode = (String)visiting.pop();
if (poppedNode != fullTargetName) {
throw new ConfigException("Problem determining dependencies " +
" - expecting '" + fullTargetName +
"' but got '" + poppedNode + "'");
}
dependencyOrder.add(fullTargetName);
}
private String getCircularDesc(String end, Stack visitingNodes) {
StringBuffer sb = new StringBuffer("Circular dependency: ");
sb.append(end);
String c;
do {
c = (String)visitingNodes.pop();
sb.append(" <- ");
sb.append(c);
} while(!c.equals(end));
return new String(sb);
}
/**
* Check whether the targets in this frame and its subframes are OK
*/
public void checkTargets(List dependencyOrder, Map state, Stack visiting)
throws ConfigException {
// get the targets and just iterate through them.
for (Iterator i = getProject().getTargets(); i.hasNext();) {
Target target = (Target)i.next();
fillinDependencyOrder(target.getName(),
dependencyOrder, state, visiting);
}
// Now do the subframes.
for (Iterator i = getImportedFrames(); i.hasNext();) {
ExecutionFrame importedFrame = (ExecutionFrame)i.next();
importedFrame.checkTargets(dependencyOrder, state, visiting);
}
}
private ExecutionTask getConfiguredExecutionTask(TaskElement model)
/**
* Create a Task and configure it according to the given model.
*/
private Task configureTask(TaskElement model)
throws ConfigException, ExecutionException {
String taskType = model.getType();
TaskDefinition taskDefinition = (TaskDefinition)taskDefs.get(taskType);
if (taskDefinition == null) {
throw new ConfigException("There is no task defintion for tasks of type <"
throw new ConfigException("There is no defintion for tasks of type <"
+ taskType + ">", model.getLocation());
}
try {
Class executionTaskClass = taskDefinition.getExecutionTaskClass();
ExecutionTask executionTask = (ExecutionTask)executionTaskClass.newInstance();
executionTask.setExecutionFrame(this);
executionTask.setBuildEventSupport(eventSupport);
executionTask.setBuildElement(model);
configureElement(executionTask, model);
return executionTask;
Class elementClass = taskDefinition.getExecutionTaskClass();
Object element = elementClass.newInstance();
Task task = null;
if (element instanceof Task) {
// create a Task context for the Task
task = (Task)element;
}
else {
task = new TaskAdapter(taskType, element);
}
ExecutionContext context = new ExecutionContext(this, eventSupport, model);
task.setTaskContext(context);
configureElement(element, model);
return task;
}
catch (ClassNotFoundException e) {
throw new ConfigException("Execution class " + taskDefinition.getTaskClassName()
@@ -339,43 +564,51 @@ public class ExecutionFrame {
e, model.getLocation());
}
}
/**
* Run the tasks returned by the give iterator
*
* @param taskIterator the iterator giving the tasks to execute
*/
public void executeTasks(Iterator taskIterator) throws ExecutionException, ConfigException {
Task task = null;
try {
while (taskIterator.hasNext()) {
task = (Task)taskIterator.next();
private List getActiveAspects(BuildElement model)
throws ConfigException, ExecutionException,
ClassIntrospectionException, ConversionException {
List activeAspects = new ArrayList();
for (Iterator i = model.getAspectNames(); i.hasNext();) {
String aspectPrefix = (String)i.next();
Class aspectHandlerClass = (Class)aspectHandlers.get(aspectPrefix);
if (aspectHandlerClass != null) {
try {
ExecutionTask executionTask = getConfiguredExecutionTask(task);
eventSupport.fireTaskStarted(this, task);
executionTask.execute();
}
catch (ExecutionException e) {
if (e.getLocation() == null || e.getLocation() == Location.UNKNOWN_LOCATION) {
e.setLocation(task.getLocation());
AspectHandler aspectHandler
= (AspectHandler)aspectHandlerClass.newInstance();
ClassIntrospector introspector = getIntrospector(aspectHandlerClass);
ExecutionContext context = new ExecutionContext(this, eventSupport, model);
aspectHandler.setAspectContext(context);
Map aspectAttributes = model.getAspectAttributes(aspectPrefix);
for (Iterator j = aspectAttributes.keySet().iterator(); j.hasNext();) {
String attributeName = (String)j.next();
String attributeValue = (String)aspectAttributes.get(attributeName);
introspector.setAttribute(aspectHandler, attributeName,
replacePropertyRefs(attributeValue));
}
throw e;
activeAspects.add(aspectHandler) ;
}
catch (ConfigException e) {
if (e.getLocation() == null || e.getLocation() == Location.UNKNOWN_LOCATION) {
e.setLocation(task.getLocation());
}
throw e;
catch (InstantiationException e) {
throw new ConfigException("Unable to instantiate aspect handler class "
+ aspectHandlerClass,
e);
}
catch (IllegalAccessException e) {
throw new ConfigException("Unable to access aspect handler class "
+ aspectHandlerClass,
e);
}
eventSupport.fireTaskFinished(this, task, null);
}
}
catch (RuntimeException e) {
eventSupport.fireTaskFinished(this, task, e);
throw e;
}
}
}
return activeAspects;
}
/**
* Configure an element according to the given model.
*/
private void configureElement(Object element, TaskElement model)
throws ExecutionException, ConfigException {
@@ -401,11 +634,9 @@ public class ExecutionFrame {
if (element instanceof TaskContainer &&
!introspector.supportsNestedElement(nestedElementModel.getType())) {
ExecutionTask nestedExecutionTask
= getConfiguredExecutionTask(nestedElementModel);
Task nestedTask = configureTask(nestedElementModel);
TaskContainer container = (TaskContainer)element;
container.addExecution Task(nestedExecution Task);
container.addTask(nestedTask);
}
else {
Object nestedElement
@@ -413,6 +644,11 @@ public class ExecutionFrame {
configureElement(nestedElement, nestedElementModel);
}
}
List aspects = getActiveAspects(model);
for (Iterator i = aspects.iterator(); i.hasNext(); ) {
AspectHandler aspectHandler = (AspectHandler)i.next();
aspectHandler.afterConfigElement(element);
}
}
catch (ClassIntrospectionException e) {
throw new ExecutionException(e, model.getLocation());
@@ -422,6 +658,42 @@ public class ExecutionFrame {
}
}
/**
* Run the tasks returned by the give iterator
*
* @param taskIterator the iterator giving the tasks to execute
*/
public void executeTasks(Iterator taskIterator) throws ExecutionException, ConfigException {
TaskElement task = null;
try {
while (taskIterator.hasNext()) {
task = (TaskElement)taskIterator.next();
try {
Task configuredTask = configureTask(task);
eventSupport.fireTaskStarted(this, task);
configuredTask.execute();
}
catch (ExecutionException e) {
if (e.getLocation() == null || e.getLocation() == Location.UNKNOWN_LOCATION) {
e.setLocation(task.getLocation());
}
throw e;
}
catch (ConfigException e) {
if (e.getLocation() == null || e.getLocation() == Location.UNKNOWN_LOCATION) {
e.setLocation(task.getLocation());
}
throw e;
}
eventSupport.fireTaskFinished(this, task, null);
}
}
catch (RuntimeException e) {
eventSupport.fireTaskFinished(this, task, e);
throw e;
}
}
private ClassIntrospector getIntrospector(Class c) {
if (introspectors.containsKey(c)) {
return (ClassIntrospector)introspectors.get(c);
@@ -510,7 +782,7 @@ public class ExecutionFrame {
* Given a name of an object, get the frame relative from this frame that
* contains that object.
*/
private ExecutionFrame getRelativeFrame(String name) throws ExecutionException {
public ExecutionFrame getRelativeFrame(String name) throws ExecutionException {
int index = name.lastIndexOf(":");
if (index == -1) {
return this;
@@ -534,7 +806,7 @@ public class ExecutionFrame {
/**
* Get the name of an object in its frame
*/
private String getNameInFrame(String name) {
public String getNameInFrame(String name) {
int index = name.lastIndexOf(":");
if (index == -1) {
return name;
@@ -553,7 +825,7 @@ public class ExecutionFrame {
/**
* Get a value from this frame or any imported frame
*/
private Object getDataValue(String name) throws ExecutionException {
public Object getDataValue(String name) throws ExecutionException {
ExecutionFrame frame = getRelativeFrame(name);
return frame.getDirectDataValue(getNameInFrame(name));
}
@@ -586,5 +858,55 @@ public class ExecutionFrame {
private boolean isDirectDataValueSet(String name) {
return dataValues.containsKey(name);
}
public void runBuild(List targetNames) throws AntException {
Throwable buildFailureCause = null;
try {
eventSupport.fireBuildStarted(this, project);
initialize();
if (targetNames.isEmpty()) {
// we just execute the default target if any
String defaultTarget = project.getDefaultTarget();
if (defaultTarget != null) {
executeTarget(defaultTarget);
}
}
else {
for (Iterator i = targetNames.iterator(); i.hasNext();) {
executeTarget((String)i.next());
}
}
eventSupport.fireBuildFinished(this, project, null);
}
catch (RuntimeException e) {
buildFailureCause = e;
throw e;
}
catch (AntException e) {
buildFailureCause = e;
throw e;
}
finally {
eventSupport.fireBuildFinished(this, project, buildFailureCause);
}
}
public void executeTarget(String targetName) throws ExecutionException, ConfigException {
// to execute a target we must determine its dependencies and
// execute them in order.
Map state = new HashMap();
Stack visiting = new Stack();
List dependencyOrder = new ArrayList();
ExecutionFrame startingFrame = getRelativeFrame(targetName);
startingFrame.fillinDependencyOrder(getNameInFrame(targetName),
dependencyOrder, state, visiting);
// Now tell each frame to execute the targets required
for (Iterator i = dependencyOrder.iterator(); i.hasNext();) {
String fullTargetName = (String)i.next();
ExecutionFrame frame = getRelativeFrame(fullTargetName);
frame.executeTargetTasks(getNameInFrame(fullTargetName));
}
}
}