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