You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by Stefan Reich <do...@drjava.de> on 2001/07/21 19:25:43 UTC
[PATCH] Lazy task class loading
Hi,
currently Ant loads all taskdef classes (as defined in defaults.properties) on startup. This mechanism has at least 3 drawbacks:
-It can take up to one second or longer (if you use ant for everyday development, you will notice the difference)
-It wastes memory
-It completely lacks descriptive error messages. For example, if you use <junit>, but junit.jar isn't in your classpath, you only get a default message telling you something about optional.jar. I've been puzzled as to why tasks couldn't be loaded more than once.
I would like to submit a patch that solves all three problems. I also took care to ensure that the public interface of org.apache.tools.ant.Project is backward compatible.
-Stefan
Here is the patch against recent CVS (I already added @author tags for your convenience ;-):
Index: main/org/apache/tools/ant/Project.java
===================================================================
RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/Project.java,v
retrieving revision 1.64
diff -u -r1.64 Project.java
--- main/org/apache/tools/ant/Project.java 2001/07/17 12:12:39 1.64
+++ main/org/apache/tools/ant/Project.java 2001/07/21 17:03:14
@@ -68,6 +68,7 @@
* file paths at runtime as well as defining various project properties.
*
* @author duncan@x180.com
+ * @author Stefan Reich <a href="mailto:doc@drjava.de">doc@drjava.de</a>
*/
public class Project {
@@ -101,7 +102,8 @@
private Hashtable references = new Hashtable();
private String defaultTarget;
private Hashtable dataClassDefinitions = new Hashtable();
- private Hashtable taskClassDefinitions = new Hashtable();
+ private TaskDefinitionHashtable taskClassDefinitions = new TaskDefinitionHashtable();
+
private Hashtable targets = new Hashtable();
private Hashtable filters = new Hashtable();
private File baseDir;
@@ -114,6 +116,27 @@
/** The system classloader - may be null */
private ClassLoader systemLoader = null;
+ public static class TaskDefinitionHashtable extends Hashtable {
+ /** lazily call Class.forName */
+ public Object get(Object key) {
+ Object value = super.get(key);
+ if (value instanceof String) {
+ try {
+ value = Class.forName((String) value);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e.toString());
+ } catch (NoClassDefFoundError e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+ return value;
+ }
+
+ public String getClassName(Object key) {
+ return (String) super.get(key);
+ }
+ }
+
static {
// Determine the Java version by looking at available classes
@@ -161,19 +184,23 @@
props.load(in);
in.close();
+ long startTime = System.currentTimeMillis();
Enumeration enum = props.propertyNames();
while (enum.hasMoreElements()) {
String key = (String) enum.nextElement();
String value = props.getProperty(key);
- try {
+ /*try {
Class taskClass = Class.forName(value);
addTaskDefinition(key, taskClass);
} catch (NoClassDefFoundError ncdfe) {
// ignore...
} catch (ClassNotFoundException cnfe) {
// ignore...
- }
+ }*/
+ addTaskDefinition(key, value);
}
+ long endTime = System.currentTimeMillis();
+ System.out.println("Class loading took "+(endTime-startTime)+" ms");
} catch (IOException ioe) {
throw new BuildException("Can't load default task list");
}
@@ -367,13 +394,19 @@
log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
}
+ public void addTaskDefinition(String taskName, String taskClass) {
+ String msg = " +User task: " + taskName + " " + taskClass;
+ log(msg, MSG_DEBUG);
+ taskClassDefinitions.put(taskName, taskClass);
+ }
+
public void addTaskDefinition(String taskName, Class taskClass) {
String msg = " +User task: " + taskName + " " + taskClass.getName();
log(msg, MSG_DEBUG);
taskClassDefinitions.put(taskName, taskClass);
}
- public Hashtable getTaskDefinitions() {
+ public TaskDefinitionHashtable getTaskDefinitions() {
return taskClassDefinitions;
}
@@ -445,7 +478,12 @@
}
public Task createTask(String taskType) throws BuildException {
- Class c = (Class) taskClassDefinitions.get(taskType);
+ Class c;
+ try {
+ c = (Class) taskClassDefinitions.get(taskType);
+ } catch (RuntimeException e) {
+ throw new BuildException(e.getMessage());
+ }
if (c == null)
return null;
Index: main/org/apache/tools/ant/taskdefs/Ant.java
===================================================================
RCS file: /home/cvspublic/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Ant.java,v
retrieving revision 1.23
diff -u -r1.23 Ant.java
--- main/org/apache/tools/ant/taskdefs/Ant.java 2001/07/06 11:57:29 1.23
+++ main/org/apache/tools/ant/taskdefs/Ant.java 2001/07/21 17:03:15
@@ -76,6 +76,7 @@
*
*
* @author costin@dnt.ro
+ * @author Stefan Reich <a href="mailto:doc@drjava.de">doc@drjava.de</a>
*/
public class Ant extends Task {
@@ -143,11 +144,11 @@
}
}
- Hashtable taskdefs = project.getTaskDefinitions();
+ Project.TaskDefinitionHashtable taskdefs = project.getTaskDefinitions();
Enumeration et = taskdefs.keys();
while (et.hasMoreElements()) {
String taskName = (String) et.nextElement();
- Class taskClass = (Class) taskdefs.get(taskName);
+ String taskClass = taskdefs.getClassName(taskName);
p1.addTaskDefinition(taskName, taskClass);
}
Re: [PATCH] Lazy task class loading
Posted by Stefan Bodewig <bo...@apache.org>.
Guten Morgen,
On Tue, 7 Aug 2001, Stefan Reich <do...@drjava.de> wrote:
> I have to admit I'm not entirely firm with how Ant uses
> classloaders. Will the subbuild always have a different classloader?
> Won't it have the outer classloader as a parent and automatically
> inherit all classes loaded by the parent?
Tasks defined by <taskdef> will be loaded by a different classloader
than the one used to load plain Ant tasks - and after your changes the
subbuild won't know about these other classloaders. The subbuild will
have the same "main" classloader as plain Ant.
> Either way, it seems to me this problem will be solved if I just
> pass the contents of the TaskDefinitionHashtable as-is.
Yep.
>> And then you want to apply the same logic to data types as well 8-)
>
> Data types...?
FileSet, PatternSet, Path, Mapper and more recent FilterSet and
FileList. See dataClassDefinitions, addDataTypeDefinition,
getDataTypeDefinitions and createDataType in Project.
Since Ant 1.4 will have a <typedef> to accompany <taskdef>
it has the same classloader issues as tasks.
Finally, the <antstructure> task will have to be rewritten to make
sure the classes have actually been loaded, no big deal.
>
>> Stefan
>
> -Stefan (hehe, if this conversation goes on people will be really
> confused about who said what)
Stefan ;-)
Re: [PATCH] Lazy task class loading
Posted by Stefan Reich <do...@drjava.de>.
Stefan,
> OK, I'll admit that your reminder triggers this response (I shouldn't
> be sitting here on front of the screen anyway, but that's a different
> story).
No problem, sometimes we all need two or more kicks until we do something
*g*
> I like the changes to Project, but the way you've implemented the Ant
> task, it will only pass on the class names of already loaded tasks,
> not the actual classes - this not only means you'll have to load them
> once again in the subbuild if you need the same task, but also that
> any custom classloader a user may have specified in <taskdef> will be
> lost.
>
> As a result, tasks that used to work in the main build may throw a
> ClassNotFoundException in the subbuild - or even worse be a different
> implementation of the same class.
I have to admit I'm not entirely firm with how Ant uses classloaders. Will
the subbuild always have a different classloader? Won't it have the outer
classloader as a parent and automatically inherit all classes loaded by the
parent?
Either way, it seems to me this problem will be solved if I just pass the
contents of the TaskDefinitionHashtable as-is. Strings as Strings, Classes
as Classes.
If I'm wrong here, please stop me - otherwise, I'll make that change and
submit a new patch.
> And then you want to apply the same logic to data types as well 8-)
Data types...?
> Stefan
-Stefan (hehe, if this conversation goes on people will be really confused
about who said what)
Re: [PATCH] Lazy task class loading
Posted by Stefan Bodewig <bo...@apache.org>.
OK, I'll admit that your reminder triggers this response (I shouldn't
be sitting here on front of the screen anyway, but that's a different
story).
I like the changes to Project, but the way you've implemented the Ant
task, it will only pass on the class names of already loaded tasks,
not the actual classes - this not only means you'll have to load them
once again in the subbuild if you need the same task, but also that
any custom classloader a user may have specified in <taskdef> will be
lost.
As a result, tasks that used to work in the main build may throw a
ClassNotFoundException in the subbuild - or even worse be a different
implementation of the same class.
And then you want to apply the same logic to data types as well 8-)
Stefan