diff --git a/WHATSNEW b/WHATSNEW index 7f4110d15..6e1e4dacd 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -7,9 +7,18 @@ Changes that could break older environments: Fixed bugs: ----------- + * Tasks that iterate over task or type definitions, references or + targets now iterate over copies instead of the live maps to avoid + ConcurrentModificationExceptions if another thread changes the + maps. + Bugzilla Report 48310. + Other changes: -------------- + * Project provides new get methods that return copies instead of the + live maps of task and type definitions, references and targets. + Changes from Ant 1.8.0RC1 TO Ant 1.8.0 ====================================== diff --git a/src/main/org/apache/tools/ant/Project.java b/src/main/org/apache/tools/ant/Project.java index 875352fa3..b2446f267 100644 --- a/src/main/org/apache/tools/ant/Project.java +++ b/src/main/org/apache/tools/ant/Project.java @@ -425,10 +425,10 @@ public class Project implements ResourceFactory { } /** - * Return a copy of the list of build listeners for the project. - * - * @return a list of build listeners for the project - */ + * Return a copy of the list of build listeners for the project. + * + * @return a list of build listeners for the project + */ public Vector getBuildListeners() { synchronized (listenersLock) { Vector r = new Vector(listeners.length); @@ -1024,6 +1024,19 @@ public class Project implements ResourceFactory { return ComponentHelper.getComponentHelper(this).getTaskDefinitions(); } + /** + * Return the current task definition map. The returned map is a + * copy of the "live" definitions. + * + * @return a map of from task name to implementing class + * (String to Class). + * + * @since Ant 1.8.1 + */ + public Map getCopyOfTaskDefinitions() { + return new HashMap(getTaskDefinitions()); + } + /** * Add a new datatype definition. * Attempting to override an existing definition with an @@ -1053,6 +1066,19 @@ public class Project implements ResourceFactory { return ComponentHelper.getComponentHelper(this).getDataTypeDefinitions(); } + /** + * Return the current datatype definition map. The returned + * map is a copy pf the "live" definitions. + * + * @return a map of from datatype name to implementing class + * (String to Class). + * + * @since Ant 1.8.1 + */ + public Map getCopyOfDataTypeDefinitions() { + return new HashMap(getDataTypeDefinitions()); + } + /** * Add a new target to the project. * @@ -1123,6 +1149,16 @@ public class Project implements ResourceFactory { return targets; } + /** + * Return the map of targets. The returned map + * is a copy of the "live" targets. + * @return a map from name to target (String to Target). + * @since Ant 1.8.1 + */ + public Map getCopyOfTargets() { + return new HashMap(targets); + } + /** * Create a new instance of a task, adding it to a list of * created tasks for later invalidation. This causes all tasks @@ -1970,6 +2006,19 @@ public class Project implements ResourceFactory { return references.containsKey(key); } + /** + * Return a map of the references in the project (String to + * Object). The returned hashtable is a copy of the + * "live" references. + * + * @return a map of the references in the project (String to Object). + * + * @since Ant 1.8.1 + */ + public Map getCopyOfReferences() { + return new HashMap(references); + } + /** * Look up a reference by its key (ID). * diff --git a/src/main/org/apache/tools/ant/taskdefs/AntStructure.java b/src/main/org/apache/tools/ant/taskdefs/AntStructure.java index 5468305b0..475cc832f 100644 --- a/src/main/org/apache/tools/ant/taskdefs/AntStructure.java +++ b/src/main/org/apache/tools/ant/taskdefs/AntStructure.java @@ -27,6 +27,7 @@ import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.Enumeration; import java.util.Hashtable; +import java.util.Iterator; import java.util.Vector; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.IntrospectionHelper; @@ -95,22 +96,24 @@ public class AntStructure extends Task { } printer.printHead(out, getProject(), - getProject().getTaskDefinitions(), - getProject().getDataTypeDefinitions()); + new Hashtable(getProject().getTaskDefinitions()), + new Hashtable(getProject().getDataTypeDefinitions())); printer.printTargetDecl(out); - Enumeration dataTypes = getProject().getDataTypeDefinitions().keys(); - while (dataTypes.hasMoreElements()) { - String typeName = (String) dataTypes.nextElement(); + Iterator dataTypes = getProject().getCopyOfDataTypeDefinitions() + .keySet().iterator(); + while (dataTypes.hasNext()) { + String typeName = (String) dataTypes.next(); printer.printElementDecl( out, getProject(), typeName, (Class) getProject().getDataTypeDefinitions().get(typeName)); } - Enumeration tasks = getProject().getTaskDefinitions().keys(); - while (tasks.hasMoreElements()) { - String tName = (String) tasks.nextElement(); + Iterator tasks = getProject().getCopyOfTaskDefinitions().keySet() + .iterator(); + while (tasks.hasNext()) { + String tName = (String) tasks.next(); printer.printElementDecl(out, getProject(), tName, (Class) getProject().getTaskDefinitions().get(tName)); } diff --git a/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java b/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java index 1bba545ae..b6f39248b 100644 --- a/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java +++ b/src/main/org/apache/tools/ant/util/ScriptRunnerBase.java @@ -306,8 +306,8 @@ public abstract class ScriptRunnerBase { project = component.getProject(); addBeans(project.getProperties()); addBeans(project.getUserProperties()); - addBeans(project.getTargets()); - addBeans(project.getReferences()); + addBeans(project.getCopyOfTargets()); + addBeans(project.getCopyOfReferences()); addBean("project", project); addBean("self", component); }