diff --git a/docs/manual/CoreTasks/typedef.html b/docs/manual/CoreTasks/typedef.html
index 9b439065e..c2238f736 100644
--- a/docs/manual/CoreTasks/typedef.html
+++ b/docs/manual/CoreTasks/typedef.html
@@ -9,23 +9,43 @@
Description
-Adds a data type definition to the current project, such that this
-new type can be used in the current project. Two attributes are
-needed, the name that identifies this data type uniquely, and the full
-name of the class (including the packages) that implements this
-type.
-You can also define a group of data types at once using the file or
-resource attributes. These attributes point to files in the format of
-Java property files. Each line defines a single data type in the
-format:
-
-typename=fully.qualified.java.classname
-
-Typedef should be used to add your own tasks and types to the system. Data
-types are things like paths or filesets that can be defined at
-the project level and referenced via their ID attribute.
-Custom data types usually need custom tasks to put them to good use.
+
+ Adds a task or a data type definition to the current project
+ such that this new type or task can be used in the current project.
+
+
+ Tasks are any class that extend org.apache.tools.ant.Task or
+ a class that is adapted to a Task using an adapter class.
+
+
+ Data types are things like paths or
+ filesets that can be defined at
+ the project level and referenced via their ID attribute.
+ Custom data types usually need custom tasks to put them to good use.
+
+
+ Two attributes are needed to make a definition,
+ the name that identifies this data type uniquely, and the full
+ name of the class (including the packages) that implements this
+ type.
+
+
+ You can also define a group of definitions at once using the file or
+ resource attributes. These attributes point to files in the format of
+ Java property files or an xml format.
+
+
+ For property files each line defines a single data type in the
+ format:
+
+ typename=fully.qualified.java.classname
+
+
+
+ The xml format is described below in the Antlib
+ section.
+
+
Parameters
@@ -55,6 +75,19 @@ the project level and referenced via their ID attribute.
Name of the resouce to load definitions from. |
No |
+
+ format |
+ The format of the file or resource. The values
+ are "properties" or "xml". If the value is "properties" the file/resource
+ is a property file contains name to classname pairs. If the value
+ is "xml", the file/resource is an xml file/resource structured according
+ to Antlib.
+ The default is "properties" unless the file/resorce name ends with
+ ".xml", in which case the format attribute will have the value "xml".
+ (introduced in ant1.6)
+ |
+ No |
+
classpath | the classpath to
use when looking up classname . |
| No |
-Parameters specified as nested elements
-classpath
-Typedef
's classpath attribute is a
-PATH like structure and can also be set
-via a nested classpath element.
+ Parameters specified as nested elements
+ classpath
+ Typedef
's classpath attribute is a
+ PATH like structure and can also be set
+ via a nested classpath element.
+
Examples
- <typedef name="urlset" classname="com.mydomain.URLSet"/>
-makes a data type called urlset
available to Ant. The
-class com.mydomain.URLSet
implements this type.
+ The following fragment defines define a type called urlset.
+
+ <typedef name="urlset" classname="com.mydomain.URLSet"/>
+ The data type is now availabe to Ant. The
+ class com.mydomain.URLSet
implements this type.
+
+ Assuming a class org.acme.ant.RunnableAdapter that
+ extends Task and implements org.apache.tools.ant.TypeAdapter,
+ and in the execute method invokes run on the proxied object,
+ one may use a Runnable class as an Ant task. The following fragment
+ defines a task called runclock.
+
+ <typedef name="runclock"
+ classname="com.acme.ant.RunClock"
+ adapter="org.acme.ant.RunnableAdapter"/>
+
+
+ An antlib file is an xml file with a root element of "antlib".
+ Antlib is actually a Sequential task with
+ special treatment for tasks that are ant definition tasks - like typedef
+ and Taskdef.
+
+ A group of tasks and types may be defined together in an antlib
+ file. For example the file sample.xml contains the following:
+
+ <?xml version="1.0"?>
+ <antlib>
+ <typedef name="if" classname="org.acme.ant.If"/>
+ <typedef name="scriptpathmapper"
+ classname="org.acme.ant.ScriptPathMapper"
+ onerror="ignore"/>
+ </antlib>
+
+ It defines two types or tasks, if and scriptpathmapper.
+ This antlib file may be used in a build script as follows:
+
+ <typedef file="sample.xml"/>
+
Copyright © 2001-2003 Apache Software
diff --git a/src/etc/testcases/taskdefs/antlib.xml b/src/etc/testcases/taskdefs/antlib.xml
new file mode 100644
index 000000000..b23023fa4
--- /dev/null
+++ b/src/etc/testcases/taskdefs/antlib.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/etc/testcases/taskdefs/test.antlib.xml b/src/etc/testcases/taskdefs/test.antlib.xml
new file mode 100644
index 000000000..a2760a076
--- /dev/null
+++ b/src/etc/testcases/taskdefs/test.antlib.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/src/main/org/apache/tools/ant/helper/ProjectHelper2.java b/src/main/org/apache/tools/ant/helper/ProjectHelper2.java
index 3d1d94e5b..8b870809e 100644
--- a/src/main/org/apache/tools/ant/helper/ProjectHelper2.java
+++ b/src/main/org/apache/tools/ant/helper/ProjectHelper2.java
@@ -57,8 +57,10 @@ package org.apache.tools.ant.helper;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.InputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
+import java.net.URL;
import java.util.Hashtable;
import java.util.Stack;
@@ -75,6 +77,7 @@ import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
import org.apache.tools.ant.RuntimeConfigurable;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Location;
@@ -102,6 +105,31 @@ public class ProjectHelper2 extends ProjectHelper {
*/
private static FileUtils fu = FileUtils.newFileUtils();
+ /**
+ * Parse an unknown element from a url
+ *
+ * @param project the current project
+ * @param source the url containing the task
+ * @return a configured task
+ * @exception BuildException if an error occurs
+ */
+ public UnknownElement parseUnknownElement(Project project, URL source)
+ throws BuildException {
+ Target dummyTarget = new Target();
+ dummyTarget.setProject(project);
+
+ AntXMLContext context = new AntXMLContext(project);
+ context.addTarget(dummyTarget);
+ context.setImplicitTarget(dummyTarget);
+
+ parse(context.getProject(), source,
+ new RootHandler(context, elementHandler));
+ Task[] tasks = dummyTarget.getTasks();
+ if (tasks.length != 1) {
+ throw new BuildException("No tasks defined");
+ }
+ return (UnknownElement) tasks[0];
+ }
/**
* Parse a source xml input.
*
@@ -127,11 +155,11 @@ public class ProjectHelper2 extends ProjectHelper {
// we are in an imported file.
context.setIgnoreProjectTag(true);
context.getCurrentTarget().startImportedTasks();
- parse(project, source, new RootHandler(context));
+ parse(project, source, new RootHandler(context, mainHandler));
context.getCurrentTarget().endImportedTasks();
} else {
// top level file
- parse(project, source, new RootHandler(context));
+ parse(project, source, new RootHandler(context, mainHandler));
// Execute the top-level target
context.getImplicitTarget().execute();
}
@@ -152,22 +180,33 @@ public class ProjectHelper2 extends ProjectHelper {
AntXMLContext context = handler.context;
File buildFile = null;
+ URL url = null;
+ String buildFileName = null;
if (source instanceof File) {
buildFile = (File) source;
-// } else if (source instanceof InputStream) {
-// } else if (source instanceof URL) {
-// } else if (source instanceof InputSource) {
+ buildFile = new File(buildFile.getAbsolutePath());
+ context.setBuildFile(buildFile);
+ buildFileName = buildFile.toString();
+// } else if (source instanceof InputStream ) {
+ } else if (source instanceof URL) {
+ if (handler.getCurrentAntHandler() != elementHandler) {
+ throw new BuildException(
+ "Source " + source.getClass().getName()
+ + " not supported by this plugin for "
+ + " non task xml");
+ }
+ url = (URL) source;
+ buildFileName = url.toString();
+// } else if (source instanceof InputSource ) {
} else {
throw new BuildException("Source " + source.getClass().getName()
- + " not supported by this plugin");
+ + " not supported by this plugin");
}
- FileInputStream inputStream = null;
+ InputStream inputStream = null;
InputSource inputSource = null;
- buildFile = new File(buildFile.getAbsolutePath());
- context.setBuildFile(buildFile);
try {
/**
@@ -175,13 +214,21 @@ public class ProjectHelper2 extends ProjectHelper {
*/
XMLReader parser = JAXPUtils.getNamespaceXMLReader();
- String uri = fu.toURI(buildFile.getAbsolutePath());
+ String uri = null;
+ if (buildFile != null) {
+ uri = fu.toURI(buildFile.getAbsolutePath());
+ inputStream = new FileInputStream(buildFile);
+ } else {
+ inputStream = url.openStream();
+ uri = url.toString(); // ?? OK ??
+ }
- inputStream = new FileInputStream(buildFile);
inputSource = new InputSource(inputStream);
- inputSource.setSystemId(uri);
- project.log("parsing buildfile " + buildFile
- + " with URI = " + uri, Project.MSG_VERBOSE);
+ if (uri != null) {
+ inputSource.setSystemId(uri);
+ }
+ project.log("parsing buildfile " + buildFileName
+ + "with URI = " + uri, Project.MSG_VERBOSE);
DefaultHandler hb = handler;
@@ -357,13 +404,22 @@ public class ProjectHelper2 extends ProjectHelper {
* Creates a new RootHandler instance.
*
* @param context The context for the handler.
+ * @param rootHandler The handler for the root element.
*/
- public RootHandler(AntXMLContext context) {
- currentHandler = ProjectHelper2.mainHandler;
+ public RootHandler(AntXMLContext context, AntHandler rootHandler) {
+ currentHandler = rootHandler;
antHandlers.push(currentHandler);
this.context = context;
}
+ /**
+ * Returns the current ant handler object.
+ * @return the current ant handler.
+ */
+ public AntHandler getCurrentAntHandler() {
+ return currentHandler;
+ }
+
/**
* Resolves file: URIs relative to the build file.
*
diff --git a/src/main/org/apache/tools/ant/taskdefs/Antlib.java b/src/main/org/apache/tools/ant/taskdefs/Antlib.java
new file mode 100644
index 000000000..c96b31355
--- /dev/null
+++ b/src/main/org/apache/tools/ant/taskdefs/Antlib.java
@@ -0,0 +1,190 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "Ant" and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.helper.ProjectHelper2;
+import org.apache.tools.ant.UnknownElement;
+
+
+/**
+ * Antlib task.
+ *
+ * @author Peter Reilly
+ *
+ * @since Ant 1.6
+ */
+public class Antlib extends Task implements TaskContainer {
+ //
+ // Static
+ //
+
+ /** The name of this task */
+ public static final String TAG = "antlib";
+
+ /**
+ * Static method to read an ant lib definition from
+ * a url.
+ *
+ * @param project the current project
+ * @param antlibUrl the url to read the definitions from
+ * @return the ant lib task
+ */
+ public static Antlib createAntlib(Project project, URL antlibUrl) {
+ // Check if we can contact the URL
+ try {
+ antlibUrl.openConnection().connect();
+ } catch (IOException ex) {
+ throw new BuildException(
+ "Unable to find " + antlibUrl, ex);
+ }
+ // Should be safe to parse
+ try {
+ ProjectHelper2 parser = new ProjectHelper2();
+ UnknownElement ue =
+ parser.parseUnknownElement(project, antlibUrl);
+ // Check name is "antlib"
+ if (!(ue.getTag().equals(TAG))) {
+ throw new BuildException(
+ "Unexpected tag " + ue.getTag() + " expecting " +
+ TAG, ue.getLocation());
+ }
+ Antlib antlib = new Antlib();
+ antlib.setProject(project);
+ antlib.setLocation(ue.getLocation());
+ antlib.init();
+ ue.configure(antlib);
+ return antlib;
+ } catch (BuildException ex) {
+ Location location = ex.getLocation();
+ if (location == null) {
+ throw ex;
+ }
+ throw new BuildException(
+ "Error in "
+ + System.getProperty("line.separator")
+ + location.toString()
+ + " " + ex.getMessage());
+ }
+ }
+
+
+ //
+ // Instance
+ //
+ private ClassLoader classLoader;
+ private String prefix;
+ private List tasks = new ArrayList();
+
+ /**
+ * Set the class loader for this antlib.
+ * This class loader is used for any tasks that
+ * derive from Definer.
+ *
+ * @param classLoader the class loader
+ */
+ protected void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ private ClassLoader getClassLoader() {
+ if (classLoader == null) {
+ classLoader = Antlib.class.getClassLoader();
+ }
+ return classLoader;
+ }
+
+ /**
+ * add a task to the list of tasks
+ *
+ * @param nestedTask Nested task to execute in antlibe
+ */
+ public void addTask(Task nestedTask) {
+ tasks.add(nestedTask);
+ }
+
+ /**
+ * Execute the nested tasks, setting the classloader for
+ * any tasks that derive from Definer.
+ */
+ public void execute() {
+ for (Iterator i = tasks.iterator(); i.hasNext();) {
+ UnknownElement ue = (UnknownElement) i.next();
+ ue.maybeConfigure();
+ Task t = ue.getTask();
+ if (t == null) {
+ continue;
+ }
+ if (t instanceof Definer) {
+ Definer d = (Definer) t;
+ d.setInternalClassLoader(getClassLoader());
+ }
+ t.init();
+ t.execute();
+ }
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/taskdefs/Definer.java b/src/main/org/apache/tools/ant/taskdefs/Definer.java
index 67f519d9b..c7ba90e3c 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Definer.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Definer.java
@@ -59,12 +59,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
+import java.util.Locale;
import java.util.Properties;
import org.apache.tools.ant.AntTypeDefinition;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.ComponentHelper;
import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
@@ -88,6 +90,7 @@ public abstract class Definer extends Task {
private String resource;
private ClasspathUtils.Delegate cpDelegate;
+ private int format = Format.PROPERTIES;
private boolean definerSet = false;
private ClassLoader internalClassLoader;
private int onError = OnError.FAIL;
@@ -129,6 +132,24 @@ public abstract class Definer extends Task {
}
}
+ /**
+ * Enumerated type for format attribute
+ *
+ * @see EnumeratedAttribute
+ */
+ public static class Format extends EnumeratedAttribute {
+ /** Enumerated values */
+ public static final int PROPERTIES = 0, XML = 1;
+
+ /**
+ * get the values
+ * @return an array of the allowed values for this attribute.
+ */
+ public String[] getValues() {
+ return new String[] {"properties", "xml"};
+ }
+ }
+
/**
* What to do if there is an error in loading the class.
*
@@ -143,6 +164,14 @@ public abstract class Definer extends Task {
this.onError = onError.getIndex();
}
+ /**
+ * Sets the format of the file or resource
+ * @param format the enumerated value - xml or properties
+ */
+ public void setFormat(Format format) {
+ this.format = format.getIndex();
+ }
+
/**
* @param reverseLoader if true a delegated loader will take precedence over
* the parent
@@ -289,7 +318,15 @@ public abstract class Definer extends Task {
return;
}
- loadProperties(al, url);
+ if (url.toString().toLowerCase(Locale.US).endsWith(".xml")) {
+ format = Format.XML;
+ }
+
+ if (format == Format.PROPERTIES) {
+ loadProperties(al, url);
+ } else {
+ loadAntlib(al, url);
+ }
}
}
@@ -359,6 +396,30 @@ public abstract class Definer extends Task {
}
}
+ /**
+ * Load an antlib from a url.
+ *
+ * @param classLoader the classloader to use.
+ * @param url the url to load the definitions from.
+ */
+ private void loadAntlib(ClassLoader classLoader, URL url) {
+ try {
+ Antlib antlib = Antlib.createAntlib(getProject(), url);
+ antlib.setClassLoader(classLoader);
+ antlib.perform();
+ } catch (BuildException ex) {
+ Location location = ex.getLocation();
+ if (location == null) {
+ throw ex;
+ }
+ throw new BuildException(
+ "Error in "
+ + System.getProperty("line.separator")
+ + getLocation().toString()
+ + " " + ex.getMessage());
+ }
+ }
+
/**
* create a classloader for this definition
* @return the classloader from the cpDelegate
@@ -543,7 +604,7 @@ public abstract class Definer extends Task {
} catch (NoClassDefFoundError ncdfe) {
String msg = getTaskName() + " A class needed by class "
+ classname + " cannot be found: " + ncdfe.getMessage();
- throw new BuildException(msg, ncdfe, location);
+ throw new BuildException(msg, ncdfe, getLocation());
}
} catch (BuildException ex) {
switch (onError) {
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/AntlibTest.java b/src/testcases/org/apache/tools/ant/taskdefs/AntlibTest.java
new file mode 100644
index 000000000..87ac94777
--- /dev/null
+++ b/src/testcases/org/apache/tools/ant/taskdefs/AntlibTest.java
@@ -0,0 +1,84 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "Ant" and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * @author Peter Reilly
+ */
+public class AntlibTest extends BuildFileTest {
+ public AntlibTest(String name) {
+ super(name);
+ }
+
+ public void setUp() {
+ configureProject("src/etc/testcases/taskdefs/antlib.xml");
+ }
+
+ public void testAntlibFile() {
+ expectLog("antlib.file", "MyTask called");
+ }
+
+ public static class MyTask extends Task {
+ public void execute() {
+ log("MyTask called");
+ }
+ }
+
+}
+