diff options
Diffstat (limited to 'common/src/at/gv/egovernment/moa')
29 files changed, 3867 insertions, 0 deletions
| diff --git a/common/src/at/gv/egovernment/moa/logging/LogMsg.java b/common/src/at/gv/egovernment/moa/logging/LogMsg.java new file mode 100644 index 000000000..4d04fc72d --- /dev/null +++ b/common/src/at/gv/egovernment/moa/logging/LogMsg.java @@ -0,0 +1,43 @@ +package at.gv.egovernment.moa.logging; + +/** + * A unified message type to log messages from inside the MOA subsystem. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class LogMsg { +  /** The message to log. */ +  private Object message; + +  /** +   * Create a <code>LogMsg</code> object. +   *  +   * @param message The actual message to log. May be <code>null</code>.  +   */ +  public LogMsg(Object message) { +    this.message = message; +  } + +  /** +   * Convert this log message to a <code>String</code>. +   *  +   * @return The <code>String</code> representation of this log message.  +   */ +  public String toString() { +    StringBuffer msg = new StringBuffer(); +    LoggingContext ctx = +      LoggingContextManager.getInstance().getLoggingContext(); +    String tid = ctx != null ? ctx.getTransactionID() : null; +    String nodeId = ctx != null ? ctx.getNodeID() : null; +     +    msg.append("TID="); +    msg.append(tid != null ? tid : "<null>"); +    msg.append(" NID="); +    msg.append(nodeId != null ? nodeId : "<null>"); +    msg.append(" MSG="); +    msg.append(message != null ? message.toString() : "<null>"); + +    return msg.toString(); +  } +} diff --git a/common/src/at/gv/egovernment/moa/logging/Logger.java b/common/src/at/gv/egovernment/moa/logging/Logger.java new file mode 100644 index 000000000..eb7aa5634 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/logging/Logger.java @@ -0,0 +1,175 @@ +package at.gv.egovernment.moa.logging; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * A utility class acting as a facade to the logging subsystem. + *  + * Configure the logging defaultHierarchy that the <code>Logger</code> uses by + * calling <code>setHierarchy</code> once before calling any of the logging + * output functions. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class Logger { +   +  /** The default logging hierarchy. */ +  private static String defaultHierarchy = ""; +   +  /** +   * Get the <code>Log</code> object for the default hierarchy. +   *  +   * @return The <code>Log</code> object to write log messages to. +   */ +  private static Log getLog() { +    return LogFactory.getLog(defaultHierarchy); +  } + +  /** +   * Get the <code>Log</code> object for a given hierarchy. +   *  +   * @param hierarchy The logging hierarchy for which to return the logger.  +   * @return The <code>Log</code> object to write log messages to. +   */ +  private static Log getLog(String hierarchy) { +    return LogFactory.getLog(hierarchy); +  } +   +  /** +   * Set the default hierarchy to which the <code>Logger</code> should send its +   * logging output. +   * @param hierarchy The logging defaultHierarchy. +   */ +  public static void setHierarchy(String hierarchy) { +    defaultHierarchy = hierarchy; +  } + +  /** +   * Test, if the trace log level is enabled. +   *  +   * @return boolean <code>true</code>, if tracing output is enabled +   * <code>false</code> otherwise. +   */ +  public static boolean isTraceEnabled() { +    return getLog().isTraceEnabled(); +  } +   +  /** +   * Test, if the trace log level is enabled for a given hierarchy. +   *  +   * @param hierarchy requested log hierarchy +   * @return boolean <code>true</code>, if tracing output is enabled +   * <code>false</code> otherwise. +   */ +  public static boolean isTraceEnabled(String hierarchy) { +    return getLog(hierarchy).isTraceEnabled(); +  } +   +  /** +   * Trace a message. +   *  +   * @param message The message to trace. +   */ +  public static void trace(Object message) { +    getLog().trace(message); +  } +   +  /** +   * Test, if the debug log level is enabled. +   *  +   * @return boolean <code>true</code>, if debug output is enabled +   * <code>false</code> otherwise. +   */ +  public static boolean isDebugEnabled() { +    return getLog().isDebugEnabled(); +  } +   +  /** +   * Test, if the debug log level is enabled for a given hierarchy. +   *  +   * @param hierarchy requested log hierarchy +   * @return boolean <code>true</code>, if debug output is enabled +   * <code>false</code> otherwise. +   */ +  public static boolean isDebugEnabled(String hierarchy) { +    return getLog(hierarchy).isDebugEnabled(); +  } +   +  /** +   * Log a debug message. +   *  +   * @param message The message to log. +   */ +  public static void debug(Object message) { +    getLog().debug(message); +  } +   +  /** +   * Log an info message. +   *  +   * @param message The message to log. +   */ +  public static void info(Object message) { +    getLog().info(message); +  } +   +  /** +   * Log a warning message. +   *  +   * @param message The message to log. +   */ +  public static void warn(Object message) { +    getLog().warn(message); +  } + +  /** +   * Log a warning message. +   *  +   * @param message The message to log. +   * @param t An exception that may be the cause of the warning. +   */ +  public static void warn(Object message, Throwable t) { +    getLog().warn(message, t); +  } +   +  /** +   * Log an error message. +   *  +   * @param message The message to log. +   */ +  public static void error(Object message) { +    getLog().error(message); +  } + +  /** +   * Log an error message. +   *  +   * @param message The message to log. +   * @param t An exception that may be the cause of the error. +   */ +  public static void error(Object message, Throwable t) { +    getLog().error(message, t); +  } +   +  /** +   * Log a fatal error message. +   *  +   * @param message The message to log. +   */ +  public static void fatal(Object message) { +    getLog().fatal(message); +  } + +  /** +   * Log a fatal error message. +   *  +   * @param message The message to log. +   * @param t An exception that may be the cause of the error. +   */ +  public static void fatal(Object message, Throwable t) { +    getLog().fatal(message, t); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/logging/LoggingContext.java b/common/src/at/gv/egovernment/moa/logging/LoggingContext.java new file mode 100644 index 000000000..42d8db06e --- /dev/null +++ b/common/src/at/gv/egovernment/moa/logging/LoggingContext.java @@ -0,0 +1,46 @@ +package at.gv.egovernment.moa.logging; + +/** + * Encapsulates contextual information (i.e. per request information) for + * logging purposes. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class LoggingContext { +  /** The name of the node ID system property. */ +  public static final String NODE_ID_PROPERTY = "moa.node-id"; +   +  /** The current transaction ID. */ +  private String transactionID; +  /** The node ID. */ +  private String nodeID; +   +  /** +   * Create a new <code>LoggingContext</code>. +   *  +   * @param transactionID The transaction ID. May be <code>null</code>. +   */ +  public LoggingContext(String transactionID) { +    this.transactionID = transactionID; +    this.nodeID = System.getProperty(NODE_ID_PROPERTY); +  } +   +  /** +   * Return the transaction ID. +   *  +   * @return The transaction ID. +   */ +  public String getTransactionID() { +    return transactionID; +  } +   +  /** +   * Return the node ID. +   *  +   * @return The node ID. +   */ +  public String getNodeID() { +    return nodeID; +  } +} diff --git a/common/src/at/gv/egovernment/moa/logging/LoggingContextManager.java b/common/src/at/gv/egovernment/moa/logging/LoggingContextManager.java new file mode 100644 index 000000000..2bbe6caa1 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/logging/LoggingContextManager.java @@ -0,0 +1,56 @@ +package at.gv.egovernment.moa.logging; + +/** + * Provides each thread with a single instance of <code>LoggingContext</code>. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class LoggingContextManager { +  /** The single instance of this class. */ +  private static LoggingContextManager instance = null; +   +  /** The <code>LoggingContext</code> for each thread. */ +  private ThreadLocal context; +   +  /** +   * Get the single instance of the <code>LoggingContextManager</code> class. +   *  +   * @return LoggingContextManager The single instance. +   */ +  public static synchronized LoggingContextManager getInstance() { +    if (instance == null) { +      instance = new LoggingContextManager(); +    } +    return instance; +  } +   +  /** +   * Creates a new <code>LoggingContextManager</code>. +   *  +   * Protected to disallow direct instantiation. +   */ +  protected LoggingContextManager() { +    context = new ThreadLocal(); +  } +   +  /** +   * Set the <code>LoggingContext</code> context for the current thread. +   *  +   * @param ctx The <code>LoggingContext</code> for the current thread. +   */ +  public void setLoggingContext(LoggingContext ctx) { +    context.set(ctx); +  } +   +  /** +   * Return the <code>LoggingContext</code> for the current thread. +   *  +   * @return LoggingContext The <code>LoggingContext</code> for the current +   * thread, or <code>null</code> if none has been set. +   */ +  public LoggingContext getLoggingContext() { +    return (LoggingContext) context.get(); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/Base64Utils.java b/common/src/at/gv/egovernment/moa/util/Base64Utils.java new file mode 100644 index 000000000..ba2c4fb0e --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/Base64Utils.java @@ -0,0 +1,109 @@ +package at.gv.egovernment.moa.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +import iaik.utils.Base64InputStream; +import iaik.utils.Base64OutputStream; + +/** + * Utitility functions for encoding/decoding Base64 strings. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class Base64Utils { + +  /** +   * Read the bytes encoded in a Base64 encoded <code>String</code>.  +   *  +   * @param base64String The <code>String</code> containing the Base64 encoded +   * bytes. +   * @param ignoreInvalidChars Whether to ignore invalid Base64 characters. +   * @return byte[] The raw bytes contained in the <code>base64String</code>. +   * @throws IOException Failed to read the Base64 data. +   */ +  public static byte[] decode(String base64String, boolean ignoreInvalidChars) +    throws IOException { + +    Base64InputStream in = +      new Base64InputStream( +        new ByteArrayInputStream(base64String.getBytes("UTF-8")), +        ignoreInvalidChars); +    ByteArrayOutputStream out = new ByteArrayOutputStream(); +    byte[] bytes = new byte[256]; +    int bytesRead; + +    while ((bytesRead = in.read(bytes)) > 0) { +      out.write(bytes, 0, bytesRead); +    } + +    return out.toByteArray(); +  } + +  /** +   * Read the bytes encoded in a Base64 encoded <code>String</code> and provide +   * them via an <code>InputStream</code>. +   *  +   * @param base64String The <code>String</code> containing the Base64 encoded +   * bytes. +   * @param ignoreInvalidChars Whether to ignore invalid Base64 characters. +   * @return The <code>InputStream</code> from which the binary content of the +   * <code>base64String</code> can be read. +   */ +  public static InputStream decodeToStream( +    String base64String, +    boolean ignoreInvalidChars) { + +    try { +      ByteArrayInputStream bin =  +        new ByteArrayInputStream(base64String.getBytes("UTF-8")); +      Base64InputStream in = new Base64InputStream(bin, ignoreInvalidChars); +       +      return in; +    } catch (UnsupportedEncodingException e) { +      // cannot occur, since UTF-8 is required to be supported by every JRE +      return null;  +    } +  } + +  /** +   * Convert a byte array to a Base64 encoded <code>String</code>. +   *  +   * @param bytes The bytes to encode. +   * @return String The Base64 encoded representation of the <code>bytes</code>. +   * @throws IOException Failed to write the bytes as Base64 data. +   */ +  public static String encode(byte[] bytes) throws IOException { +    return encode(new ByteArrayInputStream(bytes)); +  } + +  /** +   * Convert the data contained in the given stream to a Base64 encoded +   * <code>String</code>. +   *  +   * @param inputStream The stream containing the data to encode. +   * @return The Base64 encoded data of <code>inputStream</code>, as a  +   * <code>String</code>. +   * @throws IOException Failed to convert the data in the stream. +   */ +  public static String encode(InputStream inputStream) throws IOException { +    ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); +    Base64OutputStream base64Stream = new Base64OutputStream(byteStream); +    byte[] bytes = new byte[256]; +    int bytesRead; + +    while ((bytesRead = inputStream.read(bytes)) > 0) { +      base64Stream.write(bytes, 0, bytesRead); +    } +    base64Stream.flush(); +    base64Stream.close(); +    inputStream.close(); + +    return byteStream.toString("UTF-8"); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/BoolUtils.java b/common/src/at/gv/egovernment/moa/util/BoolUtils.java new file mode 100644 index 000000000..fcd39b4dd --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/BoolUtils.java @@ -0,0 +1,24 @@ +package at.gv.egovernment.moa.util; + +/** + * Utility class for parsing XML schema boolean values. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class BoolUtils { +   +  /** +   * Return the boolean value of an <code>xsd:boolean</code> type of DOM  +   * element/attribute. +   *  +   * @param boolStr The value of the <code>xsd:boolean</code> element/attribute. +   * @return <code>true</code>, if <code>boolStr</code> equals  +   * <code>"true"</code> or <code>"1;"</code>. Otherwise, +   * <code>false</code> is returned. +   */ +  public static boolean valueOf(String boolStr) { +    return "true".equals(boolStr) || "1".equals(boolStr); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/CollectionUtils.java b/common/src/at/gv/egovernment/moa/util/CollectionUtils.java new file mode 100644 index 000000000..5329dcbd2 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/CollectionUtils.java @@ -0,0 +1,36 @@ +package at.gv.egovernment.moa.util; + +import java.util.Iterator; +import java.util.List; + +/** + * Various utility methods for dealing with <code>java.util.Collection</code>  + * classes.  + *  + * @author Patrick Peck + * @version $Id$ + */ +public class CollectionUtils { +   +  /** +   * Convert a <code>List</code> of <code>Number</code> objects to an  +   * <code>int</code> array. +   *  +   * @param nums The <code>List</code> containing the numbers whose integer +   * value to put into the result.  +   * @return The <code>int</code> values of the <code>Number</code>s contained +   * in <code>nums</code>. +   */ +  public static int[] toIntArray(List nums) { +    int[] result = new int[nums.size()]; +    Iterator iter; +    int i; +     +    for (i = 0, iter = nums.iterator(); iter.hasNext(); i++) { +      Number num = (Number) iter.next(); +      result[i] = num.intValue(); +    } +     +    return result; +  } +} diff --git a/common/src/at/gv/egovernment/moa/util/Constants.java b/common/src/at/gv/egovernment/moa/util/Constants.java new file mode 100644 index 000000000..681bed55b --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/Constants.java @@ -0,0 +1,218 @@ +package at.gv.egovernment.moa.util; + +/** + * Contains various constants used throughout the system. + *  + * @author Patrick Peck + * @version $Id$ + */ +public interface Constants { +  /** Root location of the schema files. */ +  public static final String SCHEMA_ROOT = "/resources/schemas/"; + +  /** URI of the MOA XML namespace. */ +  public static final String MOA_NS_URI = +    "http://reference.e-government.gv.at/namespace/moa/20020822#"; + +  /** Prefix used for the MOA XML namespace */ +  public static final String MOA_PREFIX = "moa"; + +  /** Local location of the MOA XML schema definition. */ +  public static final String MOA_SCHEMA_LOCATION = +    SCHEMA_ROOT + "MOA-SPSS-1.1.xsd"; + +  /** URI of the MOA configuration XML namespace. */ +  public static final String MOA_CONFIG_NS_URI = +    "http://reference.e-government.gv.at/namespace/moaconfig/20021122#"; + +  /** URI of the MOA ID configuration XML namespace. */ +  public static final String MOA_ID_CONFIG_NS_URI = +    "http://www.buergerkarte.at/namespaces/moaconfig#"; + +  /** Prefix used for the MOA configuration XML namespace */ +  public static final String MOA_CONFIG_PREFIX = "conf"; + +  /** Prefix used for the MOA configuration XML namespace */ +  public static final String MOA_ID_CONFIG_PREFIX = "confID"; + +  /** Local location of the MOA configuration XML schema definition. */ +  public static final String MOA_CONFIG_SCHEMA_LOCATION = +    SCHEMA_ROOT + "MOA-SPSS-Configuration-1.0.xsd"; + +  /** Local location of the MOA ID configuration XML schema definition. */ +  public static final String MOA_ID_CONFIG_SCHEMA_LOCATION = +    SCHEMA_ROOT + "MOA-ID-Configuration-1.1.xsd"; + +  /** URI of the Security Layer 1.0 namespace. */ +  public static final String SL10_NS_URI = +    "http://www.buergerkarte.at/namespaces/securitylayer/20020225#"; + +  /** Prefix used for the Security Layer 1.0 XML namespace */ +  public static final String SL10_PREFIX = "sl10"; + +  /** Local location of the Security Layer 1.0 XML schema definition */ +  public static final String SL10_SCHEMA_LOCATION = +    SCHEMA_ROOT + "Core.20020225.xsd"; + +  /** URI of the Security Layer 1.1 XML namespace */ +  public static final String SL11_NS_URI = +    "http://www.buergerkarte.at/namespaces/securitylayer/20020831#"; + +  /** Prefix used for the Security Layer 1.1 XML namespace */ +  public static final String SL11_PREFIX = "sl11"; + +  /** Local location of the Security Layer 1.1 XML schema definition */ +  public static final String SL11_SCHEMA_LOCATION = +    SCHEMA_ROOT + "Core.20020831.xsd"; + +  /** URI of the ECDSA XML namespace */ +  public static final String ECDSA_NS_URI = +    "http://www.buergerkarte.at/namespaces/ecdsa/200206030#"; + +  /** Prefix used for ECDSA namespace */ +  public static final String ECDSA_PREFIX = "ecdsa"; + +  /** Local location of ECDSA XML schema definition */ +  public static final String ECDSA_SCHEMA_LOCATION = +    SCHEMA_ROOT + "ECDSAKeyValue.xsd"; + +  /** URI of the PersonData XML namespace. */ +  public static final String PD_NS_URI = +    "http://reference.e-government.gv.at/namespace/persondata/20020228#"; + +  /** Prefix used for the PersonData XML namespace */ +  public static final String PD_PREFIX = "pr"; + +  /** Local location of the PersonData XML schema definition */ +  public static final String PD_SCHEMA_LOCATION = +    SCHEMA_ROOT + "PersonData.xsd"; + +  /** URI of the SAML namespace. */ +  public static final String SAML_NS_URI = +    "urn:oasis:names:tc:SAML:1.0:assertion"; + +  /** Prefix used for the SAML XML namespace */ +  public static final String SAML_PREFIX = "saml"; + +  /** Local location of the SAML XML schema definition. */ +  public static final String SAML_SCHEMA_LOCATION = +    SCHEMA_ROOT + "cs-sstc-schema-assertion-01.xsd"; + +  /** URI of the SAML request-response protocol namespace. */ +  public static final String SAMLP_NS_URI = +    "urn:oasis:names:tc:SAML:1.0:protocol"; + +  /** Prefix used for the SAML request-response protocol namespace */ +  public static final String SAMLP_PREFIX = "samlp"; + +  /** Local location of the SAML request-response protocol schema definition. */ +  public static final String SAMLP_SCHEMA_LOCATION = +    SCHEMA_ROOT + "cs-sstc-schema-protocol-01.xsd"; + +  /** URI of the XML namespace. */ +  public static final String XML_NS_URI = +    "http://www.w3.org/XML/1998/namespace"; + +  /** Prefix used for the XML namespace */ +  public static final String XML_PREFIX = "xml"; + +  /** Local location of the XML schema definition. */ +  public static final String XML_SCHEMA_LOCATION = SCHEMA_ROOT + "xml.xsd"; + +  /** URI of the XMLNS namespace */ +  public static final String XMLNS_NS_URI = "http://www.w3.org/2000/xmlns/"; + +  /** Prefix used for the XSI namespace */ +  public static final String XSI_PREFIX = "xsi"; + +  /** Local location of the XSI schema definition. */ +  public static final String XSI_SCHEMA_LOCATION = +    SCHEMA_ROOT + "XMLSchema-instance.xsd"; + +  /** URI of the XSI XMLNS namespace */ +  public static final String XSI_NS_URI = +    "http://www.w3.org/2001/XMLSchema-instance"; + +  /** URI of the XSLT XML namespace */ +  public static final String XSLT_NS_URI = +    "http://www.w3.org/1999/XSL/Transform"; + +  /** Prefix used for the XSLT XML namespace */ +  public static final String XSLT_PREFIX = "xsl"; + +  /** URI of the XMLDSig XML namespace. */ +  public static final String DSIG_NS_URI = "http://www.w3.org/2000/09/xmldsig#"; + +  /** Prefix used for the XMLDSig XML namespace */ +  public static final String DSIG_PREFIX = "dsig"; + +  /** Local location of the XMLDSig XML schema. */ +  public static final String DSIG_SCHEMA_LOCATION = +    SCHEMA_ROOT + "xmldsig-core-schema.xsd"; + +  /** URI of the XMLDSig XPath Filter XML namespace. */ +  public static final String DSIG_FILTER2_NS_URI = +    "http://www.w3.org/2002/06/xmldsig-filter2"; + +  /** Prefix used for the XMLDSig XPath Filter XML namespace */ +  public static final String DSIG_FILTER2_PREFIX = "dsig-filter2"; + +  /** Local location of the XMLDSig XPath Filter XML schema definition. */ +  public static final String DSIG_FILTER2_SCHEMA_LOCATION = +    SCHEMA_ROOT + "xmldsig-filter2.xsd"; + +  /** URI of the Exclusive Canonicalization XML namespace */ +  public static final String DSIG_EC_NS_URI = +    "http://www.w3.org/2001/10/xml-exc-c14n#"; + +  /** Prefix used for the Exclusive Canonicalization XML namespace */ +  public static final String DSIG_EC_PREFIX = "ec"; + +  /** Local location of the Exclusive Canonicalizaion XML schema definition */ +  public static final String DSIG_EC_SCHEMA_LOCATION = +    SCHEMA_ROOT + "exclusive-canonicalization.xsd"; + +  /** +   * Contains all namespaces and local schema locations for XML schema +   * definitions relevant for MOA. For use in validating XML parsers. +   */ +  public static final String ALL_SCHEMA_LOCATIONS = +    (MOA_NS_URI + " " + MOA_SCHEMA_LOCATION + " ") +      + (MOA_CONFIG_NS_URI + " " + MOA_CONFIG_SCHEMA_LOCATION + " ") +      + (MOA_ID_CONFIG_NS_URI + " " + MOA_ID_CONFIG_SCHEMA_LOCATION + " ") +      + (SL10_NS_URI + " " + SL10_SCHEMA_LOCATION + " ") +      + (SL11_NS_URI + " " + SL11_SCHEMA_LOCATION + " ") +      + (ECDSA_NS_URI + " " + ECDSA_SCHEMA_LOCATION + " ") +      + (PD_NS_URI + " " + PD_SCHEMA_LOCATION + " ") +      + (SAML_NS_URI + " " + SAML_SCHEMA_LOCATION + " ") +      + (SAMLP_NS_URI + " " + SAMLP_SCHEMA_LOCATION + " ") +      + (XML_NS_URI + " " + XML_SCHEMA_LOCATION + " ") +      + (XSI_NS_URI + " " + XSI_SCHEMA_LOCATION + " ") +      + (DSIG_NS_URI + " " + DSIG_SCHEMA_LOCATION + " ") +      + (DSIG_FILTER2_NS_URI + " " + DSIG_FILTER2_SCHEMA_LOCATION + " ") +      + (DSIG_EC_NS_URI + " " + DSIG_EC_SCHEMA_LOCATION); + +  /** Security Layer manifest type URI. */ +  public static final String SL_MANIFEST_TYPE_URI = +    "http://www.buergerkarte.at/specifications/Security-Layer/20020225#SignatureManifest"; + +  /** URI of the SHA1 digest algorithm */ +  public static final String SHA1_URI = +    "http://www.w3.org/2000/09/xmldsig#sha1"; + +  /** URI of the Canonical XML algorithm */ +  public static final String C14N_URI = +    "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; + +  /** URI of the Canoncial XML with comments algorithm */ +  public static final String C14N_WITH_COMMENTS_URI = +    "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"; + +  /** URI of the Exclusive Canonical XML algorithm */ +  public static final String EXC_C14N_URI = +    "http://www.w3.org/2001/10/xml-exc-c14n#"; +   +  /** URI of the Exclusive Canonical XML with commments algorithm */ +  public static final String EXC_C14N_WITH_COMMENTS_URI = +    "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; +} diff --git a/common/src/at/gv/egovernment/moa/util/DOMUtils.java b/common/src/at/gv/egovernment/moa/util/DOMUtils.java new file mode 100644 index 000000000..6da99037e --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/DOMUtils.java @@ -0,0 +1,806 @@ +package at.gv.egovernment.moa.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import org.apache.xerces.parsers.DOMParser; +import org.apache.xerces.parsers.SAXParser; +import org.apache.xerces.parsers.XMLGrammarPreparser; +import org.apache.xerces.util.SymbolTable; +import org.apache.xerces.util.XMLGrammarPoolImpl; +import org.apache.xerces.xni.grammars.XMLGrammarDescription; +import org.apache.xerces.xni.grammars.XMLGrammarPool; +import org.apache.xerces.xni.parser.XMLInputSource; +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Various utility functions for handling XML DOM trees. + *  + * The parsing methods in this class make use of some features internal to the + * Xerces DOM parser, mainly for performance reasons. As soon as JAXP + * (currently at version 1.2) is better at schema handling, it should be used as + * the parser interface. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class DOMUtils { + +  /** Feature URI for namespace aware parsing. */ +  private static final String NAMESPACES_FEATURE = +    "http://xml.org/sax/features/namespaces"; +  /** Feature URI for validating parsing. */ +  private static final String VALIDATION_FEATURE = +    "http://xml.org/sax/features/validation"; +  /** Feature URI for schema validating parsing. */ +  private static final String SCHEMA_VALIDATION_FEATURE = +    "http://apache.org/xml/features/validation/schema"; +  /** Feature URI for normalization of element/attribute values. */ +  private static final String NORMALIZED_VALUE_FEATURE = +    "http://apache.org/xml/features/validation/schema/normalized-value"; +  /** Feature URI for parsing ignorable whitespace. */ +  private static final String INCLUDE_IGNORABLE_WHITESPACE_FEATURE = +    "http://apache.org/xml/features/dom/include-ignorable-whitespace"; +  /** Feature URI for creating EntityReference nodes in the DOM tree. */ +  private static final String CREATE_ENTITY_REF_NODES_FEATURE = +    "http://apache.org/xml/features/dom/create-entity-ref-nodes"; +  /** Property URI for providing external schema locations. */ +  private static final String EXTERNAL_SCHEMA_LOCATION_PROPERTY = +    "http://apache.org/xml/properties/schema/external-schemaLocation"; +  /** Property URI for providing the external schema location for elements  +   * without a namespace. */ +  private static final String EXTERNAL_NO_NAMESPACE_SCHEMA_LOCATION_PROPERTY = +    "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation"; +  /** Property URI for the Xerces grammar pool. */ +  private static final String GRAMMAR_POOL = +    org.apache.xerces.impl.Constants.XERCES_PROPERTY_PREFIX +      + org.apache.xerces.impl.Constants.XMLGRAMMAR_POOL_PROPERTY; +  /** A prime number for initializing the symbol table. */ +  private static final int BIG_PRIME = 2039; +  /** Symbol table for the grammar pool. */ +  private static SymbolTable symbolTable = new SymbolTable(BIG_PRIME); +  /** Xerces schema grammar pool. */ +  private static XMLGrammarPool grammarPool = new XMLGrammarPoolImpl(); + +  /** +   * Preparse a schema and add it to the schema pool. +   *  +   * @param inputStream An <code>InputStream</code> providing the contents of +   * the schema. +   * @param systemId The systemId to use for the schema. +   * @throws IOException An error occurred reading the schema. +   */ +  public static void addSchemaToPool(InputStream inputStream, String systemId) +    throws IOException { +    XMLGrammarPreparser preparser; + +    // unlock the pool so that we can add another grammar +    grammarPool.unlockPool(); + +    // prepare the preparser +    preparser = new XMLGrammarPreparser(symbolTable); +    preparser.registerPreparser(XMLGrammarDescription.XML_SCHEMA, null); +    preparser.setProperty(GRAMMAR_POOL, grammarPool); +    preparser.setFeature(NAMESPACES_FEATURE, true); +    preparser.setFeature(VALIDATION_FEATURE, true); + +    // add the grammar to the pool +    preparser.preparseGrammar( +      XMLGrammarDescription.XML_SCHEMA, +      new XMLInputSource(null, systemId, null, inputStream, null)); + +    // lock the pool again so that schemas are not added automatically +    grammarPool.lockPool(); +  } + +  /** +   * Parse an XML document from an <code>InputStream</code>. +   *  +   * @param inputStream The <code>InputStream</code> containing the XML +   * document. +   * @param validating If <code>true</code>, parse validating. +   * @param externalSchemaLocations A <code>String</code> containing namespace +   * URI to schema location pairs, the same way it is accepted by the <code>xsi: +   * schemaLocation</code> attribute.  +   * @param externalNoNamespaceSchemaLocation The schema location of the +   * schema for elements without a namespace, the same way it is accepted by the +   * <code>xsi:noNamespaceSchemaLocation</code> attribute. +   * @param entityResolver An <code>EntityResolver</code> to resolve external +   * entities (schemas and DTDs). If <code>null</code>, it will not be set. +   * @param errorHandler An <code>ErrorHandler</code> to decide what to do +   * with parsing errors. If <code>null</code>, it will not be set. +   * @return The parsed XML document as a DOM tree. +   * @throws SAXException An error occurred parsing the document. +   * @throws IOException An error occurred reading the document. +   * @throws ParserConfigurationException An error occurred configuring the XML +   * parser. +   */ +  public static Document parseDocument( +    InputStream inputStream, +    boolean validating, +    String externalSchemaLocations, +    String externalNoNamespaceSchemaLocation, +    EntityResolver entityResolver, +    ErrorHandler errorHandler) +    throws SAXException, IOException, ParserConfigurationException { + +    DOMParser parser; + +    // create the DOM parser +    if (symbolTable != null) { +      parser = new DOMParser(symbolTable, grammarPool); +    } else { +      parser = new DOMParser(); +    } + +    // set parser features and properties +    parser.setFeature(NAMESPACES_FEATURE, true); +    parser.setFeature(VALIDATION_FEATURE, validating); +    parser.setFeature(SCHEMA_VALIDATION_FEATURE, validating); +    parser.setFeature(NORMALIZED_VALUE_FEATURE, false); +    parser.setFeature(INCLUDE_IGNORABLE_WHITESPACE_FEATURE, true); +    parser.setFeature(CREATE_ENTITY_REF_NODES_FEATURE, false); + +    if (validating) { +      if (externalSchemaLocations != null) { +        parser.setProperty( +          EXTERNAL_SCHEMA_LOCATION_PROPERTY, +          externalSchemaLocations); +      } +      if (externalNoNamespaceSchemaLocation != null) { +        parser.setProperty( +          EXTERNAL_NO_NAMESPACE_SCHEMA_LOCATION_PROPERTY, +          externalNoNamespaceSchemaLocation); +      } +    } + +    // set entity resolver and error handler +    if (entityResolver != null) { +      parser.setEntityResolver(entityResolver); +    } +    if (errorHandler != null) { +      parser.setErrorHandler(errorHandler); +    } + +    // parse the document and return it +    parser.parse(new InputSource(inputStream)); + +    return parser.getDocument(); +  } + +  /** +   * Parse an XML document from an <code>InputStream</code>. +   *  +   * It uses a <code>MOAEntityResolver</code> as the <code>EntityResolver</code> +   * and a <code>MOAErrorHandler</code> as the <code>ErrorHandler</code>. +   *  +   * @param inputStream The <code>InputStream</code> containing the XML +   * document. +   * @param validating If <code>true</code>, parse validating. +   * @param externalSchemaLocations A <code>String</code> containing namespace +   * URI to schema location pairs, the same way it is accepted by the <code>xsi: +   * schemaLocation</code> attribute.  +   * @param externalNoNamespaceSchemaLocation The schema location of the +   * schema for elements without a namespace, the same way it is accepted by the +   * <code>xsi:noNamespaceSchemaLocation</code> attribute. +   * @return The parsed XML document as a DOM tree. +   * @throws SAXException An error occurred parsing the document. +   * @throws IOException An error occurred reading the document. +   * @throws ParserConfigurationException An error occurred configuring the XML +   * parser. +   */ +  public static Document parseDocument( +    InputStream inputStream, +    boolean validating, +    String externalSchemaLocations, +    String externalNoNamespaceSchemaLocation) +    throws SAXException, IOException, ParserConfigurationException { + +    return parseDocument( +      inputStream, +      validating, +      externalSchemaLocations, +      externalNoNamespaceSchemaLocation, +      new MOAEntityResolver(), +      new MOAErrorHandler()); +  } + +  /** +   * Parse an XML document from a <code>String</code>. +   *  +   * It uses a <code>MOAEntityResolver</code> as the <code>EntityResolver</code> +   * and a <code>MOAErrorHandler</code> as the <code>ErrorHandler</code>. +   *  +   * @param xmlString The <code>String</code> containing the XML document. +   * @param encoding The encoding of the XML document. +   * @param validating If <code>true</code>, parse validating. +   * @param externalSchemaLocations A <code>String</code> containing namespace +   * URI to schema location pairs, the same way it is accepted by the <code>xsi: +   * schemaLocation</code> attribute.  +   * @param externalNoNamespaceSchemaLocation The schema location of the +   * schema for elements without a namespace, the same way it is accepted by the +   * <code>xsi:noNamespaceSchemaLocation</code> attribute. +   * @return The parsed XML document as a DOM tree. +   * @throws SAXException An error occurred parsing the document. +   * @throws IOException An error occurred reading the document. +   * @throws ParserConfigurationException An error occurred configuring the XML +   * parser. +   */ +  public static Document parseDocument( +    String xmlString, +    String encoding, +    boolean validating, +    String externalSchemaLocations, +    String externalNoNamespaceSchemaLocation) +    throws SAXException, IOException, ParserConfigurationException { + +    InputStream in = new ByteArrayInputStream(xmlString.getBytes(encoding)); +    return parseDocument( +      in, +      validating, +      externalSchemaLocations, +      externalNoNamespaceSchemaLocation); +  } + +  /** +   * Parse an UTF-8 encoded XML document from a <code>String</code>. +   *  +   * @param xmlString The <code>String</code> containing the XML document. +   * @param validating If <code>true</code>, parse validating. +   * @param externalSchemaLocations A <code>String</code> containing namespace +   * URI to schema location pairs, the same way it is accepted by the <code>xsi: +   * schemaLocation</code> attribute.  +   * @param externalNoNamespaceSchemaLocation The schema location of the +   * schema for elements without a namespace, the same way it is accepted by the +   * <code>xsi:noNamespaceSchemaLocation</code> attribute. +   * @return The parsed XML document as a DOM tree. +   * @throws SAXException An error occurred parsing the document. +   * @throws IOException An error occurred reading the document. +   * @throws ParserConfigurationException An error occurred configuring the XML +   * parser. +   */ +  public static Document parseDocument( +    String xmlString, +    boolean validating, +    String externalSchemaLocations, +    String externalNoNamespaceSchemaLocation) +    throws SAXException, IOException, ParserConfigurationException { + +    return parseDocument( +      xmlString, +      "UTF-8", +      validating, +      externalSchemaLocations, +      externalNoNamespaceSchemaLocation); +  } + +  /** +   * A convenience method to parse an XML document validating. +   *  +   * @param inputStream The <code>InputStream</code> containing the XML +   * document. +   * @return The root element of the parsed XML document. +   * @throws SAXException An error occurred parsing the document. +   * @throws IOException An error occurred reading the document. +   * @throws ParserConfigurationException An error occurred configuring the XML +   * parser. +   */ +  public static Element parseXmlValidating(InputStream inputStream) +    throws ParserConfigurationException, SAXException, IOException { +    return DOMUtils +      .parseDocument(inputStream, true, Constants.ALL_SCHEMA_LOCATIONS, null) +      .getDocumentElement(); +  } + +  /** +   * Schema validate a given DOM element. +   *  +   * @param element The element to validate. +   * @param externalSchemaLocations A <code>String</code> containing namespace +   * URI to schema location pairs, the same way it is accepted by the <code>xsi: +   * schemaLocation</code> attribute.  +   * @param externalNoNamespaceSchemaLocation The schema location of the +   * schema for elements without a namespace, the same way it is accepted by the +   * <code>xsi:noNamespaceSchemaLocation</code> attribute. +   * @return <code>true</code>, if the <code>element</code> validates against +   * the schemas declared in it. +   * @throws SAXException An error occurred parsing the document. +   * @throws IOException An error occurred reading the document from its +   * serialized representation. +   * @throws ParserConfigurationException An error occurred configuring the XML +   * @throws TransformerException An error occurred serializing the element. +   */ +  public static boolean validateElement( +    Element element, +    String externalSchemaLocations, +    String externalNoNamespaceSchemaLocation) +    throws +      ParserConfigurationException, +      IOException, +      SAXException, +      TransformerException { + +    byte[] docBytes; +    SAXParser parser; + +    // create the SAX parser +    if (symbolTable != null) { +      parser = new SAXParser(symbolTable, grammarPool); +    } else { +      parser = new SAXParser(); +    } + +    // serialize the document +    docBytes = serializeNode(element, "UTF-8"); + +    // set up parser features and attributes +    parser.setFeature(NAMESPACES_FEATURE, true); +    parser.setFeature(VALIDATION_FEATURE, true); +    parser.setFeature(SCHEMA_VALIDATION_FEATURE, true); +    if (externalSchemaLocations != null) { +      parser.setProperty( +        EXTERNAL_SCHEMA_LOCATION_PROPERTY, +        externalSchemaLocations); +    } +    if (externalNoNamespaceSchemaLocation != null) { +      parser.setProperty( +        EXTERNAL_NO_NAMESPACE_SCHEMA_LOCATION_PROPERTY, +        "externalNoNamespaceSchemaLocation"); +    } + +    // set up entity resolver and error handler +    parser.setEntityResolver(new MOAEntityResolver()); +    parser.setErrorHandler(new MOAErrorHandler()); + +    // parse validating +    parser.parse(new InputSource(new ByteArrayInputStream(docBytes))); +    return true; +  } + +  /** +   * Serialize the given DOM node. +   *  +   * The node will be serialized using the UTF-8 encoding. +   *  +   * @param node The node to serialize. +   * @return String The <code>String</code> representation of the given DOM +   * node. +   * @throws TransformerException An error occurred transforming the +   * node to a <code>String</code>. +   * @throws IOException An IO error occurred writing the node to a byte array. +   */ +  public static String serializeNode(Node node) +    throws TransformerException, IOException { +    return new String(serializeNode(node, "UTF-8"), "UTF-8"); +  } + +  /** +   * Serialize the given DOM node to a byte array. +   *  +   * @param node The node to serialize. +   * @param xmlEncoding The XML encoding to use. +   * @return The serialized node, as a byte array. Using a compatible encoding +   * this can easily be converted into a <code>String</code>. +   * @throws TransformerException An error occurred transforming the node to a  +   * byte array. +   * @throws IOException An IO error occurred writing the node to a byte array. +   */ +  public static byte[] serializeNode(Node node, String xmlEncoding) +    throws TransformerException, IOException { + +    TransformerFactory transformerFactory = TransformerFactory.newInstance(); +    Transformer transformer = transformerFactory.newTransformer(); +    ByteArrayOutputStream bos = new ByteArrayOutputStream(16384); + +    transformer.setOutputProperty(OutputKeys.METHOD, "xml"); +    transformer.setOutputProperty(OutputKeys.ENCODING, xmlEncoding); +    transformer.transform(new DOMSource(node), new StreamResult(bos)); + +    bos.flush(); +    bos.close(); + +    return bos.toByteArray(); +  } + +  /** +    * Return the text that a node contains.  +    *  +    * This routine: +    * <ul> +    * <li>Ignores comments and processing instructions.</li> +    * <li>Concatenates TEXT nodes, CDATA nodes, and the results recursively +    * processing EntityRef nodes.</li> +    * <li>Ignores any element nodes in the sublist. (Other possible options are +    * to recurse into element sublists or throw an exception.)</li> +    * </ul> +    *  +    * @param node A DOM node from which to extract text. +    * @return A String representing its contents. +    */ +  public static String getText(Node node) { +    if (!node.hasChildNodes()) { +      return ""; +    } + +    StringBuffer result = new StringBuffer(); +    NodeList list = node.getChildNodes(); + +    for (int i = 0; i < list.getLength(); i++) { +      Node subnode = list.item(i); +      if (subnode.getNodeType() == Node.TEXT_NODE) { +        result.append(subnode.getNodeValue()); +      } else if (subnode.getNodeType() == Node.CDATA_SECTION_NODE) { +        result.append(subnode.getNodeValue()); +      } else if (subnode.getNodeType() == Node.ENTITY_REFERENCE_NODE) { +        // Recurse into the subtree for text +        // (and ignore comments) +        result.append(getText(subnode)); +      } +    } +    return result.toString(); +  } + +  /** +   * Build the namespace prefix to namespace URL mapping in effect for a given +   * node. +   *  +   * @param node The context node for which build the map. +   * @return The namespace prefix to namespace URL mapping ( +   * a <code>String</code> value to <code>String</code> value mapping). +   */ +  public static Map getNamespaceDeclarations(Node node) { +    Map nsDecls = new HashMap(); +    int i; + +    do { +      if (node.hasAttributes()) { +        NamedNodeMap attrs = node.getAttributes(); + +        for (i = 0; i < attrs.getLength(); i++) { +          Attr attr = (Attr) attrs.item(i); + +          // add prefix mapping if none exists +          if ("xmlns".equals(attr.getPrefix()) +            || "xmlns".equals(attr.getName())) { + +            String nsPrefix = +              attr.getPrefix() != null ? attr.getLocalName() : ""; + +            if (nsDecls.get(nsPrefix) == null) { +              nsDecls.put(nsPrefix, attr.getValue()); +            } +          } +        } +      } +    } while ((node = node.getParentNode()) != null); + +    return nsDecls; +  } + +  /** +   * Add all namespace declarations declared in the parent(s) of a given +   * element and used in the subtree of the given element to the given element.   +   *  +   * @param context The element to which to add the namespaces. +   */ +  public static void localizeNamespaceDeclarations(Element context) { +    Node parent = context.getParentNode(); + +    if (parent != null) { +      Map namespaces = getNamespaceDeclarations(context.getParentNode()); +      Set nsUris = collectNamespaceURIs(context); +      Iterator iter; + +      for (iter = namespaces.entrySet().iterator(); iter.hasNext();) { +        Map.Entry e = (Map.Entry) iter.next(); + +        if (nsUris.contains(e.getValue())) { +          String prefix = (String) e.getKey(); +          String nsUri = (String) e.getValue(); +          String nsAttrName = "".equals(prefix) ? "xmlns" : "xmlns:" + prefix; + +          context.setAttributeNS(Constants.XMLNS_NS_URI, nsAttrName, nsUri); +        } +      } +    } +  } + +  /** +   * Collect all the namespace URIs used in the subtree of a given element. +   *  +   * @param context The element that should be searched for namespace URIs. +   * @return All namespace URIs used in the subtree of <code>context</code>, +   * including the ones used in <code>context</code> itself. +   */ +  public static Set collectNamespaceURIs(Element context) { +    Set result = new HashSet(); + +    collectNamespaceURIsImpl(context, result); +    return result; +  } + +  /** +   * A recursive method to do the work of <code>collectNamespaceURIs</code>. +   *  +   * @param context The context element to evaluate. +   * @param result The result, passed as a parameter to avoid unnecessary +   * instantiations of <code>Set</code>. +   */ +  private static void collectNamespaceURIsImpl(Element context, Set result) { +    NamedNodeMap attrs = context.getAttributes(); +    NodeList childNodes = context.getChildNodes(); +    String nsUri; +    int i; + +    // add the namespace of the context element +    nsUri = context.getNamespaceURI(); +    if (nsUri != null && nsUri != Constants.XMLNS_NS_URI) { +      result.add(nsUri); +    } + +    // add all namespace URIs from attributes +    for (i = 0; i < attrs.getLength(); i++) { +      nsUri = attrs.item(i).getNamespaceURI(); +      if (nsUri != null && nsUri != Constants.XMLNS_NS_URI) { +        result.add(nsUri); +      } +    } + +    // add all namespaces from subelements +    for (i = 0; i < childNodes.getLength(); i++) { +      Node node = childNodes.item(i); + +      if (node.getNodeType() == Node.ELEMENT_NODE) { +        collectNamespaceURIsImpl((Element) node, result); +      } +    } +  } + +  /** +   * Check, that each attribute node in the given <code>NodeList</code> has its +   * parent in the <code>NodeList</code> as well. +   *  +   * @param nodes The <code>NodeList</code> to check. +   * @return <code>true</code>, if each attribute node in <code>nodes</code> +   * has its parent in <code>nodes</code> as well. +   */ +  public static boolean checkAttributeParentsInNodeList(NodeList nodes) { +    Set nodeSet = new HashSet(); +    int i; + +    // put the nodes into the nodeSet +    for (i = 0; i < nodes.getLength(); i++) { +      nodeSet.add(nodes.item(i)); +    } + +    // check that each attribute node's parent is in the node list +    for (i = 0; i < nodes.getLength(); i++) { +      Node n = nodes.item(i); + +      if (n.getNodeType() == Node.ATTRIBUTE_NODE) { +        Attr attr = (Attr) n; +        Element owner = attr.getOwnerElement(); + +        if (owner == null) { +          if (!isNamespaceDeclaration(attr)) { +            return false; +          } +        } + +        if (!nodeSet.contains(owner) && !isNamespaceDeclaration(attr)) { +          return false; +        } +      } +    } + +    return true; +  } + +  /** +   * Convert an unstructured <code>NodeList</code> into a  +   * <code>DocumentFragment</code>. +   * +   * @param nodeList Contains the node list to be converted into a DOM  +   * DocumentFragment. +   * @return the resulting DocumentFragment. The DocumentFragment will be  +   * backed by a new DOM Document, i.e. all noded of the node list will be  +   * cloned. +   * @throws ParserConfigurationException An error occurred creating the +   * DocumentFragment. +   * @precondition The nodes in the node list appear in document order. +   * @precondition For each Attr node in the node list, the owning Element is  +   * in the node list as well. +   * @precondition Each Element or Attr node in the node list is namespace  +   * aware. +   */ +  public static DocumentFragment nodeList2DocumentFragment(NodeList nodeList) +    throws ParserConfigurationException { + +    DocumentBuilder builder = +      DocumentBuilderFactory.newInstance().newDocumentBuilder(); +    Document doc = builder.newDocument(); +    DocumentFragment result = doc.createDocumentFragment(); + +    if (null == nodeList || nodeList.getLength() == 0) { +      return result; +    } + +    int currPos = 0; +    currPos = +      nodeList2DocumentFragment(nodeList, currPos, result, null, null) + 1; + +    while (currPos < nodeList.getLength()) { +      currPos = +        nodeList2DocumentFragment(nodeList, currPos, result, null, null) + 1; +    } +    return result; +  } + +  /** +   * Helper method for the <code>nodeList2DocumentFragment</code>. +   *  +   * @param nodeList The <code>NodeList</code> to convert. +   * @param currPos The current position in the <code>nodeList</code>. +   * @param result The resulting <code>DocumentFragment</code>. +   * @param currOrgElem The current original element. +   * @param currClonedElem The current cloned element. +   * @return The current position. +   */ +  private static int nodeList2DocumentFragment( +    NodeList nodeList, +    int currPos, +    DocumentFragment result, +    Element currOrgElem, +    Element currClonedElem) { + +    while (currPos < nodeList.getLength()) { +      Node currentNode = nodeList.item(currPos); +      switch (currentNode.getNodeType()) { +        case Node.COMMENT_NODE : +        case Node.PROCESSING_INSTRUCTION_NODE : +        case Node.TEXT_NODE : +          { +            // Append current node either to resulting DocumentFragment or to  +            // current cloned Element +            if (null == currClonedElem) { +              result.appendChild( +                result.getOwnerDocument().importNode(currentNode, false)); +            } else { +              // Stop processing if current Node is not a descendant of  +              // current Element +              if (!isAncestor(currOrgElem, currentNode)) { +                return --currPos; +              } + +              currClonedElem.appendChild( +                result.getOwnerDocument().importNode(currentNode, false)); +            } +            break; +          } + +        case Node.ELEMENT_NODE : +          { +            Element nextCurrOrgElem = (Element) currentNode; +            Element nextCurrClonedElem = +              result.getOwnerDocument().createElementNS( +                nextCurrOrgElem.getNamespaceURI(), +                nextCurrOrgElem.getNodeName()); + +            // Append current Node either to resulting DocumentFragment or to  +            // current cloned Element +            if (null == currClonedElem) { +              result.appendChild(nextCurrClonedElem); +              currOrgElem = nextCurrOrgElem; +              currClonedElem = nextCurrClonedElem; +            } else { +              // Stop processing if current Node is not a descendant of +              // current Element +              if (!isAncestor(currOrgElem, currentNode)) { +                return --currPos; +              } + +              currClonedElem.appendChild(nextCurrClonedElem); +            } + +            // Process current Node (of type Element) recursively +            currPos = +              nodeList2DocumentFragment( +                nodeList, +                ++currPos, +                result, +                nextCurrOrgElem, +                nextCurrClonedElem); + +            break; +          } + +        case Node.ATTRIBUTE_NODE : +          { +            Attr currAttr = (Attr) currentNode; + +            // GK 20030411: Hack to overcome problems with IAIK IXSIL +            if (currAttr.getOwnerElement() == null) +              break; +            if (currClonedElem == null) +              break; + +            // currClonedElem must be the owner Element of currAttr if  +            // preconditions are met +            currClonedElem.setAttributeNS( +              currAttr.getNamespaceURI(), +              currAttr.getNodeName(), +              currAttr.getValue()); +            break; +          } + +        default : +          { +            // All other nodes will be ignored +          } +      } + +      currPos++; +    } + +    return currPos; +  } + +  /** +   * Check, if the given attribute is a namespace declaration. +   *  +   * @param attr The attribute to check. +   * @return <code>true</code>, if the attribute is a namespace declaration, +   * <code>false</code> otherwise. +   */ +  private static boolean isNamespaceDeclaration(Attr attr) { +    return Constants.XMLNS_NS_URI.equals(attr.getNamespaceURI()); +  } + +  /** +   * Check, if a given DOM element is an ancestor of a given node. +   *  +   * @param candAnc The DOM element to check for being the ancestor. +   * @param cand The node to check for being the child. +   * @return <code>true</code>, if <code>candAnc</code> is an (indirect)  +   * ancestor of <code>cand</code>; <code>false</code> otherwise. +   */ +  public static boolean isAncestor(Element candAnc, Node cand) { +    Node currPar = cand.getParentNode(); + +    while (currPar != null) { +      if (candAnc == currPar) +        return true; +      currPar = currPar.getParentNode(); +    } +    return false; +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/DateTimeUtils.java b/common/src/at/gv/egovernment/moa/util/DateTimeUtils.java new file mode 100644 index 000000000..58cc04c4c --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/DateTimeUtils.java @@ -0,0 +1,326 @@ +package at.gv.egovernment.moa.util; + +import java.io.StringWriter; +import java.text.ParseException; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +/** + * Utility for parsing and building XML type <code>dateTime</code>, + * according to ISO 8601. + *  + * @author Patrick Peck + * @version $Id$ + * @see <code>http://www.w3.org/2001/XMLSchema-datatypes"</code> + */ +public class DateTimeUtils { +  /** Error messages. */ +  private static MessageProvider msg = MessageProvider.getInstance(); + +  /** +   * Builds a <code>dateTime</code> value from a <code>Calendar</code> value. +   * @param cal the <code>Calendar</code> value +   * @return the <code>dateTime</code> value +   */ +  public static String buildDateTime(Calendar cal) { +    StringWriter out = new StringWriter(); +    out.write("" + cal.get(Calendar.YEAR)); +    out.write("-"); +    out.write(to2DigitString(cal.get(Calendar.MONTH) + 1)); +    out.write("-"); +    out.write(to2DigitString(cal.get(Calendar.DAY_OF_MONTH))); +    out.write("T"); +    out.write(to2DigitString(cal.get(Calendar.HOUR_OF_DAY))); +    out.write(":"); +    out.write(to2DigitString(cal.get(Calendar.MINUTE))); +    out.write(":"); +    out.write(to2DigitString(cal.get(Calendar.SECOND))); +    int tzOffsetMilliseconds = +      cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET); +    if (tzOffsetMilliseconds != 0) { +      int tzOffsetMinutes = tzOffsetMilliseconds / (1000 * 60); +      int tzOffsetHours = tzOffsetMinutes / 60; +      tzOffsetMinutes -= tzOffsetHours * 60; +      if (tzOffsetMilliseconds > 0) { +        out.write("+"); +        out.write(to2DigitString(tzOffsetHours)); +        out.write(":"); +        out.write(to2DigitString(tzOffsetMinutes)); +      } else { +        out.write("-"); +        out.write(to2DigitString(-tzOffsetHours)); +        out.write(":"); +        out.write(to2DigitString(-tzOffsetMinutes)); +      } +    } +    return out.toString(); +  } +   +  /** +   * Converts month, day, hour, minute, or second value +   * to a 2 digit String. +   * @param number the month, day, hour, minute, or second value +   * @return 2 digit String +   */ +  private static String to2DigitString(int number) { +    if (number < 10) +      return "0" + number; +    else +      return "" + number; +  } + +  /** +   * Parse a <code>String</code> containing a date and time instant, given in +   * ISO 8601 format. +   *  +   * @param dateTime The <code>String</code> to parse. +   * @return The <code>Date</code> representation of the contents of +   * <code>dateTime</code>. +   * @throws ParseException Parsing the <code>dateTime</code> failed. +   */ +  public static Date parseDateTime(String dateTime) throws ParseException { +    GregorianCalendar calendar; +    long time; +    int yearSign = 1, year, month, day; +    int hour, minute, second; +    double fraction = 0.0; +    int tzSign = 1, tzHour = 0, tzMinute = 0; +    int curPos = 0; +    String fractStr; +    boolean localTime = false; +    char c; + +    // parse year sign +    ensureChars(dateTime, curPos, 1); +    c = dateTime.charAt(curPos); +    if (c == '+' || c == '-') { +      yearSign = c == '+' ? 1 : -1; +      curPos++; +    } + +    // parse year +    year = parseInt(dateTime, curPos, 4); +    curPos += 4; + +    // parse '-' +    ensureChar(dateTime, curPos, '-'); +    curPos++; + +    // parse month +    month = parseInt(dateTime, curPos, 2); +    ensureValue(month, 1, 12, curPos); +    curPos += 2; + +    // parse '-' +    ensureChar(dateTime, curPos, '-'); +    curPos++; + +    // parse day +    day = parseInt(dateTime, curPos, 2); +    ensureValue(day, 1, 31, curPos); +    curPos += 2; + +    // parse 'T' +    ensureChar(dateTime, curPos, 'T'); +    curPos++; + +    // parse hour +    hour = parseInt(dateTime, curPos, 2); +    ensureValue(hour, 0, 23, curPos); +    curPos += 2; + +    // parse ':' +    ensureChar(dateTime, curPos, ':'); +    curPos++; + +    // parse minute +    minute = parseInt(dateTime, curPos, 2); +    ensureValue(minute, 0, 59, curPos); +    curPos += 2; + +    // parse ':' +    ensureChar(dateTime, curPos, ':'); +    curPos++; + +    // parse second +    second = parseInt(dateTime, curPos, 2); +    ensureValue(second, 0, 59, curPos); +    curPos += 2; + +    // parse a fraction +    if (dateTime.length() > curPos && dateTime.charAt(curPos) == '.') { +      curPos++; +      ensureDigits(dateTime, curPos, 1); +      fractStr = "0."; +      fractStr +        += dateTime.substring(curPos, curPos + countDigits(dateTime, curPos)); +      fraction = Double.parseDouble(fractStr); +      curPos += countDigits(dateTime, curPos); +    } + +    // parse a time zone +    if (dateTime.length() > curPos) { +      c = dateTime.charAt(curPos); +      if (c == 'Z') { +        curPos++; +      } else if (c == '+' || c == '-') { +        // parse time zone sign +        tzSign = c == '+' ? 1 : -1; +        curPos++; + +        // parse time zone hour +        tzHour = parseInt(dateTime, curPos, 2); +        ensureValue(tzHour, 0, 14, curPos); +        curPos += 2; + +        // parse ':' +        ensureChar(dateTime, curPos, ':'); +        curPos++; + +        // parse time zone minute +        tzMinute = parseInt(dateTime, curPos, 2); +        ensureValue(tzMinute, 0, 59, curPos); +        curPos += 2; +      } +    } else { +      localTime = true; +    } + +    // if we have characters left, it's an error +    if (dateTime.length() != curPos) { +      throw new ParseException(msg.getMessage("datetime.00", null), curPos); +    } + +    // build the Date object +    year = year * yearSign; +    try { +      calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT")); +      calendar.set(year, month - 1, day, hour, minute, second); +      calendar.set(Calendar.MILLISECOND, 0); +      time = calendar.getTime().getTime(); +      time += (long) (fraction * 1000.0); +      time -= tzSign * ((tzHour * 60) + tzMinute) * 60 * 1000; +      if (localTime) { +        time -= TimeZone.getDefault().getRawOffset();         +      } +      return new Date(time); +    } catch (IllegalArgumentException e) { +      throw new ParseException(msg.getMessage("datetime.00", null), curPos); +    } + +  } + +  /** +   * Parse an integer value. +   *  +   * @param str The <code>String</code> containing the digits. +   * @param curPos The starting position. +   * @param digits The number of digist making up the integer value. +   * @return int The integer representation of the digits contained in +   * <code>str</code>. +   * @throws ParseException Parsing the integer value failed. +   */ +  private static int parseInt(String str, int curPos, int digits) +    throws ParseException { + +    ensureDigits(str, curPos, digits); +    return Integer.parseInt(str.substring(curPos, curPos + digits)); +  } + +  /** +   * Count the number of digits following <code>curPos</code>. +   *  +   * @param str The <code>String</code> in which to count digits. +   * @param curPos The starting position. +   * @return int The number of digits. +   */ +  private static int countDigits(String str, int curPos) { +    int i; + +    for (i = curPos; i < str.length() && Character.isDigit(str.charAt(i)); i++); +    return i - curPos; +  } + +  /** +   * Ensure that a value falls in a given min/max range. +   *  +   * @param value The value to check. +   * @param min The minimum allowed value. +   * @param max The maximum allowed value. +   * @param curPos To indicate the parsing position in the +   * <code>ParseException</code>. +   * @throws ParseException Thrown, if <code>value < min || value > +   * max</code> +   */ +  private static void ensureValue(int value, int min, int max, int curPos) +    throws ParseException { + +    if (value < min || value > max) { +      throw new ParseException(msg.getMessage("datetime.00", null), curPos); +    } +  } + +  /** +   * Ensure that the given <code>String</code> has a number of characters left. +   *  +   * @param str The <code>String</code> to check for its length. +   * @param curPos The starting position. +   * @param count The minimum number of characters that <code>str</code> must +   * contain, starting at from <code>curPos</code>. +   * @throws ParseException Thrown, if  +   * <code>curPos + count > str.length()</code>. +   */ +  private static void ensureChars(String str, int curPos, int count) +    throws ParseException { +    if (curPos + count > str.length()) { +      throw new ParseException(msg.getMessage("datetime.00", null), curPos); +    } +  } + +  /** +   * Ensure that a given <code>String</code> contains a certain character at a +   * certain position. +   *  +   * @param str The <code>String</code> in which to look up the character.  +   * @param curPos The position in <code>str</code> that must contain the +   * character. +   * @param c The character value that must be contained at position +   * <code>curPos</code>. +   * @throws ParseException Thrown, if the characters do not match or +   * <code>curPos</code> is out of range. +   */ +  private static void ensureChar(String str, int curPos, char c) +    throws ParseException { + +    ensureChars(str, curPos, 1); +    if (str.charAt(curPos) != c) { +      throw new ParseException(msg.getMessage("datetime.00", null), curPos); +    } +  } + +  /** +   * Ensure that a given <code>String</code> contains a number of digits, +   * starting at a given position. +   *  +   * @param str The <code>String</code> to scan for digits.  +   * @param curPos The starting postion. +   * @param count The number of digits that must be contained in +   * <code>str</code>, starting at <code>curPos</code>. +   * @throws ParseException Thrown, if <code>str</code> is not long enough, or +   * one of the characters following <code>curPos</code> in <code>str</code> is +   * not a digit. +   */ +  private static void ensureDigits(String str, int curPos, int count) +    throws ParseException { + +    ensureChars(str, curPos, count); +    for (int i = curPos; i < curPos + count; i++) { +      if (!Character.isDigit(str.charAt(i))) { +        throw new ParseException(msg.getMessage("datetime.00", null), curPos); +      } +    } +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/EntityResolverChain.java b/common/src/at/gv/egovernment/moa/util/EntityResolverChain.java new file mode 100644 index 000000000..e7008a701 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/EntityResolverChain.java @@ -0,0 +1,52 @@ +package at.gv.egovernment.moa.util; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * Implementation of the <code>org.xml.sax.EntityResolver</code>, + * for use by a <code>org.apache.xerces.parsers.DOMParser</code>. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class EntityResolverChain implements EntityResolver { +  /** The <code>EntityResolver</code>s in the chain. */ +  private List resolvers = new ArrayList(); + +  /** +   * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String) +   */ +  public InputSource resolveEntity(String publicId, String systemId) +    throws SAXException, IOException { +     +    Iterator iter; +     +    for (iter = resolvers.iterator(); iter.hasNext(); ) { +      EntityResolver resolver = (EntityResolver) iter.next(); +      InputSource is = resolver.resolveEntity(publicId, systemId); +       +      if (is != null) { +        return is; +      } +    } +     +    return null; +  } +   +  /** +   * Add an <code>EntityResolver</code> to the chain. +   *  +   * @param entityResolver The <code>EntityResolver</code> to add. +   */ +  public void addEntityResolver(EntityResolver entityResolver) { +    resolvers.add(entityResolver); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/FileUtils.java b/common/src/at/gv/egovernment/moa/util/FileUtils.java new file mode 100644 index 000000000..f8941568d --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/FileUtils.java @@ -0,0 +1,87 @@ +package at.gv.egovernment.moa.util; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +/** + * Utility for accessing files on the file system, and for reading from input streams. + * @author Paul Ivancsics + * @version $Id$ + */ +public class FileUtils { +   +  /** +   * Reads a file, given by URL, into a byte array. +   * @param urlString file URL +   * @return file content +   * @throws IOException on any exception thrown +   */ +  public static byte[] readURL(String urlString) throws IOException { +    URL url = new URL(urlString); +    InputStream in = new BufferedInputStream(url.openStream()); +    byte[] content = StreamUtils.readStream(in); +    in.close(); +    return content; +  } +  /** +   * Reads a file, given by URL, into a String. +   * @param urlString file URL +   * @param encoding character encoding +   * @return file content +   * @throws IOException on any exception thrown +   */ +  public static String readURL(String urlString, String encoding) throws IOException { +    byte[] content = readURL(urlString); +    return new String(content, encoding); +  } +  /** +   * Reads a file, given by filename, into a byte array. +   * @param filename filename +   * @return file content +   * @throws IOException on any exception thrown +   */ +  public static byte[] readFile(String filename) throws IOException { +    BufferedInputStream in = new BufferedInputStream(new FileInputStream(filename)); +    byte[] content = StreamUtils.readStream(in); +    in.close(); +    return content; +  } +  /** +   * Reads a file, given by filename, into a String. +   * @param filename filename +   * @param encoding character encoding +   * @return file content +   * @throws IOException on any exception thrown +   */ +  public static String readFile(String filename, String encoding) throws IOException { +    byte[] content = readFile(filename); +    return new String(content, encoding); +  } +  /** +   * Reads a file from a resource. +   * @param name resource name +   * @return file content as a byte array +   * @throws IOException on any exception thrown +   */ +  public static byte[] readResource(String name) throws IOException { +    ClassLoader cl = FileUtils.class.getClassLoader(); +    BufferedInputStream in = new BufferedInputStream(cl.getResourceAsStream(name)); +    byte[] content = StreamUtils.readStream(in); +    in.close(); +    return content; +  } +  /** +   * Reads a file from a resource. +   * @param name filename +   * @param encoding character encoding +   * @return file content +   * @throws IOException on any exception thrown +   */ +  public static String readResource(String name, String encoding) throws IOException { +    byte[] content = readResource(name); +    return new String(content, encoding); +  } +} diff --git a/common/src/at/gv/egovernment/moa/util/KeyStoreUtils.java b/common/src/at/gv/egovernment/moa/util/KeyStoreUtils.java new file mode 100644 index 000000000..d6a34a7b2 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/KeyStoreUtils.java @@ -0,0 +1,134 @@ +package at.gv.egovernment.moa.util; + +import iaik.x509.X509Certificate; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.Certificate; + +/** + * Utility for creating and loading key stores. + *  + * @author Paul Ivancsics + * @version $Id$ + */ +public class KeyStoreUtils { + +  /** +   * Loads a key store from file. +   *  +   * @param keystoreType key store type +   * @param urlString URL of key store +   * @param password password protecting the key store +   * @return key store loaded +   * @throws IOException thrown while reading the key store from file +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore loadKeyStore( +    String keystoreType, +    String urlString, +    String password) +    throws IOException, GeneralSecurityException { + +    URL keystoreURL = new URL(urlString); +    InputStream in = keystoreURL.openStream(); +    return loadKeyStore(keystoreType, in, password); +  } +  /** +   * Loads a key store from an <code>InputStream</code>, and +   * closes the <code>InputStream</code>. +   *  +   * @param keystoreType key store type +   * @param in input stream +   * @param password password protecting the key store +   * @return key store loaded +   * @throws IOException thrown while reading the key store from the stream +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore loadKeyStore( +    String keystoreType, +    InputStream in, +    String password) +    throws IOException, GeneralSecurityException { + +    char[] chPassword = null; +    if (password != null) +      chPassword = password.toCharArray(); +    KeyStore ks = KeyStore.getInstance(keystoreType); +    ks.load(in, chPassword); +    in.close(); +    return ks; +  } +  /** +   * Creates a key store from X509 certificate files, aliasing them with +   * the index in the <code>String[]</code>, starting with <code>"0"</code>. +   *  +   * @param keyStoreType key store type +   * @param certFilenames certificate filenames +   * @return key store created +   * @throws IOException thrown while reading the certificates from file +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore createKeyStore( +    String keyStoreType, +    String[] certFilenames) +    throws IOException, GeneralSecurityException { + +    KeyStore ks = KeyStore.getInstance(keyStoreType); +    ks.load(null, null); +    for (int i = 0; i < certFilenames.length; i++) { +      Certificate cert = loadCertificate(certFilenames[i]); +      ks.setCertificateEntry("" + i, cert); +    } +    return ks; +  } +  /** +   * Creates a key store from a directory containg X509 certificate files,  +   * aliasing them with the index in the <code>String[]</code>, starting with <code>"0"</code>. +   * All the files in the directory are considered to be certificates. +   *  +   * @param keyStoreType key store type +   * @param certDirURLString file URL of directory containing certificate filenames +   * @return key store created +   * @throws IOException thrown while reading the certificates from file +   * @throws GeneralSecurityException thrown while creating the key store +   */ +  public static KeyStore createKeyStoreFromCertificateDirectory( +    String keyStoreType, +    String certDirURLString) +    throws IOException, GeneralSecurityException { + +    URL certDirURL = new URL(certDirURLString); +    String certDirname = certDirURL.getFile(); +    File certDir = new File(certDirname); +    String[] certFilenames = certDir.list(); +    String separator = +      (certDirname.endsWith(File.separator) ? "" : File.separator); +    for (int i = 0; i < certFilenames.length; i++) { +      certFilenames[i] = certDirname + separator + certFilenames[i]; +    } +    return createKeyStore(keyStoreType, certFilenames); +  } + +  /** +   * Loads an X509 certificate from file. +   * @param certFilename filename +   * @return the certificate loaded +   * @throws IOException thrown while reading the certificate from file +   * @throws GeneralSecurityException thrown while creating the certificate +   */ +  private static Certificate loadCertificate(String certFilename) +    throws IOException, GeneralSecurityException { + +    FileInputStream in = new FileInputStream(certFilename); +    Certificate cert = new X509Certificate(in); +    in.close(); +    return cert; +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/MOADefaultHandler.java b/common/src/at/gv/egovernment/moa/util/MOADefaultHandler.java new file mode 100644 index 000000000..0474d92cd --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/MOADefaultHandler.java @@ -0,0 +1,82 @@ +package at.gv.egovernment.moa.util; + +import java.io.IOException; + +import org.xml.sax.EntityResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * A <code>DefaultHandler</code> that uses a <code>MOAEntityResolver</code> and + * a <code>MOAErrorHandler</code>. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class MOADefaultHandler extends DefaultHandler { +  /** The <code>EntityResolver</code> to use. */ +  private EntityResolver entityResolver; +  /** The <code>ErrorHandler</code> to use. */ +  private ErrorHandler errorHandler; + +  /** +   * Create a new <code>MOADefaultHandler</code>. +   */ +  public MOADefaultHandler() { +    entityResolver = new MOAEntityResolver(); +    errorHandler = new MOAErrorHandler(); +  } + +  /** +   * Create a new <code>MOADefaultHandler</code>. +   *  +   * @param entityResolver The <code>EntityResolver</code> to use for resolving +   * external entities. +   * @param errorHandler The <code>ErrorHandler</code> to use for reporting +   * parsing errors. +   */ +  public MOADefaultHandler( +    EntityResolver entityResolver, +    ErrorHandler errorHandler) { + +    this.entityResolver = entityResolver; +    this.errorHandler = errorHandler; +  } + +  /** +   * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String) +   */ +  public InputSource resolveEntity(String publicId, String systemId) +    throws SAXException { +    try { +      return entityResolver.resolveEntity(publicId, systemId); +    } catch (IOException e) { +      return null; +    } +  } + +  /** +   * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) +   */ +  public void warning(SAXParseException exception) throws SAXException { +    errorHandler.warning(exception); +  } + +  /** +   * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) +   */ +  public void error(SAXParseException exception) throws SAXException { +    errorHandler.error(exception); +  } + +  /** +   * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) +   */ +  public void fatalError(SAXParseException exception) throws SAXException { +    errorHandler.fatalError(exception); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/MOAEntityResolver.java b/common/src/at/gv/egovernment/moa/util/MOAEntityResolver.java new file mode 100644 index 000000000..9406612e2 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/MOAEntityResolver.java @@ -0,0 +1,103 @@ +package at.gv.egovernment.moa.util; + +import java.io.InputStream; + +import org.apache.xerces.util.URI; +import org.apache.xerces.util.URI.MalformedURIException; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; + +import at.gv.egovernment.moa.logging.LogMsg; +import at.gv.egovernment.moa.logging.Logger; + +/** + * An <code>EntityResolver</code> that looks up entities stored as + * local resources. + *  + * <p>The following DTDs are mapped to local resources:  + * <ul> + * <li>The XMLSchema.dtd</li> + * <li>The datatypes.dtd</li> + * </ul> + * </p> + * <p>For all other resources, an attempt is made to resolve them as resources, + * either absolute or relative to <code>Constants.SCHEMA_ROOT</code>. + *  + * @author Patrick Peck + * @author Sven Aigner + */ +public class MOAEntityResolver implements EntityResolver { + +  /** +   * Resolve an entity. +   *  +   * The <code>systemId</code> parameter is used to perform the lookup of the +   * entity as a resource, either by interpreting the <code>systemId</code> as +   * an absolute resource path, or by appending the last path component of +   * <code>systemId</code> to <code>Constants.SCHEMA_ROOT</code>. +   *  +   * @param publicId The public ID of the resource. +   * @param systemId The system ID of the resource. +   * @return An <code>InputSource</code> from which the entity can be read, or +   * <code>null</code>, if the entity could not be found. +   * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String) +   */ +  public InputSource resolveEntity(String publicId, String systemId) { +    InputStream stream; +    int slashPos; + +    if (Logger.isDebugEnabled()) { +      Logger.debug( +        new LogMsg("resolveEntity: p=" + publicId + " s=" + systemId)); +    } + +    if (publicId != null) { +      // check if we can resolve some standard dtd's +      if (publicId.equalsIgnoreCase("-//W3C//DTD XMLSchema 200102//EN")) { +        return new InputSource( +          getClass().getResourceAsStream( +            Constants.SCHEMA_ROOT + "XMLSchema.dtd")); +      } else if (publicId.equalsIgnoreCase("datatypes")) { +        return new InputSource( +          getClass().getResourceAsStream( +            Constants.SCHEMA_ROOT + "datatypes.dtd")); +      } +    } else if (systemId != null) { +      // get the URI path +      try { +        URI uri = new URI(systemId); +        systemId = uri.getPath(); +        if (!"file".equals(uri.getScheme()) || "".equals(systemId.trim())) { +          return null; +        } +      } catch (MalformedURIException e) { +        return null; +      } +       +      // try to get the resource from the full path +      stream = getClass().getResourceAsStream(systemId); +      if (stream != null) { +        InputSource source = new InputSource(stream); + +        source.setSystemId(systemId); +        return source; +      } + +      // try to get the resource from the last path component +      slashPos = systemId.lastIndexOf('/'); +      if (slashPos >= 0 && systemId.length() > slashPos) { +        systemId = systemId.substring(slashPos + 1, systemId.length()); +        stream = +          getClass().getResourceAsStream(Constants.SCHEMA_ROOT + systemId); +        if (stream != null) { +          InputSource source = new InputSource(stream); + +          source.setSystemId(systemId); +          return source; +        } +      } +    } + +    return null; // nothing found - let the parser handle the entity +  } +}
\ No newline at end of file diff --git a/common/src/at/gv/egovernment/moa/util/MOAErrorHandler.java b/common/src/at/gv/egovernment/moa/util/MOAErrorHandler.java new file mode 100644 index 000000000..1f7757c8f --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/MOAErrorHandler.java @@ -0,0 +1,85 @@ +package at.gv.egovernment.moa.util; + +import org.apache.xml.utils.DefaultErrorHandler; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import at.gv.egovernment.moa.logging.LogMsg; +import at.gv.egovernment.moa.logging.Logger; + +/** + * An <code>ErrorHandler</code> that logs a message and throws a + * <code>SAXException</code> upon <code>error</code> and <code>fatal</code> + * parsing errors. + *  + * @author Patrick Peck + * @author Sven Aigner + */ +public class MOAErrorHandler extends DefaultErrorHandler { + +  /** +   * Logs a warning message. +   *  +   * @see org.xml.sax.ErrorHandler#warning(SAXParseException) +   */ +  public void warning(SAXParseException exception) throws SAXException { +    warn("parser.00", messageParams(exception), null); +  } + +  /** +   * Logs a warning and rethrows the <code>exception</code>. +   *  +   * @see org.xml.sax.ErrorHandler#error(SAXParseException) +   */ +  public void error(SAXParseException exception) throws SAXException { +    warn("parser.01", messageParams(exception), null); +    throw exception; +  } + +  /** +   * Logs a warning and rethrows the <code>exception</code>. +   *  +   * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException) +   */ +  public void fatalError(SAXParseException exception) throws SAXException { +    warn("parser.02", messageParams(exception), null); +    throw exception; +  } + +  /** +   * Log a warning message. +   *  +   * @param messageId The message ID to log. +   * @param parameters Additional message parameters. +   * @param t The <code>Throwable</code> to log; usually the cause of this +   * warning. +   */ +  private static void warn( +    String messageId, +    Object[] parameters, +    Throwable t) { + +    MessageProvider msg = MessageProvider.getInstance(); +    Logger.warn(new LogMsg(msg.getMessage(messageId, parameters)), t); +  } + +  /** +   * Put the system id, line and column number information from the exception +   * into an <code>Object</code> array, to provide it as a +   * <code>MessageFormat</code> parameter. +   *  +   * @param e The <code>SAXParseException</code> containing the +   * source system id and line/column numbers. +   * @return An array containing the system id (a <code>String</code>) as well +   * as line/column numbers (2 <code>Integer</code> objects) from the +   * <code>SAXParseException</code>. +   */ +  private static Object[] messageParams(SAXParseException e) { +    return new Object[] { +      e.getMessage(), +      e.getSystemId(), +      new Integer(e.getLineNumber()), +      new Integer(e.getColumnNumber())}; +  } + +}
\ No newline at end of file diff --git a/common/src/at/gv/egovernment/moa/util/MOATimer.java b/common/src/at/gv/egovernment/moa/util/MOATimer.java new file mode 100644 index 000000000..d8bf64fc3 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/MOATimer.java @@ -0,0 +1,110 @@ +package at.gv.egovernment.moa.util; + +import java.util.Map; +import java.util.WeakHashMap; + +/** + * A timer utility for named timers. + *  + * @author Sven Aigner + */ +public class MOATimer { + +  /** The single instance of this class. */ +  private static MOATimer instance = null; +  /** The starting points of single timings. */ +  private static Map timemapstart = new WeakHashMap(); +  /** The end points of single timings. */ +  private static Map timemapend = new WeakHashMap(); + +  /** +   * Return the single instance of this class. +   *  +   * @return The single instance of this class. +   */ +  public static MOATimer getInstance() { +    if (instance == null) { +      instance = new MOATimer(); +    } +    return instance; +  } + +  /** +   * Create a new <code>MOATimer</code>. +   *  +   * Protected to disallow multiple instances. +   */ +  protected MOATimer() { +    super(); +  } + +  /** +   * Start timing a certain action. +   *  +   * The timing belonging to the action ID is garbage collected as soon as there +   * exists no other reference to the action ID. +   *  +   * @param id The action ID. +   */ +  public void startTiming(Object id) { +    timemapstart.put(id, new Long(System.currentTimeMillis())); +  } + +  /** +   * Stop timing an action. +   *  +   * @param id The action ID. +   */ +  public void stopTiming(Object id) { +    timemapend.put(id, new Long(System.currentTimeMillis())); +  } + +  /** +   * Get the duration of an action. +   *  +   * @param id The action ID for which to compute the duration. +   * @return long The duration in milliseconds between calls to +   * <code>startTiming()</code> and <code>stopTiming()</code>. If +   * only <code>startTiming()</code> has been called for the action, then +   * current difference to the system time is returned. If no timing exists for +   * the action, <code>- 1</code> is returned. +   */ +  public long duration(Object id) { +    if (timemapstart.containsKey(id)) { +      long start = ((Long) timemapstart.get(id)).longValue(); +      if (timemapend.containsKey(id)) { +        long end = ((Long) timemapend.get(id)).longValue(); +        return end - start; +      } else { +        return System.currentTimeMillis() - start; +      } +    } else +      return -1; +  } + +  /** +   * Get the duration of an action, as a nicely formatted <code>String</code>. +   *  +   * @param id The action ID. +   * @return String The <code>duration()</code> as a <code>String</code>. +   */ +  public String durationAsString(Object id) { +    long dur = duration(id); +    long second = dur / 1000; +    long mil = (dur) - (second * 1000); +    return "Duration: " + second + "." + mil + " seconds"; +  } + +  /** +   * Remove a timing. +   *  +   * @param id The action ID. +   */ +  public void clearTiming(String id) { +    if (timemapstart.containsKey(id)) +      timemapstart.remove(id); +    if (timemapend.containsKey(id)) +      timemapend.remove(id); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/MessageProvider.java b/common/src/at/gv/egovernment/moa/util/MessageProvider.java new file mode 100644 index 000000000..f5117e390 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/MessageProvider.java @@ -0,0 +1,63 @@ +package at.gv.egovernment.moa.util; + +import java.util.Locale; + +/** + * A singleton wrapper around a <code>Message</code> object. + *  + * Provides the messages used in the common project. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class MessageProvider { +  /** The location of the default message resources. */ +  private static final String[] DEFAULT_MESSAGE_RESOURCES = +    { "resources/properties/common_messages" }; +  /** The locale of the default message resources. */ +  private static final Locale[] DEFAULT_MESSAGE_LOCALES = +    new Locale[] { new Locale("de", "AT") }; +  /** The single instance of this class. */ +  private static MessageProvider instance; +   +  /** The messages provided by this <code>MessageProvider</code>. */ +  private Messages messages; +   +  /** +   * Return the single instance of the <code>MessageProvider</code>. +   *  +   * Intialilizes the <code>MessageProvider</code> with the default message +   * locations: <code>/resources/properties/common_messages</code>. +   *  +   * @return The single <code>MessageProvider</code>. +   */ +  public static synchronized MessageProvider getInstance() { +    if (instance == null) { +      instance = +        new MessageProvider(DEFAULT_MESSAGE_RESOURCES, DEFAULT_MESSAGE_LOCALES); +    } +    return instance; +  } + +  /** +   * Create a <code>MessageProvider</code>. +   *  +   * @param resourceNames The names of the resources containing the messages. +   * @param locales The corresponding locales. +   */ +  protected MessageProvider(String[] resourceNames, Locale[] locales) { +    this.messages = new Messages(resourceNames, locales); +  } + +  /** +   * Get the message corresponding to a given message ID. +   * +   * @param messageId The ID of the message. +   * @param parameters The parameters to fill in into the message arguments. +   * @return The formatted message.  +   */ +  public String getMessage(String messageId, Object[] parameters) { +    return messages.getMessage(messageId, parameters); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/Messages.java b/common/src/at/gv/egovernment/moa/util/Messages.java new file mode 100644 index 000000000..a0139ae93 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/Messages.java @@ -0,0 +1,117 @@ +package at.gv.egovernment.moa.util; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.PropertyResourceBundle; + +import at.gv.egovernment.moa.logging.Logger; + +/** + * Provides access to the system messages resource used for exception handling + * and logging messages. + *  + * Messages must be provided as a resource bundle at the path. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class Messages { +  /** Error message indicating that no messages are avaiable. */ +  private static final String ERROR_MESSAGES_UNAVAILABLE = +    "Fehler in der Server-Konfiguration. " +      + "Die Fehlertexte konnten nicht geladen werden."; +  /** Error message indicating that the message is not available. */ +  private static final String ERROR_NO_MESSAGE = +    "Keine Fehlermeldung für Fehler-Nr.={0}"; + +  /** The names of the resources containing the messages. */ +  private String[] resourceNames; +  /** The corresponding <code>Locale</code>s of the resources. */ +  private Locale[] locales; +  /** The <code>ResourceBundle</code>s containing the messages. */ +  private ResourceBundleChain messages; + +  /** +   * Create a new <code>Message</code> object containing the messages +   * in the given resources. +   *  +   * @param resourceNames The names of the resources containing the messages. +   * @param locales The corresponding locales. +   */ +  public Messages(String[] resourceNames, Locale[] locales) { +    this.resourceNames = resourceNames; +    this.locales = locales; +    this.messages = null; +  } + +  /** +   * Get the message corresponding to a given message ID. +   * +   * @param messageId The ID of the message. +   * @param parameters The parameters to fill in into the message arguments. +   * @return The formatted message.  +   */ +  public String getMessage(String messageId, Object[] parameters) { +    // initialize messages +    if (messages == null) { +      initMessages(); +    } + +    // create the message +    if (messages == null) { +      return ERROR_MESSAGES_UNAVAILABLE; +    } else { +      try { +        String rawMessage = messages.getString(messageId); +        return MessageFormat.format(rawMessage, parameters); +      } catch (MissingResourceException e2) { +        // couldn't find any message -> set to default error message  +        return MessageFormat.format( +          ERROR_NO_MESSAGE, +          new Object[] { messageId }); +      } +    } +  } + +  /** +   * Return the names of the resources containing the messages. +   *  +   * @return String[] The names of the resource bundles containing the messages. +   */ +  private String[] getResourceNames() { +    return resourceNames; +  } + +  /** +   * Return the <code>Locale</code>s of the resources containing the messages. +   *  +   * @return Locale[] The <code>Locale</code>s of the resource bundles +   * containing the messages. +   */ +  private Locale[] getLocales() { +    return locales; +  } + +  /** +   * Initialize the <code>messages</code> <code>ResourceBundle</code> containing +   * the MOA error messages. +   */ +  private void initMessages() { +    messages = new ResourceBundleChain(); +    int i; + +    // initialize the message resources +    for (i = 0; i < resourceNames.length; i++) { +      try { +        messages.addResourceBundle( +          PropertyResourceBundle.getBundle( +            getResourceNames()[i], +            getLocales()[i])); +      } catch (MissingResourceException e) { +        Logger.error(ERROR_MESSAGES_UNAVAILABLE, e); +      } +    } +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/NodeIteratorAdapter.java b/common/src/at/gv/egovernment/moa/util/NodeIteratorAdapter.java new file mode 100644 index 000000000..f71aa472d --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/NodeIteratorAdapter.java @@ -0,0 +1,87 @@ +package at.gv.egovernment.moa.util; + +import java.util.ListIterator; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; +import org.w3c.dom.traversal.NodeFilter; +import org.w3c.dom.traversal.NodeIterator; + +/** + * A <code>NodeIterator</code> implementation based on a + * <code>ListIterator</code>. + *  + * @see java.util.ListIterator + * @see org.w3c.dom.traversal.NodeIterator + *  + * @author Patrick Peck + * @version $Id$ + */ +public class NodeIteratorAdapter implements NodeIterator { + +  /** The <code>ListIterator</code> to wrap. */ +  private ListIterator nodeIterator; + +  /** +   * Create a new <code>NodeIteratorAdapter</code>. +   * @param nodeIterator The <code>ListIterator</code> to iterate over. +   */ +  public NodeIteratorAdapter(ListIterator nodeIterator) { +    this.nodeIterator = nodeIterator; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#getRoot() +   */ +  public Node getRoot() { +    return null; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#getWhatToShow() +   */ +  public int getWhatToShow() { +    return NodeFilter.SHOW_ALL; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#getFilter() +   */ +  public NodeFilter getFilter() { +    return null; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#getExpandEntityReferences() +   */ +  public boolean getExpandEntityReferences() { +    return false; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#nextNode() +   */ +  public Node nextNode() throws DOMException { +    if (nodeIterator.hasNext()) { +      return (Node) nodeIterator.next(); +    } +    return null; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#previousNode() +   */ +  public Node previousNode() throws DOMException { +    if (nodeIterator.hasPrevious()) { +      return (Node) nodeIterator.previous(); +    } +    return null; +  } + +  /** +   * @see org.w3c.dom.traversal.NodeIterator#detach() +   */ +  public void detach() { +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/NodeListAdapter.java b/common/src/at/gv/egovernment/moa/util/NodeListAdapter.java new file mode 100644 index 000000000..7102cadca --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/NodeListAdapter.java @@ -0,0 +1,44 @@ +package at.gv.egovernment.moa.util; + +import java.util.List; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * A <code>NodeList</code> implementation based on a <code>List</code>. + *  + * @see java.util.List + * @see org.w3c.dom.NodeList + *  + * @author Patrick Peck + * @version $Id$ + */ +public class NodeListAdapter implements NodeList { +  /** The <code>List</code> to wrap. */ +  private List nodeList; +   +  /** +   * Create a new <code>NodeListAdapter</code>. +   *  +   * @param nodeList The <code>List</code> containing the nodes.  +   */ +  public NodeListAdapter(List nodeList) { +    this.nodeList = nodeList; +  } + +  /** +   * @see org.w3c.dom.NodeList#item(int) +   */ +  public Node item(int index) { +    return (Node) nodeList.get(index); +  } + +  /** +   * @see org.w3c.dom.NodeList#getLength() +   */ +  public int getLength() { +    return nodeList.size(); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/ResourceBundleChain.java b/common/src/at/gv/egovernment/moa/util/ResourceBundleChain.java new file mode 100644 index 000000000..90b28548a --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/ResourceBundleChain.java @@ -0,0 +1,66 @@ +package at.gv.egovernment.moa.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * A class to chain <code>ResourceBundle</code>s. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class ResourceBundleChain { +  /** Error message indicating the resource is not available. */ +  private static final String ERROR_MISSING_RESOURCE = "Missing resource"; +  /** The <code>ResourceBundle</code>s contained in this chain. */ +  private List resourceBundles = new ArrayList(); + +  /** +   * Add a <code>ResourceBundle</code> to the chain. +   *  +   * @param resourceBundle The <code>ResourceBundle</code> to add. +   */ +  public void addResourceBundle(ResourceBundle resourceBundle) { +    resourceBundles.add(resourceBundle); +  } + +  /** +   * Return the value of the resource. +   *  +   * @param key The key to access the <code>String</code> resource. +   * @return The resource value. All the registered <code>ResourceBundle</code>s +   * are searched in the order in which they have previously been added to this +   * <code>ResourceBundleChain</code>. +   * @throws MissingResourceException The resource coult not be found in any of +   * the bundles. +   */ +  public String getString(String key) throws MissingResourceException { +    MissingResourceException lastException = null; +    Iterator iter; + +    // handle case where no resource bundles have been added +    if (resourceBundles.size() == 0) { +      throw new MissingResourceException( +        ERROR_MISSING_RESOURCE, +        this.getClass().getName(), +        key); +    } + +    // try to find the resource in one of the bundles; if it cannot be found, +    // return the exception thrown by the last bundle in the list +    for (iter = resourceBundles.iterator(); iter.hasNext();) { +      ResourceBundle resourceBundle = (ResourceBundle) iter.next(); +      try { +        String value = resourceBundle.getString(key); +        return value; +      } catch (MissingResourceException e) { +        lastException = e; +      } +    } +    throw lastException; +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/SSLUtils.java b/common/src/at/gv/egovernment/moa/util/SSLUtils.java new file mode 100644 index 000000000..621562e2d --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/SSLUtils.java @@ -0,0 +1,222 @@ +package at.gv.egovernment.moa.util; + +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import javax.net.ssl.SSLSocketFactory; + +import com.sun.net.ssl.KeyManager; +import com.sun.net.ssl.KeyManagerFactory; +import com.sun.net.ssl.SSLContext; +import com.sun.net.ssl.TrustManager; +import com.sun.net.ssl.TrustManagerFactory; + +/** + * Utility for connecting to server applications via SSL. + *  + * @author Paul Ivancsics + * @version $Id$ + */ +public class SSLUtils { +	 +	/** +	 * Creates an <code>SSLSocketFactory</code> which utilizes the given trust store. +	 *  +   * @param trustStoreType key store type of trust store +   * @param trustStoreInputStream input stream for reading JKS trust store containing +   * 				 trusted server certificates; if <code>null</code>, the default +   * 				 trust store will be utilized +   * @param trustStorePassword if provided, it will be used to check  +   * 				 the integrity of the trust store; if omitted, it will not be checked +   * @return <code>SSLSocketFactory</code> to be used by an <code>HttpsURLConnection</code> +   * @throws IOException thrown while reading from the input stream +   * @throws GeneralSecurityException thrown while creating the socket factory +	 */ +  public static SSLSocketFactory getSSLSocketFactory( +  	String trustStoreType, +  	InputStream trustStoreInputStream, +  	String trustStorePassword) + 	  throws IOException, GeneralSecurityException { +  		 +	  TrustManager[] tms = getTrustManagers(trustStoreType, trustStoreInputStream, trustStorePassword); +		SSLContext ctx = SSLContext.getInstance("TLS"); +		ctx.init(null, tms, null); + +    SSLSocketFactory sf = ctx.getSocketFactory(); +  	return sf; +  } +	/** +	 * Creates an <code>SSLSocketFactory</code> which utilizes the +	 * given trust store and keystore. +	 *  +   * @param trustStore trust store containing trusted server certificates;  +   * 				 if <code>null</code>, the default trust store will be utilized +   * @param clientKeyStoreType key store type of <code>clientKeyStore</code> +   * @param clientKeyStoreURL URL of key store containing keys to be used for +   * 				 client authentication; if <code>null</code>, the default key store will be utilized +   * @param clientKeyStorePassword if provided, it will be used to check  +   * 				 the integrity of the client key store; if omitted, it will not be checked +   * @return <code>SSLSocketFactory</code> to be used by an <code>HttpsURLConnection</code> +   * @throws IOException thrown while reading key store file +   * @throws GeneralSecurityException thrown while creating the socket factory +	 */ +  public static SSLSocketFactory getSSLSocketFactory( +  	KeyStore trustStore, +  	String clientKeyStoreType, +  	String clientKeyStoreURL, +  	String clientKeyStorePassword) + 	  throws IOException, GeneralSecurityException { +  		 +		SSLContext ctx = getSSLContext( +			trustStore, clientKeyStoreType, clientKeyStoreURL, clientKeyStorePassword); +    SSLSocketFactory sf = ctx.getSocketFactory(); +  	return sf; +  } +	/** +	 * Creates an <code>SSLContext</code> initialized for the +	 * given trust store and keystore. +	 *  +   * @param trustStore trust store containing trusted server certificates;  +   * 				 if <code>null</code>, the default trust store will be utilized +   * @param clientKeyStoreType key store type of <code>clientKeyStore</code> +   * @param clientKeyStoreURL URL of key store containing keys to be used for +   * 				 client authentication; if <code>null</code>, the default key store will be utilized +   * @param clientKeyStorePassword if provided, it will be used to check  +   * 				 the integrity of the client key store; if omitted, it will not be checked +   * @return <code>SSLContext</code> to be used for creating an <code>SSLSocketFactory</code> +   * @throws IOException thrown while reading key store file +   * @throws GeneralSecurityException thrown while creating the SSL context +	 */ +  public static SSLContext getSSLContext( +  	KeyStore trustStore, +  	String clientKeyStoreType, +  	String clientKeyStoreURL, +  	String clientKeyStorePassword) + 	  throws IOException, GeneralSecurityException { +  		 +    //System.setProperty("javax.net.debug", "all"); +	  TrustManager[] tms = getTrustManagers(trustStore); +		KeyManager[] kms = getKeyManagers(clientKeyStoreType, clientKeyStoreURL, clientKeyStorePassword); +		SSLContext ctx = SSLContext.getInstance("TLS"); +		ctx.init(kms, tms, null); +		return ctx; +  } +  /** +   * Loads the trust store from an input stream and gets the  +   * <code>TrustManager</code>s from a default <code>TrustManagerFactory</code>, +	 * initialized from the given trust store. +   * @param trustStoreType key store type of trust store +   * @param trustStoreInputStream input stream for reading JKS trust store containing +   * 				 trusted server certificates; if <code>null</code>, the default +   * 				 trust store will be utilized +   * @param trustStorePassword if provided, it will be used to check  +   * 				 the integrity of the trust store; if omitted, it will not be checked +	 * @return <code>TrustManager</code>s to be used for creating an  +	 * 				  <code>SSLSocketFactory</code> utilizing the given trust store +   * @throws IOException thrown while reading from the input stream +   * @throws GeneralSecurityException thrown while initializing the  +   * 					default <code>TrustManagerFactory</code> +   */ +	protected static TrustManager[] getTrustManagers( +		String trustStoreType, +		InputStream trustStoreInputStream, +  	String trustStorePassword) +	  throws IOException, GeneralSecurityException { +	  	 +	  if (trustStoreInputStream == null) +	  	return null; + +	  // Set up the TrustStore to use. We need to load the file into +	  // a KeyStore instance. +		KeyStore trustStore = KeyStoreUtils.loadKeyStore(trustStoreType, trustStoreInputStream, trustStorePassword); +		return getTrustManagers(trustStore); +	} +	/** +	 * Gets the <code>TrustManager</code>s from a default <code>TrustManagerFactory</code>, +	 * initialized from the given trust store. +	 *  +	 * @param trustStore the trust store to use +	 * @param trustStorePassword password protecting the given trust store +	 * @return <code>TrustManager</code>s to be used for creating an  +	 * 				  <code>SSLSocketFactory</code> utilizing the given trust store +   * @throws GeneralSecurityException thrown while initializing the  +   * 					default <code>TrustManagerFactory</code> +	 */ +	protected static TrustManager[] getTrustManagers(KeyStore trustStore) +	  throws GeneralSecurityException { +	  	 +	  if (trustStore == null) +	  	return null; + +	  // Initialize the default TrustManagerFactory with this KeyStore +	  String alg=TrustManagerFactory.getDefaultAlgorithm(); +	  TrustManagerFactory tmFact=TrustManagerFactory.getInstance(alg); +	  tmFact.init(trustStore); +	 +	  // And now get the TrustManagers +	  TrustManager[] tms=tmFact.getTrustManagers(); +	  return tms; +	} +  /** +   * Loads the client key store from file and gets the  +   * <code>KeyManager</code>s from a default <code>KeyManagerFactory</code>, +	 * initialized from the given client key store. +   * @param clientKeyStoreType key store type of <code>clientKeyStore</code> +   * @param clientKeyStoreURL URL of key store containing keys to be used for +   * 				 client authentication; if <code>null</code>, the default key store will be utilized +   * @param clientKeyStorePassword password used to check the integrity of the client key store;  +   * 				 if <code>null</code>, it will not be checked +	 * @return <code>KeyManager</code>s to be used for creating an  +	 * 				  <code>SSLSocketFactory</code> utilizing the given client key store +   * @throws IOException thrown while reading from the key store file +   * @throws GeneralSecurityException thrown while initializing the  +   * 					default <code>KeyManagerFactory</code> +   */ +  public static KeyManager[] getKeyManagers ( +		String clientKeyStoreType, +		String clientKeyStoreURL, +  	String clientKeyStorePassword) +	  throws IOException, GeneralSecurityException { +   +  	if (clientKeyStoreURL == null) +  		return null; +  		 +	  // Set up the KeyStore to use. We need to load the file into +	  // a KeyStore instance. +	  KeyStore clientKeyStore = KeyStoreUtils.loadKeyStore( +	  	clientKeyStoreType, clientKeyStoreURL, clientKeyStorePassword); +		return getKeyManagers(clientKeyStore, clientKeyStorePassword); +	}   +  /** +   * Gets the <code>KeyManager</code>s from a default <code>KeyManagerFactory</code>, +	 * initialized from the given client key store. +   * @param clientKeyStore client key store +   * @param clientKeyStorePassword if provided, it will be used to check  +   * 				 the integrity of the client key store; if omitted, it will not be checked +	 * @return <code>KeyManager</code>s to be used for creating an  +	 * 				  <code>SSLSocketFactory</code> utilizing the given client key store +   * @throws GeneralSecurityException thrown while initializing the  +   * 					default <code>KeyManagerFactory</code> +   */ +	public static KeyManager[] getKeyManagers ( +		KeyStore clientKeyStore, +  	String clientKeyStorePassword) +	  throws GeneralSecurityException { +   +  	if (clientKeyStore == null) +  		return null; +  		 +	  // Now we initialize the default KeyManagerFactory with this KeyStore +	  String alg=KeyManagerFactory.getDefaultAlgorithm(); +	  KeyManagerFactory kmFact=KeyManagerFactory.getInstance(alg); +  	char[] password = null; +  	if (clientKeyStorePassword != null) +  		password = clientKeyStorePassword.toCharArray(); +	  kmFact.init(clientKeyStore, password); +	 +	  // And now get the KeyManagers +	  KeyManager[] kms=kmFact.getKeyManagers(); +	  return kms; +	}   +} diff --git a/common/src/at/gv/egovernment/moa/util/StreamEntityResolver.java b/common/src/at/gv/egovernment/moa/util/StreamEntityResolver.java new file mode 100644 index 000000000..38c4e863c --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/StreamEntityResolver.java @@ -0,0 +1,64 @@ +package at.gv.egovernment.moa.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * An <code>EntityResolver</code> that maps system IDs to  + * <code>InputStream</code>s. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class StreamEntityResolver implements EntityResolver { +   +  /** A mapping from Public ID or System ID to an <code>InputStream</code> +   * containing the entity. */ +  private Map mappedEntities; +   +  /** +   * Create a <code>StreamEntityResolver</code>. +   *  +   * @param mappedEntities A mapping from public or system IDs  +   * (<code>String</code> objects) to <code>InputStream</code>s.  +   */ +  public StreamEntityResolver(Map mappedEntities) { +    this.mappedEntities = mappedEntities; +  } + +  /** +   * Resolve an entity by looking it up in the mapped entities. +   *  +   * First, the public ID is looked up in the mapping, then the system ID. +   *  +   * @param publicId The public ID of the entity. +   * @param systemId The system ID of the entity. +   * @return An <code>InputStream</code> containing the entity or  +   * <code>null</code> if no entity could be found.  +   * @throws SAXException Signalling a parsing exception. +   * @throws IOException Error reading the entity. +   */ +  public InputSource resolveEntity(String publicId, String systemId)  +    throws SAXException, IOException { +       +    InputSource src = null; +       +    if (publicId != null && mappedEntities.get(publicId) != null) { +      src = new InputSource((InputStream) mappedEntities.get(publicId)); +    } else if (systemId != null && mappedEntities.get(systemId) != null) { +      src = new InputSource((InputStream) mappedEntities.get(systemId)); +    } +     +    if (src != null) { +      src.setPublicId(publicId); +      src.setSystemId(systemId); +    } +     +    return src; +  } +} diff --git a/common/src/at/gv/egovernment/moa/util/StreamUtils.java b/common/src/at/gv/egovernment/moa/util/StreamUtils.java new file mode 100644 index 000000000..88db24504 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/StreamUtils.java @@ -0,0 +1,116 @@ +package at.gv.egovernment.moa.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Utility methods for streams. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class StreamUtils { +   +  /** +   * Compare the contents of two <code>InputStream</code>s. +   *  +   * @param is1 The 1st <code>InputStream</code> to compare. +   * @param is2 The 2nd <code>InputStream</code> to compare. +   * @return boolean <code>true</code>, if both streams contain the exactly the +   * same content, <code>false</code> otherwise. +   * @throws IOException An error occurred reading one of the streams. +   */ +  public static boolean compareStreams(InputStream is1, InputStream is2)  +    throws IOException { +       +    byte[] buf1 = new byte[256]; +    byte[] buf2 = new byte[256]; +    int length1; +    int length2; +   +    try { +      while (true) { +        length1 = is1.read(buf1); +        length2 = is2.read(buf2); +         +        if (length1 != length2) { +          return false; +        } +        if (length1 <= 0) { +          return true; +        } +        if (!compareBytes(buf1, buf2, length1)) { +          return false; +        } +      } +    } catch (IOException e) { +      throw e; +    } finally { +      // close both streams +      try { +        is1.close(); +        is2.close(); +      } catch (IOException e) { +        // ignore this +      } +    } +  } +   +  /** +   * Compare two byte arrays, up to a given maximum length. +   *  +   * @param b1 1st byte array to compare. +   * @param b2 2nd byte array to compare. +   * @param length The maximum number of bytes to compare. +   * @return <code>true</code>, if the byte arrays are equal, <code>false</code> +   * otherwise. +   */ +  private static boolean compareBytes(byte[] b1, byte[] b2, int length) { +    if (b1.length != b2.length) { +      return false; +    } +   +    for (int i = 0; i < b1.length && i < length; i++) { +      if (b1[i] != b2[i]) { +        return false; +      } +    } +   +    return true; +  } + +  /** +   * Reads a byte array from a stream. +   * @param in The <code>InputStream</code> to read. +   * @return The bytes contained in the given <code>InputStream</code>. +   * @throws IOException on any exception thrown +   */ +  public static byte[] readStream(InputStream in) throws IOException { +    ByteArrayOutputStream out = new ByteArrayOutputStream(); +    int b; +    while ((b = in.read()) >= 0) +      out.write(b); +    in.close(); +    return out.toByteArray(); +  } + +  /** +   * Reads a <code>String</code> from a stream, using given encoding. +   * @param in The <code>InputStream</code> to read. +   * @param encoding The character encoding to use for converting the bytes +   * of the <code>InputStream</code> into a <code>String</code>. +   * @return The content of the given <code>InputStream</code> converted into +   * a <code>String</code>. +   * @throws IOException on any exception thrown +   */ +  public static String readStream(InputStream in, String encoding) throws IOException { +    ByteArrayOutputStream out = new ByteArrayOutputStream(); +    int b; +    while ((b = in.read()) >= 0) +      out.write(b); +    in.close(); +    return out.toString(encoding); +  } +  +} diff --git a/common/src/at/gv/egovernment/moa/util/URLDecoder.java b/common/src/at/gv/egovernment/moa/util/URLDecoder.java new file mode 100644 index 000000000..a20820f7e --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/URLDecoder.java @@ -0,0 +1,60 @@ +package at.gv.egovernment.moa.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; + +/** + * Decodes an URL encoded String using a specified character encoding. + * Provides a function missing in JDK 1.3.  + * @author Paul Ivancsics + * @version $Id$ + */ +public class URLDecoder { + +  /** +   * Decodes an <code>application/x-www-form-urlencoded</code> string using a specific encoding scheme. +   * @param s the string to decode +   * @param encoding name of character encoding +   * @return the newly decoded string +   * @throws UnsupportedEncodingException if the encoding is not supported +   */ +  public static String decode(String s, String encoding) throws UnsupportedEncodingException { +    StringReader in = new StringReader(s); +    ByteArrayOutputStream bout = new ByteArrayOutputStream(); +    for (int b = read(in); b >= 0; b = read(in)) +      bout.write(b); +    return bout.toString(encoding); +  } +  /** +   * Decodes the next byte from the string reader. +   * @param in string reader +   * @return the next byte decoded;  +   *          -1 upon end of string, on erroneous data, and on any exception caught +   * @todo syntax check on string +   */ +  private static int read(StringReader in) { +    try {  +      int b = in.read(); +      if (b == '+') +        return ' '; +      if (b == '%') { +        char[] hex = new char[2]; +        if (in.read(hex, 0, 2) >= 0) { +          String hexString = new String(hex); +          return Integer.valueOf(hexString, 16).intValue(); +        } +        else +          return -1; +      } +      return b; +    }  +    catch (IOException ex) {  +      return -1;  +    } +    catch (NumberFormatException ex) {  +      return -1;  +    } +  } +} diff --git a/common/src/at/gv/egovernment/moa/util/URLEncoder.java b/common/src/at/gv/egovernment/moa/util/URLEncoder.java new file mode 100644 index 000000000..840c0c3bc --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/URLEncoder.java @@ -0,0 +1,63 @@ +package at.gv.egovernment.moa.util; + +import java.io.ByteArrayInputStream; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; + +/** + * Translates a string into mime format "x-www-form-urlencoded". + * Provides a function missing in JDK 1.3.  + * @author Paul Ivancsics + * @version $Id$ + */ +public class URLEncoder { +   +  /** +   * Translates a string into x-www-form-urlencoded format. +   * @param s the string to be translated +   * @param encoding the encoding to use +   * @return the translated string +   * @throws UnsupportedEncodingException when the desired encoding is not supported +   */ +  public static String encode(String s, String encoding) throws UnsupportedEncodingException { +    byte[] barr = s.getBytes(encoding); +    ByteArrayInputStream bin = new ByteArrayInputStream(barr); +    StringWriter out = new StringWriter(); +    for (int b = bin.read(); b >= 0; b = bin.read()) +      encode(b, out); +    return out.toString(); +  } +   +  /** +   * Encode a character. +   * @param ch The character to encode. +   * @param out The <code>StringWriter</code> containing the result. +   */ +  private static void encode(int ch, StringWriter out) { +    if ((ch >= 'a' && ch <= 'z') +      || (ch >= 'A' && ch <= 'Z') +      || (ch >= '0' && ch <= '9') +      || ch == '.' || ch == '-' || ch == '*' || ch == '_') +      out.write(ch); +    else if (ch == ' ') +      out.write('+'); +    else +      encodeHex(ch, out); +  } +   +  /** +   * Encode a character as an escaped hex value.  +   * @param ch The character to encode. +   * @param out The <code>StringWriter</code> containing the result. +   */ +  private static void encodeHex(int ch, StringWriter out) { +    out.write('%'); +    String hex = Integer.toHexString(ch).toUpperCase(); +    if (hex.length() < 2) +      out.write('0'); +    else +      out.write(hex.charAt(hex.length() - 2)); +    out.write(hex.charAt(hex.length() - 1)); +  } + +} diff --git a/common/src/at/gv/egovernment/moa/util/XPathException.java b/common/src/at/gv/egovernment/moa/util/XPathException.java new file mode 100644 index 000000000..e10c882e5 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/XPathException.java @@ -0,0 +1,58 @@ +package at.gv.egovernment.moa.util; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * An exception occurred evaluating an XPath. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class XPathException extends RuntimeException { +  /** The wrapped exception. */ +  private Throwable wrapped; +   +  /** +   * Create a <code>XPathException</code>. +   *  +   * @param message The exception message. +   * @param wrapped The exception being the likely cause of this exception. +   */ +  public XPathException(String message, Throwable wrapped) { +    super(message); +    this.wrapped = wrapped;  +  } +   +  /** +   * Return the wrapped exception. +   *  +   * @return The wrapped exception being the likely cause of this exception. +   */ +  public Throwable getWrapped() { +    return wrapped; +  } + +  /** +   * @see java.lang.Throwable#printStackTrace(java.io.PrintStream) +   */ +  public void printStackTrace(PrintStream s) { +    super.printStackTrace(s); +    if (getWrapped() != null) { +      s.print("Caused by: "); +      getWrapped().printStackTrace(s); +    } +  } + +  /** +   * @see java.lang.Throwable#printStackTrace(java.io.PrintWriter) +   */ +  public void printStackTrace(PrintWriter s) { +    super.printStackTrace(s); +    if (getWrapped() != null) { +      s.print("Caused by: "); +      getWrapped().printStackTrace(s); +    } +  } +  +} diff --git a/common/src/at/gv/egovernment/moa/util/XPathUtils.java b/common/src/at/gv/egovernment/moa/util/XPathUtils.java new file mode 100644 index 000000000..0ed4fcda3 --- /dev/null +++ b/common/src/at/gv/egovernment/moa/util/XPathUtils.java @@ -0,0 +1,415 @@ +package at.gv.egovernment.moa.util; + +import java.util.List; +import java.util.Map; + +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.traversal.NodeIterator; + +import org.jaxen.JaxenException; +import org.jaxen.NamespaceContext; +import org.jaxen.SimpleNamespaceContext; +import org.jaxen.dom.DOMXPath; +import org.jaxen.dom.DocumentNavigator; + +/** + * Utility methods to evaluate XPath expressions on DOM nodes. + *  + * @author Patrick Peck + * @version $Id$ + */ +public class XPathUtils { + +  /** +   * The XPath expression selecting all nodes under a given root (including the +   * root node itself). +   */ +  public static final String ALL_NODES_XPATH = +    "(.//. | .//@* | .//namespace::*)"; + +  /** The <code>DocumentNavigator</code> to use for navigating the document. */ +  private static DocumentNavigator documentNavigator = +    DocumentNavigator.getInstance(); +  /** The default namespace prefix to namespace URI mappings. */ +  private static NamespaceContext NS_CONTEXT; + +  static { +    SimpleNamespaceContext ctx = new SimpleNamespaceContext(); +    ctx.addNamespace(Constants.MOA_PREFIX, Constants.MOA_NS_URI); +    ctx.addNamespace(Constants.MOA_CONFIG_PREFIX, Constants.MOA_CONFIG_NS_URI); +    ctx.addNamespace( +      Constants.MOA_ID_CONFIG_PREFIX, +      Constants.MOA_ID_CONFIG_NS_URI); +    ctx.addNamespace(Constants.SL10_PREFIX, Constants.SL10_NS_URI); +    ctx.addNamespace(Constants.SL11_PREFIX, Constants.SL11_NS_URI); +    ctx.addNamespace(Constants.ECDSA_PREFIX, Constants.ECDSA_NS_URI); +    ctx.addNamespace(Constants.PD_PREFIX, Constants.PD_NS_URI); +    ctx.addNamespace(Constants.SAML_PREFIX, Constants.SAML_NS_URI); +    ctx.addNamespace(Constants.SAMLP_PREFIX, Constants.SAMLP_NS_URI); +    ctx.addNamespace(Constants.DSIG_PREFIX, Constants.DSIG_NS_URI); +    ctx.addNamespace(Constants.XSLT_PREFIX, Constants.XSLT_NS_URI); +    ctx.addNamespace(Constants.XSI_PREFIX, Constants.XSI_NS_URI); +    ctx.addNamespace( +      Constants.DSIG_FILTER2_PREFIX, +      Constants.DSIG_FILTER2_NS_URI); +    ctx.addNamespace(Constants.DSIG_EC_PREFIX, Constants.DSIG_EC_NS_URI); +    NS_CONTEXT = ctx; +  } + +  /** +   * Return a <code>NodeIterator</code> over the nodes matching the XPath +   * expression. +   *  +   * All namespace URIs and prefixes declared in the <code>Constants</code> +   * interface are used for resolving namespaces. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param exp The XPath expression to evaluate. +   * @return An iterator over the resulting nodes. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  public static NodeIterator selectNodeIterator(Node contextNode, String exp) +    throws XPathException { + +    return selectNodeIterator(contextNode, NS_CONTEXT, exp); +  } + +  /** +   * Return a <code>NodeIterator</code> over the nodes matching the XPath +   * expression. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param namespaceElement An element from which to build the +   * namespace mapping for evaluating the XPath expression +   * @param exp The XPath expression to evaluate. +   * @return An iterator over the resulting nodes. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  public static NodeIterator selectNodeIterator( +    Node contextNode, +    Element namespaceElement, +    String exp) +    throws XPathException { + +    try { +      SimpleNamespaceContext ctx = new SimpleNamespaceContext(); +      ctx.addElementNamespaces(documentNavigator, namespaceElement); +      return selectNodeIterator(contextNode, ctx, exp); +    } catch (JaxenException e) { +      MessageProvider msg = MessageProvider.getInstance(); +      String message = msg.getMessage("xpath.00", new Object[] { exp }); +      throw new XPathException(message, e); +    } +  } + +  /** +   * Return a <code>NodeIterator</code> over the nodes matching the XPath +   * expression. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param namespaceMapping A namespace prefix to namespace URI mapping +   * (<code>String</code> to <code>String</code>) for evaluating the XPath  +   * expression. +   * @param exp The XPath expression to evaluate. +   * @return An iterator over the resulting nodes. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  public static NodeIterator selectNodeIterator( +    Node contextNode, +    Map namespaceMapping, +    String exp) +    throws XPathException { + +    SimpleNamespaceContext ctx = new SimpleNamespaceContext(namespaceMapping); + +    return selectNodeIterator(contextNode, ctx, exp); +  } + +  /** +   * Return a <code>NodeIterator</code> over the nodes matching the XPath +   * expression. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param nsContext The <code>NamespaceContext</code> for resolving namespace +   * prefixes to namespace URIs for evaluating the XPath expression. +   * @param exp The XPath expression to evaluate. +   * @return An iterator over the resulting nodes. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  private static NodeIterator selectNodeIterator( +    Node contextNode, +    NamespaceContext nsContext, +    String exp) +    throws XPathException { + +    try { +      DOMXPath xpath = new DOMXPath(exp); +      List nodes; + +      xpath.setNamespaceContext(nsContext); +      nodes = xpath.selectNodes(contextNode); +      return new NodeIteratorAdapter(nodes.listIterator()); +    } catch (JaxenException e) { +      MessageProvider msg = MessageProvider.getInstance(); +      String message = msg.getMessage("xpath.00", new Object[] { exp }); +      throw new XPathException(message, e); +    } +  } + +  /** +   * Return a <code>NodeList</code> of all the nodes matching the XPath +   * expression. +   *  +   * All namespace URIs and prefixes declared in the <code>Constants</code> +   * interface are used for resolving namespaces. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param exp The XPath expression to evaluate. +   * @return A <code>NodeList</code> containing the matching nodes. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  public static NodeList selectNodeList(Node contextNode, String exp) +    throws XPathException { + +    return selectNodeList(contextNode, NS_CONTEXT, exp); +  } + +  /** +   * Return a <code>NodeList</code> of all the nodes matching the XPath +   * expression. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param namespaceElement An element from which to build the +   * namespace mapping for evaluating the XPath expression +   * @param exp The XPath expression to evaluate. +   * @return A <code>NodeList</code> containing the matching nodes. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  public static NodeList selectNodeList( +    Node contextNode, +    Element namespaceElement, +    String exp) +    throws XPathException { + +    try { +      SimpleNamespaceContext ctx = new SimpleNamespaceContext(); + +      ctx.addElementNamespaces(documentNavigator, namespaceElement); +      return selectNodeList(contextNode, ctx, exp); +    } catch (JaxenException e) { +      MessageProvider msg = MessageProvider.getInstance(); +      String message = msg.getMessage("xpath.00", new Object[] { exp }); +      throw new XPathException(message, e); +    } +  } + +  /** +   * Return a <code>NodeList</code> of all the nodes matching the XPath +   * expression. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param namespaceMapping A namespace prefix to namespace URI mapping +   * (<code>String</code> to <code>String</code>) for evaluating the XPath  +   * expression. +   * @param exp The XPath expression to evaluate. +   * @return A <code>NodeList</code> containing the matching nodes. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  public static NodeList selectNodeList( +    Node contextNode, +    Map namespaceMapping, +    String exp) +    throws XPathException { + +    SimpleNamespaceContext ctx = new SimpleNamespaceContext(namespaceMapping); + +    return selectNodeList(contextNode, ctx, exp); +  } + +  /** +   * Return a <code>NodeList</code> of all the nodes matching the XPath +   * expression. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param nsContext The <code>NamespaceContext</code> for resolving namespace +   * prefixes to namespace URIs for evaluating the XPath expression. +   * @param exp The XPath expression to evaluate. +   * @return A <code>NodeList</code> containing the matching nodes. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  private static NodeList selectNodeList( +    Node contextNode, +    NamespaceContext nsContext, +    String exp) +    throws XPathException { + +    try { +      DOMXPath xpath = new DOMXPath(exp); +      List nodes; + +      xpath.setNamespaceContext(nsContext); +      nodes = xpath.selectNodes(contextNode); +      return new NodeListAdapter(nodes); +    } catch (JaxenException e) { +      MessageProvider msg = MessageProvider.getInstance(); +      String message = msg.getMessage("xpath.00", new Object[] { exp }); +      throw new XPathException(message, e); +    } +  } + +  /** +   * Select the first node matching an XPath expression. +   *  +   * All namespace URIs and prefixes declared in the <code>Constants</code> +   * interface are used for resolving namespaces. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param exp The XPath expression to evaluate. +   * @return Node The first node matching the XPath expression, or +   * <code>null</code>, if no node matched. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  public static Node selectSingleNode(Node contextNode, String exp) +    throws XPathException { + +    return selectSingleNode(contextNode, NS_CONTEXT, exp); +  } + +  /** +   * Select the first node matching an XPath expression. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param namespaceElement An element from which to build the +   * namespace mapping for evaluating the XPath expression +   * @param exp The XPath expression to evaluate. +   * @return Node The first node matching the XPath expression, or +   * <code>null</code>, if no node matched. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  public static Node selectSingleNode( +    Node contextNode, +    Element namespaceElement, +    String exp) +    throws XPathException { + +    try { +      SimpleNamespaceContext ctx = new SimpleNamespaceContext(); +      ctx.addElementNamespaces(documentNavigator, namespaceElement); + +      return selectSingleNode(contextNode, ctx, exp); +    } catch (JaxenException e) { +      MessageProvider msg = MessageProvider.getInstance(); +      String message = msg.getMessage("xpath.00", new Object[] { exp }); +      throw new XPathException(message, e); +    } +  } + +  /** +   * Select the first node matching an XPath expression. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param namespaceMapping A namespace prefix to namespace URI mapping +   * (<code>String</code> to <code>String</code>) for evaluating the XPath  +   * expression. +   * @param exp The XPath expression to evaluate. +   * @return Node The first node matching the XPath expression, or +   * <code>null</code>, if no node matched. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  public static Node selectSingleNode( +    Node contextNode, +    Map namespaceMapping, +    String exp) +    throws XPathException { + +    SimpleNamespaceContext ctx = new SimpleNamespaceContext(namespaceMapping); + +    return selectSingleNode(contextNode, ctx, exp); +  } + +  /** +   * Select the first node matching an XPath expression. +   *  +   * @param contextNode The root node from which to evaluate the XPath +   * expression. +   * @param nsContext The <code>NamespaceContext</code> for resolving namespace +   * prefixes to namespace URIs for evaluating the XPath expression. +   * @param exp The XPath expression to evaluate. +   * @return Node The first node matching the XPath expression, or +   * <code>null</code>, if no node matched. +   * @throws XPathException An error occurred evaluating the XPath expression. +   */ +  private static Node selectSingleNode( +    Node contextNode, +    NamespaceContext nsContext, +    String exp) +    throws XPathException { + +    try { +      DOMXPath xpath = new DOMXPath(exp); +      xpath.setNamespaceContext(nsContext); +      return (Node) xpath.selectSingleNode(contextNode); +    } catch (JaxenException e) { +      MessageProvider msg = MessageProvider.getInstance(); +      String message = msg.getMessage("xpath.00", new Object[] { exp }); +      throw new XPathException(message, e); +    } +  } + +  /** +   * Return the value of a DOM element whose location is given by an XPath +   * expression. +   *  +   * @param root The root element from which to evaluate the XPath. +   * @param xpath The XPath expression pointing to the element whose value +   * to return. +   * @param def The default value to return, if no element can be found using +   * the given <code>xpath</code>. +   * @return The element value, if it can be located using the +   * <code>xpath</code>. Otherwise, <code>def</code> is returned. +   */ +  public static String getElementValue( +    Element root, +    String xpath, +    String def) { + +    Element elem = (Element) XPathUtils.selectSingleNode(root, xpath); +    return elem != null ? DOMUtils.getText(elem) : def; +  } + +  /** +   * Return the value of a DOM attribute whose location is given by an XPath +   * expression. +   *  +   * @param root The root element from which to evaluate the XPath. +   * @param xpath The XPath expression pointing to the attribute whose value to +   * return. +   * @param def The default value to return, if no attribute can be found using +   * the given <code>xpath</code>. +   * @return The element value, if it can be located using the +   * <code>xpath</code>. Otherwise, <code>def</code> is returned. +   */ +  public static String getAttributeValue( +    Element root, +    String xpath, +    String def) { + +    Attr attr = (Attr) XPathUtils.selectSingleNode(root, xpath); +    return attr != null ? attr.getValue() : def; +  } + +} | 
