diff options
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ClasspathUtils.java')
-rw-r--r-- | framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ClasspathUtils.java | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ClasspathUtils.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ClasspathUtils.java new file mode 100644 index 00000000..309860e9 --- /dev/null +++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/util/ClasspathUtils.java @@ -0,0 +1,463 @@ +/* + * 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.MagicNames; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.ProjectComponent; +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 + } +} |