001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.tools.ant;
019:
020: import org.apache.tools.ant.util.LoaderUtils;
021: import org.apache.tools.ant.util.FileUtils;
022: import org.apache.tools.ant.util.JAXPUtils;
023: import org.apache.tools.ant.util.ProxySetup;
024: import org.apache.tools.ant.util.JavaEnvUtils;
025: import org.apache.tools.ant.launch.Launcher;
026: import org.xml.sax.XMLReader;
027:
028: import javax.xml.parsers.SAXParserFactory;
029: import javax.xml.parsers.SAXParser;
030: import java.io.File;
031: import java.io.FilenameFilter;
032: import java.io.PrintStream;
033: import java.io.InputStream;
034: import java.io.IOException;
035: import java.io.FileOutputStream;
036: import java.util.Enumeration;
037: import java.util.Properties;
038: import java.util.Calendar;
039: import java.util.TimeZone;
040: import java.lang.reflect.Method;
041: import java.lang.reflect.InvocationTargetException;
042:
043: /**
044: * A little diagnostic helper that output some information that may help
045: * in support. It should quickly give correct information about the
046: * jar existing in ant.home/lib and the jar versions...
047: *
048: * @since Ant 1.5
049: */
050: public final class Diagnostics {
051:
052: /**
053: * value for which a difference between clock and temp file time triggers
054: * a warning.
055: * {@value}
056: */
057: private static final int BIG_DRIFT_LIMIT = 10000;
058: /**
059: * How big a test file to write.
060: * {@value}
061: */
062: private static final int TEST_FILE_SIZE = 32;
063: private static final int KILOBYTE = 1024;
064: private static final int SECONDS_PER_MILLISECOND = 1000;
065: private static final int SECONDS_PER_MINUTE = 60;
066: private static final int MINUTES_PER_HOUR = 60;
067: private static final String TEST_CLASS = "org.apache.tools.ant.taskdefs.optional.Test";
068:
069: /**
070: * The error text when a security manager blocks access to a property.
071: * {@value}
072: */
073: protected static final String ERROR_PROPERTY_ACCESS_BLOCKED = "Access to this property blocked by a security manager";
074:
075: /** utility class */
076: private Diagnostics() {
077: // hidden constructor
078: }
079:
080: /**
081: * Check if optional tasks are available. Not that it does not check
082: * for implementation version. Use <tt>validateVersion()</tt> for this.
083: * @return <tt>true</tt> if optional tasks are available.
084: */
085: public static boolean isOptionalAvailable() {
086: try {
087: Class.forName(TEST_CLASS);
088: } catch (ClassNotFoundException e) {
089: return false;
090: }
091: return true;
092: }
093:
094: /**
095: * Check if core and optional implementation version do match.
096: * @throws BuildException if the implementation version of optional tasks
097: * does not match the core implementation version.
098: */
099: public static void validateVersion() throws BuildException {
100: try {
101: Class optional = Class.forName(TEST_CLASS);
102: String coreVersion = getImplementationVersion(Main.class);
103: String optionalVersion = getImplementationVersion(optional);
104:
105: if (coreVersion != null
106: && !coreVersion.equals(optionalVersion)) {
107: throw new BuildException(
108: "Invalid implementation version "
109: + "between Ant core and Ant optional tasks.\n"
110: + " core : " + coreVersion + "\n"
111: + " optional: " + optionalVersion);
112: }
113: } catch (ClassNotFoundException e) {
114: // ignore
115: ignoreThrowable(e);
116: }
117: }
118:
119: /**
120: * return the list of jar files existing in ANT_HOME/lib
121: * and that must have been picked up by Ant script.
122: * @return the list of jar files existing in ant.home/lib or
123: * <tt>null</tt> if an error occurs.
124: */
125: public static File[] listLibraries() {
126: String home = System.getProperty(MagicNames.ANT_HOME);
127: if (home == null) {
128: return null;
129: }
130: File libDir = new File(home, "lib");
131: return listJarFiles(libDir);
132:
133: }
134:
135: /**
136: * get a list of all JAR files in a directory
137: * @param libDir directory
138: * @return array of files (or null for no such directory)
139: */
140: private static File[] listJarFiles(File libDir) {
141: FilenameFilter filter = new FilenameFilter() {
142: public boolean accept(File dir, String name) {
143: return name.endsWith(".jar");
144: }
145: };
146:
147: File[] files = libDir.listFiles(filter);
148: return files;
149: }
150:
151: /**
152: * main entry point for command line
153: * @param args command line arguments.
154: */
155: public static void main(String[] args) {
156: doReport(System.out);
157: }
158:
159: /**
160: * Helper method to get the implementation version.
161: * @param clazz the class to get the information from.
162: * @return null if there is no package or implementation version.
163: * '?.?' for JDK 1.0 or 1.1.
164: */
165: private static String getImplementationVersion(Class clazz) {
166: Package pkg = clazz.getPackage();
167: return pkg.getImplementationVersion();
168: }
169:
170: /**
171: * what parser are we using.
172: * @return the classname of the parser
173: */
174: private static String getXmlParserName() {
175: SAXParser saxParser = getSAXParser();
176: if (saxParser == null) {
177: return "Could not create an XML Parser";
178: }
179:
180: // check to what is in the classname
181: String saxParserName = saxParser.getClass().getName();
182: return saxParserName;
183: }
184:
185: /**
186: * Create a JAXP SAXParser
187: * @return parser or null for trouble
188: */
189: private static SAXParser getSAXParser() {
190: SAXParserFactory saxParserFactory = SAXParserFactory
191: .newInstance();
192: if (saxParserFactory == null) {
193: return null;
194: }
195: SAXParser saxParser = null;
196: try {
197: saxParser = saxParserFactory.newSAXParser();
198: } catch (Exception e) {
199: // ignore
200: ignoreThrowable(e);
201: }
202: return saxParser;
203: }
204:
205: /**
206: * get the location of the parser
207: * @return path or null for trouble in tracking it down
208: */
209:
210: private static String getXMLParserLocation() {
211: SAXParser saxParser = getSAXParser();
212: if (saxParser == null) {
213: return null;
214: }
215: String location = getClassLocation(saxParser.getClass());
216: return location;
217: }
218:
219: private static String getNamespaceParserName() {
220: try {
221: XMLReader reader = JAXPUtils.getNamespaceXMLReader();
222: return reader.getClass().getName();
223: } catch (BuildException e) {
224: //ignore
225: ignoreThrowable(e);
226: return null;
227: }
228: }
229:
230: private static String getNamespaceParserLocation() {
231: try {
232: XMLReader reader = JAXPUtils.getNamespaceXMLReader();
233: return getClassLocation(reader.getClass());
234: } catch (BuildException e) {
235: //ignore
236: ignoreThrowable(e);
237: return null;
238: }
239: }
240:
241: /**
242: * ignore exceptions. This is to allow future
243: * implementations to log at a verbose level
244: * @param thrown
245: */
246: private static void ignoreThrowable(Throwable thrown) {
247: }
248:
249: /**
250: * get the location of a class. Stolen from axis/webapps/happyaxis.jsp
251: * @param clazz
252: * @return the jar file or path where a class was found, or null
253: */
254:
255: private static String getClassLocation(Class clazz) {
256: File f = LoaderUtils.getClassSource(clazz);
257: return f == null ? null : f.getAbsolutePath();
258: }
259:
260: /**
261: * Print a report to the given stream.
262: * @param out the stream to print the report to.
263: */
264: public static void doReport(PrintStream out) {
265: out.println("------- Ant diagnostics report -------");
266: out.println(Main.getAntVersion());
267: header(out, "Implementation Version");
268:
269: out.println("core tasks : "
270: + getImplementationVersion(Main.class));
271:
272: Class optional = null;
273: try {
274: optional = Class.forName(TEST_CLASS);
275: out.println("optional tasks : "
276: + getImplementationVersion(optional));
277: } catch (ClassNotFoundException e) {
278: ignoreThrowable(e);
279: out.println("optional tasks : not available");
280: }
281:
282: header(out, "ANT PROPERTIES");
283: doReportAntProperties(out);
284:
285: header(out, "ANT_HOME/lib jar listing");
286: doReportAntHomeLibraries(out);
287:
288: header(out, "USER_HOME/.ant/lib jar listing");
289: doReportUserHomeLibraries(out);
290:
291: header(out, "Tasks availability");
292: doReportTasksAvailability(out);
293:
294: header(out, "org.apache.env.Which diagnostics");
295: doReportWhich(out);
296:
297: header(out, "XML Parser information");
298: doReportParserInfo(out);
299:
300: header(out, "System properties");
301: doReportSystemProperties(out);
302:
303: header(out, "Temp dir");
304: doReportTempDir(out);
305:
306: header(out, "Locale information");
307: doReportLocale(out);
308:
309: header(out, "Proxy information");
310: doReportProxy(out);
311:
312: out.println();
313: }
314:
315: private static void header(PrintStream out, String section) {
316: out.println();
317: out.println("-------------------------------------------");
318: out.print(" ");
319: out.println(section);
320: out.println("-------------------------------------------");
321: }
322:
323: /**
324: * Report a listing of system properties existing in the current vm.
325: * @param out the stream to print the properties to.
326: */
327: private static void doReportSystemProperties(PrintStream out) {
328: Properties sysprops = null;
329: try {
330: sysprops = System.getProperties();
331: } catch (SecurityException e) {
332: ignoreThrowable(e);
333: out.println("Access to System.getProperties() blocked "
334: + "by a security manager");
335: }
336: for (Enumeration keys = sysprops.propertyNames(); keys
337: .hasMoreElements();) {
338: String key = (String) keys.nextElement();
339: String value = getProperty(key);
340: out.println(key + " : " + value);
341: }
342: }
343:
344: /**
345: * Get the value of a system property. If a security manager
346: * blocks access to a property it fills the result in with an error
347: * @param key
348: * @return the system property's value or error text
349: * @see #ERROR_PROPERTY_ACCESS_BLOCKED
350: */
351: private static String getProperty(String key) {
352: String value;
353: try {
354: value = System.getProperty(key);
355: } catch (SecurityException e) {
356: value = ERROR_PROPERTY_ACCESS_BLOCKED;
357: }
358: return value;
359: }
360:
361: /**
362: * Report the content of ANT_HOME/lib directory
363: * @param out the stream to print the content to
364: */
365: private static void doReportAntProperties(PrintStream out) {
366: Project p = new Project();
367: p.initProperties();
368: out.println(MagicNames.ANT_VERSION + ": "
369: + p.getProperty(MagicNames.ANT_VERSION));
370: out.println(MagicNames.ANT_JAVA_VERSION + ": "
371: + p.getProperty(MagicNames.ANT_JAVA_VERSION));
372: out.println(MagicNames.ANT_LIB + ": "
373: + p.getProperty(MagicNames.ANT_LIB));
374: out.println(MagicNames.ANT_HOME + ": "
375: + p.getProperty(MagicNames.ANT_HOME));
376: }
377:
378: /**
379: * Report the content of ANT_HOME/lib directory
380: * @param out the stream to print the content to
381: */
382: private static void doReportAntHomeLibraries(PrintStream out) {
383: out.println(MagicNames.ANT_HOME + ": "
384: + System.getProperty(MagicNames.ANT_HOME));
385: File[] libs = listLibraries();
386: printLibraries(libs, out);
387: }
388:
389: /**
390: * Report the content of ~/.ant/lib directory
391: *
392: * @param out the stream to print the content to
393: */
394: private static void doReportUserHomeLibraries(PrintStream out) {
395: String home = System.getProperty(Launcher.USER_HOMEDIR);
396: out.println("user.home: " + home);
397: File libDir = new File(home, Launcher.USER_LIBDIR);
398: File[] libs = listJarFiles(libDir);
399: printLibraries(libs, out);
400: }
401:
402: /**
403: * list the libraries
404: * @param libs array of libraries (can be null)
405: * @param out output stream
406: */
407: private static void printLibraries(File[] libs, PrintStream out) {
408: if (libs == null) {
409: out.println("No such directory.");
410: return;
411: }
412: for (int i = 0; i < libs.length; i++) {
413: out.println(libs[i].getName() + " (" + libs[i].length()
414: + " bytes)");
415: }
416: }
417:
418: /**
419: * Call org.apache.env.Which if available
420: * @param out the stream to print the content to.
421: */
422: private static void doReportWhich(PrintStream out) {
423: Throwable error = null;
424: try {
425: Class which = Class.forName("org.apache.env.Which");
426: Method method = which.getMethod("main",
427: new Class[] { String[].class });
428: method.invoke(null, new Object[] { new String[] {} });
429: } catch (ClassNotFoundException e) {
430: out.println("Not available.");
431: out
432: .println("Download it at http://xml.apache.org/commons/");
433: } catch (InvocationTargetException e) {
434: error = e.getTargetException() == null ? e : e
435: .getTargetException();
436: } catch (Throwable e) {
437: error = e;
438: }
439: // report error if something weird happens...this is diagnostic.
440: if (error != null) {
441: out.println("Error while running org.apache.env.Which");
442: error.printStackTrace();
443: }
444: }
445:
446: /**
447: * Create a report about non-available tasks that are defined in the
448: * mapping but could not be found via lookup. It might generally happen
449: * because Ant requires multiple libraries to compile and one of them
450: * was missing when compiling Ant.
451: * @param out the stream to print the tasks report to
452: * <tt>null</tt> for a missing stream (ie mapping).
453: */
454: private static void doReportTasksAvailability(PrintStream out) {
455: InputStream is = Main.class
456: .getResourceAsStream(MagicNames.TASKDEF_PROPERTIES_RESOURCE);
457: if (is == null) {
458: out.println("None available");
459: } else {
460: Properties props = new Properties();
461: try {
462: props.load(is);
463: for (Enumeration keys = props.keys(); keys
464: .hasMoreElements();) {
465: String key = (String) keys.nextElement();
466: String classname = props.getProperty(key);
467: try {
468: Class.forName(classname);
469: props.remove(key);
470: } catch (ClassNotFoundException e) {
471: out
472: .println(key
473: + " : Not Available "
474: + "(the implementation class is not present)");
475: } catch (NoClassDefFoundError e) {
476: String pkg = e.getMessage().replace('/', '.');
477: out.println(key + " : Missing dependency "
478: + pkg);
479: } catch (LinkageError e) {
480: out.println(key + " : Initialization error");
481: }
482: }
483: if (props.size() == 0) {
484: out.println("All defined tasks are available");
485: } else {
486: out
487: .println("A task being missing/unavailable should only "
488: + "matter if you are trying to use it");
489: }
490: } catch (IOException e) {
491: out.println(e.getMessage());
492: }
493: }
494: }
495:
496: /**
497: * tell the user about the XML parser
498: * @param out
499: */
500: private static void doReportParserInfo(PrintStream out) {
501: String parserName = getXmlParserName();
502: String parserLocation = getXMLParserLocation();
503: printParserInfo(out, "XML Parser", parserName, parserLocation);
504: printParserInfo(out, "Namespace-aware parser",
505: getNamespaceParserName(), getNamespaceParserLocation());
506: }
507:
508: private static void printParserInfo(PrintStream out,
509: String parserType, String parserName, String parserLocation) {
510: if (parserName == null) {
511: parserName = "unknown";
512: }
513: if (parserLocation == null) {
514: parserLocation = "unknown";
515: }
516: out.println(parserType + " : " + parserName);
517: out.println(parserType + " Location: " + parserLocation);
518: }
519:
520: /**
521: * try and create a temp file in our temp dir; this
522: * checks that it has space and access.
523: * We also do some clock reporting.
524: * @param out
525: */
526: private static void doReportTempDir(PrintStream out) {
527: String tempdir = System.getProperty("java.io.tmpdir");
528: if (tempdir == null) {
529: out.println("Warning: java.io.tmpdir is undefined");
530: return;
531: }
532: out.println("Temp dir is " + tempdir);
533: File tempDirectory = new File(tempdir);
534: if (!tempDirectory.exists()) {
535: out
536: .println("Warning, java.io.tmpdir directory does not exist: "
537: + tempdir);
538: return;
539: }
540: //create the file
541: long now = System.currentTimeMillis();
542: File tempFile = null;
543: FileOutputStream fileout = null;
544: try {
545: tempFile = File
546: .createTempFile("diag", "txt", tempDirectory);
547: //do some writing to it
548: fileout = new FileOutputStream(tempFile);
549: byte[] buffer = new byte[KILOBYTE];
550: for (int i = 0; i < TEST_FILE_SIZE; i++) {
551: fileout.write(buffer);
552: }
553: fileout.close();
554: fileout = null;
555: long filetime = tempFile.lastModified();
556: tempFile.delete();
557: out.println("Temp dir is writeable");
558: long drift = filetime - now;
559: out.println("Temp dir alignment with system clock is "
560: + drift + " ms");
561: if (Math.abs(drift) > BIG_DRIFT_LIMIT) {
562: out
563: .println("Warning: big clock drift -maybe a network filesystem");
564: }
565: } catch (IOException e) {
566: ignoreThrowable(e);
567: out
568: .println("Failed to create a temporary file in the temp dir "
569: + tempdir);
570: out.println("File " + tempFile
571: + " could not be created/written to");
572: } finally {
573: FileUtils.close(fileout);
574: if (tempFile != null && tempFile.exists()) {
575: tempFile.delete();
576: }
577: }
578: }
579:
580: /**
581: * Report locale information
582: * @param out stream to print to
583: */
584: private static void doReportLocale(PrintStream out) {
585: //calendar stuff.
586: Calendar cal = Calendar.getInstance();
587: TimeZone tz = cal.getTimeZone();
588: out.println("Timezone "
589: + tz.getDisplayName()
590: + " offset="
591: + tz.getOffset(cal.get(Calendar.ERA), cal
592: .get(Calendar.YEAR), cal.get(Calendar.MONTH),
593: cal.get(Calendar.DAY_OF_MONTH), cal
594: .get(Calendar.DAY_OF_WEEK), ((cal
595: .get(Calendar.HOUR_OF_DAY)
596: * MINUTES_PER_HOUR + cal
597: .get(Calendar.MINUTE))
598: * SECONDS_PER_MINUTE + cal
599: .get(Calendar.SECOND))
600: * SECONDS_PER_MILLISECOND
601: + cal.get(Calendar.MILLISECOND)));
602: }
603:
604: /**
605: * print a property name="value" pair if the property is set;
606: * print nothing if it is null
607: * @param out stream to print on
608: * @param key property name
609: */
610: private static void printProperty(PrintStream out, String key) {
611: String value = getProperty(key);
612: if (value != null) {
613: out.print(key);
614: out.print(" = ");
615: out.print('"');
616: out.print(value);
617: out.println('"');
618: }
619: }
620:
621: /**
622: * Report proxy information
623: *
624: * @param out stream to print to
625: * @since Ant1.7
626: */
627: private static void doReportProxy(PrintStream out) {
628: printProperty(out, ProxySetup.HTTP_PROXY_HOST);
629: printProperty(out, ProxySetup.HTTP_PROXY_PORT);
630: printProperty(out, ProxySetup.HTTP_PROXY_USERNAME);
631: printProperty(out, ProxySetup.HTTP_PROXY_PASSWORD);
632: printProperty(out, ProxySetup.HTTP_NON_PROXY_HOSTS);
633: printProperty(out, ProxySetup.HTTPS_PROXY_HOST);
634: printProperty(out, ProxySetup.HTTPS_PROXY_PORT);
635: printProperty(out, ProxySetup.HTTPS_NON_PROXY_HOSTS);
636: printProperty(out, ProxySetup.FTP_PROXY_HOST);
637: printProperty(out, ProxySetup.FTP_PROXY_PORT);
638: printProperty(out, ProxySetup.FTP_NON_PROXY_HOSTS);
639: printProperty(out, ProxySetup.SOCKS_PROXY_HOST);
640: printProperty(out, ProxySetup.SOCKS_PROXY_PORT);
641: printProperty(out, ProxySetup.SOCKS_PROXY_USERNAME);
642: printProperty(out, ProxySetup.SOCKS_PROXY_PASSWORD);
643:
644: if (JavaEnvUtils.getJavaVersionNumber() < 15) {
645: return;
646: }
647: printProperty(out, ProxySetup.USE_SYSTEM_PROXIES);
648: final String proxyDiagClassname = "org.apache.tools.ant.util.java15.ProxyDiagnostics";
649: try {
650: Class proxyDiagClass = Class.forName(proxyDiagClassname);
651: Object instance = proxyDiagClass.newInstance();
652: out.println("Java1.5+ proxy settings:");
653: out.println(instance.toString());
654: } catch (ClassNotFoundException e) {
655: //not included, do nothing
656: } catch (IllegalAccessException e) {
657: //not included, do nothing
658: } catch (InstantiationException e) {
659: //not included, do nothing
660: } catch (NoClassDefFoundError e) {
661: // not included, to nothing
662: }
663: }
664:
665: }
|