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() | ||||