/*
 *  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
 *
 *      https://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.
 *
 */
package org.apache.tools.ant;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;

import org.apache.tools.ant.helper.ProjectHelper2;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.util.LoaderUtils;
import org.apache.tools.ant.util.StreamUtils;

Repository of ProjectHelper found in the classpath or via some System properties.

See the ProjectHelper documentation in the manual.

Since:Ant 1.8.0
/** * Repository of {@link ProjectHelper} found in the classpath or via * some System properties. * * <p>See the ProjectHelper documentation in the manual.</p> * * @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 instantiated statically private static final boolean DEBUG = "true".equals(System.getProperty(DEBUG_PROJECT_HELPER_REPOSITORY)); private static ProjectHelperRepository instance = new ProjectHelperRepository(); private List<Constructor<? extends ProjectHelper>> helpers = new ArrayList<>(); private static Constructor<ProjectHelper2> PROJECTHELPER2_CONSTRUCTOR; static { try { PROJECTHELPER2_CONSTRUCTOR = ProjectHelper2.class.getConstructor(); } catch (Exception e) { // ProjectHelper2 must be available throw new BuildException(e); } } public static ProjectHelperRepository getInstance() { return instance; } private ProjectHelperRepository() { collectProjectHelpers(); } private void collectProjectHelpers() { // First, try the system property registerProjectHelper(getProjectHelperBySystemProperty()); // 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) { for (URL resource : Collections.list(classLoader.getResources(ProjectHelper.SERVICE_ID))) { URLConnection conn = resource.openConnection(); conn.setUseCaches(false); registerProjectHelper(getProjectHelperByService(conn.getInputStream())); } } InputStream systemResource = ClassLoader.getSystemResourceAsStream(ProjectHelper.SERVICE_ID); if (systemResource != null) { registerProjectHelper(getProjectHelperByService(systemResource)); } } 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); //NOSONAR } } }
Register the specified project helper into the repository.

The helper will be added after all the already registered helpers, but before the default one (ProjectHelper2)

Params:
  • helperClassName – the fully qualified name of the helper
Throws:
  • BuildException – if the class cannot be loaded or if there is no constructor with no argument
Since:Ant 1.8.2
/** * Register the specified project helper into the repository. * <p> * The helper will be added after all the already registered helpers, but * before the default one (ProjectHelper2) * * @param helperClassName * the fully qualified name of the helper * @throws BuildException * if the class cannot be loaded or if there is no constructor * with no argument * @since Ant 1.8.2 */
public void registerProjectHelper(String helperClassName) throws BuildException { registerProjectHelper(getHelperConstructor(helperClassName)); }
Register the specified project helper into the repository.

The helper will be added after all the already registered helpers, but before the default one (ProjectHelper2)

Params:
  • helperClass – the class of the helper
Throws:
Since:Ant 1.8.2
/** * Register the specified project helper into the repository. * <p> * The helper will be added after all the already registered helpers, but * before the default one (ProjectHelper2) * * @param helperClass * the class of the helper * @throws BuildException * if there is no constructor with no argument * @since Ant 1.8.2 */
public void registerProjectHelper(Class<? extends ProjectHelper> helperClass) throws BuildException { try { registerProjectHelper(helperClass.getConstructor()); } catch (NoSuchMethodException e) { throw new BuildException("Couldn't find no-arg constructor in " + helperClass.getName()); } } private void registerProjectHelper(Constructor<? extends ProjectHelper> helperConstructor) { if (helperConstructor == null) { return; } if (DEBUG) { System.out.println("ProjectHelper " + helperConstructor.getClass().getName() + " registered."); } helpers.add(helperConstructor); } private Constructor<? extends ProjectHelper> getProjectHelperBySystemProperty() { String helperClass = System.getProperty(ProjectHelper.HELPER_PROPERTY); try { if (helperClass != null) { return getHelperConstructor(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); //NOSONAR } } return null; } private Constructor<? extends ProjectHelper> getProjectHelperByService(InputStream is) { try { // This code is needed by EBCDIC and other strange systems. // It's a fix for bugs reported in xerces BufferedReader rd = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); String helperClassName = rd.readLine(); rd.close(); if (helperClassName != null && !helperClassName.isEmpty()) { return getHelperConstructor(helperClassName); } } catch (Exception e) { System.out.println("Unable to load ProjectHelper from service " + ProjectHelper.SERVICE_ID + " (" + e.getMessage() + ")"); if (DEBUG) { e.printStackTrace(System.err); //NOSONAR } } return null; }
Get the constructor with not argument of an helper from its class name. It'll first try the thread class loader, then Class.forName() will load from the same loader that loaded this class.
Params:
  • helperClass – The name of the class to create an instance of. Must not be null.
Throws:
  • BuildException – if the class cannot be found or if a constructor with no argument cannot be found.
Returns:the constructor of the specified class.
/** * Get the constructor with not argument of an helper from its class name. * 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 the constructor of the specified class. * * @exception BuildException * if the class cannot be found or if a constructor with no * argument cannot be found. */
private Constructor<? extends ProjectHelper> getHelperConstructor(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 clazz.asSubclass(ProjectHelper.class).getConstructor(); } catch (Exception e) { throw new BuildException(e); } }
Get the helper that will be able to parse the specified build file. The helper will be chosen among the ones found in the classpath
Params:
  • buildFile – Resource
Returns:the first ProjectHelper that fit the requirement (never null).
/** * Get the helper that will be able to parse the specified build file. The helper * will be chosen among the ones found in the classpath * * @param buildFile Resource * @return the first ProjectHelper that fit the requirement (never <code>null</code>). */
public ProjectHelper getProjectHelperForBuildFile(Resource buildFile) throws BuildException { ProjectHelper ph = StreamUtils.iteratorAsStream(getHelpers()) .filter(helper -> helper.canParseBuildFile(buildFile)) .findFirst().orElse(null); if (ph == null) { throw new BuildException("BUG: at least the ProjectHelper2 should " + "have supported the file " + buildFile); } if (DEBUG) { System.out.println("ProjectHelper " + ph.getClass().getName() + " selected for the build file " + buildFile); } return ph; }
Get the helper that will be able to parse the specified antlib. The helper will be chosen among the ones found in the classpath
Params:
  • antlib – Resource
Returns:the first ProjectHelper that fit the requirement (never null).
/** * Get the helper that will be able to parse the specified antlib. The helper * will be chosen among the ones found in the classpath * * @param antlib Resource * @return the first ProjectHelper that fit the requirement (never <code>null</code>). */
public ProjectHelper getProjectHelperForAntlib(Resource antlib) throws BuildException { ProjectHelper ph = StreamUtils.iteratorAsStream(getHelpers()) .filter(helper -> helper.canParseAntlibDescriptor(antlib)) .findFirst().orElse(null); if (ph == null) { throw new BuildException("BUG: at least the ProjectHelper2 should " + "have supported the file " + antlib); } if (DEBUG) { System.out.println("ProjectHelper " + ph.getClass().getName() + " selected for the antlib " + antlib); } return ph; }
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.
Returns:an iterator of ProjectHelper
/** * 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<ProjectHelper> getHelpers() { Stream.Builder<Constructor<? extends ProjectHelper>> b = Stream.builder(); helpers.forEach(b::add); return b.add(PROJECTHELPER2_CONSTRUCTOR).build().map(c -> { try { return c.newInstance(); } catch (Exception e) { throw new BuildException("Failed to invoke no-arg constructor" + " on " + c.getName()); } }).map(ProjectHelper.class::cast).iterator(); } }