Browse Source

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.


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@277750 13f79535-47bb-0310-9956-ffa450edef68
master
Steve Loughran 20 years ago
parent
commit
b0003afe36
6 changed files with 273 additions and 80 deletions
  1. +73
    -25
      src/main/org/apache/tools/ant/AntTypeDefinition.java
  2. +156
    -8
      src/main/org/apache/tools/ant/ComponentHelper.java
  3. +4
    -5
      src/main/org/apache/tools/ant/Diagnostics.java
  4. +34
    -2
      src/main/org/apache/tools/ant/MagicNames.java
  5. +2
    -39
      src/main/org/apache/tools/ant/UnknownElement.java
  6. +4
    -1
      src/main/org/apache/tools/ant/launch/Launcher.java

+ 73
- 25
src/main/org/apache/tools/ant/AntTypeDefinition.java View File

@@ -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).
*


+ 156
- 8
src/main/org/apache/tools/ant/ComponentHelper.java View File

@@ -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.
*/


+ 4
- 5
src/main/org/apache/tools/ant/Diagnostics.java View File

@@ -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 {


+ 34
- 2
src/main/org/apache/tools/ant/MagicNames.java View File

@@ -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";

}


+ 2
- 39
src/main/org/apache/tools/ant/UnknownElement.java View File

@@ -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());
}



+ 4
- 1
src/main/org/apache/tools/ant/launch/Launcher.java View File

@@ -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 */


Loading…
Cancel
Save