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