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