| /* |
| * 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. |
| * |
| */ |
| package org.apache.tools.ant.util; |
| |
| import org.apache.tools.ant.AntClassLoader; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.ProjectComponent; |
| import org.apache.tools.ant.MagicNames; |
| import org.apache.tools.ant.types.Path; |
| import org.apache.tools.ant.types.Reference; |
| |
| // CheckStyle:HideUtilityClassConstructorCheck OFF - bc |
| |
| /** |
| * Offers some helper methods on the Path structure in ant. |
| * |
| * <p>The basic idea behind this utility class is to use it from inside the |
| * different Ant objects (and user defined objects) that need classLoading |
| * for their operation. |
| * Normally those would have a setClasspathRef() {for the @classpathref} |
| * and/or a createClasspath() {for the nested <classpath>} |
| * Typically one would have in your Ant Task or DataType</p> |
| * |
| * <pre><code> |
| * ClasspathUtils.Delegate cpDelegate; |
| * |
| * public void init() { |
| * this.cpDelegate = ClasspathUtils.getDelegate(this); |
| * super.init(); |
| * } |
| * |
| * public void setClasspathRef(Reference r) { |
| * this.cpDelegate.setClasspathRef(r); |
| * } |
| * |
| * public Path createClasspath() { |
| * return this.cpDelegate.createClasspath(); |
| * } |
| * |
| * public void setClassname(String fqcn) { |
| * this.cpDelegate.setClassname(fqcn); |
| * } |
| * </code></pre> |
| * |
| * <p>At execution time, when you actually need the classloading |
| * you can just:</p> |
| * |
| * <pre><code> |
| * Object o = this.cpDelegate.newInstance(); |
| * </code></pre> |
| * |
| * @since Ant 1.6 |
| */ |
| public class ClasspathUtils { |
| |
| /** |
| * Name of the magic property that controls classloader reuse in Ant 1.4. |
| */ |
| public static final String REUSE_LOADER_REF = MagicNames.REFID_CLASSPATH_REUSE_LOADER; |
| |
| /** |
| * Convenience overloaded version of {@link |
| * #getClassLoaderForPath(Project, Reference, boolean)}. |
| * |
| * <p>Assumes the logical 'false' for the reverseLoader.</p> |
| * |
| * @param p the project |
| * @param ref the reference |
| * @return The class loader |
| */ |
| public static ClassLoader getClassLoaderForPath(Project p, Reference ref) { |
| return getClassLoaderForPath(p, ref, false); |
| } |
| |
| /** |
| * Convenience overloaded version of {@link #getClassLoaderForPath(Project, Path, |
| * String, boolean)}. |
| * |
| * <p>Delegates to the other one after extracting the referenced |
| * Path from the Project. This checks also that the passed |
| * Reference is pointing to a Path all right.</p> |
| * @param p current Ant project |
| * @param ref Reference to Path structure |
| * @param reverseLoader if set to true this new loader will take |
| * precedence over its parent (which is contra the regular |
| * classloader behaviour) |
| * @return The class loader |
| */ |
| public static ClassLoader getClassLoaderForPath( |
| Project p, Reference ref, boolean reverseLoader) { |
| String pathId = ref.getRefId(); |
| Object path = p.getReference(pathId); |
| if (!(path instanceof Path)) { |
| throw new BuildException("The specified classpathref " + pathId |
| + " does not reference a Path."); |
| } |
| String loaderId = MagicNames.REFID_CLASSPATH_LOADER_PREFIX + pathId; |
| return getClassLoaderForPath(p, (Path) path, loaderId, reverseLoader); |
| } |
| |
| /** |
| * Convenience overloaded version of {@link |
| * #getClassLoaderForPath(Project, Path, String, boolean)}. |
| * |
| * <p>Assumes the logical 'false' for the reverseLoader.</p> |
| * |
| * @param p current Ant project |
| * @param path the path |
| * @param loaderId the loader id string |
| * @return The class loader |
| */ |
| public static ClassLoader getClassLoaderForPath(Project p, Path path, String loaderId) { |
| return getClassLoaderForPath(p, path, loaderId, false); |
| } |
| |
| /** |
| * Convenience overloaded version of {@link |
| * #getClassLoaderForPath(Project, Path, String, boolean, boolean)}. |
| * |
| * <p>Sets value for 'reuseLoader' to true if the magic property |
| * has been set.</p> |
| * |
| * @param p the project |
| * @param path the path |
| * @param loaderId the loader id string |
| * @param reverseLoader if set to true this new loader will take |
| * precedence over its parent (which is contra the regular |
| * classloader behaviour) |
| * @return The class loader |
| */ |
| public static ClassLoader getClassLoaderForPath( |
| Project p, Path path, String loaderId, boolean reverseLoader) { |
| return getClassLoaderForPath(p, path, loaderId, reverseLoader, isMagicPropertySet(p)); |
| } |
| |
| /** |
| * Gets a classloader that loads classes from the classpath |
| * defined in the path argument. |
| * |
| * <p>Based on the setting of the magic property |
| * 'ant.reuse.loader' this will try to reuse the previously |
| * created loader with that id, and of course store it there upon |
| * creation.</p> |
| * @param p Ant Project where the handled components are living in. |
| * @param path Path object to be used as classpath for this classloader |
| * @param loaderId identification for this Loader, |
| * @param reverseLoader if set to true this new loader will take |
| * precedence over its parent (which is contra the regular |
| * classloader behaviour) |
| * @param reuseLoader if true reuse the loader if it is found |
| * @return ClassLoader that uses the Path as its classpath. |
| */ |
| public static ClassLoader getClassLoaderForPath( |
| Project p, Path path, String loaderId, boolean reverseLoader, boolean reuseLoader) { |
| ClassLoader cl = null; |
| |
| // magic property |
| if (loaderId != null && reuseLoader) { |
| Object reusedLoader = p.getReference(loaderId); |
| if (reusedLoader != null && !(reusedLoader instanceof ClassLoader)) { |
| throw new BuildException("The specified loader id " + loaderId |
| + " does not reference a class loader"); |
| } |
| cl = (ClassLoader) reusedLoader; |
| } |
| if (cl == null) { |
| cl = getUniqueClassLoaderForPath(p, path, reverseLoader); |
| if (loaderId != null && reuseLoader) { |
| p.addReference(loaderId, cl); |
| } |
| } |
| return cl; |
| } |
| |
| /** |
| * Gets a fresh, different, previously unused classloader that uses the |
| * passed path as its classpath. |
| * |
| * <p>This method completely ignores the ant.reuse.loader magic |
| * property and should be used with caution.</p> |
| * @param p Ant Project where the handled components are living in. |
| * @param path the classpath for this loader |
| * @param reverseLoader if set to true this new loader will take |
| * precedence over its parent (which is contra the regular |
| * classloader behaviour) |
| * @return The fresh, different, previously unused class loader. |
| */ |
| public static ClassLoader getUniqueClassLoaderForPath(Project p, Path path, |
| boolean reverseLoader) { |
| AntClassLoader acl = p.createClassLoader(path); |
| if (reverseLoader) { |
| acl.setParentFirst(false); |
| acl.addJavaLibraries(); |
| } |
| return acl; |
| } |
| |
| /** |
| * Creates a fresh object instance of the specified classname. |
| * |
| * <p> This uses the userDefinedLoader to load the specified class, |
| * and then makes an instance using the default no-argument constructor. |
| * </p> |
| * |
| * @param className the full qualified class name to load. |
| * @param userDefinedLoader the classloader to use. |
| * @return The fresh object instance |
| * @throws BuildException when loading or instantiation failed. |
| */ |
| public static Object newInstance(String className, ClassLoader userDefinedLoader) { |
| return newInstance(className, userDefinedLoader, Object.class); |
| } |
| |
| /** |
| * Creates a fresh object instance of the specified classname. |
| * |
| * <p> This uses the userDefinedLoader to load the specified class, |
| * and then makes an instance using the default no-argument constructor. |
| * </p> |
| * |
| * @param className the full qualified class name to load. |
| * @param userDefinedLoader the classloader to use. |
| * @param expectedType the Class that the result should be assignment |
| * compatible with. (No ClassCastException will be thrown in case |
| * the result of this method is casted to the expectedType) |
| * @return The fresh object instance |
| * @throws BuildException when loading or instantiation failed. |
| * @since Ant 1.7 |
| */ |
| public static Object newInstance(String className, ClassLoader userDefinedLoader, |
| Class expectedType) { |
| try { |
| Class clazz = Class.forName(className, true, userDefinedLoader); |
| Object o = clazz.newInstance(); |
| if (!expectedType.isInstance(o)) { |
| throw new BuildException("Class of unexpected Type: " + className + " expected :" |
| + expectedType); |
| } |
| return o; |
| } catch (ClassNotFoundException e) { |
| throw new BuildException("Class not found: " + className, e); |
| } catch (InstantiationException e) { |
| throw new BuildException("Could not instantiate " + className |
| + ". Specified class should have a no " + "argument constructor.", e); |
| } catch (IllegalAccessException e) { |
| throw new BuildException("Could not instantiate " + className |
| + ". Specified class should have a " + "public constructor.", e); |
| } catch (LinkageError e) { |
| throw new BuildException("Class " + className |
| + " could not be loaded because of an invalid dependency.", e); |
| } |
| } |
| |
| /** |
| * Obtains a delegate that helps out with classic classpath configuration. |
| * |
| * @param component your projectComponent that needs the assistence |
| * @return the helper, delegate. |
| * @see ClasspathUtils.Delegate |
| */ |
| public static Delegate getDelegate(ProjectComponent component) { |
| return new Delegate(component); |
| } |
| |
| /** |
| * Checks for the magic property that enables class loader reuse |
| * for <taskdef> and <typedef> in Ant 1.5 and earlier. |
| */ |
| private static boolean isMagicPropertySet(Project p) { |
| return p.getProperty(REUSE_LOADER_REF) != null; |
| } |
| |
| /** |
| * Delegate that helps out any specific ProjectComponent that needs |
| * dynamic classloading. |
| * |
| * <p>Ant ProjectComponents that need a to be able to dynamically load |
| * Classes and instantiate them often expose the following ant syntax |
| * sugar: </p> |
| * |
| * <ul><li> nested <classpath> </li> |
| * <li> attribute @classpathref </li> |
| * <li> attribute @classname </li></ul> |
| * |
| * <p> This class functions as a delegate handling the configuration |
| * issues for this recurring pattern. Its usage pattern, as the name |
| * suggests, is delegation rather than inheritance. </p> |
| * |
| * @since Ant 1.6 |
| */ |
| public static class Delegate { |
| private final ProjectComponent component; |
| private Path classpath; |
| private String classpathId; |
| private String className; |
| private String loaderId; |
| private boolean reverseLoader = false; |
| |
| /** |
| * Construct a Delegate |
| * @param component the ProjectComponent this delegate is for. |
| */ |
| Delegate(ProjectComponent component) { |
| this.component = component; |
| } |
| |
| /** |
| * This method is a Delegate method handling the @classpath attribute. |
| * |
| * <p>This attribute can set a path to add to the classpath.</p> |
| * |
| * @param classpath the path to use for the classpath. |
| */ |
| public void setClasspath(Path classpath) { |
| if (this.classpath == null) { |
| this.classpath = classpath; |
| } else { |
| this.classpath.append(classpath); |
| } |
| } |
| |
| /** |
| * Delegate method handling the <classpath> tag. |
| * |
| * <p>This nested path-like structure can set a path to add to the |
| * classpath.</p> |
| * |
| * @return the created path. |
| */ |
| public Path createClasspath() { |
| if (this.classpath == null) { |
| this.classpath = new Path(component.getProject()); |
| } |
| return this.classpath.createPath(); |
| } |
| |
| /** |
| * Delegate method handling the @classname attribute. |
| * |
| * <p>This attribute sets the full qualified class name of the class |
| * to load and instantiate.</p> |
| * |
| * @param fcqn the name of the class to load. |
| */ |
| public void setClassname(String fcqn) { |
| this.className = fcqn; |
| } |
| |
| /** |
| * Delegate method handling the @classpathref attribute. |
| * |
| * <p>This attribute can add a referenced path-like structure to the |
| * classpath.</p> |
| * |
| * @param r the reference to the classpath. |
| */ |
| public void setClasspathref(Reference r) { |
| this.classpathId = r.getRefId(); |
| createClasspath().setRefid(r); |
| } |
| |
| /** |
| * Delegate method handling the @reverseLoader attribute. |
| * |
| * <p>This attribute can set a boolean indicating that the used |
| * classloader should NOT follow the classical parent-first scheme. |
| * </p> |
| * |
| * <p>By default this is supposed to be false.</p> |
| * |
| * <p>Caution: this behaviour is contradictory to the normal way |
| * classloaders work. Do not let your ProjectComponent use it if |
| * you are not really sure.</p> |
| * |
| * @param reverseLoader if true reverse the order of looking up a class. |
| */ |
| public void setReverseLoader(boolean reverseLoader) { |
| this.reverseLoader = reverseLoader; |
| } |
| |
| /** |
| * Sets the loaderRef. |
| * @param r the reference to the loader. |
| */ |
| public void setLoaderRef(Reference r) { |
| this.loaderId = r.getRefId(); |
| } |
| |
| |
| /** |
| * Finds or creates the classloader for this object. |
| * @return The class loader. |
| */ |
| public ClassLoader getClassLoader() { |
| return getClassLoaderForPath(getContextProject(), classpath, getClassLoadId(), |
| reverseLoader, loaderId != null || isMagicPropertySet(getContextProject())); |
| } |
| |
| /** |
| * The project of the ProjectComponent we are working for. |
| */ |
| private Project getContextProject() { |
| return component.getProject(); |
| } |
| |
| /** |
| * Computes the loaderId based on the configuration of the component. |
| * @return a loader identifier. |
| */ |
| public String getClassLoadId() { |
| if (loaderId == null && classpathId != null) { |
| return MagicNames.REFID_CLASSPATH_LOADER_PREFIX + classpathId; |
| } else { |
| return loaderId; |
| } |
| } |
| |
| /** |
| * Helper method obtaining a fresh instance of the class specified |
| * in the @classname and using the specified classpath. |
| * |
| * @return the fresh instantiated object. |
| */ |
| public Object newInstance() { |
| return ClasspathUtils.newInstance(this.className, getClassLoader()); |
| } |
| |
| /** |
| * The classpath. |
| * @return the classpath. |
| */ |
| public Path getClasspath() { |
| return classpath; |
| } |
| |
| /** |
| * Get the reverseLoader setting. |
| * @return true if looking up in reverse order. |
| */ |
| public boolean isReverseLoader() { |
| return reverseLoader; |
| } |
| |
| //TODO no methods yet for getClassname |
| //TODO no method for newInstance using a reverse-classloader |
| } |
| } |