/*
* reserved comment block
* DO NOT REMOVE OR ALTER!
*/
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: EnvironmentCheck.java,v 1.2.4.1 2005/09/09 07:13:59 pvedula Exp $
*/
/**
* Utility class to report simple information about the environment.
* Simplistic reporting about certain classes found in your JVM may
* help answer some FAQs for simple problems.
*
* <p>Usage-command line:
* <code>
* java com.sun.org.apache.xalan.internal.xslt.EnvironmentCheck [-out outFile]
* </code></p>
*
* <p>Usage-from program:
* <code>
* boolean environmentOK =
* (new EnvironmentCheck()).checkEnvironment(yourPrintWriter);
* </code></p>
*
* <p>Usage-from stylesheet:
* <code><pre>
* <?xml version="1.0"?>
* <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
* xmlns:xalan="http://xml.apache.org/xalan"
* exclude-result-prefixes="xalan">
* <xsl:output indent="yes"/>
* <xsl:template match="/">
* <xsl:copy-of select="xalan:checkEnvironment()"/>
* </xsl:template>
* </xsl:stylesheet>
* </pre></code></p>
*
* <p>Xalan users reporting problems are encouraged to use this class
* to see if there are potential problems with their actual
* Java environment <b>before</b> reporting a bug. Note that you
* temporarily calling checkEnvironment() directly from your code,
* since the classpath may differ (especially for servlets, etc).</p>
*
* <p>Also see http://xml.apache.org/xalan-j/faq.html</p>
*
* <p>Note: This class is pretty simplistic:
* results are not necessarily definitive nor will it find all
* problems related to environment setup. Also, you should avoid
* calling this in deployed production code, both because it is
* quite slow and because it forces classes to get loaded.</p>
*
* <p>Note: This class explicitly has very limited compile-time
* dependencies to enable easy compilation and usage even when
*
* <p>Note: for an improved version of this utility, please see
* the xml-commons' project Which utility which does the same kind
* of thing but in a much simpler manner.</p>
*
* @author Shane_Curcuru@us.ibm.com
* @version $Id: EnvironmentCheck.java,v 1.10 2010-11-01 04:34:13 joehw Exp $
*/
public class EnvironmentCheck
{
/**
* Command line runnability: checks for [-out outFilename] arg.
* <p>Command line entrypoint; Sets output and calls
* {@link #checkEnvironment(PrintWriter)}.</p>
* @param args command line args
*/
{
// Default to System.out, autoflushing
// Read our simplistic input args, if supplied
{
{
i++;
{
try
{
}
catch (Exception e)
{
+ e.toString());
}
}
else
{
"# WARNING: -out argument should have a filename, output sent to console");
}
}
}
}
/**
* Programmatic entrypoint: Report on basic Java environment
* and CLASSPATH settings that affect Xalan.
*
* <p>Note that this class is not advanced enough to tell you
* everything about the environment that affects Xalan, and
* sometimes reports errors that will not actually affect
* Xalan's behavior. Currently, it very simplistically
* checks the JVM's environment for some basic properties and
* logs them out; it will report a problem if it finds a setting
* or .jar file that is <i>likely</i> to cause problems.</p>
*
* <p>Advanced users can peruse the code herein to help them
* investigate potential environment problems found; other users
* may simply send the output from this tool along with any bugs
* they submit to help us in the debugging process.</p>
*
* @param pw PrintWriter to send output to; can be sent to a
* file that will look similar to a Properties file; defaults
* to System.out if null
* @return true if your environment appears to have no major
* problems; false if potential environment problems found
* @see #getEnvironmentHash()
*/
{
// Use user-specified output writer if non-null
// Setup a hash to store various environment information in
// Check for ERROR keys in the hashtable, and print report
if (environmentHasErrors)
{
// Note: many logMsg calls have # at the start to
// fake a property-file like output
logMsg("# WARNING: Potential problems found in your environment!");
logMsg("# Check any 'ERROR' items above against the Xalan FAQs");
return false;
}
else
{
logMsg("# YAHOO! Your environment seems to be OK.");
return true;
}
}
/**
* Fill a hash with basic environment settings that affect Xalan.
*
* <p>Worker method called from various places.</p>
* <p>Various system and CLASSPATH, etc. properties are put into
* the hash as keys with a brief description of the current state
* of that item as the value. Any serious problems will be put in
* with a key that is prefixed with {@link #ERROR 'ERROR.'} so it
* stands out in any resulting report; also a key with just that
* constant will be set as well for any error.</p>
* <p>Note that some legitimate cases are flaged as potential
* errors - namely when a developer recompiles xalan.jar on their
* own - and even a non-error state doesn't guaruntee that
* everything in the environment is correct. But this will help
* point out the most common classpath and system property
* problems that we've seen.</p>
*
* @return Hashtable full of useful environment info about Xalan
* and related system properties, etc.
*/
{
// Setup a hash to store various environment information in
// Call various worker methods to fill in the hash
// These are explicitly separate for maintenance and so
// advanced users could call them standalone
if (!checkDOML3(hash)) {
}
return hash;
}
/**
* Dump a basic Xalan environment report to outWriter.
*
* <p>This dumps a simple header and then each of the entries in
* the Hashtable to our PrintWriter; it does special processing
* for entries that are .jars found in the classpath.</p>
*
* @param h Hashtable of items to report on; presumably
* filled in by our various check*() methods
* @return true if your environment appears to have no major
* problems; false if potential environment problems found
* @see #appendEnvironmentReport(Node, Document, Hashtable)
* for an equivalent that appends to a Node instead
*/
{
if (null == h)
{
logMsg("# ERROR: writeEnvironmentReport called with null Hashtable");
return false;
}
boolean errors = false;
"#---- BEGIN writeEnvironmentReport($Revision: 1.10 $): Useful stuff found: ----");
// Fake the Properties-like output
/* no increment portion */
)
{
try
{
// Special processing for classes found..
{
}
// ..normal processing for all other entries
else
{
// Note: we could just check for the ERROR key by itself,
// since we now set that, but since we have to go
// through the whole hash anyway, do it this way,
// which is safer for maintenance
{
errors = true;
}
}
}
catch (Exception e)
{
}
}
"#----- END writeEnvironmentReport: Useful properties found: -----");
return errors;
}
/** Prefixed to hash keys that signify serious problems. */
/** Added to descriptions that signify potential problems. */
/** Value for any error found. */
/** Prefixed to hash keys that signify version numbers. */
/** Prefixed to hash keys that signify .jars found in classpath. */
/** Marker that a class or .jar was found. */
/** Marker that a class or .jar was not found. */
/** Listing of common .jar files that include Xalan-related classes. */
{
"xalan.jar", "xalansamples.jar", "xalanj1compat.jar", "xalanservlet.jar",
"serializer.jar", // Serializer (shared between Xalan & Xerces)
"xerces.jar", // Xerces-J 1.x
"xercesImpl.jar", // Xerces-J 2.x
"testxsl.jar",
"crimson.jar",
"lotusxsl.jar",
"jaxp.jar", "parser.jar", "dom.jar", "sax.jar", "xml.jar",
"xml-apis.jar",
"xsltc.jar"
};
/**
* Print out report of .jars found in a classpath.
*
* Takes the information encoded from a checkPathForJars()
* call and dumps it out to our PrintWriter.
*
* @param v Vector of Hashtables of .jar file info
* @param desc description to print out in header
*
* @return false if OK, true if any .jars were reported
* as having errors
* @see #checkPathForJars(String, String[])
*/
{
return false;
boolean errors = false;
for (int i = 0; i < v.size(); i++)
{
/* no increment portion */
)
{
try
{
{
errors = true;
}
}
catch (Exception e)
{
errors = true;
}
}
}
return errors;
}
/**
* Stylesheet extension entrypoint: Dump a basic Xalan
* environment report from getEnvironmentHash() to a Node.
*
* <p>Copy of writeEnvironmentReport that creates a Node suitable
* for other processing instead of a properties-like text output.
* </p>
* @param container Node to append our report to
* @param factory Document providing createElement, etc. services
* @param h Hash presumably from {@link #getEnvironmentHash()}
* @see #writeEnvironmentReport(Hashtable)
* for an equivalent that writes to a PrintWriter instead
*/
{
{
return;
}
try
{
if (null == h)
{
statusNode.appendChild(factory.createTextNode("appendEnvironmentReport called with null Hashtable!"));
return;
}
boolean errors = false;
/* no increment portion */
)
{
try
{
// Special processing for classes found..
{
// errors |= logFoundJars(v, keyStr);
}
// ..normal processing for all other entries
else
{
// Note: we could just check for the ERROR key by itself,
// since we now set that, but since we have to go
// through the whole hash anyway, do it this way,
// which is safer for maintenance
{
errors = true;
}
}
}
catch (Exception e)
{
errors = true;
}
} // end of for...
}
{
}
}
/**
* Print out report of .jars found in a classpath.
*
* Takes the information encoded from a checkPathForJars()
* call and dumps it out to our PrintWriter.
*
* @param container Node to append our report to
* @param factory Document providing createElement, etc. services
* @param v Vector of Hashtables of .jar file info
* @param desc description to print out in header
*
* @return false if OK, true if any .jars were reported
* as having errors
* @see #checkPathForJars(String, String[])
*/
{
return false;
boolean errors = false;
for (int i = 0; i < v.size(); i++)
{
/* no increment portion */
)
{
try
{
{
errors = true;
}
}
catch (Exception e)
{
errors = true;
}
}
}
return errors;
}
/**
* Fillin hash with info about SystemProperties.
*
* Logs java.class.path and other likely paths; then attempts
* to search those paths for .jar files with Xalan-related classes.
*
* //@todo NOTE: We don't actually search java.ext.dirs for
* // *.jar files therein! This should be updated
*
* @param h Hashtable to put information in
* @see #jarNames
* @see #checkPathForJars(String, String[])
*/
{
if (null == h)
h = new Hashtable();
// Grab java version for later use
try
{
}
catch (SecurityException se)
{
// For applet context, etc.
h.put(
"java.version",
"WARNING: SecurityException thrown accessing system version properties");
}
// Printout jar files on classpath(s) that may affect operation
// Do this in order
try
{
// This is present in all JVM's
if (null != classpathJars)
// Also check for JDK 1.2+ type classpaths
{
if (null != classpathJars)
}
//@todo NOTE: We don't actually search java.ext.dirs for
// *.jar files therein! This should be updated
{
if (null != classpathJars)
}
//@todo also check other System properties' paths?
// v2 = checkPathForJars(System.getProperty("sun.boot.library.path"), jarNames); // ?? may not be needed
// v3 = checkPathForJars(System.getProperty("java.library.path"), jarNames); // ?? may not be needed
}
catch (SecurityException se2)
{
// For applet context, etc.
h.put(
"java.class.path",
"WARNING: SecurityException thrown accessing system classpath properties");
}
}
/**
* Cheap-o listing of specified .jars found in the classpath.
*
* cp should be separated by the usual File.pathSeparator. We
* then do a simplistic search of the path for any requested
* .jar filenames, and return a listing of their names and
* where (apparently) they came from.
*
* @param cp classpath to search
* @param jars array of .jar base filenames to look for
*
* @return Vector of Hashtables filled with info about found .jars
* @see #jarNames
* @see #logFoundJars(Vector, String)
* @see #appendFoundJars(Node, Document, Vector, String )
* @see #getApparentVersion(String, long)
*/
{
return null;
while (st.hasMoreTokens())
{
// Look at each classpath entry for each of our requested jarNames
{
{
if (f.exists())
{
// If any requested jarName exists, report on
// the details of that .jar file
try
{
// Note "-" char is looked for in appendFoundJars
// We won't bother reporting on the xalan.jar apparent version
// since this requires knowing the jar size of the xalan.jar
// before we build it.
// For other jars, eg. xml-apis.jar and xercesImpl.jar, we
// report the apparent version of the file we've found
}
v.addElement(h);
}
catch (Exception e)
{
/* no-op, don't add it */
}
}
else
{
// Note "-" char is looked for in appendFoundJars
+ filename + " does not exist");
v.addElement(h);
}
}
}
}
return v;
}
/**
* Cheap-o method to determine the product version of a .jar.
*
* Currently does a lookup into a local table of some recent
* shipped Xalan builds to determine where the .jar probably
* came from. Note that if you recompile Xalan or Xerces
* yourself this will likely report a potential error, since
* we can't certify builds other than the ones we ship.
* Only reports against selected posted Xalan-J builds.
*
* //@todo actually look up version info in manifests
*
* @param jarName base filename of the .jarfile
* @param jarSize size of the .jarfile
*
* @return String describing where the .jar file probably
* came from
*/
{
// If we found a matching size and it's for our
// jar, then return it's description
// Lookup in static jarVersions Hashtable
{
return foundSize;
}
else
{
// || "xalan.jar".equalsIgnoreCase(jarName))
{
// For xalan.jar and xerces.jar/xercesImpl.jar, which we ship together:
// The jar is not from a shipped copy of xalan-j, so
// it's up to the user to ensure that it's compatible
}
else
{
// Otherwise, it's just a jar we don't have the version info calculated for
}
}
}
/**
* Report version information about JAXP interfaces.
*
* Currently distinguishes between JAXP 1.0.1 and JAXP 1.1,
* and not found; only tests the interfaces, and does not
* check for reference implementation versions.
*
* @param h Hashtable to put information in
*/
{
if (null == h)
h = new Hashtable();
try
{
// If we succeeded, we have JAXP 1.4 available
}
catch (Exception e)
{
}
}
/**
* Report product version information from Xalan-J.
*
* Looks for version info in xalan.jar from Xalan-J products.
*
* @param h Hashtable to put information in
*/
{
if (null == h)
h = new Hashtable();
try
{
final String XALAN1_VERSION_CLASS =
"com.sun.org.apache.xalan.internal.xslt.XSLProcessorVersion";
// Found Xalan-J 1.x, grab it's version fields
}
{
}
try
{
// NOTE: This is the old Xalan 2.0, 2.1, 2.2 version class,
// is being replaced by class below
final String XALAN2_VERSION_CLASS =
"com.sun.org.apache.xalan.internal.processor.XSLProcessorVersion";
// Found Xalan-J 2.x, grab it's version fields
}
{
}
try
{
// NOTE: This is the new Xalan 2.2+ version class
final String XALAN2_2_VERSION_CLASS =
"com.sun.org.apache.xalan.internal.Version";
}
{
}
}
/**
* Report product version information from common parsers.
*
* Looks for version info in xerces.jar/xercesImpl.jar/crimson.jar.
*
* //@todo actually look up version info in crimson manifest
*
* @param h Hashtable to put information in
*/
{
if (null == h)
h = new Hashtable();
try
{
// Found Xerces-J 1.x, grab it's version fields
}
catch (Exception e)
{
}
// Look for xerces1 and xerces2 parsers separately
try
{
// Found Xerces-J 2.x, grab it's version fields
}
catch (Exception e)
{
}
try
{
//@todo determine specific crimson version
}
catch (Exception e)
{
}
}
/**
* Report product version information from Ant.
*
* @param h Hashtable to put information in
*/
{
if (null == h)
h = new Hashtable();
try
{
}
catch (Exception e)
{
}
}
/**
* Report version info from DOM interfaces.
*
* @param h Hashtable to put information in
*/
{
if (null == h)
h = new Hashtable();
try
{
// If we succeeded, we have loaded interfaces from a
// level 3 DOM somewhere
return true;
}
catch (Exception e)
{
return false;
}
}
/**
* Report version info from DOM interfaces.
*
* Currently distinguishes between pre-DOM level 2, the DOM
* level 2 working draft, the DOM level 2 final draft,
* and not found.
*
* @param h Hashtable to put information in
*/
{
if (null == h)
h = new Hashtable();
try
{
// If we succeeded, we have loaded interfaces from a
// level 2 DOM somewhere
try
{
// Check for the working draft version, which is
// commonly found, but won't work anymore
}
{
try
{
// Check for the final draft version as well
}
{
}
}
}
catch (Exception e)
{
"ERROR attempting to load DOM level 2 class: " + e.toString());
}
//@todo load an actual DOM implmementation and query it as well
//@todo load an actual DOM implmementation and check if
// isNamespaceAware() == true, which is needed to parse
// xsl stylesheet files into a DOM
}
/**
* Report version info from SAX interfaces.
*
* Currently distinguishes between SAX 2, SAX 2.0beta2,
* SAX1, and not found.
*
* @param h Hashtable to put information in
*/
{
if (null == h)
h = new Hashtable();
// Note this introduces a minor compile dependency on SAX...
try
{
// This method was only added in the final SAX 2.0 release;
// see changes.html "Changes from SAX 2.0beta2 to SAX 2.0prerelease"
// If we succeeded, we have loaded interfaces from a
// real, final SAX version 2.0 somewhere
}
catch (Exception e)
{
// If we didn't find the SAX 2.0 class, look for a 2.0beta2
"ERROR attempting to load SAX version 2 class: " + e.toString());
try
{
// If we succeeded, we have loaded interfaces from a
// SAX version 2.0beta2 or earlier; these might work but
// you should really have the final SAX 2.0
}
{
// If we didn't find the SAX 2.0beta2 class, look for a 1.0 one
"ERROR attempting to load SAX version 2 class: " + e.toString());
try
{
// If we succeeded, we have loaded interfaces from a
// SAX version 1.0 somewhere; which won't work very
// well for JAXP 1.1 or beyond!
}
{
// If we didn't find the SAX 2.0 class, look for a 1.0 one
// Note that either 1.0 or no SAX are both errors
}
}
}
}
/**
* Manual table of known .jar sizes.
* Only includes shipped versions of certain projects.
* key=jarsize, value=jarname ' from ' distro name
* Note assumption: two jars cannot have the same size!
*
* @see #getApparentVersion(String, long)
*/
/**
* Static initializer for jarVersions table.
* Doing this just once saves time and space.
*
* @see #getApparentVersion(String, long)
*/
static
{
// Note: hackish Hashtable, this could use improvement
// Stop recording xalan.jar sizes as of Xalan Java 2.5.0
// Stop recording xsltc.jar sizes as of Xalan Java 2.5.0
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");
jarVersions.put(new Long(109049), "xml-apis.jar from xalan-j_2_4_0 from xml-commons RIVERCOURT1 branch");
jarVersions.put(new Long(113749), "xml-apis.jar from xalan-j_2_4_1 from factoryfinder-build of xml-commons RIVERCOURT1");
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");
jarVersions.put(new Long(194205), "xml-apis.jar from head branch of xml-commons, tag: xml-commons-external_1_3_02");
// If the below were more common I would update it to report
// errors better; but this is so old hardly anyone has it
jarVersions.put(new Long(1730053), "xercesImpl.jar from xalan-j_2_3_0 or xalan-j_2_3_1 from xerces-2_0_0");
// Stop recording xalanservlet.jar sizes as of Xalan Java 2.5.0; now a .war file
// For those who've downloaded JAXP from sun
// jakarta-ant: since many people use ant these days
}
/** Simple PrintWriter we send output to; defaults to System.out. */
/**
* Bottleneck output: calls outWriter.println(s).
* @param s String to print
*/
{
}
}