/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-08 The eXist Project
* http://exist-db.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* $Id$
*/
package org.exist.validation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import org.apache.log4j.Logger;
import org.exist.util.ExistSAXParserFactory;
import org.xml.sax.XMLReader;
/**
* Class for checking dependencies with XML libraries.
*
* @author Adam Retter <adam.retter@devon.gov.uk>
*/
public class XmlLibraryChecker {
/**
* Possible XML Parsers, at least one must be valid
*/
private final static ClassVersion[] validParsers = {
new ClassVersion("Xerces", "Xerces-J 2.9.1",
"org.apache.xerces.impl.Version.getVersion()")
};
/**
* Possible XML Transformers, at least one must be valid
*/
private final static ClassVersion[] validTransformers = {
new ClassVersion("Saxon", "8.9.0",
"net.sf.saxon.Version.getProductVersion()"),
new ClassVersion("Xalan", "Xalan Java 2.7.1",
"org.apache.xalan.Version.getVersion()"),
};
/**
* Possible XML resolvers, at least one must be valid
*/
private final static ClassVersion[] validResolvers = {
new ClassVersion("Resolver", "XmlResolver 1.2",
"org.apache.xml.resolver.Version.getVersion()"),
};
private final static Logger logger = Logger.getLogger( XmlLibraryChecker.class );
/**
* Remove "@" from string.
*/
private static String getClassName(String classid) {
String className;
int lastChar = classid.lastIndexOf("@");
if (lastChar == -1) {
className = classid;
} else {
className = classid.substring(0, lastChar);
}
return className;
}
/**
* Determine the class that is actually used as XML parser.
*
* @return Full classname of parser.
*/
private static String determineActualParserClass() {
String parserClass = "Unable to determine parser class";
try {
SAXParserFactory factory = ExistSAXParserFactory.getSAXParserFactory();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
String classId = xmlReader.toString();
parserClass = getClassName(classId);
} catch (Exception ex) {
ex.printStackTrace();
}
return parserClass;
}
/**
* Determine the class that is actually used as XML transformer.
*
* @return Full classname of transformer.
*/
private static String determineActualTransformerClass(){
String transformerClass = "Unable to determine transformer class";
try {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
String classId = transformer.toString();
transformerClass = getClassName(classId);
} catch (Exception ex) {
ex.printStackTrace();
}
return transformerClass;
}
/**
* Perform checks on parsers, transformers and resolvers.
*/
public static void check() {
StringBuilder message = new StringBuilder();
boolean invalidVersionFound = false;
if( hasValidClassVersion( "Parser", validParsers, message ) ) {
logger.info( message.toString() );
} else {
logger.warn( message.toString() );
System.err.println( message.toString() );
invalidVersionFound = true;
}
message = new StringBuilder();
if( hasValidClassVersion( "Transformer", validTransformers, message ) ) {
logger.info( message.toString() );
} else {
logger.warn( message.toString() );
System.err.println( message.toString() );
invalidVersionFound = true;
}
message = new StringBuilder();
if( hasValidClassVersion( "Resolver", validResolvers, message ) ) {
logger.info( message.toString() );
} else {
logger.warn( message.toString() );
System.err.println( message.toString() );
invalidVersionFound = true;
}
logger.info( "Using parser " + determineActualParserClass() );
logger.info( "Using transformer " + determineActualTransformerClass() );
if( invalidVersionFound ) {
System.err.println( "Using parser " + determineActualParserClass() );
System.err.println( "Using transformer " + determineActualTransformerClass() );
System.err.println();
}
}
/**
* Check if for the specified service object one of the required
* classes is availabe.
*
* @param type Parser, Transformer or Resolver, used for reporting only.
* @param validClasses Array of valid classes.
* @param message Output message of detecting classes.
* @return TRUE if valid class has been found, otherwise FALSE.
*/
public static boolean hasValidClassVersion(String type,
ClassVersion[] validClasses, StringBuilder message) {
String sep = System.getProperty("line.separator");
message.append("Looking for a valid " + type + "..." + sep);
for (int i = 0; i < validClasses.length; i++) {
String actualVersion = validClasses[i].getActualVersion();
message.append("Checking for " + validClasses[i].getSimpleName());
if (actualVersion != null) {
message.append(", found version " + actualVersion);
if (actualVersion.compareToIgnoreCase(
validClasses[i].getRequiredVersion()) >= 0) {
message.append(sep + "OK!" + sep);
return true;
} else {
message.append(" needed version " +
validClasses[i].getRequiredVersion() + sep);
}
} else {
message.append(", not found!" + sep);
}
}
message.append("Warning: Failed find a valid " + type + "!" + sep);
message.append(sep + "Please add an appropriate " + type
+ " to the " + "class-path, e.g. in the 'endorsed' folder of "
+ "the servlet container or in the 'endorsed' folder "
+ "of the JRE." + sep);
return false;
}
/**
* Checks to see if a valid XML Parser exists
*
* @return boolean true indicates a valid Parser was found, false otherwise
*/
public static boolean hasValidParser() {
return hasValidParser(new StringBuilder());
}
/**
* Checks to see if a valid XML Parser exists
*
* @param message Messages about the status of available Parser's will
* be appended to this buffer
*
* @return boolean true indicates a valid Parser was found, false otherwise
*/
public static boolean hasValidParser(StringBuilder message) {
return hasValidClassVersion("Parser", validParsers, message);
}
/**
* Checks to see if a valid XML Transformer exists
*
* @return boolean true indicates a valid Transformer was found,
* false otherwise
*/
public static boolean hasValidTransformer() {
return hasValidTransformer(new StringBuilder());
}
/**
* Checks to see if a valid XML Transformer exists
*
* @param message Messages about the status of available Transformer's
* will be appended to this buffer
*
* @return boolean true indicates a valid Transformer was found,
* false otherwise
*/
public static boolean hasValidTransformer(StringBuilder message) {
return hasValidClassVersion("Transformer", validTransformers, message);
}
/**
* Simple class to describe a class, its required version and how to
* obtain the actual version
*/
public static class ClassVersion {
private String simpleName;
private String requiredVersion;
private String versionFunction;
/**
* Default Constructor
*
* @param simpleName The simple name for the class (just a
* descriptor really)
* @param requiredVersion The required version of the class
* @param versionFunction The function to be invoked to obtain the
* actual version of the class, must be fully
* qualified (i.e. includes the package name)
*/
ClassVersion(String simpleName, String requiredVersion, String versionFunction) {
this.simpleName = simpleName;
this.requiredVersion = requiredVersion;
this.versionFunction = versionFunction;
}
/**
* @return the simple name of the class
*/
public String getSimpleName() {
return simpleName;
}
/**
* @return the required version of the class
*/
public String getRequiredVersion() {
return requiredVersion;
}
/**
* Invokes the specified versionFunction using reflection to get the
* actual version of the class
*
* @return the actual version of the class
*/
public String getActualVersion() {
String actualVersion = null;
//get the class name from the specifiec version function string
String versionClassName = versionFunction
.substring(0, versionFunction.lastIndexOf('.'));
//get the function name from the specifiec version function string
String versionFunctionName = versionFunction.substring(
versionFunction.lastIndexOf('.') + 1, versionFunction.lastIndexOf('('));
try {
//get the class
Class versionClass = Class.forName(versionClassName);
//get the method
Method getVersionMethod = versionClass
.getMethod(versionFunctionName, (Class[]) null);
//invoke the method on the class
actualVersion = (String) getVersionMethod
.invoke(versionClass, (Object[]) null);
} catch (ClassNotFoundException cfe) {
} catch (NoSuchMethodException nsme) {
} catch (InvocationTargetException ite) {
} catch (IllegalAccessException iae) {
}
//return the actual version
return actualVersion;
}
}
}