You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by ps...@apache.org on 2003/04/28 08:58:32 UTC

cvs commit: jakarta-log4j-sandbox/src/java/org/apache/log4j/chainsaw ChainsawAppender.java ChainsawViewer.java EventDetails.java EventDetailSink.java MyTableModel.java

psmith      2003/04/27 23:58:32

  Added:       src/java/org/apache/log4j/chainsaw ChainsawAppender.java
                        ChainsawViewer.java EventDetails.java
                        EventDetailSink.java MyTableModel.java
  Log:
  These 'original' Chainsaw files are also needed to compile correctly.
  
  Revision  Changes    Path
  1.1                  jakarta-log4j-sandbox/src/java/org/apache/log4j/chainsaw/ChainsawAppender.java
  
  Index: ChainsawAppender.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
   *    apache@apache.org.
   *
   * 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.chainsaw;
  
  import java.util.ArrayList;
  import java.util.Iterator;
  
  import javax.swing.event.TableModelListener;
  import javax.swing.table.TableModel;
  
  import org.apache.log4j.Appender;
  import org.apache.log4j.AppenderSkeleton;
  import org.apache.log4j.spi.LoggingEvent;
  import org.apache.log4j.helpers.OptionConverter;
  
  
  /**
   * ChainsawAppender receives LoggingEvents from the local
   * Log4J environment, and appends them into a model that
   * can be used inside a Swing GUI
   * @author Paul Smith
   * @version 1.0
   */
  public class ChainsawAppender
      extends AppenderSkeleton
      implements EventDetailSink, TableModel {
  
    private final ArrayList appenders=new ArrayList();
    
    /**
     * Shared model used by the shared Appender
     */
    private static MyTableModel sSharedModel;
  
    /**
     * The model that is used by this Appender, we ensure
     * here that we only use a single Model as the current
     * release is effetively an in-JVM singleton
     */
    private final MyTableModel wrappedTableModel = getDefaultModel();
  
    /**
     * The in-JVM singleton instance of the ChainsawAppender.
     *
     * If somehow Log4j initialises more than one, then the first one to
     * initialise wins!
     */
    private static ChainsawAppender sSharedAppender = null;
  
    /**
     * The classname of the viewer to create to view the events.
     */
    private String viewerClassname;
  
    /**
     * Constructor, initialises the singleton instance of the appender
     */
    public ChainsawAppender() {
      synchronized (ChainsawAppender.class) {
        if (sSharedAppender == null) {
          sSharedAppender = this;
        }
      }
    }
  
    /**
     * Returns the singleton MyTableModel instance that has been configured.
     *
     * This will be eventually replaced to allow each ChainsawAppender
     * to have it's own model, but for now it's important that all log events
     * received inside Chainsaw go to a single model.
     * @return MyTableModel
     */
    private static synchronized MyTableModel getDefaultModel() {
      if (sSharedModel == null) {
        sSharedModel = new MyTableModel();
      }
      return sSharedModel;
    }
  
    /**
     * Return the singleton instance of the ChainsawAppender, it should only
     * be initialised once.
     * @return the One and only instance of the ChainsawAppender that is
     * allowed to be referenced by the GUI
     */
    static ChainsawAppender getInstance() {
      return sSharedAppender;
    }
  
    /**
     * Returns the internally wrapped Model
     *
     * NOTE: it is strongly recommended at this time not to rely on this method
     * until further refactoring is completed.
     * @return MyTableModel the MyTableModel that can be used by external
     * components
     */
    MyTableModel getWrappedModel() {
      return wrappedTableModel;
    }
  
    /**
     * This appender does not require layout and so return false
     * @return false and only false
     */
    public boolean requiresLayout() {
      return false;
    }
  
    public void addAppender(Appender appender) {
      appenders.add(appender);
    }
    
    /**
     * Implements the EventDetailSink interface by forwarding the EventDetails
     * object onto an internal Model
     * @param aDetails the EventDetails to add to the model
     */
    public void addEvent(EventDetails aDetails) {
      synchronized (wrappedTableModel) {
        wrappedTableModel.addEvent(aDetails);
      }
    }
  
    /**
     * Appends the event into the internal wrapped TableModel
     * @param aEvent the LoggingEvent to append
     */
    protected void append(LoggingEvent aEvent) {
      synchronized (wrappedTableModel) {
        wrappedTableModel.addEvent(new EventDetails(aEvent));
      }
          for (int i=0;i<appenders.size();i++) {
              Appender appender=(Appender)appenders.get(i);
              appender.doAppend(aEvent);
          }
    }
  
    /**
     * Instantiates and activates an instance of a ChainsawViewer
     * to view the contents of this appender.
     */
    public void activateOptions() {
      if (viewerClassname == null) {
        viewerClassname = "org.apache.log4j.chainsaw.DefaultViewer";
      }
        
      ChainsawViewer viewer = 
        (ChainsawViewer) OptionConverter.instantiateByClassName(viewerClassname, 
          ChainsawViewer.class, null);
          
      if (viewer != null) {
        viewer.activateViewer(this);
      }
    }
  
    /**
     * Close does nothing
     */
    public void close() {
      /** @todo  perhaps it should clear the internal TableModel */
    }
  
    /**
     * Sets the viewer class to use to view the events.  The class must
     * implement the ChainsawViewer interface.
     *
     * @param classname The class name of the viewer class.
     */
    public void setViewerClass(String classname) {
      viewerClassname = classname;
    }
  
    /**
     * Gets the viewer class to use to view the events.
     *
     * @return The class name of the viewer class.
     */
    public String getViewerClass() {
      return viewerClassname;
    }
  
    // ==========================================================================
    // All methods here are from TableModel, and simply forward on to the
    // internal wrappedTableModel instance
    // ==========================================================================
  
    /**
     * Implementation of TableModel interface
     * @return int rowCount
     */
    public int getRowCount() {
      return wrappedTableModel.getRowCount();
    }
  
    /**
     * Implementation of TableModel interface
     * @return int column Count
     */
    public int getColumnCount() {
      return wrappedTableModel.getColumnCount();
    }
  
    /**
     * Implementation of TableModel interface
     * @param aColumnIndex the Column index to query the name for
     * @return String column name
     */
    public String getColumnName(int aColumnIndex) {
      return wrappedTableModel.getColumnName(aColumnIndex);
    }
  
    /**
     * Implementation of TableModel interface
     * @param columnIndex column Index to query the Class of
     * @return Class class of Column
     */
    public Class getColumnClass(int columnIndex) {
      return wrappedTableModel.getColumnClass(columnIndex);
    }
  
    /**
     * Implementation of TableModel interface
     * @param rowIndex row Index to query
     * @param columnIndex column Index to query
     * @return boolean is Cell Editable?
     */
    public boolean isCellEditable(int rowIndex, int columnIndex) {
      return wrappedTableModel.isCellEditable(rowIndex, columnIndex);
    }
  
    /**
     * Implementation of TableModel interface
     * @param rowIndex the row index to retrieve value from
     * @param columnIndex to the column index to retrieve value from
     * @return Object value at a particular row/column point
     */
    public Object getValueAt(int rowIndex, int columnIndex) {
      return wrappedTableModel.getValueAt(rowIndex, columnIndex);
    }
  
    /**
     * Implementation of TableModel interface
     * @param aValue the value to set
     * @param rowIndex the row
     * @param columnIndex the column
     */
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
      wrappedTableModel.setValueAt(aValue, rowIndex, columnIndex);
    }
  
    /**
     * Implementation of TableModel interface
     * @param l a TableModelListener to add
     */
    public void addTableModelListener(TableModelListener l) {
      wrappedTableModel.addTableModelListener(l);
    }
  
    /**
     * Implementation of TableModel interface
     * @param l listener to remove from the currently registered listeners
     */
    public void removeTableModelListener(TableModelListener l) {
      wrappedTableModel.removeTableModelListener(l);
    }
  }
  
  
  
  1.1                  jakarta-log4j-sandbox/src/java/org/apache/log4j/chainsaw/ChainsawViewer.java
  
  Index: ChainsawViewer.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
   *    apache@apache.org.
   *
   * 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.chainsaw;
  
  
  /**
    Chainsaw compatible gui viewers must implement this interface
    in order to be opened and configured by the ChainsawAppender class.
  
    @author Mark Womack
  */
  public interface ChainsawViewer {
    /**
      Called when the viewer should activate.
  
      @param model The ChainsawAppender model instance the viewer should use. */
    void activateViewer(ChainsawAppender model);
  }
  
  
  
  1.1                  jakarta-log4j-sandbox/src/java/org/apache/log4j/chainsaw/EventDetails.java
  
  Index: EventDetails.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
   *    apache@apache.org.
   *
   * 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.chainsaw;
  
  import java.util.Iterator;
  import java.util.Set;
  import org.apache.log4j.Priority;
  import org.apache.log4j.spi.LoggingEvent;
  
  
  /**
   * Represents the details of a logging event. It is intended to overcome the
   * problem that a LoggingEvent cannot be constructed with purely fake data.
   *
   * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
   * @version 1.0
   */
  class EventDetails {
    /** the time of the event **/
    private final long mTimeStamp;
  
    /** the priority of the event **/
    private final Priority mPriority;
  
    /** the category of the event **/
    private final String mCategoryName;
  
    /** the NDC for the event **/
    private final String mNDC;
  
    /** the MDC for the event **/
    private final String mMDC;
  
    /** the Properties for the event **/
    private final String mProperties;
  
    /** the thread for the event **/
    private final String mThreadName;
  
    /** the msg for the event **/
    private final String mMessage;
  
    /** the throwable details the event **/
    private final String[] mThrowableStrRep;
  
    /** the location details for the event **/
    private final String mLocationDetails;
  
    /**
     * Creates a new <code>EventDetails</code> instance.
     * @param aTimeStamp a <code>long</code> value
     * @param aPriority a <code>Priority</code> value
     * @param aCategoryName a <code>String</code> value
     * @param aNDC a <code>String</code> value
     * @param aThreadName a <code>String</code> value
     * @param aMessage a <code>String</code> value
     * @param aThrowableStrRep a <code>String[]</code> value
     * @param aLocationDetails a <code>String</code> value
     */
    EventDetails(
      long aTimeStamp, Priority aPriority, String aCategoryName, String aNDC,
      String aMDC, String aProperties, String aThreadName, String aMessage, 
      String[] aThrowableStrRep, String aLocationDetails) {
      mTimeStamp = aTimeStamp;
      mPriority = aPriority;
      mCategoryName = aCategoryName;
      mNDC = aNDC;
      mMDC = aMDC;
      mProperties = aProperties;
      mThreadName = aThreadName;
      mMessage = aMessage;
      mThrowableStrRep = aThrowableStrRep;
      mLocationDetails = aLocationDetails;
    }
  
    /**
     * Creates a new <code>EventDetails</code> instance.
     *
     * @param aEvent a <code>LoggingEvent</code> value
     */
    EventDetails(LoggingEvent aEvent) {
      this(
        aEvent.timeStamp, aEvent.getLevel(), aEvent.getLoggerName(),
        aEvent.getNDC(), getEventMDC(aEvent), getEventProperties(aEvent),
        aEvent.getThreadName(), aEvent.getRenderedMessage(),
        aEvent.getThrowableStrRep(),
        (aEvent.getLocationInformation() == null) ? null
                                                  : aEvent
        .getLocationInformation().fullInfo);
    }
  
    /** @see #mTimeStamp **/
    long getTimeStamp() {
      return mTimeStamp;
    }
  
    /** @see #mPriority **/
    Priority getPriority() {
      return mPriority;
    }
  
    /** @see #mCategoryName **/
    String getCategoryName() {
      return mCategoryName;
    }
  
    /** @see #mNDC **/
    String getNDC() {
      return mNDC;
    }
  
    /** @see #mMDC **/
    String getMDC() {
      return mMDC;
    }
  
    /** @see #mProperties **/
    String getProperties() {
      return mProperties;
    }
  
    /** @see #mThreadName **/
    String getThreadName() {
      return mThreadName;
    }
  
    /** @see #mMessage **/
    String getMessage() {
      return mMessage;
    }
  
    /** @see #mLocationDetails **/
    String getLocationDetails() {
      return mLocationDetails;
    }
  
    /** @see #mThrowableStrRep **/
    String[] getThrowableStrRep() {
      return mThrowableStrRep;
    }
    
    /**
      Used internally to convert the MDC contents to a string.
      
      @param event The LoggingEvent to use for the MDC contents.
      @return String The MDC contents in string form. */
    private static String getEventMDC(LoggingEvent event) {
      Set keySet = event.getMDCKeySet();
      if (!keySet.isEmpty()) {
        String mdcString = "";
        Iterator keyIter = keySet.iterator();
        while (keyIter.hasNext()) {
          if (mdcString.length() != 0) {
            mdcString += ',';
          }
          String key = (String)keyIter.next();
          String value = (String)event.getMDC(key);
          mdcString += key + "=" + value;
        }
        return mdcString;
      }
      
      return null;
    }
    
    /**
      Used internally to convert the properties contents to a string.
      
      @param event The LoggingEvent to use for the properties contents.
      @return String The properties contents in string form. */
    private static String getEventProperties(LoggingEvent event) {
      Set keySet = event.getPropertyKeySet();
      if (!keySet.isEmpty()) {
        String propertyString = "";
        Iterator keyIter = keySet.iterator();
        while (keyIter.hasNext()) {
          if (propertyString.length() != 0) {
            propertyString += ',';
          }
          String key = (String)keyIter.next();
          String value = (String)event.getProperty(key);
          propertyString += key + "=" + value;
        }
        return propertyString;
      }
      
      return null;
    }
  }
  
  
  
  1.1                  jakarta-log4j-sandbox/src/java/org/apache/log4j/chainsaw/EventDetailSink.java
  
  Index: EventDetailSink.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
   *    apache@apache.org.
   *
   * 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.chainsaw;
  
  
  /**
   * Simple interface for objects that can receive/work with EventDetails
   * instances
   * @author Paul Smith
   * @version 1.0
   */
  public interface EventDetailSink {
    void addEvent(EventDetails aDetail);
  }
  
  
  
  1.1                  jakarta-log4j-sandbox/src/java/org/apache/log4j/chainsaw/MyTableModel.java
  
  Index: MyTableModel.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
   *    apache@apache.org.
   *
   * 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.chainsaw;
  
  import org.apache.log4j.Category;
  import org.apache.log4j.Priority;
  
  import java.text.DateFormat;
  
  import java.util.ArrayList;
  import java.util.Comparator;
  import java.util.Date;
  import java.util.Iterator;
  import java.util.List;
  import java.util.SortedSet;
  import java.util.TreeSet;
  
  import javax.swing.table.AbstractTableModel;
  
  
  /**
   * Represents a list of <code>EventDetails</code> objects that are sorted on
   * logging time. Methods are provided to filter the events that are visible.
   *
   * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
   */
  class MyTableModel extends AbstractTableModel implements EventDetailSink {
    /** used to log messages **/
    private static final Category LOG = Category.getInstance(MyTableModel.class);
  
    /** use the compare logging events **/
    private static final Comparator MY_COMP =
      new Comparator() {
        /** @see Comparator **/
        public int compare(Object aObj1, Object aObj2) {
          if ((aObj1 == null) && (aObj2 == null)) {
            return 0; // treat as equal
          } else if (aObj1 == null) {
            return -1; // null less than everything
          } else if (aObj2 == null) {
            return 1; // think about it. :->
          }
  
          // will assume only have LoggingEvent
          final EventDetails le1 = (EventDetails) aObj1;
          final EventDetails le2 = (EventDetails) aObj2;
  
          if (le1.getTimeStamp() < le2.getTimeStamp()) {
            return 1;
          }
  
          // assume not two events are logged at exactly the same time
          return -1;
        }
      };
  
    /** names of the columns in the table **/
    private static final String[] COL_NAMES =
    { "Time", "Priority", "Trace", "Category", "NDC", "MDC", "Properties", "Message" };
  
    /** definition of an empty list **/
    private static final EventDetails[] EMPTY_LIST = new EventDetails[] {  };
  
    /** used to format dates **/
    private static final DateFormat DATE_FORMATTER =
      DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
  
    /** the lock to control access **/
    private final Object mLock = new Object();
  
    /** set of all logged events - not filtered **/
    private final SortedSet mAllEvents = new TreeSet(MY_COMP);
  
    /** events that are visible after filtering **/
    private EventDetails[] mFilteredEvents = EMPTY_LIST;
  
    /** list of events that are buffered for processing **/
    private final List mPendingEvents = new ArrayList();
  
    /** indicates whether event collection is paused to the UI **/
    private boolean mPaused = false;
  
    /** filter for the thread **/
    private String mThreadFilter = "";
  
    /** filter for the message **/
    private String mMessageFilter = "";
  
    /** filter for the NDC **/
    private String mNDCFilter = "";
  
    /** filter for the MDC **/
    private String mMDCFilter = "";
  
    /** filter for the Properties **/
    private String mPropertiesFilter = "";
  
    /** filter for the category **/
    private String mCategoryFilter = "";
  
    /** filter for the priority **/
    private Priority mPriorityFilter = Priority.DEBUG;
  
    /**
    * Creates a new <code>MyTableModel</code> instance.
    *
    */
    MyTableModel() {
      final Thread t = new Thread(new Processor());
      t.setDaemon(true);
      t.start();
    }
  
    ////////////////////////////////////////////////////////////////////////////
    // Table Methods
    ////////////////////////////////////////////////////////////////////////////
  
    /** @see javax.swing.table.TableModel **/
    public int getRowCount() {
      synchronized (mLock) {
        return mFilteredEvents.length;
      }
    }
  
    /** @see javax.swing.table.TableModel **/
    public int getColumnCount() {
      // does not need to be synchronized
      return COL_NAMES.length;
    }
  
    /** @see javax.swing.table.TableModel **/
    public String getColumnName(int aCol) {
      // does not need to be synchronized
      return COL_NAMES[aCol];
    }
  
    /** @see javax.swing.table.TableModel **/
    public Class getColumnClass(int aCol) {
      // does not need to be synchronized
      return (aCol == 2) ? Boolean.class : Object.class;
    }
  
    /** @see javax.swing.table.TableModel **/
    public Object getValueAt(int aRow, int aCol) {
      synchronized (mLock) {
        final EventDetails event = mFilteredEvents[aRow];
  
        if (aCol == 0) {
          return DATE_FORMATTER.format(new Date(event.getTimeStamp()));
        } else if (aCol == 1) {
          return event.getPriority();
        } else if (aCol == 2) {
          return (event.getThrowableStrRep() == null) ? Boolean.FALSE
                                                      : Boolean.TRUE;
        } else if (aCol == 3) {
          return event.getCategoryName();
        } else if (aCol == 4) {
          return event.getNDC();
        } else if (aCol == 5) {
          return event.getMDC();
        } else if (aCol == 6) {
          return event.getProperties();
        }
  
        return event.getMessage();
      }
    }
  
    ////////////////////////////////////////////////////////////////////////////
    // Public Methods
    ////////////////////////////////////////////////////////////////////////////
  
    /**
    * Sets the priority to filter events on. Only events of equal or higher
    * property are now displayed.
    *
    * @param aPriority the priority to filter on
    */
    public void setPriorityFilter(Priority aPriority) {
      synchronized (mLock) {
        mPriorityFilter = aPriority;
        updateFilteredEvents(false);
      }
    }
  
    /**
    * Set the filter for the thread field.
    *
    * @param aStr the string to match
    */
    public void setThreadFilter(String aStr) {
      synchronized (mLock) {
        mThreadFilter = aStr.trim();
        updateFilteredEvents(false);
      }
    }
  
    /**
    * Set the filter for the message field.
    *
    * @param aStr the string to match
    */
    public void setMessageFilter(String aStr) {
      synchronized (mLock) {
        mMessageFilter = aStr.trim();
        updateFilteredEvents(false);
      }
    }
  
    /**
    * Set the filter for the NDC field.
    *
    * @param aStr the string to match
    */
    public void setNDCFilter(String aStr) {
      synchronized (mLock) {
        mNDCFilter = aStr.trim();
        updateFilteredEvents(false);
      }
    }
  
    /**
    * Set the filter for the MDC field.
    *
    * @param aStr the string to match
    */
    public void setMDCFilter(String aStr) {
      synchronized (mLock) {
        mMDCFilter = aStr.trim();
        updateFilteredEvents(false);
      }
    }
  
    /**
    * Set the filter for the Properties field.
    *
    * @param aStr the string to match
    */
    public void setPropertiesFilter(String aStr) {
      synchronized (mLock) {
        mPropertiesFilter = aStr.trim();
        updateFilteredEvents(false);
      }
    }
  
    /**
    * Set the filter for the category field.
    *
    * @param aStr the string to match
    */
    public void setCategoryFilter(String aStr) {
      synchronized (mLock) {
        mCategoryFilter = aStr.trim();
        updateFilteredEvents(false);
      }
    }
  
    /**
    * Add an event to the list.
    *
    * @param aEvent a <code>EventDetails</code> value
    */
    public void addEvent(EventDetails aEvent) {
      synchronized (mLock) {
        mPendingEvents.add(aEvent);
      }
    }
  
    /**
    * Clear the list of all events.
    */
    public void clear() {
      synchronized (mLock) {
        mAllEvents.clear();
        mFilteredEvents = new EventDetails[0];
        mPendingEvents.clear();
        fireTableDataChanged();
      }
    }
  
    /** Toggle whether collecting events **/
    public void toggle() {
      synchronized (mLock) {
        mPaused = !mPaused;
      }
    }
  
    /** @return whether currently paused collecting events **/
    public boolean isPaused() {
      synchronized (mLock) {
        return mPaused;
      }
    }
  
    /**
    * Get the throwable information at a specified row in the filtered events.
    *
    * @param aRow the row index of the event
    * @return the throwable information
    */
    public EventDetails getEventDetails(int aRow) {
      synchronized (mLock) {
        return mFilteredEvents[aRow];
      }
    }
  
    ////////////////////////////////////////////////////////////////////////////
    // Private methods
    ////////////////////////////////////////////////////////////////////////////
  
    /**
    * Update the filtered events data structure.
    * @param aInsertedToFront indicates whether events were added to front of
    *        the events. If true, then the current first event must still exist
    *        in the list after the filter is applied.
    */
    private void updateFilteredEvents(boolean aInsertedToFront) {
      final long start = System.currentTimeMillis();
      final List filtered = new ArrayList();
      final int size = mAllEvents.size();
      final Iterator it = mAllEvents.iterator();
  
      while (it.hasNext()) {
        final EventDetails event = (EventDetails) it.next();
  
        if (matchFilter(event)) {
          filtered.add(event);
        }
      }
  
      final EventDetails lastFirst =
        (mFilteredEvents.length == 0) ? null : mFilteredEvents[0];
      mFilteredEvents = (EventDetails[]) filtered.toArray(EMPTY_LIST);
  
      if (aInsertedToFront && (lastFirst != null)) {
        final int index = filtered.indexOf(lastFirst);
  
        if (index < 1) {
          LOG.warn("In strange state");
          fireTableDataChanged();
        } else {
          fireTableRowsInserted(0, index - 1);
        }
      } else {
        fireTableDataChanged();
      }
  
      final long end = System.currentTimeMillis();
      LOG.debug(
        "Total time [ms]: " + (end - start) + " in update, size: " + size);
    }
  
    /**
    * Returns whether an event matches the filters.
    *
    * @param aEvent the event to check for a match
    * @return whether the event matches
    */
    private boolean matchFilter(EventDetails aEvent) {
      if (
        aEvent.getPriority().isGreaterOrEqual(mPriorityFilter)
          && (aEvent.getThreadName().indexOf(mThreadFilter) >= 0)
          && (aEvent.getCategoryName().indexOf(mCategoryFilter) >= 0)
          && ((mNDCFilter.length() == 0)
          || ((aEvent.getNDC() != null) 
          && (aEvent.getNDC().indexOf(mNDCFilter) >= 0)))
          && ((mMDCFilter.length() == 0)
          || ((aEvent.getMDC() != null) 
          && (aEvent.getMDC().indexOf(mMDCFilter) >= 0)))
          && ((mPropertiesFilter.length() == 0)
          || ((aEvent.getProperties() != null) 
          && (aEvent.getProperties().indexOf(mPropertiesFilter) >= 0)))) {
        final String rm = aEvent.getMessage();
  
        if (rm == null) {
          // only match if we have not filtering in place
          return (mMessageFilter.length() == 0);
        } else {
          return (rm.indexOf(mMessageFilter) >= 0);
        }
      }
  
      return false; // by default not match
    }
  
    /**
    * Helper that actually processes incoming events.
    * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
    */
    private class Processor implements Runnable {
      /** loops getting the events **/
      public void run() {
        while (true) {
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            // ignore
          }
  
          synchronized (mLock) {
            if (mPaused) {
              continue;
            }
  
            boolean toHead = true; // were events added to head
            boolean needUpdate = false;
            final Iterator it = mPendingEvents.iterator();
  
            while (it.hasNext()) {
              final EventDetails event = (EventDetails) it.next();
              mAllEvents.add(event);
              toHead = toHead && (event == mAllEvents.first());
              needUpdate = needUpdate || matchFilter(event);
            }
  
            mPendingEvents.clear();
  
            if (needUpdate) {
              updateFilteredEvents(toHead);
            }
          }
        }
      }
    }
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: log4j-dev-help@jakarta.apache.org