/* * The Apache Software License, Version 1.1 * * Copyright (c) 2001-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "Ant" and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . */ package org.apache.tools.ant.taskdefs; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Enumeration; import java.util.Properties; import org.apache.tools.ant.AntTypeDefinition; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.ComponentHelper; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Reference; import org.apache.tools.ant.util.ClasspathUtils; import org.apache.tools.ant.types.EnumeratedAttribute; /** * Base class for Taskdef and Typedef - does all the classpath * handling and and class loading. * * @author Costin Manolache * @author Stefan Bodewig * * @since Ant 1.4 */ public abstract class Definer extends Task { private String name; private String classname; private File file; private String resource; private ClasspathUtils.Delegate cpDelegate; private boolean definerSet = false; private ClassLoader internalClassLoader; private int onError = OnError.FAIL; private String adapter; private String adaptTo; private Class adapterClass; private Class adaptToClass; /** * Enumerated type for onError attribute * * @see EnumeratedAttribute */ public static class OnError extends EnumeratedAttribute { /** Enumerated values */ public static final int FAIL = 0, REPORT = 1, IGNORE = 2; /** * Constructor */ public OnError() { super(); } /** * Constructor using a string. * @param value the value of the attribute */ public OnError(String value) { setValue(value); } /** * get the values * @return an array of the allowed values for this attribute. */ public String[] getValues() { return new String[] {"fail", "report", "ignore"}; } } /** * What to do if there is an error in loading the class. *
*
  • error - throw build exception
  • *
  • report - output at warning level
  • *
  • ignore - output at debug level
  • *
    * * @param onError an OnError value */ public void setOnError(OnError onError) { this.onError = onError.getIndex(); } /** * @param reverseLoader if true a delegated loader will take precedence over * the parent * @deprecated stop using this attribute * @ant.attribute ignore="true" */ public void setReverseLoader(boolean reverseLoader) { this.cpDelegate.setReverseLoader(reverseLoader); log("The reverseloader attribute is DEPRECATED. It will be removed", Project.MSG_WARN); } /** * @return the name for this definition */ public String getName() { return name; } /** * @return the class path path for this definition */ public Path getClasspath() { return cpDelegate.getClasspath(); } /** * @return the file containing definitions */ public File getFile() { return file; } /** * @return the resource containing definitions */ public String getResource() { return resource; } /** * @return the reverse loader attribute of the classpath delegate. */ public boolean isReverseLoader() { return cpDelegate.isReverseLoader(); } /** * Returns the loader id of the class path Delegate. * @return the loader id */ public String getLoaderId() { return cpDelegate.getClassLoadId(); } /** * Returns the class path id of the class path delegate. * @return the class path id */ public String getClasspathId() { return cpDelegate.getClassLoadId(); } /** * Set the classpath to be used when searching for component being defined * * @param classpath an Ant Path object containing the classpath. */ public void setClasspath(Path classpath) { this.cpDelegate.setClasspath(classpath); } /** * Create the classpath to be used when searching for component being * defined * @return the classpath of the this definition */ public Path createClasspath() { return this.cpDelegate.createClasspath(); } /** * reference to a classpath to use when loading the files. * To actually share the same loader, set loaderref as well * @param r the reference to the classpath */ public void setClasspathRef(Reference r) { this.cpDelegate.setClasspathref(r); } /** * Use the reference to locate the loader. If the loader is not * found, taskdef will use the specified classpath and register it * with the specified name. * * This allow multiple taskdef/typedef to use the same class loader, * so they can be used together. It eliminate the need to * put them in the CLASSPATH. * * @param r the reference to locate the loader. * @since Ant 1.5 */ public void setLoaderRef(Reference r) { this.cpDelegate.setLoaderRef(r); } /** * Run the definition. * * @exception BuildException if an error occurs */ public void execute() throws BuildException { ClassLoader al = createLoader(); if (!definerSet) { throw new BuildException( "name, file or resource attribute of " + getTaskName() + " is undefined", getLocation()); } if (name != null) { if (classname == null) { throw new BuildException( "classname attribute of " + getTaskName() + " element " + "is undefined", getLocation()); } addDefinition(al, name, classname); } else { if (classname != null) { String msg = "You must not specify classname " + "together with file or resource."; throw new BuildException(msg, getLocation()); } URL url = null; if (file != null) { url = fileToURL(); } if (resource != null) { url = resourceToURL(al); } if (url == null) { return; } loadProperties(al, url); } } private URL fileToURL() { if (!(file.exists())) { log("File " + file + " does not exist", Project.MSG_WARN); return null; } if (!(file.isFile())) { log("File " + file + " is not a file", Project.MSG_WARN); return null; } try { return file.toURL(); } catch (Exception ex) { log("File " + file + " cannot use as URL: " + ex.toString(), Project.MSG_WARN); return null; } } private URL resourceToURL(ClassLoader classLoader) { URL ret = classLoader.getResource(resource); if (ret == null) { if (onError != OnError.IGNORE) { log("Could not load definitions from resource " + resource + ". It could not be found.", Project.MSG_WARN); } } return ret; } /** * Load type definitions as properties from a url. * * @param al the classloader to use * @param url the url to get the definitions from */ protected void loadProperties(ClassLoader al, URL url) { InputStream is = null; try { is = url.openStream(); if (is == null) { log("Could not load definitions from " + url, Project.MSG_WARN); return; } Properties props = new Properties(); props.load(is); Enumeration keys = props.keys(); while (keys.hasMoreElements()) { name = ((String) keys.nextElement()); classname = props.getProperty(name); addDefinition(al, name, classname); } } catch (IOException ex) { throw new BuildException(ex, getLocation()); } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } } } /** * create a classloader for this definition * @return the classloader from the cpDelegate */ protected ClassLoader createLoader() { if (internalClassLoader != null) { return internalClassLoader; } ClassLoader al = this.cpDelegate.getClassLoader(); // need to load Task via system classloader or the new // task we want to define will never be a Task but always // be wrapped into a TaskAdapter. ((AntClassLoader) al).addSystemPackageRoot("org.apache.tools.ant"); return al; } /** * Name of the property file to load * ant name/classname pairs from. * @param file the file */ public void setFile(File file) { if (definerSet) { tooManyDefinitions(); } definerSet = true; this.file = file; } /** * Name of the property resource to load * ant name/classname pairs from. * @param res the resource to use */ public void setResource(String res) { if (definerSet) { tooManyDefinitions(); } definerSet = true; this.resource = res; } /** * Name of the definition * @param name the name of the definition */ public void setName(String name) { if (definerSet) { tooManyDefinitions(); } definerSet = true; this.name = name; } /** * Returns the classname of the object we are defining. * May be null. * @return the class name */ public String getClassname() { return classname; } /** * The full class name of the object being defined. * Required, unless file or resource have * been specified. * @param classname the name of the class */ public void setClassname(String classname) { this.classname = classname; } /** * Set the class name of the adapter class. * An adapter class is used to proxy the * definition class. It is used if the * definition class is not assignable to * the adaptto class, or if the adaptto * class is not present. * * @param adapter the name of the adapter class */ public void setAdapter(String adapter) { this.adapter = adapter; } /** * Set the adapter class. * * @param adapterClass the class to use to adapt the definition class */ protected void setAdapterClass(Class adapterClass) { this.adapterClass = adapterClass; } /** * Set the classname of the class that the definition * must be compatible with, either directly or * by use of the adapeter class. * * @param adaptTo the name of the adaptto class */ public void setAdaptTo(String adaptTo) { this.adaptTo = adaptTo; } /** * Set the class for adaptToClass, to be * used by derived classes, used instead of * the adaptTo attribute. * * @param adaptToClass the class for adapto. */ protected void setAdaptToClass(Class adaptToClass) { this.adaptToClass = adaptToClass; } /** * Set the class loader, overrides the cpDelagate * classloader. * * @param classLoader a ClassLoader value */ protected void setInternalClassLoader(ClassLoader classLoader) { this.internalClassLoader = classLoader; } /** * @see org.apache.tools.ant.Task#init() * @since Ant 1.6 */ public void init() throws BuildException { this.cpDelegate = ClasspathUtils.getDelegate(this); super.init(); } /** * Add a definition using the attributes of Definer * * @param al the ClassLoader to use * @param name the name of the definition * @param classname the classname of the definition * @exception BuildException if an error occurs */ protected void addDefinition(ClassLoader al, String name, String classname) throws BuildException { Class cl = null; try { try { if (onError != OnError.IGNORE) { cl = al.loadClass(classname); AntClassLoader.initializeClass(cl); } if (adapter != null) { adapterClass = al.loadClass(adapter); AntClassLoader.initializeClass(adapterClass); } if (adaptTo != null) { adaptToClass = al.loadClass(adaptTo); AntClassLoader.initializeClass(adaptToClass); } AntTypeDefinition def = new AntTypeDefinition(); def.setName(name); def.setClassName(classname); def.setClass(cl); def.setAdapterClass(adapterClass); def.setAdaptToClass(adaptToClass); def.setClassLoader(al); if (cl != null) { def.checkClass(getProject()); } ComponentHelper.getComponentHelper(getProject()) .addDataTypeDefinition(def); } catch (ClassNotFoundException cnfe) { String msg = getTaskName() + " class " + classname + " cannot be found"; throw new BuildException(msg, cnfe, getLocation()); } catch (NoClassDefFoundError ncdfe) { String msg = getTaskName() + "A class needed by class " + classname + " cannot be found: " + ncdfe.getMessage(); throw new BuildException(msg, ncdfe, location); } } catch (BuildException ex) { switch (onError) { case OnError.FAIL: throw ex; case OnError.REPORT: log(ex.getLocation() + "Warning: " + ex.getMessage(), Project.MSG_WARN); break; default: log(ex.getLocation() + ex.getMessage(), Project.MSG_DEBUG); } } } private void tooManyDefinitions() { throw new BuildException( "Only one of the attributes name,file,resource" + " can be set", getLocation()); } }