stevel 2005/03/01 06:55:47 Modified: src/main/org/apache/tools/ant AntTypeDefinition.java MagicNames.java ComponentHelper.java Diagnostics.java UnknownElement.java src/main/org/apache/tools/ant/launch Launcher.java Log: complete rework of reporting when we cant instantiate an element 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. Revision Changes Path 1.16 +73 -25 ant/src/main/org/apache/tools/ant/AntTypeDefinition.java Index: AntTypeDefinition.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/AntTypeDefinition.java,v retrieving revision 1.15 retrieving revision 1.16 diff -u -r1.15 -r1.16 --- AntTypeDefinition.java 15 Dec 2004 23:44:05 -0000 1.15 +++ AntTypeDefinition.java 1 Mar 2005 14:55:46 -0000 1.16 @@ -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 @@ * @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 @@ 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 @@ */ 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,13 +262,61 @@ 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 [EMAIL PROTECTED] #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). * * @param other another definition. 1.7 +34 -2 ant/src/main/org/apache/tools/ant/MagicNames.java Index: MagicNames.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/MagicNames.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- MagicNames.java 9 Mar 2004 16:47:59 -0000 1.6 +++ MagicNames.java 1 Mar 2005 14:55:46 -0000 1.7 @@ -24,11 +24,43 @@ * @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 [EMAIL PROTECTED] + */ 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 [EMAIL PROTECTED] + **/ 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 [EMAIL PROTECTED] + */ + 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 [EMAIL PROTECTED] + */ + public static final String REPOSITORY_URL_PROPERTY = "ant.maven.repository.url"; + + /** + * name of the resource that taskdefs are stored under + * Value: [EMAIL PROTECTED] + */ + public static final String TASKDEF_PROPERTIES_RESOURCE = + "/org/apache/tools/ant/taskdefs/defaults.properties"; + /** + * name of the resource that typedefs are stored under + * Value: [EMAIL PROTECTED] + */ + public static final String TYPEDEFS_PROPERTIES_RESOURCE = + "/org/apache/tools/ant/types/defaults.properties"; + } 1.49 +156 -8 ant/src/main/org/apache/tools/ant/ComponentHelper.java Index: ComponentHelper.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/ComponentHelper.java,v retrieving revision 1.48 retrieving revision 1.49 diff -u -r1.48 -r1.49 --- ComponentHelper.java 28 Jan 2005 16:01:07 -0000 1.48 +++ ComponentHelper.java 1 Mar 2005 14:55:46 -0000 1.49 @@ -28,10 +28,15 @@ 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 @@ 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 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 @@ && !("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 @@ 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 @@ && !("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 @@ 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 { @@ -762,6 +770,146 @@ } /** + * 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. */ private static class AntTypeTable extends Hashtable { 1.22 +4 -5 ant/src/main/org/apache/tools/ant/Diagnostics.java Index: Diagnostics.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/Diagnostics.java,v retrieving revision 1.21 retrieving revision 1.22 diff -u -r1.21 -r1.22 --- Diagnostics.java 8 Feb 2005 18:50:35 -0000 1.21 +++ Diagnostics.java 1 Mar 2005 14:55:46 -0000 1.22 @@ -72,7 +72,7 @@ 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 @@ * <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 @@ 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 @@ */ 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 { 1.86 +2 -39 ant/src/main/org/apache/tools/ant/UnknownElement.java Index: UnknownElement.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/UnknownElement.java,v retrieving revision 1.85 retrieving revision 1.86 diff -u -r1.85 -r1.86 --- UnknownElement.java 22 Feb 2005 15:30:19 -0000 1.85 +++ UnknownElement.java 1 Mar 2005 14:55:46 -0000 1.86 @@ -463,45 +463,8 @@ */ 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()); } 1.29 +4 -1 ant/src/main/org/apache/tools/ant/launch/Launcher.java Index: Launcher.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/launch/Launcher.java,v retrieving revision 1.28 retrieving revision 1.29 diff -u -r1.28 -r1.29 --- Launcher.java 2 Feb 2005 09:09:12 -0000 1.28 +++ Launcher.java 1 Mar 2005 14:55:47 -0000 1.29 @@ -32,7 +32,10 @@ * @since Ant 1.6 */ public class Launcher { - /** The Ant Home property */ + /** + * Ant home directory + * Value : [EMAIL PROTECTED] + */ public static final String ANTHOME_PROPERTY = "ant.home"; /** The Ant Library Directory property */
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]