git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@819284 13f79535-47bb-0310-9956-ffa450edef68master
@@ -174,6 +174,11 @@ Changes that could break older environments: | |||||
(i.e. in a very unlikely event) then the new syntax would yield a | (i.e. in a very unlikely event) then the new syntax would yield a | ||||
different result (an expanded property) than Ant 1.7.1 did. | different result (an expanded property) than Ant 1.7.1 did. | ||||
* A ProjectHelper implementation can now provide the default build file | |||||
name it is expecting, and can specify if they can support a specific build | |||||
file. So Ant is now capable of supporting several ProjectHelper | |||||
implementations, deciding on which to use depending of the input build file. | |||||
Fixed bugs: | Fixed bugs: | ||||
----------- | ----------- | ||||
@@ -37,6 +37,7 @@ | |||||
<a href="develop.html#integration">Source-code Integration</a><br/> | <a href="develop.html#integration">Source-code Integration</a><br/> | ||||
<a href="inputhandler.html">InputHandler</a><br/> | <a href="inputhandler.html">InputHandler</a><br/> | ||||
<a href="antexternal.html">Using Ant Tasks Outside of Ant</a><br/> | <a href="antexternal.html">Using Ant Tasks Outside of Ant</a><br/> | ||||
<a href="projecthelper.html">The Ant frontend: ProjectHelper</a><br/> | |||||
<br/>Tutorials<br/> | <br/>Tutorials<br/> | ||||
<a href="tutorial-HelloWorldWithAnt.html">Hello World with Ant</a><br/> | <a href="tutorial-HelloWorldWithAnt.html">Hello World with Ant</a><br/> | ||||
@@ -0,0 +1,131 @@ | |||||
<!-- | |||||
Licensed to the Apache Software Foundation (ASF) under one or more | |||||
contributor license agreements. See the NOTICE file distributed with | |||||
this work for additional information regarding copyright ownership. | |||||
The ASF licenses this file to You under the Apache License, Version 2.0 | |||||
(the "License"); you may not use this file except in compliance with | |||||
the License. You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
--> | |||||
<html> | |||||
<head> | |||||
<meta http-equiv="Content-Language" content="en-us"> | |||||
<link rel="stylesheet" type="text/css" href="stylesheets/style.css"> | |||||
<title>The Ant frontend: ProjectHelper</title> | |||||
</head> | |||||
<body> | |||||
<h1>The Ant frontend: ProjectHelper</h1> | |||||
<h2><a name="definition">What is a ProjectHelper?</a></h2> | |||||
<p> | |||||
The <code>ProjectHelper</code> in Ant is responsible to parse the build file | |||||
and create java instances representing the build workflow. It also declares which | |||||
kind of file it can parse, and which file name it expects as default input file. | |||||
</p> | |||||
<p> | |||||
So in Ant there is a default <code>ProjectHelper</code> | |||||
(<code>org.apache.tools.ant.helper.ProjectHelper2</code>) which will parse the | |||||
usual build.xml files. And if no build file is specified on the command line, it | |||||
will expect to find a build.xml file. | |||||
</p> | |||||
<p> | |||||
The immediate benefit of a such abstraction it that it is possible to make Ant | |||||
understand other kind of descriptive language than XML. Some experiment have | |||||
been done around a pure java frontend, and a groovy one too (ask the dev mailing | |||||
list for further info about these). | |||||
</p> | |||||
<h2><a name="repository">How is Ant is selecting the proper ProjectHelper</a></h2> | |||||
<p> | |||||
Ant can now know about several implementations of <code>ProjectHelper</code> | |||||
and have to decide which to use for each build file. | |||||
</p> | |||||
<p>So Ant at startup will list the found implementations and will keep it | |||||
ordered as it finds them in an internal 'repository': | |||||
<ul> | |||||
<li>the first to be searched for is the one declared by the system property | |||||
<code>org.apache.tools.ant.ProjectHelper</code> (see | |||||
<a href="running.html#sysprops">Java System Properties</a>);</li> | |||||
<li>then it searches with its class loader for a <code>ProjectHelper</code> | |||||
service declarations in the META-INF: it searches in the classpath for a | |||||
file <code>META-INF/services/org.apache.tools.ant.ProjectHelper</code>. | |||||
This file will just contain the fully qualified name of the | |||||
implementation of <code>ProjectHelper</code> to instanciate;</li> | |||||
<li>it will also search with the system class loader for | |||||
<code>ProjectHelper</code> service declarations in the META-INF;</li> | |||||
<li>last but not least it will add its default <code>ProjectHelper</code> | |||||
that can parse classical build.xml files.</li> | |||||
</ul> | |||||
In case of error while trying to instanciate a <code>ProjectHelper</code>, Ant | |||||
will log an error but still won't stop. If you want further debugging | |||||
info about the <code>ProjectHelper</code> internal 'repository', use the system | |||||
property <code>ant.project-helper-repo.debug</code> and set it to | |||||
<code>true</code>; the full stack trace will then also be printed. | |||||
</p> | |||||
<p> | |||||
Then when Ant is expected to parse a file, it will ask the | |||||
<code>ProjectHelper</code> repository to found an implementation that will be | |||||
able to parse the input file. Actually it will just iterate on the ordered list | |||||
and the first implementation that returns <code>true</code> to | |||||
<code>supportsBuildFile(File buildFile)</code> will be selected. | |||||
</p> | |||||
<p> | |||||
And when Ant is launching and there is no input file specified, it will search for | |||||
a default input file. It will iterate on the list of <code>ProjectHelper</code> | |||||
and will select the first one that expects a default file that actually exist. | |||||
</p> | |||||
<h2><a name="writing">Writing your own ProjectHelper</a></h2> | |||||
<p> | |||||
The class <code>org.apache.tools.ant.ProjectHelper</code> is the API expected to | |||||
be implemented. So write your own <code>ProjectHelper</code> by extending that | |||||
abstract class. You are then expected to implement at least the function | |||||
<code>parse(Project project, Object source)</code>. Note also that your | |||||
implementation will be instanciated by Ant, and it is expecting a default | |||||
constructor with no arguments. | |||||
</p> | |||||
<p> | |||||
Then there are some functions that will help you define what your helper is | |||||
capable of and what is is expecting: | |||||
<ul> | |||||
<li><code>getDefaultBuildFile()</code>: defines which file name is expected if | |||||
none provided</li> | |||||
<li><code>supportsBuildFile(File buildFile)</code>: defines if your parser | |||||
can parse the input file</li> | |||||
</ul> | |||||
</p> | |||||
<p> | |||||
Now that you have your implementation ready, you have to declare it to Ant. Two | |||||
solutions here: | |||||
<ul> | |||||
<li>use the system property <code>org.apache.tools.ant.ProjectHelper</code> | |||||
(see also the <a href="running.html#sysprops">Java System Properties</a>);</li> | |||||
<li>use the service file in META-INF: in the jar you will build with your | |||||
implementation, add a file | |||||
<code>META-INF/services/org.apache.tools.ant.ProjectHelper</code>. | |||||
And then in this file just put the fully qualified name of your | |||||
implementation</li> | |||||
</ul> | |||||
</p> | |||||
</body> | |||||
</html> | |||||
@@ -415,7 +415,6 @@ org.apache.tools.ant.Executor implementation specified here. | |||||
<td>see <a href="sysclasspath.html">its dedicated page</a>, no | <td>see <a href="sysclasspath.html">its dedicated page</a>, no | ||||
default value</td> | default value</td> | ||||
<td>see <a href="sysclasspath.html">its dedicated page</a></td> | <td>see <a href="sysclasspath.html">its dedicated page</a></td> | ||||
</td> | |||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td><code>file.encoding</code></td> | <td><code>file.encoding</code></td> | ||||
@@ -455,18 +454,25 @@ org.apache.tools.ant.Executor implementation specified here. | |||||
</td> | </td> | ||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td><code>websphere.home | |||||
<td><code>websphere.home</code></td> | |||||
<td>path</td> | <td>path</td> | ||||
<td>Points to home directory of websphere. | <td>Points to home directory of websphere. | ||||
see <a href="OptionalTasks/ejb.html#ejbjar_websphere">EJB Tasks</a> | see <a href="OptionalTasks/ejb.html#ejbjar_websphere">EJB Tasks</a> | ||||
</td> | </td> | ||||
</tr> | </tr> | ||||
<tr> | <tr> | ||||
<td><code>XmlLogger.file | |||||
<td><code>XmlLogger.file</code></td> | |||||
<td>filename (default 'log.xml')</td> | <td>filename (default 'log.xml')</td> | ||||
<td>Name for the logfile for <a href="listeners.html#MailLogger">MailLogger</a>. | <td>Name for the logfile for <a href="listeners.html#MailLogger">MailLogger</a>. | ||||
</td> | </td> | ||||
</tr> | </tr> | ||||
<tr> | |||||
<td><code>ant.project-helper-repo.debug</code></td> | |||||
<td>boolean (default 'false')</td> | |||||
<td>Set it to true to enable debuging with Ant's | |||||
<a href="projecthelper.html#repository">ProjectHelper internal repository</a>. | |||||
</td> | |||||
</tr> | |||||
</table> | </table> | ||||
<p> | <p> | ||||
@@ -529,7 +535,7 @@ have some documentation inside.</p> | |||||
Unix(-like) systems</a></h2> | Unix(-like) systems</a></h2> | ||||
<p>If you start Ant as a background process (like in <code>ant | <p>If you start Ant as a background process (like in <code>ant | ||||
&</code>) and the build process creates another process, Ant will | |||||
&</code>) and the build process creates another process, Ant will | |||||
immediately try to read from standard input, which in turn will | immediately try to read from standard input, which in turn will | ||||
most likely suspend the process. In order to avoid this, you must | most likely suspend the process. In order to avoid this, you must | ||||
redirect Ant's standard input or explicitly provide input to each | redirect Ant's standard input or explicitly provide input to each | ||||
@@ -20,6 +20,7 @@ package org.apache.tools.ant; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
import java.io.FileNotFoundException; | |||||
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
@@ -301,6 +302,7 @@ public class Main implements AntMain { | |||||
*/ | */ | ||||
private void processArgs(String[] args) { | private void processArgs(String[] args) { | ||||
String searchForThis = null; | String searchForThis = null; | ||||
boolean searchForFile = false; | |||||
PrintStream logTo = null; | PrintStream logTo = null; | ||||
// cycle through given args | // cycle through given args | ||||
@@ -359,11 +361,10 @@ public class Main implements AntMain { | |||||
// set the flag to display the targets and quit | // set the flag to display the targets and quit | ||||
projectHelp = true; | projectHelp = true; | ||||
} else if (arg.equals("-find") || arg.equals("-s")) { | } else if (arg.equals("-find") || arg.equals("-s")) { | ||||
searchForFile = true; | |||||
// eat up next arg if present, default to build.xml | // eat up next arg if present, default to build.xml | ||||
if (i < args.length - 1) { | if (i < args.length - 1) { | ||||
searchForThis = args[++i]; | searchForThis = args[++i]; | ||||
} else { | |||||
searchForThis = DEFAULT_BUILD_FILENAME; | |||||
} | } | ||||
} else if (arg.startsWith("-propertyfile")) { | } else if (arg.startsWith("-propertyfile")) { | ||||
i = handleArgPropertyFile(args, i); | i = handleArgPropertyFile(args, i); | ||||
@@ -411,11 +412,37 @@ public class Main implements AntMain { | |||||
// if buildFile was not specified on the command line, | // if buildFile was not specified on the command line, | ||||
if (buildFile == null) { | if (buildFile == null) { | ||||
// but -find then search for it | // but -find then search for it | ||||
if (searchForThis != null) { | |||||
buildFile = findBuildFile(System.getProperty("user.dir"), | |||||
searchForThis); | |||||
if (searchForFile) { | |||||
if (searchForThis != null) { | |||||
buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis); | |||||
if (buildFile == null) { | |||||
throw new BuildException("Could not locate a build file!"); | |||||
} | |||||
} else { | |||||
// no search file specified: so search an existing default file | |||||
Iterator it = ProjectHelperRepository.getInstance().getHelpers(); | |||||
do { | |||||
ProjectHelper helper = (ProjectHelper) it.next(); | |||||
searchForThis = helper.getDefaultBuildFile(); | |||||
if (msgOutputLevel >= Project.MSG_VERBOSE) { | |||||
System.out.println("Searching the default build file: " + searchForThis); | |||||
} | |||||
buildFile = findBuildFile(System.getProperty("user.dir"), searchForThis); | |||||
} while (buildFile == null && it.hasNext()); | |||||
if (buildFile == null) { | |||||
throw new BuildException("Could not locate a build file!"); | |||||
} | |||||
} | |||||
} else { | } else { | ||||
buildFile = new File(DEFAULT_BUILD_FILENAME); | |||||
// no build file specified: so search an existing default file | |||||
Iterator it = ProjectHelperRepository.getInstance().getHelpers(); | |||||
do { | |||||
ProjectHelper helper = (ProjectHelper) it.next(); | |||||
buildFile = new File(helper.getDefaultBuildFile()); | |||||
if (msgOutputLevel >= Project.MSG_VERBOSE) { | |||||
System.out.println("Trying the default build file: " + buildFile); | |||||
} | |||||
} while (!buildFile.exists() && it.hasNext()); | |||||
} | } | ||||
} | } | ||||
@@ -633,20 +660,17 @@ public class Main implements AntMain { | |||||
* <p> | * <p> | ||||
* Takes the given target as a suffix to append to each | * Takes the given target as a suffix to append to each | ||||
* parent directory in search of a build file. Once the | * parent directory in search of a build file. Once the | ||||
* root of the file-system has been reached an exception | |||||
* is thrown. | |||||
* root of the file-system has been reached <code>null</code> | |||||
* is returned. | |||||
* | * | ||||
* @param start Leaf directory of search. | * @param start Leaf directory of search. | ||||
* Must not be <code>null</code>. | * Must not be <code>null</code>. | ||||
* @param suffix Suffix filename to look for in parents. | * @param suffix Suffix filename to look for in parents. | ||||
* Must not be <code>null</code>. | * Must not be <code>null</code>. | ||||
* | * | ||||
* @return A handle to the build file if one is found | |||||
* | |||||
* @exception BuildException if no build file is found | |||||
* @return A handle to the build file if one is found, <code>null</code> if not | |||||
*/ | */ | ||||
private File findBuildFile(String start, String suffix) | |||||
throws BuildException { | |||||
private File findBuildFile(String start, String suffix) { | |||||
if (msgOutputLevel >= Project.MSG_INFO) { | if (msgOutputLevel >= Project.MSG_INFO) { | ||||
System.out.println("Searching for " + suffix + " ..."); | System.out.println("Searching for " + suffix + " ..."); | ||||
} | } | ||||
@@ -662,7 +686,7 @@ public class Main implements AntMain { | |||||
// if parent is null, then we are at the root of the fs, | // if parent is null, then we are at the root of the fs, | ||||
// complain that we can't find the build file. | // complain that we can't find the build file. | ||||
if (parent == null) { | if (parent == null) { | ||||
throw new BuildException("Could not locate a build file!"); | |||||
return null; | |||||
} | } | ||||
// refresh our file handle | // refresh our file handle | ||||
@@ -17,37 +17,21 @@ | |||||
*/ | */ | ||||
package org.apache.tools.ant; | package org.apache.tools.ant; | ||||
import java.io.BufferedReader; | |||||
import java.io.File; | import java.io.File; | ||||
import java.io.InputStream; | |||||
import java.io.InputStreamReader; | |||||
import java.net.URL; | import java.net.URL; | ||||
import java.util.Hashtable; | import java.util.Hashtable; | ||||
import java.util.Locale; | import java.util.Locale; | ||||
import java.util.Vector; | import java.util.Vector; | ||||
import org.xml.sax.AttributeList; | |||||
import org.apache.tools.ant.helper.ProjectHelper2; | |||||
import org.apache.tools.ant.util.LoaderUtils; | import org.apache.tools.ant.util.LoaderUtils; | ||||
import org.xml.sax.AttributeList; | |||||
/** | /** | ||||
* Configures a Project (complete with Targets and Tasks) based on | * Configures a Project (complete with Targets and Tasks) based on | ||||
* a XML build file. It'll rely on a plugin to do the actual processing | |||||
* of the xml file. | |||||
* | |||||
* a build file. It'll rely on a plugin to do the actual processing | |||||
* of the file. | |||||
* <p> | |||||
* This class also provide static wrappers for common introspection. | * This class also provide static wrappers for common introspection. | ||||
* | |||||
* All helper plugins must provide backward compatibility with the | |||||
* original ant patterns, unless a different behavior is explicitly | |||||
* specified. For example, if namespace is used on the <project> tag | |||||
* the helper can expect the entire build file to be namespace-enabled. | |||||
* Namespaces or helper-specific tags can provide meta-information to | |||||
* the helper, allowing it to use new ( or different policies ). | |||||
* | |||||
* However, if no namespace is used the behavior should be exactly | |||||
* identical with the default helper. | |||||
* | |||||
*/ | */ | ||||
public class ProjectHelper { | public class ProjectHelper { | ||||
/** The URI for ant name space */ | /** The URI for ant name space */ | ||||
@@ -89,7 +73,7 @@ public class ProjectHelper { | |||||
* @exception BuildException if the configuration is invalid or cannot be read | * @exception BuildException if the configuration is invalid or cannot be read | ||||
*/ | */ | ||||
public static void configureProject(Project project, File buildFile) throws BuildException { | public static void configureProject(Project project, File buildFile) throws BuildException { | ||||
ProjectHelper helper = ProjectHelper.getProjectHelper(); | |||||
ProjectHelper helper = ProjectHelperRepository.getInstance().getProjectHelper(buildFile); | |||||
project.addReference(PROJECTHELPER_REFERENCE, helper); | project.addReference(PROJECTHELPER_REFERENCE, helper); | ||||
helper.parse(project, buildFile); | helper.parse(project, buildFile); | ||||
} | } | ||||
@@ -224,103 +208,13 @@ public class ProjectHelper { | |||||
} | } | ||||
/** | /** | ||||
* Discovers a project helper instance. Uses the same patterns | |||||
* as JAXP, commons-logging, etc: a system property, a JDK1.3 | |||||
* service discovery, default. | |||||
* | |||||
* @return a ProjectHelper, either a custom implementation | |||||
* if one is available and configured, or the default implementation | |||||
* otherwise. | |||||
* | |||||
* @exception BuildException if a specified helper class cannot | |||||
* be loaded/instantiated. | |||||
*/ | |||||
public static ProjectHelper getProjectHelper() throws BuildException { | |||||
// Identify the class loader we will be using. Ant may be | |||||
// in a webapp or embedded in a different app | |||||
ProjectHelper helper = null; | |||||
// First, try the system property | |||||
String helperClass = System.getProperty(HELPER_PROPERTY); | |||||
try { | |||||
if (helperClass != null) { | |||||
helper = newHelper(helperClass); | |||||
} | |||||
} catch (SecurityException e) { | |||||
System.out.println("Unable to load ProjectHelper class \"" | |||||
+ helperClass + " specified in system property " | |||||
+ HELPER_PROPERTY); | |||||
} | |||||
// A JDK1.3 'service' ( like in JAXP ). That will plug a helper | |||||
// automatically if in CLASSPATH, with the right META-INF/services. | |||||
if (helper == null) { | |||||
try { | |||||
ClassLoader classLoader = LoaderUtils.getContextClassLoader(); | |||||
InputStream is = null; | |||||
if (classLoader != null) { | |||||
is = classLoader.getResourceAsStream(SERVICE_ID); | |||||
} | |||||
if (is == null) { | |||||
is = ClassLoader.getSystemResourceAsStream(SERVICE_ID); | |||||
} | |||||
if (is != null) { | |||||
// This code is needed by EBCDIC and other strange systems. | |||||
// It's a fix for bugs reported in xerces | |||||
InputStreamReader isr; | |||||
try { | |||||
isr = new InputStreamReader(is, "UTF-8"); | |||||
} catch (java.io.UnsupportedEncodingException e) { | |||||
isr = new InputStreamReader(is); | |||||
} | |||||
BufferedReader rd = new BufferedReader(isr); | |||||
String helperClassName = rd.readLine(); | |||||
rd.close(); | |||||
if (helperClassName != null && !"".equals(helperClassName)) { | |||||
helper = newHelper(helperClassName); | |||||
} | |||||
} | |||||
} catch (Exception ex) { | |||||
System.out.println("Unable to load ProjectHelper from service " + SERVICE_ID); | |||||
} | |||||
} | |||||
return helper == null ? new ProjectHelper2() : helper; | |||||
} | |||||
/** | |||||
* Creates a new helper instance from the name of the class. | |||||
* It'll first try the thread class loader, then Class.forName() | |||||
* will load from the same loader that loaded this class. | |||||
* | |||||
* @param helperClass The name of the class to create an instance | |||||
* of. Must not be <code>null</code>. | |||||
* | |||||
* @return a new instance of the specified class. | |||||
* | |||||
* @exception BuildException if the class cannot be found or | |||||
* cannot be appropriate instantiated. | |||||
* Get the first project helper found in the classpath | |||||
* | |||||
* @return an project helper, never <code>null</code> | |||||
* @see #getHelpers() | |||||
*/ | */ | ||||
private static ProjectHelper newHelper(String helperClass) | |||||
throws BuildException { | |||||
ClassLoader classLoader = LoaderUtils.getContextClassLoader(); | |||||
try { | |||||
Class clazz = null; | |||||
if (classLoader != null) { | |||||
try { | |||||
clazz = classLoader.loadClass(helperClass); | |||||
} catch (ClassNotFoundException ex) { | |||||
// try next method | |||||
} | |||||
} | |||||
if (clazz == null) { | |||||
clazz = Class.forName(helperClass); | |||||
} | |||||
return ((ProjectHelper) clazz.newInstance()); | |||||
} catch (Exception e) { | |||||
throw new BuildException(e); | |||||
} | |||||
public static ProjectHelper getProjectHelper() { | |||||
return (ProjectHelper) ProjectHelperRepository.getInstance().getHelpers().next(); | |||||
} | } | ||||
/** | /** | ||||
@@ -618,4 +512,27 @@ public class ProjectHelper { | |||||
URL source) { | URL source) { | ||||
throw new BuildException("can't parse antlib descriptors"); | throw new BuildException("can't parse antlib descriptors"); | ||||
} | } | ||||
/** | |||||
* Check if the helper supports the kind of file. Some basic check on the | |||||
* extension's file should be done here. | |||||
* | |||||
* @param buildFile | |||||
* the file expected to be parsed (never <code>null</code>) | |||||
* @return true if the helper supports it | |||||
* @since Ant 1.8.0 | |||||
*/ | |||||
public boolean supportsBuildFile(File buildFile) { | |||||
return true; | |||||
} | |||||
/** | |||||
* The file name of the build script to be parsed if none specified on the command line | |||||
* | |||||
* @return the name of the default file (never <code>null</code>) | |||||
* @since Ant 1.8.0 | |||||
*/ | |||||
public String getDefaultBuildFile() { | |||||
return Main.DEFAULT_BUILD_FILENAME; | |||||
} | |||||
} | } |
@@ -0,0 +1,203 @@ | |||||
package org.apache.tools.ant; | |||||
import java.io.BufferedReader; | |||||
import java.io.File; | |||||
import java.io.InputStream; | |||||
import java.io.InputStreamReader; | |||||
import java.net.URL; | |||||
import java.util.ArrayList; | |||||
import java.util.Enumeration; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
import org.apache.tools.ant.helper.ProjectHelper2; | |||||
import org.apache.tools.ant.util.LoaderUtils; | |||||
/** | |||||
* Repository of {@link ProjectHelper} found in the classpath or via some System | |||||
* properties. | |||||
* <p> | |||||
* See the ProjectHelper documentation in the manual. | |||||
* | |||||
* @since Ant 1.8.0 | |||||
*/ | |||||
public class ProjectHelperRepository { | |||||
private static final String DEBUG_PROJECT_HELPER_REPOSITORY = "ant.project-helper-repo.debug"; | |||||
// The message log level is not accessible here because everything is instanciated statically | |||||
private static final boolean DEBUG = "true".equals(System.getProperty(DEBUG_PROJECT_HELPER_REPOSITORY)); | |||||
private static ProjectHelperRepository instance = new ProjectHelperRepository(); | |||||
private List/* <ProjectHelper> */helpers = new ArrayList(); | |||||
public static ProjectHelperRepository getInstance() { | |||||
return instance; | |||||
} | |||||
private ProjectHelperRepository() { | |||||
collectProjectHelpers(); | |||||
} | |||||
private void collectProjectHelpers() { | |||||
// First, try the system property | |||||
ProjectHelper projectHelper = getProjectHelperBySystemProperty(); | |||||
registerProjectHelper(projectHelper); | |||||
// A JDK1.3 'service' ( like in JAXP ). That will plug a helper | |||||
// automatically if in CLASSPATH, with the right META-INF/services. | |||||
try { | |||||
ClassLoader classLoader = LoaderUtils.getContextClassLoader(); | |||||
if (classLoader != null) { | |||||
Enumeration resources = classLoader.getResources(ProjectHelper.SERVICE_ID); | |||||
while (resources.hasMoreElements()) { | |||||
URL resource = (URL) resources.nextElement(); | |||||
projectHelper = getProjectHelperBySerice(resource.openStream()); | |||||
registerProjectHelper(projectHelper); | |||||
} | |||||
} | |||||
InputStream systemResource = ClassLoader.getSystemResourceAsStream(ProjectHelper.SERVICE_ID); | |||||
if (systemResource != null) { | |||||
projectHelper = getProjectHelperBySerice(systemResource); | |||||
registerProjectHelper(projectHelper); | |||||
} | |||||
} catch (Exception e) { | |||||
System.err.println("Unable to load ProjectHelper from service " | |||||
+ ProjectHelper.SERVICE_ID + " (" + e.getClass().getName() + ": " | |||||
+ e.getMessage() + ")"); | |||||
if (DEBUG) { | |||||
e.printStackTrace(System.err); | |||||
} | |||||
} | |||||
// last but not least, ant default project helper | |||||
projectHelper = new ProjectHelper2(); | |||||
registerProjectHelper(projectHelper); | |||||
} | |||||
private void registerProjectHelper(ProjectHelper projectHelper) { | |||||
if (projectHelper == null) { | |||||
return; | |||||
} | |||||
if (DEBUG) { | |||||
System.out.println("ProjectHelper " + | |||||
projectHelper.getClass().getName() + " registered."); | |||||
} | |||||
helpers.add(projectHelper); | |||||
} | |||||
private ProjectHelper getProjectHelperBySystemProperty() { | |||||
String helperClass = System.getProperty(ProjectHelper.HELPER_PROPERTY); | |||||
try { | |||||
if (helperClass != null) { | |||||
return newHelper(helperClass); | |||||
} | |||||
} catch (SecurityException e) { | |||||
System.err.println("Unable to load ProjectHelper class \"" | |||||
+ helperClass + " specified in system property " | |||||
+ ProjectHelper.HELPER_PROPERTY + " (" + e.getMessage() + ")"); | |||||
if (DEBUG) { | |||||
e.printStackTrace(System.err); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
private ProjectHelper getProjectHelperBySerice(InputStream is) { | |||||
try { | |||||
// This code is needed by EBCDIC and other strange systems. | |||||
// It's a fix for bugs reported in xerces | |||||
InputStreamReader isr; | |||||
try { | |||||
isr = new InputStreamReader(is, "UTF-8"); | |||||
} catch (java.io.UnsupportedEncodingException e) { | |||||
isr = new InputStreamReader(is); | |||||
} | |||||
BufferedReader rd = new BufferedReader(isr); | |||||
String helperClassName = rd.readLine(); | |||||
rd.close(); | |||||
if (helperClassName != null && !"".equals(helperClassName)) { | |||||
return newHelper(helperClassName); | |||||
} | |||||
} catch (Exception e) { | |||||
System.out.println("Unable to load ProjectHelper from service " | |||||
+ ProjectHelper.SERVICE_ID + " (" + e.getMessage() + ")"); | |||||
if (DEBUG) { | |||||
e.printStackTrace(System.err); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* Creates a new helper instance from the name of the class. It'll first try | |||||
* the thread class loader, then Class.forName() will load from the same | |||||
* loader that loaded this class. | |||||
* | |||||
* @param helperClass | |||||
* The name of the class to create an instance of. Must not be | |||||
* <code>null</code>. | |||||
* | |||||
* @return a new instance of the specified class. | |||||
* | |||||
* @exception BuildException | |||||
* if the class cannot be found or cannot be appropriate | |||||
* instantiated. | |||||
*/ | |||||
private ProjectHelper newHelper(String helperClass) throws BuildException { | |||||
ClassLoader classLoader = LoaderUtils.getContextClassLoader(); | |||||
try { | |||||
Class clazz = null; | |||||
if (classLoader != null) { | |||||
try { | |||||
clazz = classLoader.loadClass(helperClass); | |||||
} catch (ClassNotFoundException ex) { | |||||
// try next method | |||||
} | |||||
} | |||||
if (clazz == null) { | |||||
clazz = Class.forName(helperClass); | |||||
} | |||||
return ((ProjectHelper) clazz.newInstance()); | |||||
} catch (Exception e) { | |||||
throw new BuildException(e); | |||||
} | |||||
} | |||||
/** | |||||
* Get the helper that will be able to parse the specified file. The helper | |||||
* will be chosen among the ones found in the classpath | |||||
* | |||||
* @return the first ProjectHelper that fit the requirement (never <code>null</code>). | |||||
*/ | |||||
public ProjectHelper getProjectHelper(File buildFile) throws BuildException { | |||||
Iterator it = helpers.iterator(); | |||||
while (it.hasNext()) { | |||||
ProjectHelper helper = (ProjectHelper) it.next(); | |||||
if (helper.supportsBuildFile(buildFile)) { | |||||
if (DEBUG) { | |||||
System.out.println("ProjectHelper " | |||||
+ helper.getClass().getName() + " selected for the file " | |||||
+ buildFile); | |||||
} | |||||
return helper; | |||||
} | |||||
} | |||||
throw new RuntimeException("BUG: at least the ProjectHelper2 should have supported the file " + buildFile); | |||||
} | |||||
/** | |||||
* Get an iterator on the list of project helpers configured. The iterator | |||||
* will always return at least one element as there will always be the | |||||
* default project helper configured. | |||||
* | |||||
* @return an iterator of {@link ProjectHelper} | |||||
*/ | |||||
public Iterator getHelpers() { | |||||
return helpers.iterator(); | |||||
} | |||||
} |
@@ -359,7 +359,7 @@ public class Ant extends Task { | |||||
overrideProperties(); | overrideProperties(); | ||||
if (antFile == null) { | if (antFile == null) { | ||||
antFile = Main.DEFAULT_BUILD_FILENAME; | |||||
antFile = getDefaultBuildFile(); | |||||
} | } | ||||
File file = FILE_UTILS.resolveFile(dir, antFile); | File file = FILE_UTILS.resolveFile(dir, antFile); | ||||
@@ -469,6 +469,19 @@ public class Ant extends Task { | |||||
} | } | ||||
} | } | ||||
/** | |||||
* Get the default build file name to use when launching the task. | |||||
* <p> | |||||
* This function may be overrided by providers of custom ProjectHelper so they can implement easily their sub | |||||
* launcher. | |||||
* | |||||
* @return the name of the default file | |||||
* @since Ant 1.8.0 | |||||
*/ | |||||
protected String getDefaultBuildFile() { | |||||
return Main.DEFAULT_BUILD_FILENAME; | |||||
} | |||||
/** | /** | ||||
* Override the properties in the new project with the one | * Override the properties in the new project with the one | ||||
* explicitly defined as nested elements here. | * explicitly defined as nested elements here. | ||||
@@ -67,7 +67,7 @@ public class SubAnt extends Task { | |||||
private Ant ant = null; | private Ant ant = null; | ||||
private String subTarget = null; | private String subTarget = null; | ||||
private String antfile = Main.DEFAULT_BUILD_FILENAME; | |||||
private String antfile = getDefaultBuildFile(); | |||||
private File genericantfile = null; | private File genericantfile = null; | ||||
private boolean verbose = false; | private boolean verbose = false; | ||||
private boolean inheritAll = false; | private boolean inheritAll = false; | ||||
@@ -82,6 +82,19 @@ public class SubAnt extends Task { | |||||
/** the targets to call on the new project */ | /** the targets to call on the new project */ | ||||
private Vector/*<TargetElement>*/ targets = new Vector(); | private Vector/*<TargetElement>*/ targets = new Vector(); | ||||
/** | |||||
* Get the default build file name to use when launching the task. | |||||
* <p> | |||||
* This function may be overrided by providers of custom ProjectHelper so they can implement easily their sub | |||||
* launcher. | |||||
* | |||||
* @return the name of the default file | |||||
* @since Ant 1.8.0 | |||||
*/ | |||||
protected String getDefaultBuildFile() { | |||||
return Main.DEFAULT_BUILD_FILENAME; | |||||
} | |||||
/** | /** | ||||
* Pass output sent to System.out to the new project. | * Pass output sent to System.out to the new project. | ||||
* | * | ||||
@@ -50,7 +50,7 @@ public class Description extends DataType { | |||||
*/ | */ | ||||
public void addText(String text) { | public void addText(String text) { | ||||
ProjectHelper ph = ProjectHelper.getProjectHelper(); | |||||
ProjectHelper ph = (ProjectHelper) getProject().getReference(ProjectHelper.PROJECTHELPER_REFERENCE); | |||||
if (!(ph instanceof ProjectHelperImpl)) { | if (!(ph instanceof ProjectHelperImpl)) { | ||||
// New behavior for delayed task creation. Description | // New behavior for delayed task creation. Description | ||||
// will be evaluated in Project.getDescription() | // will be evaluated in Project.getDescription() | ||||