old: generic error message new: step by step diagnostics with instructions. The code treats ant tasks and ant optional tasks specially, based on package names. Also: moved some constants into the appropriate places. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277750 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -17,6 +17,9 @@ | |||
| package org.apache.tools.ant; | |||
| import java.lang.reflect.InvocationTargetException; | |||
| import java.lang.reflect.Constructor; | |||
| /** | |||
| * This class contains all the information | |||
| @@ -143,15 +146,8 @@ public class AntTypeDefinition { | |||
| * @return the type of the definition. | |||
| */ | |||
| public Class getTypeClass(Project project) { | |||
| if (clazz != null) { | |||
| return clazz; | |||
| } | |||
| try { | |||
| if (classLoader == null) { | |||
| clazz = Class.forName(className); | |||
| } else { | |||
| clazz = classLoader.loadClass(className); | |||
| } | |||
| return innerGetTypeClass(); | |||
| } catch (NoClassDefFoundError ncdfe) { | |||
| project.log("Could not load a dependent class (" | |||
| + ncdfe.getMessage() + ") for type " | |||
| @@ -160,6 +156,24 @@ public class AntTypeDefinition { | |||
| project.log("Could not load class (" + className | |||
| + ") for type " + name, Project.MSG_DEBUG); | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * Try and load a class, with no attempt to catch any fault. | |||
| * @return the class that implements this component | |||
| * @throws ClassNotFoundException | |||
| * @throws NoClassDefFoundError | |||
| */ | |||
| public Class innerGetTypeClass() throws ClassNotFoundException { | |||
| if (clazz != null) { | |||
| return clazz; | |||
| } | |||
| if (classLoader == null) { | |||
| clazz = Class.forName(className); | |||
| } else { | |||
| clazz = classLoader.loadClass(className); | |||
| } | |||
| return clazz; | |||
| } | |||
| @@ -238,23 +252,9 @@ public class AntTypeDefinition { | |||
| */ | |||
| private Object createAndSet(Project project, Class c) { | |||
| try { | |||
| java.lang.reflect.Constructor ctor = null; | |||
| boolean noArg = false; | |||
| // DataType can have a "no arg" constructor or take a single | |||
| // Project argument. | |||
| try { | |||
| ctor = c.getConstructor(new Class[0]); | |||
| noArg = true; | |||
| } catch (NoSuchMethodException nse) { | |||
| ctor = c.getConstructor(new Class[] {Project.class}); | |||
| noArg = false; | |||
| } | |||
| Object o = ctor.newInstance( | |||
| ((noArg) ? new Object[0] : new Object[] {project})); | |||
| project.setProjectReference(o); | |||
| Object o = innerCreateAndSet(c, project); | |||
| return o; | |||
| } catch (java.lang.reflect.InvocationTargetException ex) { | |||
| } catch (InvocationTargetException ex) { | |||
| Throwable t = ex.getTargetException(); | |||
| throw new BuildException( | |||
| "Could not create type " + name + " due to " + t, t); | |||
| @@ -262,12 +262,60 @@ public class AntTypeDefinition { | |||
| String msg = "Type " + name + ": A class needed by class " | |||
| + c + " cannot be found: " + ncdfe.getMessage(); | |||
| throw new BuildException(msg, ncdfe); | |||
| } catch (Throwable t) { | |||
| } catch (NoSuchMethodException nsme) { | |||
| throw new BuildException("Could not create type " + name | |||
| + " as the class " + c +" has no compatible constructor" ); | |||
| } catch (InstantiationException nsme) { | |||
| throw new BuildException("Could not create type " + | |||
| name | |||
| + " as the class " + c + " is abstract"); | |||
| } catch(IllegalAccessException e) { | |||
| throw new BuildException("Could not create type " + | |||
| name | |||
| + " as the constructor " + c + " is not accessible"); | |||
| } catch (Throwable t) { | |||
| throw new BuildException( | |||
| "Could not create type " + name + " due to " + t, t); | |||
| } | |||
| } | |||
| /** | |||
| * Inner implementation of the {@see #createAndSet} logic, with no | |||
| * exception catching | |||
| * @param newclass class to create | |||
| * @param project | |||
| * @return a newly constructed and bound instance. | |||
| * @throws NoSuchMethodException | |||
| * @throws InstantiationException | |||
| * @throws IllegalAccessException | |||
| * @throws InvocationTargetException | |||
| */ | |||
| public Object innerCreateAndSet(Class newclass, Project project) | |||
| throws NoSuchMethodException, | |||
| InstantiationException, | |||
| IllegalAccessException, | |||
| InvocationTargetException { | |||
| Constructor ctor = null; | |||
| boolean noArg = false; | |||
| // DataType can have a "no arg" constructor or take a single | |||
| // Project argument. | |||
| try { | |||
| ctor = newclass.getConstructor(new Class[0]); | |||
| noArg = true; | |||
| } catch (NoSuchMethodException nse) { | |||
| //can throw the same exception, if there is no this(Project) ctor. | |||
| ctor = newclass.getConstructor(new Class[] {Project.class}); | |||
| noArg = false; | |||
| } | |||
| //now we instantiate | |||
| Object o = ctor.newInstance( | |||
| ((noArg) ? new Object[0] : new Object[] {project})); | |||
| //set up project references. | |||
| project.setProjectReference(o); | |||
| return o; | |||
| } | |||
| /** | |||
| * Equality method for this definition (assumes the names are the same). | |||
| * | |||
| @@ -28,10 +28,15 @@ import java.util.Stack; | |||
| import java.util.Vector; | |||
| import java.io.InputStream; | |||
| import java.io.IOException; | |||
| import java.io.File; | |||
| import java.io.StringWriter; | |||
| import java.io.PrintWriter; | |||
| import java.lang.ref.WeakReference; | |||
| import java.lang.reflect.Modifier; | |||
| import java.lang.reflect.InvocationTargetException; | |||
| import org.apache.tools.ant.taskdefs.Typedef; | |||
| import org.apache.tools.ant.launch.Launcher; | |||
| /** | |||
| * Component creation and configuration. | |||
| @@ -83,6 +88,9 @@ public class ComponentHelper { | |||
| private ComponentHelper next; | |||
| private Project project; | |||
| private static final String ERROR_NO_TASK_LIST_LOAD = "Can't load default task list"; | |||
| private static final String ERROR_NO_TYPE_LIST_LOAD = "Can't load default type list"; | |||
| public static final String COMPONENT_HELPER_REFERENCE = "ant.ComponentHelper"; | |||
| /** | |||
| * Find a project component for a specific project, creating | |||
| @@ -93,14 +101,14 @@ public class ComponentHelper { | |||
| public static ComponentHelper getComponentHelper(Project project) { | |||
| // Singleton for now, it may change ( per/classloader ) | |||
| ComponentHelper ph = (ComponentHelper) project.getReference( | |||
| "ant.ComponentHelper"); | |||
| COMPONENT_HELPER_REFERENCE); | |||
| if (ph != null) { | |||
| return ph; | |||
| } | |||
| ph = new ComponentHelper(); | |||
| ph.setProject(project); | |||
| project.addReference("ant.ComponentHelper", ph); | |||
| project.addReference(COMPONENT_HELPER_REFERENCE, ph); | |||
| return ph; | |||
| } | |||
| @@ -654,14 +662,14 @@ public class ComponentHelper { | |||
| && !("only".equals(project.getProperty("build.sysclasspath")))) { | |||
| classLoader = project.getCoreLoader(); | |||
| } | |||
| String dataDefs = "/org/apache/tools/ant/taskdefs/defaults.properties"; | |||
| String dataDefs = MagicNames.TASKDEF_PROPERTIES_RESOURCE; | |||
| InputStream in = null; | |||
| try { | |||
| Properties props = new Properties(); | |||
| in = this.getClass().getResourceAsStream(dataDefs); | |||
| if (in == null) { | |||
| throw new BuildException("Can't load default task list"); | |||
| throw new BuildException(ERROR_NO_TASK_LIST_LOAD); | |||
| } | |||
| props.load(in); | |||
| @@ -678,7 +686,7 @@ public class ComponentHelper { | |||
| antTypeTable.put(name, def); | |||
| } | |||
| } catch (IOException ex) { | |||
| throw new BuildException("Can't load default type list"); | |||
| throw new BuildException(ERROR_NO_TASK_LIST_LOAD); | |||
| } finally { | |||
| if (in != null) { | |||
| try { | |||
| @@ -699,14 +707,14 @@ public class ComponentHelper { | |||
| && !("only".equals(project.getProperty("build.sysclasspath")))) { | |||
| classLoader = project.getCoreLoader(); | |||
| } | |||
| String dataDefs = "/org/apache/tools/ant/types/defaults.properties"; | |||
| String dataDefs = MagicNames.TYPEDEFS_PROPERTIES_RESOURCE; | |||
| InputStream in = null; | |||
| try { | |||
| Properties props = new Properties(); | |||
| in = this.getClass().getResourceAsStream(dataDefs); | |||
| if (in == null) { | |||
| throw new BuildException("Can't load default datatype list"); | |||
| throw new BuildException(ERROR_NO_TYPE_LIST_LOAD); | |||
| } | |||
| props.load(in); | |||
| @@ -721,7 +729,7 @@ public class ComponentHelper { | |||
| antTypeTable.put(name, def); | |||
| } | |||
| } catch (IOException ex) { | |||
| throw new BuildException("Can't load default type list"); | |||
| throw new BuildException(ERROR_NO_TYPE_LIST_LOAD); | |||
| } finally { | |||
| if (in != null) { | |||
| try { | |||
| @@ -761,6 +769,146 @@ public class ComponentHelper { | |||
| definer.execute(); | |||
| } | |||
| /** | |||
| * Handler called to do decent diagnosis on instantiation failure | |||
| * @param componentName | |||
| * @return a string containing as much diagnostics info as possible. | |||
| */ | |||
| public String diagnoseCreationFailure(String componentName,String type) { | |||
| StringWriter errorText=new StringWriter(); | |||
| PrintWriter out=new PrintWriter(errorText); | |||
| out.println("Problem: failed to create "+ type +" "+componentName); | |||
| //class of problem | |||
| boolean lowlevel=false; | |||
| boolean jars=false; | |||
| boolean definitions=false; | |||
| boolean antTask; | |||
| //look up the name | |||
| AntTypeDefinition def = getDefinition(componentName); | |||
| if(def==null) { | |||
| //not a known type | |||
| out.println("Cause: The name is undefined."); | |||
| out.println("Action: Check the spelling."); | |||
| out.println("Action: Check that any custom tasks/types have been declared"); | |||
| out.println("Action: Check that any <presetdef>/<macrodefs> declarations have taken place"); | |||
| definitions=true; | |||
| } else{ | |||
| //we are defined, so it is an instantiation problem | |||
| final String classname = def.getClassName(); | |||
| antTask = classname.startsWith("org.apache.tools.ant."); | |||
| boolean optional = classname.startsWith("org.apache.tools.ant.taskdefs.optional"); | |||
| optional |= classname.startsWith("org.apache.tools.ant.types.optional"); | |||
| String home = System.getProperty(Launcher.USER_HOMEDIR); | |||
| File libDir = new File(home, | |||
| Launcher.ANT_PRIVATEDIR + | |||
| File.separator + | |||
| Launcher.ANT_PRIVATELIB); | |||
| //start with instantiating the class. | |||
| Class clazz= null; | |||
| try { | |||
| clazz = def.innerGetTypeClass(); | |||
| } catch (ClassNotFoundException e) { | |||
| out.println("Cause: the class " + | |||
| classname | |||
| + " was not found"); | |||
| jars = true; | |||
| if (optional) { | |||
| out.println(" This looks like one of Ant's optional components"); | |||
| out.println("Action: check that the appropriate optional JAR exists " | |||
| + "in ANT_HOME/lib or in "); | |||
| out.println(" " + libDir); | |||
| } else { | |||
| out.println("Action: check that the component has been correctly declared"); | |||
| out.println(" And that the implementing JAR is in ANT_HOME/lib or in"); | |||
| out.println(" " + libDir); | |||
| definitions = true; | |||
| } | |||
| } catch (NoClassDefFoundError ncdfe) { | |||
| jars = true; | |||
| out.println("Cause: Could not load a dependent class " | |||
| + ncdfe.getMessage()); | |||
| if(optional) { | |||
| out.println(" It is not enough to have Ant's optional JAR, you need the JAR"); | |||
| out.println(" files that it depends upon"); | |||
| out.println("Ant's optional task dependencies are listed in the manual"); | |||
| } else { | |||
| out.println(" This class may be in a separate JAR, that is not installed."); | |||
| } | |||
| out.println("Action: determine what extra JAR files are needed, and place them"); | |||
| out.println(" In ANT_HOME/lib or"); | |||
| out.println(" in " + libDir ); | |||
| } | |||
| //here we successfully loaded the class or failed. | |||
| if(clazz!=null) { | |||
| //success: proceed with more steps | |||
| try { | |||
| def.innerCreateAndSet(clazz,project); | |||
| //hey, there is nothing wrong with us | |||
| out.println("The component could be instantiated"); | |||
| } catch (NoSuchMethodException e) { | |||
| lowlevel = true; | |||
| out.println("Cause: The class " + classname | |||
| + " has no compatible constructor"); | |||
| } catch (InstantiationException e) { | |||
| lowlevel = true; | |||
| out.println("Cause: The class " + | |||
| classname | |||
| + " is abstract and cannot be instantiated"); | |||
| } catch (IllegalAccessException e) { | |||
| lowlevel = true; | |||
| out.println("Cause: The constructor for " + | |||
| classname | |||
| + " is private and cannot be invoked"); | |||
| } catch (InvocationTargetException ex) { | |||
| lowlevel = true; | |||
| Throwable t = ex.getTargetException(); | |||
| out.println("Cause: The constructor threw the exception "); | |||
| out.println(t.toString()); | |||
| t.printStackTrace(out); | |||
| } catch (NoClassDefFoundError ncdfe) { | |||
| jars = true; | |||
| out.println("Cause: A class needed by class " | |||
| + classname + " cannot be found: "); | |||
| out.println(" "+ ncdfe.getMessage()); | |||
| out.println("Action: determine what extra JAR files are needed, and place them"); | |||
| out.println(" In ANT_HOME/lib or"); | |||
| out.println(" in " + libDir); | |||
| } | |||
| } | |||
| out.println(); | |||
| out.println("Do not panic, this is a common problem."); | |||
| if(definitions) { | |||
| out.println("It may just be a typing error in the build file " + | |||
| "or the task/type declaration"); | |||
| } | |||
| if (jars) { | |||
| out.println("The commonest cause is a missing JAR. "); | |||
| } | |||
| if (lowlevel) { | |||
| out.println("This is quite a low level problem, which may need" + | |||
| "consultation with the author of the task"); | |||
| if(antTask) { | |||
| out.println("This may be the Ant team. Please file a " + | |||
| "defect or contact the developer team"); | |||
| } else { | |||
| out.println("This does not appear to be a task bundled with Ant"); | |||
| out.println("Please take it up with the supplier of the third-party "+type); | |||
| out.println("If you have written it yourself, you probably have a bug to fix"); | |||
| } | |||
| } else { | |||
| out.println(); | |||
| out.println("It is not an Ant bug; there is no need to file a bug" + | |||
| " report or contact the developers"); | |||
| } | |||
| } | |||
| out.flush(); | |||
| out.close(); | |||
| return errorText.toString(); | |||
| } | |||
| /** | |||
| * Map that contains the component definitions. | |||
| */ | |||
| @@ -72,7 +72,7 @@ public final class Diagnostics { | |||
| public static void validateVersion() throws BuildException { | |||
| try { | |||
| Class optional | |||
| = Class.forName("org.apache.tools.ant.taskdefs.optional.Test"); | |||
| = Class.forName(TEST_CLASS); | |||
| String coreVersion = getImplementationVersion(Main.class); | |||
| String optionalVersion = getImplementationVersion(optional); | |||
| @@ -94,7 +94,7 @@ public final class Diagnostics { | |||
| * <tt>null</tt> if an error occurs. | |||
| */ | |||
| public static File[] listLibraries() { | |||
| String home = System.getProperty("ant.home"); | |||
| String home = System.getProperty(Launcher.ANTHOME_PROPERTY); | |||
| if (home == null) { | |||
| return null; | |||
| } | |||
| @@ -211,8 +211,7 @@ public final class Diagnostics { | |||
| Class optional = null; | |||
| try { | |||
| optional = Class.forName( | |||
| "org.apache.tools.ant.taskdefs.optional.Test"); | |||
| optional = Class.forName(TEST_CLASS); | |||
| out.println("optional tasks : " | |||
| + getImplementationVersion(optional)); | |||
| } catch (ClassNotFoundException e) { | |||
| @@ -340,7 +339,7 @@ public final class Diagnostics { | |||
| */ | |||
| private static void doReportTasksAvailability(PrintStream out) { | |||
| InputStream is = Main.class.getResourceAsStream( | |||
| "/org/apache/tools/ant/taskdefs/defaults.properties"); | |||
| MagicNames.TASKDEF_PROPERTIES_RESOURCE); | |||
| if (is == null) { | |||
| out.println("None available"); | |||
| } else { | |||
| @@ -24,11 +24,43 @@ package org.apache.tools.ant; | |||
| * @since Ant 1.6 | |||
| */ | |||
| public class MagicNames { | |||
| /** The name of the script repository used by the script repo task */ | |||
| /** | |||
| * The name of the script repository used by the script repo task | |||
| * Value {@value} | |||
| */ | |||
| public static final String SCRIPT_REPOSITORY = "org.apache.ant.scriptrepo"; | |||
| /** The name of the reference to the System Class Loader */ | |||
| /** | |||
| * The name of the reference to the System Class Loader | |||
| * Value {@value} | |||
| **/ | |||
| public static final String SYSTEM_LOADER_REF = "ant.coreLoader"; | |||
| /** | |||
| * Name of the property which can provide an override of the repository dir | |||
| * for the libraries task | |||
| * Value {@value} | |||
| */ | |||
| public static final String REPOSITORY_DIR_PROPERTY = "ant.maven.repository.dir"; | |||
| /** | |||
| * Name of the property which can provide an override of the repository URL | |||
| * for the libraries task | |||
| * Value {@value} | |||
| */ | |||
| public static final String REPOSITORY_URL_PROPERTY = "ant.maven.repository.url"; | |||
| /** | |||
| * name of the resource that taskdefs are stored under | |||
| * Value: {@value} | |||
| */ | |||
| public static final String TASKDEF_PROPERTIES_RESOURCE = | |||
| "/org/apache/tools/ant/taskdefs/defaults.properties"; | |||
| /** | |||
| * name of the resource that typedefs are stored under | |||
| * Value: {@value} | |||
| */ | |||
| public static final String TYPEDEFS_PROPERTIES_RESOURCE = | |||
| "/org/apache/tools/ant/types/defaults.properties"; | |||
| } | |||
| @@ -463,45 +463,8 @@ public class UnknownElement extends Task { | |||
| */ | |||
| protected BuildException getNotFoundException(String what, | |||
| String elementName) { | |||
| String lSep = System.getProperty("line.separator"); | |||
| String msg = "Could not create " + what + " of type: " + elementName | |||
| + "." + lSep + lSep | |||
| + "Ant could not find the task or a class this " | |||
| + "task relies upon." + lSep + lSep | |||
| + "This is common and has a number of causes; the usual " + lSep | |||
| + "solutions are to read the manual pages then download and" + lSep | |||
| + "install needed JAR files, or fix the build file: " + lSep | |||
| + " - You have misspelt '" + elementName + "'." + lSep | |||
| + " Fix: check your spelling." + lSep | |||
| + " - The task needs an external JAR file to execute" + lSep | |||
| + " and this is not found at the right place in the classpath." + lSep | |||
| + " Fix: check the documentation for dependencies." + lSep | |||
| + " Fix: declare the task." + lSep | |||
| + " - The task is an Ant optional task and the JAR file and/or libraries" + lSep | |||
| + " implementing the functionality were not found at the time you" + lSep | |||
| + " yourself built your installation of Ant from the Ant sources." + lSep | |||
| + " Fix: Look in the ANT_HOME/lib for the 'ant-' JAR corresponding to the" + lSep | |||
| + " task and make sure it contains more than merely a META-INF/MANIFEST.MF." + lSep | |||
| + " If all it contains is the manifest, then rebuild Ant with the needed" + lSep | |||
| + " libraries present in ${ant.home}/lib/optional/ , or alternatively," + lSep | |||
| + " download a pre-built release version from apache.org" + lSep | |||
| + " - The build file was written for a later version of Ant" + lSep | |||
| + " Fix: upgrade to at least the latest release version of Ant" + lSep | |||
| + " - The task is not an Ant core or optional task " + lSep | |||
| + " and needs to be declared using <taskdef>." + lSep | |||
| + " - You are attempting to use a task defined using " + lSep | |||
| + " <presetdef> or <macrodef> but have spelt wrong or not " + lSep | |||
| + " defined it at the point of use" + lSep | |||
| + lSep | |||
| + "Remember that for JAR files to be visible to Ant tasks implemented" + lSep | |||
| + "in ANT_HOME/lib, the files must be in the same directory or on the" + lSep | |||
| + "classpath" + lSep | |||
| + lSep | |||
| + "Please neither file bug reports on this problem, nor email the" + lSep | |||
| + "Ant mailing lists, until all of these causes have been explored," + lSep | |||
| + "as this is not an Ant bug."; | |||
| ComponentHelper helper = ComponentHelper.getComponentHelper(getProject()); | |||
| String msg = helper.diagnoseCreationFailure(elementName, what); | |||
| return new BuildException(msg, getLocation()); | |||
| } | |||
| @@ -32,7 +32,10 @@ import java.util.Iterator; | |||
| * @since Ant 1.6 | |||
| */ | |||
| public class Launcher { | |||
| /** The Ant Home property */ | |||
| /** | |||
| * Ant home directory | |||
| * Value : {@value} | |||
| */ | |||
| public static final String ANTHOME_PROPERTY = "ant.home"; | |||
| /** The Ant Library Directory property */ | |||