blob: 4309731e6ed4188a538399b43d1fbaa049772c85 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.tools.ant;
import org.apache.tools.ant.util.LoaderUtils;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.JAXPUtils;
import org.apache.tools.ant.util.ProxySetup;
import org.apache.tools.ant.util.JavaEnvUtils;
import org.apache.tools.ant.launch.Launcher;
import org.xml.sax.XMLReader;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import java.io.File;
import java.io.FilenameFilter;
import java.io.PrintStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.FileOutputStream;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Calendar;
import java.util.TimeZone;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
/**
* A little diagnostic helper that output some information that may help
* in support. It should quickly give correct information about the
* jar existing in ant.home/lib and the jar versions...
*
* @since Ant 1.5
*/
public final class Diagnostics {
/** the version number for java 1.5 returned from JavaEnvUtils */
private static final int JAVA_1_5_NUMBER = 15;
/**
* value for which a difference between clock and temp file time triggers
* a warning.
* {@value}
*/
private static final int BIG_DRIFT_LIMIT = 10000;
/**
* How big a test file to write.
* {@value}
*/
private static final int TEST_FILE_SIZE = 32;
private static final int KILOBYTE = 1024;
private static final int SECONDS_PER_MILLISECOND = 1000;
private static final int SECONDS_PER_MINUTE = 60;
private static final int MINUTES_PER_HOUR = 60;
private static final String TEST_CLASS = "org.apache.tools.ant.taskdefs.optional.Test";
/**
* The error text when a security manager blocks access to a property.
* {@value}
*/
protected static final String ERROR_PROPERTY_ACCESS_BLOCKED
= "Access to this property blocked by a security manager";
/** utility class */
private Diagnostics() {
// hidden constructor
}
/**
* Check if optional tasks are available. Not that it does not check
* for implementation version. Use <tt>validateVersion()</tt> for this.
* @return <tt>true</tt> if optional tasks are available.
*/
public static boolean isOptionalAvailable() {
try {
Class.forName(TEST_CLASS);
} catch (ClassNotFoundException e) {
return false;
}
return true;
}
/**
* Check if core and optional implementation version do match.
* @throws BuildException if the implementation version of optional tasks
* does not match the core implementation version.
*/
public static void validateVersion() throws BuildException {
try {
Class optional = Class.forName(TEST_CLASS);
String coreVersion = getImplementationVersion(Main.class);
String optionalVersion = getImplementationVersion(optional);
if (coreVersion != null && !coreVersion.equals(optionalVersion)) {
throw new BuildException("Invalid implementation version "
+ "between Ant core and Ant optional tasks.\n" + " core : "
+ coreVersion + "\n" + " optional: " + optionalVersion);
}
} catch (ClassNotFoundException e) {
// ignore
ignoreThrowable(e);
}
}
/**
* return the list of jar files existing in ANT_HOME/lib
* and that must have been picked up by Ant script.
* @return the list of jar files existing in ant.home/lib or
* <tt>null</tt> if an error occurs.
*/
public static File[] listLibraries() {
String home = System.getProperty(MagicNames.ANT_HOME);
if (home == null) {
return null;
}
File libDir = new File(home, "lib");
return listJarFiles(libDir);
}
/**
* get a list of all JAR files in a directory
* @param libDir directory
* @return array of files (or null for no such directory)
*/
private static File[] listJarFiles(File libDir) {
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".jar");
}
};
File[] files = libDir.listFiles(filter);
return files;
}
/**
* main entry point for command line
* @param args command line arguments.
*/
public static void main(String[] args) {
doReport(System.out);
}
/**
* Helper method to get the implementation version.
* @param clazz the class to get the information from.
* @return null if there is no package or implementation version.
* '?.?' for JDK 1.0 or 1.1.
*/
private static String getImplementationVersion(Class clazz) {
return clazz.getPackage().getImplementationVersion();
}
/**
* what parser are we using.
* @return the classname of the parser
*/
private static String getXmlParserName() {
SAXParser saxParser = getSAXParser();
if (saxParser == null) {
return "Could not create an XML Parser";
}
// check to what is in the classname
String saxParserName = saxParser.getClass().getName();
return saxParserName;
}
/**
* Create a JAXP SAXParser
* @return parser or null for trouble
*/
private static SAXParser getSAXParser() {
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
if (saxParserFactory == null) {
return null;
}
SAXParser saxParser = null;
try {
saxParser = saxParserFactory.newSAXParser();
} catch (Exception e) {
// ignore
ignoreThrowable(e);
}
return saxParser;
}
/**
* get the location of the parser
* @return path or null for trouble in tracking it down
*/
private static String getXMLParserLocation() {
SAXParser saxParser = getSAXParser();
if (saxParser == null) {
return null;
}
String location = getClassLocation(saxParser.getClass());
return location;
}
private static String getNamespaceParserName() {
try {
XMLReader reader = JAXPUtils.getNamespaceXMLReader();
return reader.getClass().getName();
} catch (BuildException e) {
//ignore
ignoreThrowable(e);
return null;
}
}
private static String getNamespaceParserLocation() {
try {
XMLReader reader = JAXPUtils.getNamespaceXMLReader();
return getClassLocation(reader.getClass());
} catch (BuildException e) {
//ignore
ignoreThrowable(e);
return null;
}
}
/**
* ignore exceptions. This is to allow future
* implementations to log at a verbose level
* @param thrown
*/
private static void ignoreThrowable(Throwable thrown) {
}
/**
* get the location of a class. Stolen from axis/webapps/happyaxis.jsp
* @param clazz
* @return the jar file or path where a class was found, or null
*/
private static String getClassLocation(Class clazz) {
File f = LoaderUtils.getClassSource(clazz);
return f == null ? null : f.getAbsolutePath();
}
/**
* Print a report to the given stream.
* @param out the stream to print the report to.
*/
public static void doReport(PrintStream out) {
out.println("------- Ant diagnostics report -------");
out.println(Main.getAntVersion());
header(out, "Implementation Version");
out.println("core tasks : " + getImplementationVersion(Main.class));
Class optional = null;
try {
optional = Class.forName(TEST_CLASS);
out.println("optional tasks : " + getImplementationVersion(optional));
} catch (ClassNotFoundException e) {
ignoreThrowable(e);
out.println("optional tasks : not available");
}
header(out, "ANT PROPERTIES");
doReportAntProperties(out);
header(out, "ANT_HOME/lib jar listing");
doReportAntHomeLibraries(out);
header(out, "USER_HOME/.ant/lib jar listing");
doReportUserHomeLibraries(out);
header(out, "Tasks availability");
doReportTasksAvailability(out);
header(out, "org.apache.env.Which diagnostics");
doReportWhich(out);
header(out, "XML Parser information");
doReportParserInfo(out);
header(out, "System properties");
doReportSystemProperties(out);
header(out, "Temp dir");
doReportTempDir(out);
header(out, "Locale information");
doReportLocale(out);
header(out, "Proxy information");
doReportProxy(out);
out.println();
}
private static void header(PrintStream out, String section) {
out.println();
out.println("-------------------------------------------");
out.print(" ");
out.println(section);
out.println("-------------------------------------------");
}
/**
* Report a listing of system properties existing in the current vm.
* @param out the stream to print the properties to.
*/
private static void doReportSystemProperties(PrintStream out) {
Properties sysprops = null;
try {
sysprops = System.getProperties();
} catch (SecurityException e) {
ignoreThrowable(e);
out.println("Access to System.getProperties() blocked " + "by a security manager");
}
for (Enumeration keys = sysprops.propertyNames();
keys.hasMoreElements();) {
String key = (String) keys.nextElement();
String value = getProperty(key);
out.println(key + " : " + value);
}
}
/**
* Get the value of a system property. If a security manager
* blocks access to a property it fills the result in with an error
* @param key
* @return the system property's value or error text
* @see #ERROR_PROPERTY_ACCESS_BLOCKED
*/
private static String getProperty(String key) {
String value;
try {
value = System.getProperty(key);
} catch (SecurityException e) {
value = ERROR_PROPERTY_ACCESS_BLOCKED;
}
return value;
}
/**
* Report the content of ANT_HOME/lib directory
* @param out the stream to print the content to
*/
private static void doReportAntProperties(PrintStream out) {
Project p = new Project();
p.initProperties();
out.println(MagicNames.ANT_VERSION + ": " + p.getProperty(MagicNames.ANT_VERSION));
out.println(MagicNames.ANT_JAVA_VERSION + ": "
+ p.getProperty(MagicNames.ANT_JAVA_VERSION));
out.println(MagicNames.ANT_LIB + ": " + p.getProperty(MagicNames.ANT_LIB));
out.println(MagicNames.ANT_HOME + ": " + p.getProperty(MagicNames.ANT_HOME));
}
/**
* Report the content of ANT_HOME/lib directory
* @param out the stream to print the content to
*/
private static void doReportAntHomeLibraries(PrintStream out) {
out.println(MagicNames.ANT_HOME + ": " + System.getProperty(MagicNames.ANT_HOME));
File[] libs = listLibraries();
printLibraries(libs, out);
}
/**
* Report the content of ~/.ant/lib directory
*
* @param out the stream to print the content to
*/
private static void doReportUserHomeLibraries(PrintStream out) {
String home = System.getProperty(Launcher.USER_HOMEDIR);
out.println("user.home: " + home);
File libDir = new File(home, Launcher.USER_LIBDIR);
File[] libs = listJarFiles(libDir);
printLibraries(libs, out);
}
/**
* list the libraries
* @param libs array of libraries (can be null)
* @param out output stream
*/
private static void printLibraries(File[] libs, PrintStream out) {
if (libs == null) {
out.println("No such directory.");
return;
}
for (int i = 0; i < libs.length; i++) {
out.println(libs[i].getName() + " (" + libs[i].length() + " bytes)");
}
}
/**
* Call org.apache.env.Which if available
* @param out the stream to print the content to.
*/
private static void doReportWhich(PrintStream out) {
Throwable error = null;
try {
Class which = Class.forName("org.apache.env.Which");
Method method = which.getMethod(
"main", new Class[] {String[].class});
method.invoke(null, new Object[]{new String[]{}});
} catch (ClassNotFoundException e) {
out.println("Not available.");
out.println("Download it at http://xml.apache.org/commons/");
} catch (InvocationTargetException e) {
error = e.getTargetException() == null ? e : e.getTargetException();
} catch (Throwable e) {
error = e;
}
// report error if something weird happens...this is diagnostic.
if (error != null) {
out.println("Error while running org.apache.env.Which");
error.printStackTrace();
}
}
/**
* Create a report about non-available tasks that are defined in the
* mapping but could not be found via lookup. It might generally happen
* because Ant requires multiple libraries to compile and one of them
* was missing when compiling Ant.
* @param out the stream to print the tasks report to
* <tt>null</tt> for a missing stream (ie mapping).
*/
private static void doReportTasksAvailability(PrintStream out) {
InputStream is = Main.class.getResourceAsStream(
MagicNames.TASKDEF_PROPERTIES_RESOURCE);
if (is == null) {
out.println("None available");
} else {
Properties props = new Properties();
try {
props.load(is);
for (Enumeration keys = props.keys(); keys.hasMoreElements();) {
String key = (String) keys.nextElement();
String classname = props.getProperty(key);
try {
Class.forName(classname);
props.remove(key);
} catch (ClassNotFoundException e) {
out.println(key + " : Not Available "
+ "(the implementation class is not present)");
} catch (NoClassDefFoundError e) {
String pkg = e.getMessage().replace('/', '.');
out.println(key + " : Missing dependency " + pkg);
} catch (LinkageError e) {
out.println(key + " : Initialization error");
}
}
if (props.size() == 0) {
out.println("All defined tasks are available");
} else {
out.println("A task being missing/unavailable should only "
+ "matter if you are trying to use it");
}
} catch (IOException e) {
out.println(e.getMessage());
}
}
}
/**
* tell the user about the XML parser
* @param out
*/
private static void doReportParserInfo(PrintStream out) {
String parserName = getXmlParserName();
String parserLocation = getXMLParserLocation();
printParserInfo(out, "XML Parser", parserName, parserLocation);
printParserInfo(out, "Namespace-aware parser", getNamespaceParserName(),
getNamespaceParserLocation());
}
private static void printParserInfo(PrintStream out, String parserType, String parserName,
String parserLocation) {
if (parserName == null) {
parserName = "unknown";
}
if (parserLocation == null) {
parserLocation = "unknown";
}
out.println(parserType + " : " + parserName);
out.println(parserType + " Location: " + parserLocation);
}
/**
* try and create a temp file in our temp dir; this
* checks that it has space and access.
* We also do some clock reporting.
* @param out
*/
private static void doReportTempDir(PrintStream out) {
String tempdir = System.getProperty("java.io.tmpdir");
if (tempdir == null) {
out.println("Warning: java.io.tmpdir is undefined");
return;
}
out.println("Temp dir is " + tempdir);
File tempDirectory = new File(tempdir);
if (!tempDirectory.exists()) {
out.println("Warning, java.io.tmpdir directory does not exist: " + tempdir);
return;
}
//create the file
long now = System.currentTimeMillis();
File tempFile = null;
FileOutputStream fileout = null;
try {
tempFile = File.createTempFile("diag", "txt", tempDirectory);
//do some writing to it
fileout = new FileOutputStream(tempFile);
byte[] buffer = new byte[KILOBYTE];
for (int i = 0; i < TEST_FILE_SIZE; i++) {
fileout.write(buffer);
}
fileout.close();
fileout = null;
long filetime = tempFile.lastModified();
tempFile.delete();
out.println("Temp dir is writeable");
long drift = filetime - now;
out.println("Temp dir alignment with system clock is " + drift + " ms");
if (Math.abs(drift) > BIG_DRIFT_LIMIT) {
out.println("Warning: big clock drift -maybe a network filesystem");
}
} catch (IOException e) {
ignoreThrowable(e);
out.println("Failed to create a temporary file in the temp dir " + tempdir);
out.println("File " + tempFile + " could not be created/written to");
} finally {
FileUtils.close(fileout);
if (tempFile != null && tempFile.exists()) {
tempFile.delete();
}
}
}
/**
* Report locale information
* @param out stream to print to
*/
private static void doReportLocale(PrintStream out) {
//calendar stuff.
Calendar cal = Calendar.getInstance();
TimeZone tz = cal.getTimeZone();
out.println("Timezone "
+ tz.getDisplayName()
+ " offset="
+ tz.getOffset(cal.get(Calendar.ERA), cal.get(Calendar.YEAR), cal
.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), cal
.get(Calendar.DAY_OF_WEEK), ((cal.get(Calendar.HOUR_OF_DAY)
* MINUTES_PER_HOUR + cal.get(Calendar.MINUTE))
* SECONDS_PER_MINUTE + cal.get(Calendar.SECOND))
* SECONDS_PER_MILLISECOND + cal.get(Calendar.MILLISECOND)));
}
/**
* print a property name="value" pair if the property is set;
* print nothing if it is null
* @param out stream to print on
* @param key property name
*/
private static void printProperty(PrintStream out, String key) {
String value = getProperty(key);
if (value != null) {
out.print(key);
out.print(" = ");
out.print('"');
out.print(value);
out.println('"');
}
}
/**
* Report proxy information
*
* @param out stream to print to
* @since Ant1.7
*/
private static void doReportProxy(PrintStream out) {
printProperty(out, ProxySetup.HTTP_PROXY_HOST);
printProperty(out, ProxySetup.HTTP_PROXY_PORT);
printProperty(out, ProxySetup.HTTP_PROXY_USERNAME);
printProperty(out, ProxySetup.HTTP_PROXY_PASSWORD);
printProperty(out, ProxySetup.HTTP_NON_PROXY_HOSTS);
printProperty(out, ProxySetup.HTTPS_PROXY_HOST);
printProperty(out, ProxySetup.HTTPS_PROXY_PORT);
printProperty(out, ProxySetup.HTTPS_NON_PROXY_HOSTS);
printProperty(out, ProxySetup.FTP_PROXY_HOST);
printProperty(out, ProxySetup.FTP_PROXY_PORT);
printProperty(out, ProxySetup.FTP_NON_PROXY_HOSTS);
printProperty(out, ProxySetup.SOCKS_PROXY_HOST);
printProperty(out, ProxySetup.SOCKS_PROXY_PORT);
printProperty(out, ProxySetup.SOCKS_PROXY_USERNAME);
printProperty(out, ProxySetup.SOCKS_PROXY_PASSWORD);
if (JavaEnvUtils.getJavaVersionNumber() < JAVA_1_5_NUMBER) {
return;
}
printProperty(out, ProxySetup.USE_SYSTEM_PROXIES);
final String proxyDiagClassname = "org.apache.tools.ant.util.java15.ProxyDiagnostics";
try {
Class proxyDiagClass = Class.forName(proxyDiagClassname);
Object instance = proxyDiagClass.newInstance();
out.println("Java1.5+ proxy settings:");
out.println(instance.toString());
} catch (ClassNotFoundException e) {
//not included, do nothing
} catch (IllegalAccessException e) {
//not included, do nothing
} catch (InstantiationException e) {
//not included, do nothing
} catch (NoClassDefFoundError e) {
// not included, to nothing
}
}
}