1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: EnvironmentCheck.java,v 1.26 2004/02/26 04:00:47 zongaro Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.xslt;
  20. import java.io.File;
  21. import java.io.FileWriter;
  22. import java.io.PrintWriter;
  23. import java.lang.reflect.Field;
  24. import java.lang.reflect.Method;
  25. import java.util.Enumeration;
  26. import java.util.Hashtable;
  27. import java.util.StringTokenizer;
  28. import java.util.Vector;
  29. import org.w3c.dom.Document;
  30. import org.w3c.dom.Element;
  31. import org.w3c.dom.Node;
  32. /**
  33. * Utility class to report simple information about the environment.
  34. * Simplistic reporting about certain classes found in your JVM may
  35. * help answer some FAQs for simple problems.
  36. *
  37. * <p>Usage-command line:
  38. * <code>
  39. * java com.sun.org.apache.xalan.internal.xslt.EnvironmentCheck [-out outFile]
  40. * </code></p>
  41. *
  42. * <p>Usage-from program:
  43. * <code>
  44. * boolean environmentOK =
  45. * (new EnvironmentCheck()).checkEnvironment(yourPrintWriter);
  46. * </code></p>
  47. *
  48. * <p>Usage-from stylesheet:
  49. * <code><pre>
  50. * <?xml version="1.0"?>
  51. * <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  52. * xmlns:xalan="http://xml.apache.org/xalan"
  53. * exclude-result-prefixes="xalan">
  54. * <xsl:output indent="yes"/>
  55. * <xsl:template match="/">
  56. * <xsl:copy-of select="xalan:checkEnvironment()"/>
  57. * </xsl:template>
  58. * </xsl:stylesheet>
  59. * </pre></code></p>
  60. *
  61. * <p>Xalan users reporting problems are encouraged to use this class
  62. * to see if there are potential problems with their actual
  63. * Java environment <b>before</b> reporting a bug. Note that you
  64. * should both check from the JVM/JRE's command line as well as
  65. * temporarily calling checkEnvironment() directly from your code,
  66. * since the classpath may differ (especially for servlets, etc).</p>
  67. *
  68. * <p>Also see http://xml.apache.org/xalan-j/faq.html</p>
  69. *
  70. * <p>Note: This class is pretty simplistic:
  71. * results are not necessarily definitive nor will it find all
  72. * problems related to environment setup. Also, you should avoid
  73. * calling this in deployed production code, both because it is
  74. * quite slow and because it forces classes to get loaded.</p>
  75. *
  76. * <p>Note: This class explicitly has very limited compile-time
  77. * dependencies to enable easy compilation and usage even when
  78. * Xalan, DOM/SAX/JAXP, etc. are not present.</p>
  79. *
  80. * <p>Note: for an improved version of this utility, please see
  81. * the xml-commons' project Which utility which does the same kind
  82. * of thing but in a much simpler manner.</p>
  83. *
  84. * @author Shane_Curcuru@us.ibm.com
  85. * @version $Id: EnvironmentCheck.java,v 1.26 2004/02/26 04:00:47 zongaro Exp $
  86. */
  87. public class EnvironmentCheck
  88. {
  89. /**
  90. * Command line runnability: checks for [-out outFilename] arg.
  91. * <p>Command line entrypoint; Sets output and calls
  92. * {@link #checkEnvironment(PrintWriter)}.</p>
  93. * @param args command line args
  94. */
  95. public static void _main(String[] args)
  96. {
  97. // Default to System.out, autoflushing
  98. PrintWriter sendOutputTo = new PrintWriter(System.out, true);
  99. // Read our simplistic input args, if supplied
  100. for (int i = 0; i < args.length; i++)
  101. {
  102. if ("-out".equalsIgnoreCase(args[i]))
  103. {
  104. i++;
  105. if (i < args.length)
  106. {
  107. try
  108. {
  109. sendOutputTo = new PrintWriter(new FileWriter(args[i], true));
  110. }
  111. catch (Exception e)
  112. {
  113. System.err.println("# WARNING: -out " + args[i] + " threw "
  114. + e.toString());
  115. }
  116. }
  117. else
  118. {
  119. System.err.println(
  120. "# WARNING: -out argument should have a filename, output sent to console");
  121. }
  122. }
  123. }
  124. EnvironmentCheck app = new EnvironmentCheck();
  125. app.checkEnvironment(sendOutputTo);
  126. }
  127. /**
  128. * Programmatic entrypoint: Report on basic Java environment
  129. * and CLASSPATH settings that affect Xalan.
  130. *
  131. * <p>Note that this class is not advanced enough to tell you
  132. * everything about the environment that affects Xalan, and
  133. * sometimes reports errors that will not actually affect
  134. * Xalan's behavior. Currently, it very simplistically
  135. * checks the JVM's environment for some basic properties and
  136. * logs them out; it will report a problem if it finds a setting
  137. * or .jar file that is <i>likely</i> to cause problems.</p>
  138. *
  139. * <p>Advanced users can peruse the code herein to help them
  140. * investigate potential environment problems found; other users
  141. * may simply send the output from this tool along with any bugs
  142. * they submit to help us in the debugging process.</p>
  143. *
  144. * @param pw PrintWriter to send output to; can be sent to a
  145. * file that will look similar to a Properties file; defaults
  146. * to System.out if null
  147. * @return true if your environment appears to have no major
  148. * problems; false if potential environment problems found
  149. * @see #getEnvironmentHash()
  150. */
  151. public boolean checkEnvironment(PrintWriter pw)
  152. {
  153. // Use user-specified output writer if non-null
  154. if (null != pw)
  155. outWriter = pw;
  156. // Setup a hash to store various environment information in
  157. Hashtable hash = getEnvironmentHash();
  158. // Check for ERROR keys in the hashtable, and print report
  159. boolean environmentHasErrors = writeEnvironmentReport(hash);
  160. if (environmentHasErrors)
  161. {
  162. // Note: many logMsg calls have # at the start to
  163. // fake a property-file like output
  164. logMsg("# WARNING: Potential problems found in your environment!");
  165. logMsg("# Check any 'ERROR' items above against the Xalan FAQs");
  166. logMsg("# to correct potential problems with your classes/jars");
  167. logMsg("# http://xml.apache.org/xalan-j/faq.html");
  168. if (null != outWriter)
  169. outWriter.flush();
  170. return false;
  171. }
  172. else
  173. {
  174. logMsg("# YAHOO! Your environment seems to be OK.");
  175. if (null != outWriter)
  176. outWriter.flush();
  177. return true;
  178. }
  179. }
  180. /**
  181. * Fill a hash with basic environment settings that affect Xalan.
  182. *
  183. * <p>Worker method called from various places.</p>
  184. * <p>Various system and CLASSPATH, etc. properties are put into
  185. * the hash as keys with a brief description of the current state
  186. * of that item as the value. Any serious problems will be put in
  187. * with a key that is prefixed with {@link #ERROR 'ERROR.'} so it
  188. * stands out in any resulting report; also a key with just that
  189. * constant will be set as well for any error.</p>
  190. * <p>Note that some legitimate cases are flaged as potential
  191. * errors - namely when a developer recompiles xalan.jar on their
  192. * own - and even a non-error state doesn't guaruntee that
  193. * everything in the environment is correct. But this will help
  194. * point out the most common classpath and system property
  195. * problems that we've seen.</p>
  196. *
  197. * @return Hashtable full of useful environment info about Xalan
  198. * and related system properties, etc.
  199. */
  200. public Hashtable getEnvironmentHash()
  201. {
  202. // Setup a hash to store various environment information in
  203. Hashtable hash = new Hashtable();
  204. // Call various worker methods to fill in the hash
  205. // These are explicitly separate for maintenance and so
  206. // advanced users could call them standalone
  207. checkJAXPVersion(hash);
  208. checkProcessorVersion(hash);
  209. checkParserVersion(hash);
  210. checkAntVersion(hash);
  211. checkDOMVersion(hash);
  212. checkSAXVersion(hash);
  213. checkSystemProperties(hash);
  214. return hash;
  215. }
  216. /**
  217. * Dump a basic Xalan environment report to outWriter.
  218. *
  219. * <p>This dumps a simple header and then each of the entries in
  220. * the Hashtable to our PrintWriter; it does special processing
  221. * for entries that are .jars found in the classpath.</p>
  222. *
  223. * @param h Hashtable of items to report on; presumably
  224. * filled in by our various check*() methods
  225. * @return true if your environment appears to have no major
  226. * problems; false if potential environment problems found
  227. * @see #appendEnvironmentReport(Node, Document, Hashtable)
  228. * for an equivalent that appends to a Node instead
  229. */
  230. protected boolean writeEnvironmentReport(Hashtable h)
  231. {
  232. if (null == h)
  233. {
  234. logMsg("# ERROR: writeEnvironmentReport called with null Hashtable");
  235. return false;
  236. }
  237. boolean errors = false;
  238. logMsg(
  239. "#---- BEGIN writeEnvironmentReport($Revision: 1.26 $): Useful stuff found: ----");
  240. // Fake the Properties-like output
  241. for (Enumeration keys = h.keys();
  242. keys.hasMoreElements();
  243. /* no increment portion */
  244. )
  245. {
  246. Object key = keys.nextElement();
  247. String keyStr = (String) key;
  248. try
  249. {
  250. // Special processing for classes found..
  251. if (keyStr.startsWith(FOUNDCLASSES))
  252. {
  253. Vector v = (Vector) h.get(keyStr);
  254. errors |= logFoundJars(v, keyStr);
  255. }
  256. // ..normal processing for all other entries
  257. else
  258. {
  259. // Note: we could just check for the ERROR key by itself,
  260. // since we now set that, but since we have to go
  261. // through the whole hash anyway, do it this way,
  262. // which is safer for maintenance
  263. if (keyStr.startsWith(ERROR))
  264. {
  265. errors = true;
  266. }
  267. logMsg(keyStr + "=" + h.get(keyStr));
  268. }
  269. }
  270. catch (Exception e)
  271. {
  272. logMsg("Reading-" + key + "= threw: " + e.toString());
  273. }
  274. }
  275. logMsg(
  276. "#----- END writeEnvironmentReport: Useful properties found: -----");
  277. return errors;
  278. }
  279. /** Prefixed to hash keys that signify serious problems. */
  280. public static final String ERROR = "ERROR.";
  281. /** Added to descriptions that signify potential problems. */
  282. public static final String WARNING = "WARNING.";
  283. /** Value for any error found. */
  284. public static final String ERROR_FOUND = "At least one error was found!";
  285. /** Prefixed to hash keys that signify version numbers. */
  286. public static final String VERSION = "version.";
  287. /** Prefixed to hash keys that signify .jars found in classpath. */
  288. public static final String FOUNDCLASSES = "foundclasses.";
  289. /** Marker that a class or .jar was found. */
  290. public static final String CLASS_PRESENT = "present-unknown-version";
  291. /** Marker that a class or .jar was not found. */
  292. public static final String CLASS_NOTPRESENT = "not-present";
  293. /** Listing of common .jar files that include Xalan-related classes. */
  294. public String[] jarNames =
  295. {
  296. "xalan.jar", "xalansamples.jar", "xalanj1compat.jar", "xalanservlet.jar",
  297. "xerces.jar", // Xerces-J 1.x
  298. "xercesImpl.jar", // Xerces-J 2.x
  299. "testxsl.jar",
  300. "crimson.jar",
  301. "lotusxsl.jar",
  302. "jaxp.jar", "parser.jar", "dom.jar", "sax.jar", "xml.jar",
  303. "xml-apis.jar",
  304. "xsltc.jar"
  305. };
  306. /**
  307. * Print out report of .jars found in a classpath.
  308. *
  309. * Takes the information encoded from a checkPathForJars()
  310. * call and dumps it out to our PrintWriter.
  311. *
  312. * @param v Vector of Hashtables of .jar file info
  313. * @param desc description to print out in header
  314. *
  315. * @return false if OK, true if any .jars were reported
  316. * as having errors
  317. * @see #checkPathForJars(String, String[])
  318. */
  319. protected boolean logFoundJars(Vector v, String desc)
  320. {
  321. if ((null == v) || (v.size() < 1))
  322. return false;
  323. boolean errors = false;
  324. logMsg("#---- BEGIN Listing XML-related jars in: " + desc + " ----");
  325. for (int i = 0; i < v.size(); i++)
  326. {
  327. Hashtable subhash = (Hashtable) v.elementAt(i);
  328. for (Enumeration keys = subhash.keys();
  329. keys.hasMoreElements();
  330. /* no increment portion */
  331. )
  332. {
  333. Object key = keys.nextElement();
  334. String keyStr = (String) key;
  335. try
  336. {
  337. if (keyStr.startsWith(ERROR))
  338. {
  339. errors = true;
  340. }
  341. logMsg(keyStr + "=" + subhash.get(keyStr));
  342. }
  343. catch (Exception e)
  344. {
  345. errors = true;
  346. logMsg("Reading-" + key + "= threw: " + e.toString());
  347. }
  348. }
  349. }
  350. logMsg("#----- END Listing XML-related jars in: " + desc + " -----");
  351. return errors;
  352. }
  353. /**
  354. * Stylesheet extension entrypoint: Dump a basic Xalan
  355. * environment report from getEnvironmentHash() to a Node.
  356. *
  357. * <p>Copy of writeEnvironmentReport that creates a Node suitable
  358. * for other processing instead of a properties-like text output.
  359. * </p>
  360. * @param container Node to append our report to
  361. * @param factory Document providing createElement, etc. services
  362. * @param h Hash presumably from {@link #getEnvironmentHash()}
  363. * @see #writeEnvironmentReport(Hashtable)
  364. * for an equivalent that writes to a PrintWriter instead
  365. */
  366. public void appendEnvironmentReport(Node container, Document factory, Hashtable h)
  367. {
  368. if ((null == container) || (null == factory))
  369. {
  370. return;
  371. }
  372. try
  373. {
  374. Element envCheckNode = factory.createElement("EnvironmentCheck");
  375. envCheckNode.setAttribute("version", "$Revision: 1.26 $");
  376. container.appendChild(envCheckNode);
  377. if (null == h)
  378. {
  379. Element statusNode = factory.createElement("status");
  380. statusNode.setAttribute("result", "ERROR");
  381. statusNode.appendChild(factory.createTextNode("appendEnvironmentReport called with null Hashtable!"));
  382. envCheckNode.appendChild(statusNode);
  383. return;
  384. }
  385. boolean errors = false;
  386. Element hashNode = factory.createElement("environment");
  387. envCheckNode.appendChild(hashNode);
  388. for (Enumeration keys = h.keys();
  389. keys.hasMoreElements();
  390. /* no increment portion */
  391. )
  392. {
  393. Object key = keys.nextElement();
  394. String keyStr = (String) key;
  395. try
  396. {
  397. // Special processing for classes found..
  398. if (keyStr.startsWith(FOUNDCLASSES))
  399. {
  400. Vector v = (Vector) h.get(keyStr);
  401. // errors |= logFoundJars(v, keyStr);
  402. errors |= appendFoundJars(hashNode, factory, v, keyStr);
  403. }
  404. // ..normal processing for all other entries
  405. else
  406. {
  407. // Note: we could just check for the ERROR key by itself,
  408. // since we now set that, but since we have to go
  409. // through the whole hash anyway, do it this way,
  410. // which is safer for maintenance
  411. if (keyStr.startsWith(ERROR))
  412. {
  413. errors = true;
  414. }
  415. Element node = factory.createElement("item");
  416. node.setAttribute("key", keyStr);
  417. node.appendChild(factory.createTextNode((String)h.get(keyStr)));
  418. hashNode.appendChild(node);
  419. }
  420. }
  421. catch (Exception e)
  422. {
  423. errors = true;
  424. Element node = factory.createElement("item");
  425. node.setAttribute("key", keyStr);
  426. node.appendChild(factory.createTextNode(ERROR + " Reading " + key + " threw: " + e.toString()));
  427. hashNode.appendChild(node);
  428. }
  429. } // end of for...
  430. Element statusNode = factory.createElement("status");
  431. statusNode.setAttribute("result", (errors ? "ERROR" : "OK" ));
  432. envCheckNode.appendChild(statusNode);
  433. }
  434. catch (Exception e2)
  435. {
  436. System.err.println("appendEnvironmentReport threw: " + e2.toString());
  437. e2.printStackTrace();
  438. }
  439. }
  440. /**
  441. * Print out report of .jars found in a classpath.
  442. *
  443. * Takes the information encoded from a checkPathForJars()
  444. * call and dumps it out to our PrintWriter.
  445. *
  446. * @param container Node to append our report to
  447. * @param factory Document providing createElement, etc. services
  448. * @param v Vector of Hashtables of .jar file info
  449. * @param desc description to print out in header
  450. *
  451. * @return false if OK, true if any .jars were reported
  452. * as having errors
  453. * @see #checkPathForJars(String, String[])
  454. */
  455. protected boolean appendFoundJars(Node container, Document factory,
  456. Vector v, String desc)
  457. {
  458. if ((null == v) || (v.size() < 1))
  459. return false;
  460. boolean errors = false;
  461. for (int i = 0; i < v.size(); i++)
  462. {
  463. Hashtable subhash = (Hashtable) v.elementAt(i);
  464. for (Enumeration keys = subhash.keys();
  465. keys.hasMoreElements();
  466. /* no increment portion */
  467. )
  468. {
  469. Object key = keys.nextElement();
  470. try
  471. {
  472. String keyStr = (String) key;
  473. if (keyStr.startsWith(ERROR))
  474. {
  475. errors = true;
  476. }
  477. Element node = factory.createElement("foundJar");
  478. node.setAttribute("name", keyStr.substring(0, keyStr.indexOf("-")));
  479. node.setAttribute("desc", keyStr.substring(keyStr.indexOf("-") + 1));
  480. node.appendChild(factory.createTextNode((String)subhash.get(keyStr)));
  481. container.appendChild(node);
  482. }
  483. catch (Exception e)
  484. {
  485. errors = true;
  486. Element node = factory.createElement("foundJar");
  487. node.appendChild(factory.createTextNode(ERROR + " Reading " + key + " threw: " + e.toString()));
  488. container.appendChild(node);
  489. }
  490. }
  491. }
  492. return errors;
  493. }
  494. /**
  495. * Fillin hash with info about SystemProperties.
  496. *
  497. * Logs java.class.path and other likely paths; then attempts
  498. * to search those paths for .jar files with Xalan-related classes.
  499. *
  500. * //@todo NOTE: We don't actually search java.ext.dirs for
  501. * // *.jar files therein! This should be updated
  502. *
  503. * @param h Hashtable to put information in
  504. * @see #jarNames
  505. * @see #checkPathForJars(String, String[])
  506. */
  507. protected void checkSystemProperties(Hashtable h)
  508. {
  509. if (null == h)
  510. h = new Hashtable();
  511. // Grab java version for later use
  512. try
  513. {
  514. String javaVersion = System.getProperty("java.version");
  515. h.put("java.version", javaVersion);
  516. }
  517. catch (SecurityException se)
  518. {
  519. // For applet context, etc.
  520. h.put(
  521. "java.version",
  522. "WARNING: SecurityException thrown accessing system version properties");
  523. }
  524. // Printout jar files on classpath(s) that may affect operation
  525. // Do this in order
  526. try
  527. {
  528. // This is present in all JVM's
  529. String cp = System.getProperty("java.class.path");
  530. h.put("java.class.path", cp);
  531. Vector classpathJars = checkPathForJars(cp, jarNames);
  532. if (null != classpathJars)
  533. h.put(FOUNDCLASSES + "java.class.path", classpathJars);
  534. // Also check for JDK 1.2+ type classpaths
  535. String othercp = System.getProperty("sun.boot.class.path");
  536. if (null != othercp)
  537. {
  538. h.put("sun.boot.class.path", othercp);
  539. classpathJars = checkPathForJars(othercp, jarNames);
  540. if (null != classpathJars)
  541. h.put(FOUNDCLASSES + "sun.boot.class.path", classpathJars);
  542. }
  543. //@todo NOTE: We don't actually search java.ext.dirs for
  544. // *.jar files therein! This should be updated
  545. othercp = System.getProperty("java.ext.dirs");
  546. if (null != othercp)
  547. {
  548. h.put("java.ext.dirs", othercp);
  549. classpathJars = checkPathForJars(othercp, jarNames);
  550. if (null != classpathJars)
  551. h.put(FOUNDCLASSES + "java.ext.dirs", classpathJars);
  552. }
  553. //@todo also check other System properties' paths?
  554. // v2 = checkPathForJars(System.getProperty("sun.boot.library.path"), jarNames); // ?? may not be needed
  555. // v3 = checkPathForJars(System.getProperty("java.library.path"), jarNames); // ?? may not be needed
  556. }
  557. catch (SecurityException se2)
  558. {
  559. // For applet context, etc.
  560. h.put(
  561. "java.class.path",
  562. "WARNING: SecurityException thrown accessing system classpath properties");
  563. }
  564. }
  565. /**
  566. * Cheap-o listing of specified .jars found in the classpath.
  567. *
  568. * cp should be separated by the usual File.pathSeparator. We
  569. * then do a simplistic search of the path for any requested
  570. * .jar filenames, and return a listing of their names and
  571. * where (apparently) they came from.
  572. *
  573. * @param cp classpath to search
  574. * @param jars array of .jar base filenames to look for
  575. *
  576. * @return Vector of Hashtables filled with info about found .jars
  577. * @see #jarNames
  578. * @see #logFoundJars(Vector, String)
  579. * @see #appendFoundJars(Node, Document, Vector, String )
  580. * @see #getApparentVersion(String, long)
  581. */
  582. protected Vector checkPathForJars(String cp, String[] jars)
  583. {
  584. if ((null == cp) || (null == jars) || (0 == cp.length())
  585. || (0 == jars.length))
  586. return null;
  587. Vector v = new Vector();
  588. StringTokenizer st = new StringTokenizer(cp, File.pathSeparator);
  589. while (st.hasMoreTokens())
  590. {
  591. // Look at each classpath entry for each of our requested jarNames
  592. String filename = st.nextToken();
  593. for (int i = 0; i < jars.length; i++)
  594. {
  595. if (filename.indexOf(jars[i]) > -1)
  596. {
  597. File f = new File(filename);
  598. if (f.exists())
  599. {
  600. // If any requested jarName exists, report on
  601. // the details of that .jar file
  602. try
  603. {
  604. Hashtable h = new Hashtable(2);
  605. // Note "-" char is looked for in appendFoundJars
  606. h.put(jars[i] + "-path", f.getAbsolutePath());
  607. // We won't bother reporting on the xalan.jar apparent version
  608. // since this requires knowing the jar size of the xalan.jar
  609. // before we build it.
  610. // For other jars, eg. xml-apis.jar and xercesImpl.jar, we
  611. // report the apparent version of the file we've found
  612. if (!("xalan.jar".equalsIgnoreCase(jars[i]))) {
  613. h.put(jars[i] + "-apparent.version",
  614. getApparentVersion(jars[i], f.length()));
  615. }
  616. v.addElement(h);
  617. }
  618. catch (Exception e)
  619. {
  620. /* no-op, don't add it */
  621. }
  622. }
  623. else
  624. {
  625. Hashtable h = new Hashtable(2);
  626. // Note "-" char is looked for in appendFoundJars
  627. h.put(jars[i] + "-path", WARNING + " Classpath entry: "
  628. + filename + " does not exist");
  629. h.put(jars[i] + "-apparent.version", CLASS_NOTPRESENT);
  630. v.addElement(h);
  631. }
  632. }
  633. }
  634. }
  635. return v;
  636. }
  637. /**
  638. * Cheap-o method to determine the product version of a .jar.
  639. *
  640. * Currently does a lookup into a local table of some recent
  641. * shipped Xalan builds to determine where the .jar probably
  642. * came from. Note that if you recompile Xalan or Xerces
  643. * yourself this will likely report a potential error, since
  644. * we can't certify builds other than the ones we ship.
  645. * Only reports against selected posted Xalan-J builds.
  646. *
  647. * //@todo actually look up version info in manifests
  648. *
  649. * @param jarName base filename of the .jarfile
  650. * @param jarSize size of the .jarfile
  651. *
  652. * @return String describing where the .jar file probably
  653. * came from
  654. */
  655. protected String getApparentVersion(String jarName, long jarSize)
  656. {
  657. // If we found a matching size and it's for our
  658. // jar, then return it's description
  659. // Lookup in static jarVersions Hashtable
  660. String foundSize = (String) jarVersions.get(new Long(jarSize));
  661. if ((null != foundSize) && (foundSize.startsWith(jarName)))
  662. {
  663. return foundSize;
  664. }
  665. else
  666. {
  667. if ("xerces.jar".equalsIgnoreCase(jarName)
  668. || "xercesImpl.jar".equalsIgnoreCase(jarName))
  669. // || "xalan.jar".equalsIgnoreCase(jarName))
  670. {
  671. // For xalan.jar and xerces.jar/xercesImpl.jar, which we ship together:
  672. // The jar is not from a shipped copy of xalan-j, so
  673. // it's up to the user to ensure that it's compatible
  674. return jarName + " " + WARNING + CLASS_PRESENT;
  675. }
  676. else
  677. {
  678. // Otherwise, it's just a jar we don't have the version info calculated for
  679. return jarName + " " + CLASS_PRESENT;
  680. }
  681. }
  682. }
  683. /**
  684. * Report version information about JAXP interfaces.
  685. *
  686. * Currently distinguishes between JAXP 1.0.1 and JAXP 1.1,
  687. * and not found; only tests the interfaces, and does not
  688. * check for reference implementation versions.
  689. *
  690. * @param h Hashtable to put information in
  691. */
  692. protected void checkJAXPVersion(Hashtable h)
  693. {
  694. if (null == h)
  695. h = new Hashtable();
  696. final Class noArgs[] = new Class[0];
  697. Class clazz = null;
  698. try
  699. {
  700. final String JAXP1_CLASS = "javax.xml.parsers.DocumentBuilder";
  701. final String JAXP11_METHOD = "getDOMImplementation";
  702. clazz = ObjectFactory.findProviderClass(
  703. JAXP1_CLASS, ObjectFactory.findClassLoader(), true);
  704. Method method = clazz.getMethod(JAXP11_METHOD, noArgs);
  705. // If we succeeded, we at least have JAXP 1.1 available
  706. h.put(VERSION + "JAXP", "1.1 or higher");
  707. }
  708. catch (Exception e)
  709. {
  710. if (null != clazz)
  711. {
  712. // We must have found the class itself, just not the
  713. // method, so we (probably) have JAXP 1.0.1
  714. h.put(ERROR + VERSION + "JAXP", "1.0.1");
  715. h.put(ERROR, ERROR_FOUND);
  716. }
  717. else
  718. {
  719. // We couldn't even find the class, and don't have
  720. // any JAXP support at all, or only have the
  721. // transform half of it
  722. h.put(ERROR + VERSION + "JAXP", CLASS_NOTPRESENT);
  723. h.put(ERROR, ERROR_FOUND);
  724. }
  725. }
  726. }
  727. /**
  728. * Report product version information from Xalan-J.
  729. *
  730. * Looks for version info in xalan.jar from Xalan-J products.
  731. *
  732. * @param h Hashtable to put information in
  733. */
  734. protected void checkProcessorVersion(Hashtable h)
  735. {
  736. if (null == h)
  737. h = new Hashtable();
  738. try
  739. {
  740. final String XALAN1_VERSION_CLASS =
  741. "com.sun.org.apache.xalan.internal.xslt.XSLProcessorVersion";
  742. Class clazz = ObjectFactory.findProviderClass(
  743. XALAN1_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
  744. // Found Xalan-J 1.x, grab it's version fields
  745. StringBuffer buf = new StringBuffer();
  746. Field f = clazz.getField("PRODUCT");
  747. buf.append(f.get(null));
  748. buf.append(';');
  749. f = clazz.getField("LANGUAGE");
  750. buf.append(f.get(null));
  751. buf.append(';');
  752. f = clazz.getField("S_VERSION");
  753. buf.append(f.get(null));
  754. buf.append(';');
  755. h.put(VERSION + "xalan1", buf.toString());
  756. }
  757. catch (Exception e1)
  758. {
  759. h.put(VERSION + "xalan1", CLASS_NOTPRESENT);
  760. }
  761. try
  762. {
  763. // NOTE: This is the old Xalan 2.0, 2.1, 2.2 version class,
  764. // is being replaced by class below
  765. final String XALAN2_VERSION_CLASS =
  766. "com.sun.org.apache.xalan.internal.processor.XSLProcessorVersion";
  767. Class clazz = ObjectFactory.findProviderClass(
  768. XALAN2_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
  769. // Found Xalan-J 2.x, grab it's version fields
  770. StringBuffer buf = new StringBuffer();
  771. Field f = clazz.getField("S_VERSION");
  772. buf.append(f.get(null));
  773. h.put(VERSION + "xalan2x", buf.toString());
  774. }
  775. catch (Exception e2)
  776. {
  777. h.put(VERSION + "xalan2x", CLASS_NOTPRESENT);
  778. }
  779. try
  780. {
  781. // NOTE: This is the new Xalan 2.2+ version class
  782. final String XALAN2_2_VERSION_CLASS =
  783. "com.sun.org.apache.xalan.internal.Version";
  784. final String XALAN2_2_VERSION_METHOD = "getVersion";
  785. final Class noArgs[] = new Class[0];
  786. Class clazz = ObjectFactory.findProviderClass(
  787. XALAN2_2_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
  788. Method method = clazz.getMethod(XALAN2_2_VERSION_METHOD, noArgs);
  789. Object returnValue = method.invoke(null, new Object[0]);
  790. h.put(VERSION + "xalan2_2", (String)returnValue);
  791. }
  792. catch (Exception e2)
  793. {
  794. h.put(VERSION + "xalan2_2", CLASS_NOTPRESENT);
  795. }
  796. }
  797. /**
  798. * Report product version information from common parsers.
  799. *
  800. * Looks for version info in xerces.jar/xercesImpl.jar/crimson.jar.
  801. *
  802. * //@todo actually look up version info in crimson manifest
  803. *
  804. * @param h Hashtable to put information in
  805. */
  806. protected void checkParserVersion(Hashtable h)
  807. {
  808. if (null == h)
  809. h = new Hashtable();
  810. try
  811. {
  812. final String XERCES1_VERSION_CLASS = "com.sun.org.apache.xerces.internal.framework.Version";
  813. Class clazz = ObjectFactory.findProviderClass(
  814. XERCES1_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
  815. // Found Xerces-J 1.x, grab it's version fields
  816. Field f = clazz.getField("fVersion");
  817. String parserVersion = (String) f.get(null);
  818. h.put(VERSION + "xerces1", parserVersion);
  819. }
  820. catch (Exception e)
  821. {
  822. h.put(VERSION + "xerces1", CLASS_NOTPRESENT);
  823. }
  824. // Look for xerces1 and xerces2 parsers separately
  825. try
  826. {
  827. final String XERCES2_VERSION_CLASS = "com.sun.org.apache.xerces.internal.impl.Version";
  828. Class clazz = ObjectFactory.findProviderClass(
  829. XERCES2_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
  830. // Found Xerces-J 2.x, grab it's version fields
  831. Field f = clazz.getField("fVersion");
  832. String parserVersion = (String) f.get(null);
  833. h.put(VERSION + "xerces2", parserVersion);
  834. }
  835. catch (Exception e)
  836. {
  837. h.put(VERSION + "xerces2", CLASS_NOTPRESENT);
  838. }
  839. try
  840. {
  841. final String CRIMSON_CLASS = "org.apache.crimson.parser.Parser2";
  842. Class clazz = ObjectFactory.findProviderClass(
  843. CRIMSON_CLASS, ObjectFactory.findClassLoader(), true);
  844. //@todo determine specific crimson version
  845. h.put(VERSION + "crimson", CLASS_PRESENT);
  846. }
  847. catch (Exception e)
  848. {
  849. h.put(VERSION + "crimson", CLASS_NOTPRESENT);
  850. }
  851. }
  852. /**
  853. * Report product version information from Ant.
  854. *
  855. * @param h Hashtable to put information in
  856. */
  857. protected void checkAntVersion(Hashtable h)
  858. {
  859. if (null == h)
  860. h = new Hashtable();
  861. try
  862. {
  863. final String ANT_VERSION_CLASS = "org.apache.tools.ant.Main";
  864. final String ANT_VERSION_METHOD = "getAntVersion"; // noArgs
  865. final Class noArgs[] = new Class[0];
  866. Class clazz = ObjectFactory.findProviderClass(
  867. ANT_VERSION_CLASS, ObjectFactory.findClassLoader(), true);
  868. Method method = clazz.getMethod(ANT_VERSION_METHOD, noArgs);
  869. Object returnValue = method.invoke(null, new Object[0]);
  870. h.put(VERSION + "ant", (String)returnValue);
  871. }
  872. catch (Exception e)
  873. {
  874. h.put(VERSION + "ant", CLASS_NOTPRESENT);
  875. }
  876. }
  877. /**
  878. * Report version info from DOM interfaces.
  879. *
  880. * Currently distinguishes between pre-DOM level 2, the DOM
  881. * level 2 working draft, the DOM level 2 final draft,
  882. * and not found.
  883. *
  884. * @param h Hashtable to put information in
  885. */
  886. protected void checkDOMVersion(Hashtable h)
  887. {
  888. if (null == h)
  889. h = new Hashtable();
  890. final String DOM_LEVEL2_CLASS = "org.w3c.dom.Document";
  891. final String DOM_LEVEL2_METHOD = "createElementNS"; // String, String
  892. final String DOM_LEVEL2WD_CLASS = "org.w3c.dom.Node";
  893. final String DOM_LEVEL2WD_METHOD = "supported"; // String, String
  894. final String DOM_LEVEL2FD_CLASS = "org.w3c.dom.Node";
  895. final String DOM_LEVEL2FD_METHOD = "isSupported"; // String, String
  896. final Class twoStringArgs[] = { java.lang.String.class,
  897. java.lang.String.class };
  898. try
  899. {
  900. Class clazz = ObjectFactory.findProviderClass(
  901. DOM_LEVEL2_CLASS, ObjectFactory.findClassLoader(), true);
  902. Method method = clazz.getMethod(DOM_LEVEL2_METHOD, twoStringArgs);
  903. // If we succeeded, we have loaded interfaces from a
  904. // level 2 DOM somewhere
  905. h.put(VERSION + "DOM", "2.0");
  906. try
  907. {
  908. // Check for the working draft version, which is
  909. // commonly found, but won't work anymore
  910. clazz = ObjectFactory.findProviderClass(
  911. DOM_LEVEL2WD_CLASS, ObjectFactory.findClassLoader(), true);
  912. method = clazz.getMethod(DOM_LEVEL2WD_METHOD, twoStringArgs);
  913. h.put(ERROR + VERSION + "DOM.draftlevel", "2.0wd");
  914. h.put(ERROR, ERROR_FOUND);
  915. }
  916. catch (Exception e2)
  917. {
  918. try
  919. {
  920. // Check for the final draft version as well
  921. clazz = ObjectFactory.findProviderClass(
  922. DOM_LEVEL2FD_CLASS, ObjectFactory.findClassLoader(), true);
  923. method = clazz.getMethod(DOM_LEVEL2FD_METHOD, twoStringArgs);
  924. h.put(VERSION + "DOM.draftlevel", "2.0fd");
  925. }
  926. catch (Exception e3)
  927. {
  928. h.put(ERROR + VERSION + "DOM.draftlevel", "2.0unknown");
  929. h.put(ERROR, ERROR_FOUND);
  930. }
  931. }
  932. }
  933. catch (Exception e)
  934. {
  935. h.put(ERROR + VERSION + "DOM",
  936. "ERROR attempting to load DOM level 2 class: " + e.toString());
  937. h.put(ERROR, ERROR_FOUND);
  938. }
  939. //@todo load an actual DOM implmementation and query it as well
  940. //@todo load an actual DOM implmementation and check if
  941. // isNamespaceAware() == true, which is needed to parse
  942. // xsl stylesheet files into a DOM
  943. }
  944. /**
  945. * Report version info from SAX interfaces.
  946. *
  947. * Currently distinguishes between SAX 2, SAX 2.0beta2,
  948. * SAX1, and not found.
  949. *
  950. * @param h Hashtable to put information in
  951. */
  952. protected void checkSAXVersion(Hashtable h)
  953. {
  954. if (null == h)
  955. h = new Hashtable();
  956. final String SAX_VERSION1_CLASS = "org.xml.sax.Parser";
  957. final String SAX_VERSION1_METHOD = "parse"; // String
  958. final String SAX_VERSION2_CLASS = "org.xml.sax.XMLReader";
  959. final String SAX_VERSION2_METHOD = "parse"; // String
  960. final String SAX_VERSION2BETA_CLASSNF = "org.xml.sax.helpers.AttributesImpl";
  961. final String SAX_VERSION2BETA_METHODNF = "setAttributes"; // Attributes
  962. final Class oneStringArg[] = { java.lang.String.class };
  963. // Note this introduces a minor compile dependency on SAX...
  964. final Class attributesArg[] = { org.xml.sax.Attributes.class };
  965. try
  966. {
  967. // This method was only added in the final SAX 2.0 release;
  968. // see changes.html "Changes from SAX 2.0beta2 to SAX 2.0prerelease"
  969. Class clazz = ObjectFactory.findProviderClass(
  970. SAX_VERSION2BETA_CLASSNF, ObjectFactory.findClassLoader(), true);
  971. Method method = clazz.getMethod(SAX_VERSION2BETA_METHODNF, attributesArg);
  972. // If we succeeded, we have loaded interfaces from a
  973. // real, final SAX version 2.0 somewhere
  974. h.put(VERSION + "SAX", "2.0");
  975. }
  976. catch (Exception e)
  977. {
  978. // If we didn't find the SAX 2.0 class, look for a 2.0beta2
  979. h.put(ERROR + VERSION + "SAX",
  980. "ERROR attempting to load SAX version 2 class: " + e.toString());
  981. h.put(ERROR, ERROR_FOUND);
  982. try
  983. {
  984. Class clazz = ObjectFactory.findProviderClass(
  985. SAX_VERSION2_CLASS, ObjectFactory.findClassLoader(), true);
  986. Method method = clazz.getMethod(SAX_VERSION2_METHOD, oneStringArg);
  987. // If we succeeded, we have loaded interfaces from a
  988. // SAX version 2.0beta2 or earlier; these might work but
  989. // you should really have the final SAX 2.0
  990. h.put(VERSION + "SAX-backlevel", "2.0beta2-or-earlier");
  991. }
  992. catch (Exception e2)
  993. {
  994. // If we didn't find the SAX 2.0beta2 class, look for a 1.0 one
  995. h.put(ERROR + VERSION + "SAX",
  996. "ERROR attempting to load SAX version 2 class: " + e.toString());
  997. h.put(ERROR, ERROR_FOUND);
  998. try
  999. {
  1000. Class clazz = ObjectFactory.findProviderClass(
  1001. SAX_VERSION1_CLASS, ObjectFactory.findClassLoader(), true);
  1002. Method method = clazz.getMethod(SAX_VERSION1_METHOD, oneStringArg);
  1003. // If we succeeded, we have loaded interfaces from a
  1004. // SAX version 1.0 somewhere; which won't work very
  1005. // well for JAXP 1.1 or beyond!
  1006. h.put(VERSION + "SAX-backlevel", "1.0");
  1007. }
  1008. catch (Exception e3)
  1009. {
  1010. // If we didn't find the SAX 2.0 class, look for a 1.0 one
  1011. // Note that either 1.0 or no SAX are both errors
  1012. h.put(ERROR + VERSION + "SAX-backlevel",
  1013. "ERROR attempting to load SAX version 1 class: " + e3.toString());
  1014. }
  1015. }
  1016. }
  1017. }
  1018. /**
  1019. * Manual table of known .jar sizes.
  1020. * Only includes shipped versions of certain projects.
  1021. * key=jarsize, value=jarname ' from ' distro name
  1022. * Note assumption: two jars cannot have the same size!
  1023. *
  1024. * @see #getApparentVersion(String, long)
  1025. */
  1026. protected static Hashtable jarVersions = new Hashtable();
  1027. /**
  1028. * Static initializer for jarVersions table.
  1029. * Doing this just once saves time and space.
  1030. *
  1031. * @see #getApparentVersion(String, long)
  1032. */
  1033. static
  1034. {
  1035. // Note: hackish Hashtable, this could use improvement
  1036. jarVersions.put(new Long(857192), "xalan.jar from xalan-j_1_1");
  1037. jarVersions.put(new Long(440237), "xalan.jar from xalan-j_1_2");
  1038. jarVersions.put(new Long(436094), "xalan.jar from xalan-j_1_2_1");
  1039. jarVersions.put(new Long(426249), "xalan.jar from xalan-j_1_2_2");
  1040. jarVersions.put(new Long(702536), "xalan.jar from xalan-j_2_0_0");
  1041. jarVersions.put(new Long(720930), "xalan.jar from xalan-j_2_0_1");
  1042. jarVersions.put(new Long(732330), "xalan.jar from xalan-j_2_1_0");
  1043. jarVersions.put(new Long(872241), "xalan.jar from xalan-j_2_2_D10");
  1044. jarVersions.put(new Long(882739), "xalan.jar from xalan-j_2_2_D11");
  1045. jarVersions.put(new Long(923866), "xalan.jar from xalan-j_2_2_0");
  1046. jarVersions.put(new Long(905872), "xalan.jar from xalan-j_2_3_D1");
  1047. jarVersions.put(new Long(906122), "xalan.jar from xalan-j_2_3_0");
  1048. jarVersions.put(new Long(906248), "xalan.jar from xalan-j_2_3_1");
  1049. jarVersions.put(new Long(983377), "xalan.jar from xalan-j_2_4_D1");
  1050. jarVersions.put(new Long(997276), "xalan.jar from xalan-j_2_4_0");
  1051. jarVersions.put(new Long(1031036), "xalan.jar from xalan-j_2_4_1");
  1052. // Stop recording xalan.jar sizes as of Xalan Java 2.5.0
  1053. jarVersions.put(new Long(596540), "xsltc.jar from xalan-j_2_2_0");
  1054. jarVersions.put(new Long(590247), "xsltc.jar from xalan-j_2_3_D1");
  1055. jarVersions.put(new Long(589914), "xsltc.jar from xalan-j_2_3_0");
  1056. jarVersions.put(new Long(589915), "xsltc.jar from xalan-j_2_3_1");
  1057. jarVersions.put(new Long(1306667), "xsltc.jar from xalan-j_2_4_D1");
  1058. jarVersions.put(new Long(1328227), "xsltc.jar from xalan-j_2_4_0");
  1059. jarVersions.put(new Long(1344009), "xsltc.jar from xalan-j_2_4_1");
  1060. jarVersions.put(new Long(1348361), "xsltc.jar from xalan-j_2_5_D1");
  1061. // Stop recording xsltc.jar sizes as of Xalan Java 2.5.0
  1062. jarVersions.put(new Long(1268634), "xsltc.jar-bundled from xalan-j_2_3_0");
  1063. jarVersions.put(new Long(100196), "xml-apis.jar from xalan-j_2_2_0 or xalan-j_2_3_D1");
  1064. jarVersions.put(new Long(108484), "xml-apis.jar from xalan-j_2_3_0, or xalan-j_2_3_1 from xml-commons-1.0.b2");
  1065. jarVersions.put(new Long(109049), "xml-apis.jar from xalan-j_2_4_0 from xml-commons RIVERCOURT1 branch");
  1066. jarVersions.put(new Long(113749), "xml-apis.jar from xalan-j_2_4_1 from factoryfinder-build of xml-commons RIVERCOURT1");
  1067. jarVersions.put(new Long(124704), "xml-apis.jar from tck-jaxp-1_2_0 branch of xml-commons");
  1068. jarVersions.put(new Long(124724), "xml-apis.jar from tck-jaxp-1_2_0 branch of xml-commons, tag: xml-commons-external_1_2_01");
  1069. // If the below were more common I would update it to report
  1070. // errors better; but this is so old hardly anyone has it
  1071. jarVersions.put(new Long(424490), "xalan.jar from Xerces Tools releases - ERROR:DO NOT USE!");
  1072. jarVersions.put(new Long(1591855), "xerces.jar from xalan-j_1_1 from xerces-1...");
  1073. jarVersions.put(new Long(1498679), "xerces.jar from xalan-j_1_2 from xerces-1_2_0.bin");
  1074. jarVersions.put(new Long(1484896), "xerces.jar from xalan-j_1_2_1 from xerces-1_2_1.bin");
  1075. jarVersions.put(new Long(804460), "xerces.jar from xalan-j_1_2_2 from xerces-1_2_2.bin");
  1076. jarVersions.put(new Long(1499244), "xerces.jar from xalan-j_2_0_0 from xerces-1_2_3.bin");
  1077. jarVersions.put(new Long(1605266), "xerces.jar from xalan-j_2_0_1 from xerces-1_3_0.bin");
  1078. jarVersions.put(new Long(904030), "xerces.jar from xalan-j_2_1_0 from xerces-1_4.bin");
  1079. jarVersions.put(new Long(904030), "xerces.jar from xerces-1_4_0.bin");
  1080. jarVersions.put(new Long(1802885), "xerces.jar from xerces-1_4_2.bin");
  1081. jarVersions.put(new Long(1734594), "xerces.jar from Xerces-J-bin.2.0.0.beta3");
  1082. jarVersions.put(new Long(1808883), "xerces.jar from xalan-j_2_2_D10,D11,D12 or xerces-1_4_3.bin");
  1083. jarVersions.put(new Long(1812019), "xerces.jar from xalan-j_2_2_0");
  1084. jarVersions.put(new Long(1720292), "xercesImpl.jar from xalan-j_2_3_D1");
  1085. jarVersions.put(new Long(1730053), "xercesImpl.jar from xalan-j_2_3_0 or xalan-j_2_3_1 from xerces-2_0_0");
  1086. jarVersions.put(new Long(1728861), "xercesImpl.jar from xalan-j_2_4_D1 from xerces-2_0_1");
  1087. jarVersions.put(new Long(972027), "xercesImpl.jar from xalan-j_2_4_0 from xerces-2_1");
  1088. jarVersions.put(new Long(831587), "xercesImpl.jar from xalan-j_2_4_1 from xerces-2_2");
  1089. jarVersions.put(new Long(891817), "xercesImpl.jar from xalan-j_2_5_D1 from xerces-2_3");
  1090. jarVersions.put(new Long(895924), "xercesImpl.jar from xerces-2_4");
  1091. jarVersions.put(new Long(1010806), "xercesImpl.jar from Xerces-J-bin.2.6.2");
  1092. jarVersions.put(new Long(37485), "xalanj1compat.jar from xalan-j_2_0_0");
  1093. jarVersions.put(new Long(38100), "xalanj1compat.jar from xalan-j_2_0_1");
  1094. jarVersions.put(new Long(18779), "xalanservlet.jar from xalan-j_2_0_0");
  1095. jarVersions.put(new Long(21453), "xalanservlet.jar from xalan-j_2_0_1");
  1096. jarVersions.put(new Long(24826), "xalanservlet.jar from xalan-j_2_3_1 or xalan-j_2_4_1");
  1097. jarVersions.put(new Long(24831), "xalanservlet.jar from xalan-j_2_4_1");
  1098. // Stop recording xalanservlet.jar sizes as of Xalan Java 2.5.0; now a .war file
  1099. // For those who've downloaded JAXP from sun
  1100. jarVersions.put(new Long(5618), "jaxp.jar from jaxp1.0.1");
  1101. jarVersions.put(new Long(136133), "parser.jar from jaxp1.0.1");
  1102. jarVersions.put(new Long(28404), "jaxp.jar from jaxp-1.1");
  1103. jarVersions.put(new Long(187162), "crimson.jar from jaxp-1.1");
  1104. jarVersions.put(new Long(801714), "xalan.jar from jaxp-1.1");
  1105. jarVersions.put(new Long(196399), "crimson.jar from crimson-1.1.1");
  1106. jarVersions.put(new Long(33323), "jaxp.jar from crimson-1.1.1 or jakarta-ant-1.4.1b1");
  1107. jarVersions.put(new Long(152717), "crimson.jar from crimson-1.1.2beta2");
  1108. jarVersions.put(new Long(88143), "xml-apis.jar from crimson-1.1.2beta2");
  1109. jarVersions.put(new Long(206384), "crimson.jar from crimson-1.1.3 or jakarta-ant-1.4.1b1");
  1110. // jakarta-ant: since many people use ant these days
  1111. jarVersions.put(new Long(136198), "parser.jar from jakarta-ant-1.3 or 1.2");
  1112. jarVersions.put(new Long(5537), "jaxp.jar from jakarta-ant-1.3 or 1.2");
  1113. }
  1114. /** Simple PrintWriter we send output to; defaults to System.out. */
  1115. protected PrintWriter outWriter = new PrintWriter(System.out, true);
  1116. /**
  1117. * Bottleneck output: calls outWriter.println(s).
  1118. * @param s String to print
  1119. */
  1120. protected void logMsg(String s)
  1121. {
  1122. outWriter.println(s);
  1123. }
  1124. }