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]

Reply via email to