psmith      2003/06/24 01:08:44

  Added:       src/java/org/apache/log4j/xml UtilLoggingEntityResolver.java
                        logger.dtd UtilLoggingXMLDecoder.java
                        XMLDecoder.java
  Log:
  Additional files from the sandbox.
  
  These XML classes are for decoding XML into events, or to deal
  with java.util logging events.
  
  Revision  Changes    Path
  1.1                  
jakarta-log4j/src/java/org/apache/log4j/xml/UtilLoggingEntityResolver.java
  
  Index: UtilLoggingEntityResolver.java
  ===================================================================
  /*
   * ============================================================================
   *                   The Apache Software License, Version 1.1
   * ============================================================================
   *
   *    Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modifica-
   * tion, are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of  source code must  retain the above copyright  notice,
   *    this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright notice,
   *    this list of conditions and the following disclaimer in the documentation
   *    and/or other materials provided with the distribution.
   *
   * 3. The end-user documentation included with the redistribution, if any, must
   *    include  the following  acknowledgment:  "This product includes  software
   *    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
   *    Alternately, this  acknowledgment may  appear in the software itself,  if
   *    and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "log4j" and  "Apache Software Foundation"  must not be used to
   *    endorse  or promote  products derived  from this  software without  prior
   *    written permission. For written permission, please contact
   *    [EMAIL PROTECTED]
   *
   * 5. Products  derived from this software may not  be called "Apache", nor may
   *    "Apache" appear  in their name,  without prior written permission  of the
   *    Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   * FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   * APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   * DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   * ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   * (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * This software  consists of voluntary contributions made  by many individuals
   * on  behalf of the Apache Software  Foundation.  For more  information on the
   * Apache Software Foundation, please see <http://www.apache.org/>.
   *
   */
  
  package org.apache.log4j.xml;
  
  import java.io.InputStream;
  
  import org.xml.sax.EntityResolver;
  import org.xml.sax.InputSource;
  
  import org.apache.log4j.helpers.LogLog;
  
  /**
   * An [EMAIL PROTECTED] EntityResolver} specifically designed to return
   * <code>java 1.4's logging dtd, logger.dtd</code> which is embedded within the 
log4j jar
   * file.  Based on EntityResolver. 
   *
   * @author Paul Austin
   * @author Scott Deboy <[EMAIL PROTECTED]>
   * 
   */
  public class UtilLoggingEntityResolver implements EntityResolver {
  
    public InputSource resolveEntity (String publicId, String systemId) {
      System.err.println("publicID: ["+publicId+"]");
      System.err.println("systemId: ["+systemId+"]");
      if (systemId.endsWith("logger.dtd")) {
        Class clazz = getClass();
        InputStream in = clazz.getResourceAsStream("/org/apache/log4j/xml/logger.dtd");
        if (in == null) {
        LogLog.error("Could not find [logger.dtd]. Used [" + clazz.getClassLoader() 
                     + "] class loader in the search.");
        return null;
        } else {
        return new InputSource(in);
        }
      } else {
        return null;
      }
    }
  }
  
  
  
  1.1                  jakarta-log4j/src/java/org/apache/log4j/xml/logger.dtd
  
  Index: logger.dtd
  ===================================================================
  <!-- DTD used by the java.util.logging.XMLFormatter -->
  <!-- This provides an XML formatted log message. -->
  
  <!-- The document type is "log" which consists of a sequence
  of record elements -->
  <!ELEMENT log (record*)>
  
  <!-- Each logging call is described by a record element. -->
  <!ELEMENT record (date, millis, sequence, logger?, level,
  class?, method?, thread?, message, key?, catalog?, param*, exception?)>
  
  <!-- Date and time when LogRecord was created in ISO 8601 format -->
  <!ELEMENT date (#PCDATA)>
  
  <!-- Time when LogRecord was created in milliseconds since
  midnight January 1st, 1970, UTC. -->
  <!ELEMENT millis (#PCDATA)>
  
  <!-- Unique sequence number within source VM. -->
  <!ELEMENT sequence (#PCDATA)>
  
  <!-- Name of source Logger object. -->
  <!ELEMENT logger (#PCDATA)>
  
  <!-- Logging level, may be either one of the constant
  names from java.util.logging.Constants (such as "SEVERE"
  or "WARNING") or an integer value such as "20". -->
  <!ELEMENT level (#PCDATA)>
  
  <!-- Fully qualified name of class that issued
  logging call, e.g. "javax.marsupial.Wombat". -->
  <!ELEMENT class (#PCDATA)>
  
  <!-- Name of method that issued logging call.
  It may be either an unqualified method name such as
  "fred" or it may include argument type information
  in parenthesis, for example "fred(int,String)". -->
  <!ELEMENT method (#PCDATA)>
  
  <!-- Integer thread ID. -->
  <!ELEMENT thread (#PCDATA)>
  
  <!-- The message element contains the text string of a log message. -->
  <!ELEMENT message (#PCDATA)>
  
  <!-- If the message string was localized, the key element provides
  the original localization message key. -->
  <!ELEMENT key (#PCDATA)>
  
  <!-- If the message string was localized, the catalog element provides
  the logger's localization resource bundle name. -->
  <!ELEMENT catalog (#PCDATA)>
  
  <!-- If the message string was localized, each of the param elements
  provides the String value (obtained using Object.toString())
  of the corresponding LogRecord parameter. -->
  <!ELEMENT param (#PCDATA)>
  
  <!-- An exception consists of an optional message string followed
  by a series of StackFrames. Exception elements are used
  for Java exceptions and other java Throwables. -->
  <!ELEMENT exception (message?, frame+)>
  
  <!-- A frame describes one line in a Throwable backtrace. -->
  <!ELEMENT frame (class, method, line?)>
  
  <!-- an integer line number within a class's source file. -->
  <!ELEMENT line (#PCDATA)>
  
  
  1.1                  
jakarta-log4j/src/java/org/apache/log4j/xml/UtilLoggingXMLDecoder.java
  
  Index: UtilLoggingXMLDecoder.java
  ===================================================================
  /*
   * ============================================================================
   *                   The Apache Software License, Version 1.1
   * ============================================================================
   *
   *    Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modifica-
   * tion, are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of  source code must  retain the above copyright  notice,
   *    this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright notice,
   *    this list of conditions and the following disclaimer in the documentation
   *    and/or other materials provided with the distribution.
   *
   * 3. The end-user documentation included with the redistribution, if any, must
   *    include  the following  acknowledgment:  "This product includes  software
   *    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
   *    Alternately, this  acknowledgment may  appear in the software itself,  if
   *    and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "log4j" and  "Apache Software Foundation"  must not be used to
   *    endorse  or promote  products derived  from this  software without  prior
   *    written permission. For written permission, please contact
   *    [EMAIL PROTECTED]
   *
   * 5. Products  derived from this software may not  be called "Apache", nor may
   *    "Apache" appear  in their name,  without prior written permission  of the
   *    Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   * FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   * APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   * DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   * ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   * (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * This software  consists of voluntary contributions made  by many individuals
   * on  behalf of the Apache Software  Foundation.  For more  information on the
   * Apache Software Foundation, please see <http://www.apache.org/>.
   *
   */
  
  package org.apache.log4j.xml;
  
  import org.apache.log4j.Decoder;
  import org.apache.log4j.Level;
  import org.apache.log4j.Logger;
  import org.apache.log4j.UtilLoggingLevel;
  import org.apache.log4j.spi.LocationInfo;
  import org.apache.log4j.spi.LoggingEvent;
  
  import org.w3c.dom.Document;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  import org.xml.sax.InputSource;
  
  import java.io.File;
  import java.io.FileReader;
  import java.io.IOException;
  import java.io.LineNumberReader;
  import java.io.StringReader;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Hashtable;
  import java.util.Map;
  import java.util.Vector;
  
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  
  
  /**
   * Decodes JDK 1.4's java.util.logging package events delivered via XML (using the 
logger.dtd).
   *
   * @author Scott Deboy <[EMAIL PROTECTED]>
   * @author Paul Smith <[EMAIL PROTECTED]>
   *
   */
  public class UtilLoggingXMLDecoder implements Decoder {
    //NOTE: xml section is only handed on first delivery of events
    //on this first delivery of events, there is no end tag for the log element
    private static final String BEGIN_PART =
      "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE log SYSTEM 
\"logger.dtd\"><log>";
    private static final String END_PART= "</log>";
    private DocumentBuilderFactory dbf;
    private DocumentBuilder docBuilder;
    private Map additionalProperties = new HashMap();
    private String partialEvent;
    private static final String RECORD_END="</record>";
  
    public UtilLoggingXMLDecoder() {
      dbf = DocumentBuilderFactory.newInstance();
      dbf.setValidating(false);
  
      try {
        docBuilder = dbf.newDocumentBuilder();
        docBuilder.setErrorHandler(new SAXErrorHandler());
        docBuilder.setEntityResolver(new UtilLoggingEntityResolver());
      } catch (ParserConfigurationException pce) {
        System.err.println("Unable to get document builder");
      }
  
      additionalProperties.put("log4j.eventtype", "util-logging");
    }
  
    /**
     * Sets an additionalProperty map, where each Key/Value pair is
     * automatically added to each LoggingEvent as it is decoded.
     *
     * This is useful, say, to include the source file name of the Logging events
     * @param additionalProperties
     */
    public void setAdditionalProperties(Map additionalProperties) {
      this.additionalProperties.putAll(additionalProperties);
    }
  
    /**
     * Converts the LoggingEvent data in XML string format into an actual
     * XML Document class instance.
     * @param data
     * @return
     */
    private Document parse(String data) {
      if (docBuilder == null) {
        return null;
      }
  
      Document document = null;
  
      try {
        // we change the system ID to a valid URI so that Crimson won't
        // complain. Indeed, "log4j.dtd" alone is not a valid URI which
        // causes Crimson to barf. The Log4jEntityResolver only cares
        // about the "log4j.dtd" ending.
  
        /**
         * resetting the length of the StringBuffer is dangerous, particularly
         * on some JDK 1.4 impls, there's a known Bug that causes a memory leak
         */
        if (data != null) {
          data = data.trim();
        }
  
        StringBuffer buf = new StringBuffer(1024);
  
        if (!data.startsWith("<?xml")) {
          buf.append(BEGIN_PART);
        }
  
        buf.append(data);
  
        if (!data.endsWith(END_PART)) {
          buf.append(END_PART);
        }
  
        InputSource inputSource =
          new InputSource(new StringReader(buf.toString()));
        inputSource.setSystemId("dummy://logger.dtd");
        document = docBuilder.parse(inputSource);
      } catch (Exception e) {
        e.printStackTrace();
      }
  
      return document;
    }
  
    /**
     * Reads the contents of the file into a String
     * @param file the file to load
     * @return The contents of the file as a String
     * @throws IOException if an error occurred during the loading process
     */
    private String loadFileSource(File file) throws IOException {
      LineNumberReader reader = null;
      StringBuffer buf = new StringBuffer(1024);
  
      try {
        reader = new LineNumberReader(new FileReader(file));
  
        String line = null;
  
        while ((line = reader.readLine()) != null) {
          buf.append(line);
        }
      } catch (IOException e) {
        throw e;
      } finally {
        try {
          if (reader != null) {
            reader.close();
          }
        } catch (Exception e) {
        }
      }
  
      return buf.toString();
    }
  
    /**
     * Decodes a File into a Vector of LoggingEvents
     * @param file the file to decode events from
     * @return Vector of LoggingEvents
     * @throws IOException
     */
    public Vector decode(File file) throws IOException {
      String fileContents = loadFileSource(file);
      Document doc = parse(fileContents);
      
      if (doc == null) {
        return null;
      }
  
      return decodeEvents(doc);
    }
  
    /**
     * Decodes a String with possibly multiple events into a Vector of LoggingEvents
     * @param String to decode events from
     * @return Vector of LoggingEvents
     */
    public Vector decodeEvents(String document) {
        
      if (document != null) {
        document = document.trim();
  
        if (document.equals("")) {
          return null;
        } else {
                String newDoc=null;
                String newPartialEvent=null;
                //separate the string into the last portion ending with </record> 
(which will
                //be processed) and the partial event which will be combined and 
processed in the next section
  
                if (document.lastIndexOf(RECORD_END) + RECORD_END.length() < 
document.length()) {
                newDoc = document.substring(0, document.lastIndexOf(RECORD_END) + 
RECORD_END.length());
                        newPartialEvent = 
document.substring(document.lastIndexOf(RECORD_END) + RECORD_END.length());
        } else {
                        newDoc = document;
                }
                if (partialEvent != null) {
                        newDoc=partialEvent + newDoc;
                }                       
                partialEvent=newPartialEvent;
                
          Document doc = parse(newDoc);
          if (doc == null) {
            return null;
          }
          return decodeEvents(doc);
        }
      }
      return null;
    }
  
    /**
     * Converts the string data into an XML Document, and then soaks out the
     * relevant bits to form a new LoggingEvent instance which can be used
     * by any Log4j element locally.
     * @param data
     * @return a single LoggingEvent
     */
    public LoggingEvent decode(String data) {
      Document document = parse(data);
  
      if (document == null) {
        return null;
      }
  
      Vector events = decodeEvents(document);
  
      if (events.size() > 0) {
        return (LoggingEvent) events.firstElement();
      }
  
      return null;
    }
  
    /**
     * Given a Document, converts the XML into a Vector of LoggingEvents
     * @param document
     * @return
     */
    private Vector decodeEvents(Document document) {
      Vector events = new Vector();
  
      NodeList eventList = document.getElementsByTagName("record");
  
      for (int eventIndex = 0; eventIndex < eventList.getLength();
          eventIndex++) {
        Node eventNode = eventList.item(eventIndex);
  
        Logger logger = null;
        long timeStamp = 0L;
        Level level = null;
        String threadName = null;
        Object message = null;
        String ndc = null;
        Hashtable mdc = null;
        String[] exception = null;
        String className = null;
        String methodName = null;
        String fileName = null;
        String lineNumber = null;
        Hashtable properties = new Hashtable();
  
        //format of date: 2003-05-04T11:04:52
        //ignore date or set as a property? using millis in constructor instead
        NodeList list = eventNode.getChildNodes();
        int listLength = list.getLength();
  
        if (listLength == 0) {
          continue;
        }
  
        for (int y = 0; y < listLength; y++) {
          String tagName = list.item(y).getNodeName();
  
          if (tagName.equalsIgnoreCase("logger")) {
            logger = Logger.getLogger(getCData(list.item(y)));
          }
  
          if (tagName.equalsIgnoreCase("millis")) {
            timeStamp = Long.parseLong(getCData(list.item(y)));
          }
  
          if (tagName.equalsIgnoreCase("level")) {
            level = UtilLoggingLevel.toLevel(getCData(list.item(y)));
          }
  
          if (tagName.equalsIgnoreCase("thread")) {
            threadName = getCData(list.item(y));
          }
  
          if (tagName.equalsIgnoreCase("sequence")) {
            properties.put("log4jid", getCData(list.item(y)));
          }
  
          if (tagName.equalsIgnoreCase("message")) {
            message = getCData(list.item(y));
          }
  
          if (tagName.equalsIgnoreCase("class")) {
            className = getCData(list.item(y));
          }
  
          if (tagName.equalsIgnoreCase("method")) {
            methodName = getCData(list.item(y));
          }
  
          if (tagName.equalsIgnoreCase("exception")) {
            ArrayList exceptionList = new ArrayList();
            NodeList exList = list.item(y).getChildNodes();
            int exlistLength = exList.getLength();
  
            for (int i2 = 0; i2 < exlistLength; i2++) {
              Node exNode = exList.item(i2);
              String exName = exList.item(i2).getNodeName();
  
              if (exName.equalsIgnoreCase("message")) {
                exceptionList.add(getCData(exList.item(i2)));
              }
  
              if (exName.equalsIgnoreCase("frame")) {
                NodeList exList2 = exNode.getChildNodes();
                int exlist2Length = exList2.getLength();
  
                for (int i3 = 0; i3 < exlist2Length; i3++) {
                  exceptionList.add(getCData(exList2.item(i3)) + "\n");
                }
              }
            }
  
            exception =
              (String[]) exceptionList.toArray(new String[exceptionList.size()]);
          }
        }
  
        /**
         * We add all the additional properties to the properties
         * hashtable
         */
        if (additionalProperties.size() > 0) {
          if (properties == null) {
            properties = new Hashtable(additionalProperties);
          } else {
            properties.putAll(additionalProperties);
          }
        }
  
        events.add(
          new LoggingEvent(
            logger.getName(), logger, timeStamp, level, threadName, message, ndc,
            mdc, exception,
            new LocationInfo(fileName, className, methodName, lineNumber),
            properties));
      }
  
      return events;
    }
  
    private String getCData(Node n) {
      StringBuffer buf = new StringBuffer();
      NodeList nl = n.getChildNodes();
  
      for (int x = 0; x < nl.getLength(); x++) {
        Node innerNode = nl.item(x);
  
        if (
          (innerNode.getNodeType() == Node.TEXT_NODE)
            || (innerNode.getNodeType() == Node.CDATA_SECTION_NODE)) {
          buf.append(innerNode.getNodeValue());
        }
      }
  
      return buf.toString();
    }
  }
  
  
  
  1.1                  jakarta-log4j/src/java/org/apache/log4j/xml/XMLDecoder.java
  
  Index: XMLDecoder.java
  ===================================================================
  /*
   * ============================================================================
   *                   The Apache Software License, Version 1.1
   * ============================================================================
   *
   *    Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modifica-
   * tion, are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of  source code must  retain the above copyright  notice,
   *    this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright notice,
   *    this list of conditions and the following disclaimer in the documentation
   *    and/or other materials provided with the distribution.
   *
   * 3. The end-user documentation included with the redistribution, if any, must
   *    include  the following  acknowledgment:  "This product includes  software
   *    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
   *    Alternately, this  acknowledgment may  appear in the software itself,  if
   *    and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "log4j" and  "Apache Software Foundation"  must not be used to
   *    endorse  or promote  products derived  from this  software without  prior
   *    written permission. For written permission, please contact
   *    [EMAIL PROTECTED]
   *
   * 5. Products  derived from this software may not  be called "Apache", nor may
   *    "Apache" appear  in their name,  without prior written permission  of the
   *    Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
   * FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
   * APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
   * DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
   * ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
   * (INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * This software  consists of voluntary contributions made  by many individuals
   * on  behalf of the Apache Software  Foundation.  For more  information on the
   * Apache Software Foundation, please see <http://www.apache.org/>.
   *
   */
  
  package org.apache.log4j.xml;
  
  import java.io.File;
  import java.io.FileReader;
  import java.io.IOException;
  import java.io.LineNumberReader;
  import java.io.StringReader;
  import java.util.Collections;
  import java.util.Hashtable;
  import java.util.Map;
  import java.util.Vector;
  
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  
  import org.apache.log4j.Decoder;
  import org.apache.log4j.Level;
  import org.apache.log4j.Logger;
  import org.apache.log4j.UtilLoggingLevel;
  import org.apache.log4j.spi.LocationInfo;
  import org.apache.log4j.spi.LoggingEvent;
  import org.w3c.dom.Document;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  import org.xml.sax.InputSource;
  
  
  /**
   * Decodes Logging Events in XML formated into elements that are used by
   * Chainsaw.
   *
   * NOTE:  Only a single LoggingEvent is returned from the decode method
   * even though the DTD supports multiple events nested in an eventSet.
   *
   * @author Scott Deboy <[EMAIL PROTECTED]>
   * @author Paul Smith <[EMAIL PROTECTED]>
   *
   */
  public class XMLDecoder implements Decoder {
    private static final String BEGINPART =
      "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE log4j:eventSet SYSTEM 
\"log4j.dtd\"><log4j:eventSet version=\"1.2\" 
xmlns:log4j=\"http://jakarta.apache.org/log4j/\";>";
    private static final String ENDPART = "</log4j:eventSet>";
    private static final String RECORD_END = "</log4j:event>";
    private StringBuffer buf = new StringBuffer();
    private DocumentBuilderFactory dbf;
    private DocumentBuilder docBuilder;
    private Map additionalProperties = Collections.EMPTY_MAP;
    private String partialEvent;
    
    public XMLDecoder() {
      dbf = DocumentBuilderFactory.newInstance();
      dbf.setValidating(false);
  
      try {
        docBuilder = dbf.newDocumentBuilder();
        docBuilder.setErrorHandler(new SAXErrorHandler());
        docBuilder.setEntityResolver(new Log4jEntityResolver());
      } catch (ParserConfigurationException pce) {
        System.err.println("Unable to get document builder");
      }
    }
  
    /**
     * Sets an additionalProperty map, where each Key/Value pair is
     * automatically added to each LoggingEvent as it is decoded.
     *
     * This is useful, say, to include the source file name of the Logging events
     * @param additionalProperties
     */
    public void setAdditionalProperties(Map additionalProperties) {
      this.additionalProperties = additionalProperties;
    }
  
    /**
     * Converts the LoggingEvent data in XML string format into an actual
     * XML Document class instance.
     * @param data
     * @return
     */
    private Document parse(String data) {
      if (docBuilder == null) {
        return null;
      }
  
      Document document = null;
  
      try {
        // we change the system ID to a valid URI so that Crimson won't
        // complain. Indeed, "log4j.dtd" alone is not a valid URI which
        // causes Crimson to barf. The Log4jEntityResolver only cares
        // about the "log4j.dtd" ending.
        //      buf.setLength(0);
  
        /**
         * resetting the length of the StringBuffer is dangerous, particularly
         * on some JDK 1.4 impls, there's a known Bug that causes a memory leak
         */
        buf = new StringBuffer(1024);
  
        buf.append(BEGINPART);
        buf.append(data);
        buf.append(ENDPART);
  
        InputSource inputSource =
          new InputSource(new StringReader(buf.toString()));
        inputSource.setSystemId("dummy://log4j.dtd");
        document = docBuilder.parse(inputSource);
      } catch (Exception e) {
        e.printStackTrace();
      }
  
      return document;
    }
  
    /**
     * Reads the contents of the file into a String
     * @param file the file to load
     * @return The contents of the file as a String
     * @throws IOException if an error occurred during the loading process
     */
    private String loadFileSource(File file) throws IOException {
      LineNumberReader reader = null;
      StringBuffer buf = new StringBuffer(1024);
  
      try {
        reader = new LineNumberReader(new FileReader(file));
  
        String line = null;
  
        while ((line = reader.readLine()) != null) {
          buf.append(line);
        }
      } catch (IOException e) {
        throw e;
      } finally {
        try {
          if (reader != null) {
            reader.close();
          }
        } catch (Exception e) {
        }
      }
  
      return buf.toString();
    }
  
    /**
     * Decodes a File into a Vector of LoggingEvents
     * @param file the file to decode events from
     * @return Vector of LoggingEvents
     * @throws IOException
     */
    public Vector decode(File file) throws IOException {
      String fileContents = loadFileSource(file);
      Document doc = parse(fileContents);
  
      if (doc == null) {
        return null;
      }
      return decodeEvents(fileContents);
    }
  
    public Vector decodeEvents(String document) {
      if (document != null) {
        document = document.trim();
  
        if (document.equals("")) {
          return null;
        } else {
                String newDoc=null;
                String newPartialEvent=null;
                //separate the string into the last portion ending with </log4j:event> 
(which will
                //be processed) and the partial event which will be combined and 
processed in the next section
                if (document.lastIndexOf(RECORD_END) + RECORD_END.length() < 
document.length()) {
                newDoc = document.substring(0, document.lastIndexOf(RECORD_END) + 
RECORD_END.length());
                        newPartialEvent = 
document.substring(document.lastIndexOf(RECORD_END) + RECORD_END.length());
        } else {
                        newDoc = document;
                }
                if (partialEvent != null) {
                        newDoc=partialEvent + newDoc;
                }                       
                partialEvent=newPartialEvent;
                
          Document doc = parse(newDoc);
          if (doc == null) {
            return null;
          }
          return decodeEvents(doc);
        }
      }
      return null;
    }
  
    /**
     * Converts the string data into an XML Document, and then soaks out the
     * relevant bits to form a new LoggingEvent instance which can be used
     * by any Log4j element locally.
     * @param data
     * @return a single LoggingEvent
     */
    public LoggingEvent decode(String data) {
      Document document = parse(data);
  
      if (document == null) {
        return null;
      }
  
      Vector events = decodeEvents(document);
  
      if (events.size() > 0) {
        return (LoggingEvent) events.firstElement();
      }
  
      return null;
    }
  
    /**
     * Given a Document, converts the XML into a Vector of LoggingEvents
     * @param document
     * @return
     */
    private Vector decodeEvents(Document document) {
      Vector events = new Vector();
  
      Logger logger = null;
      long timeStamp = 0L;
      String level = null;
      String threadName = null;
      Object message = null;
      String ndc = null;
      Hashtable mdc = null;
      String[] exception = null;
      String className = null;
      String methodName = null;
      String fileName = null;
      String lineNumber = null;
      Hashtable properties = null;
  
      NodeList nl = document.getElementsByTagName("log4j:eventSet");
      Node eventSet = nl.item(0);
  
      NodeList eventList = eventSet.getChildNodes();
  
      for (int eventIndex = 0; eventIndex < eventList.getLength();
          eventIndex++) {
        Node eventNode = eventList.item(eventIndex);
  
        logger =
          Logger.getLogger(
            eventNode.getAttributes().getNamedItem("logger").getNodeValue());
        timeStamp =
          Long.parseLong(
            eventNode.getAttributes().getNamedItem("timestamp").getNodeValue());
        level =eventNode.getAttributes().getNamedItem("level").getNodeValue();
        threadName =
          eventNode.getAttributes().getNamedItem("thread").getNodeValue();
  
        NodeList list = eventNode.getChildNodes();
        int listLength = list.getLength();
  
        for (int y = 0; y < listLength; y++) {
          String tagName = list.item(y).getNodeName();
  
          if (tagName.equalsIgnoreCase("log4j:message")) {
            message = getCData(list.item(y));
          }
  
          if (tagName.equalsIgnoreCase("log4j:NDC")) {
            ndc = getCData(list.item(y));
          }
  
          if (tagName.equalsIgnoreCase("log4j:MDC")) {
            mdc = new Hashtable();
  
            NodeList propertyList = list.item(y).getChildNodes();
            int propertyLength = propertyList.getLength();
  
            for (int i = 0; i < propertyLength; i++) {
              String propertyTag = propertyList.item(i).getNodeName();
  
              if (propertyTag.equalsIgnoreCase("log4j:data")) {
                Node property = propertyList.item(i);
                String name =
                  property.getAttributes().getNamedItem("name").getNodeValue();
                String value =
                  property.getAttributes().getNamedItem("value").getNodeValue();
                mdc.put(name, value);
              }
            }
          }
  
          if (tagName.equalsIgnoreCase("log4j:throwable")) {
            exception = new String[] { getCData(list.item(y)) };
          }
  
          if (tagName.equalsIgnoreCase("log4j:locationinfo")) {
            className =
              list.item(y).getAttributes().getNamedItem("class").getNodeValue();
            methodName =
              list.item(y).getAttributes().getNamedItem("method").getNodeValue();
            fileName =
              list.item(y).getAttributes().getNamedItem("file").getNodeValue();
            lineNumber =
              list.item(y).getAttributes().getNamedItem("line").getNodeValue();
          }
  
          if (tagName.equalsIgnoreCase("log4j:properties")) {
            properties = new Hashtable();
  
            NodeList propertyList = list.item(y).getChildNodes();
            int propertyLength = propertyList.getLength();
  
            for (int i = 0; i < propertyLength; i++) {
              String propertyTag = propertyList.item(i).getNodeName();
  
              if (propertyTag.equalsIgnoreCase("log4j:data")) {
                Node property = propertyList.item(i);
                String name =
                  property.getAttributes().getNamedItem("name").getNodeValue();
                String value =
                  property.getAttributes().getNamedItem("value").getNodeValue();
                properties.put(name, value);
              }
            }
          }
  
          /**
           * We add all the additional properties to the properties
           * hashtable
           */
          if (additionalProperties.size() > 0) {
            if (properties == null) {
              properties = new Hashtable(additionalProperties);
            } else {
              properties.putAll(additionalProperties);
            }
          }
        }
        Level levelImpl = null;
        if ((properties != null)  && (properties.get("log4j.eventtype") != null)) {
                String s = (String)properties.get("log4j.eventtype");
                if (s.equalsIgnoreCase("util-logging")) {
                        levelImpl=UtilLoggingLevel.toLevel(level);
                }
        }
        
        if (levelImpl==null) {
                levelImpl=Level.toLevel(level);
        }
                        
        events.add(
          new LoggingEvent(
            logger.getName(), logger, timeStamp, levelImpl, threadName, message, ndc,
            mdc, exception,
            new LocationInfo(fileName, className, methodName, lineNumber),
            properties));
      }
  
      return events;
    }
  
    private String getCData(Node n) {
      StringBuffer buf = new StringBuffer();
      NodeList nl = n.getChildNodes();
  
      for (int x = 0; x < nl.getLength(); x++) {
        Node innerNode = nl.item(x);
  
        if (
          (innerNode.getNodeType() == Node.TEXT_NODE)
            || (innerNode.getNodeType() == Node.CDATA_SECTION_NODE)) {
          buf.append(innerNode.getNodeValue());
        }
      }
  
      return buf.toString();
    }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to