logging-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sde...@apache.org
Subject svn commit: r1178304 [5/6] - in /logging/chainsaw/trunk: ./ src/main/java/org/apache/log4j/ src/main/java/org/apache/log4j/db/ src/main/java/org/apache/log4j/db/dialect/ src/main/java/org/apache/log4j/helpers/ src/main/java/org/apache/log4j/net/ src/ma...
Date Mon, 03 Oct 2011 06:15:43 GMT
Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/spi/SimpleULogger.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/spi/SimpleULogger.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/spi/SimpleULogger.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/spi/SimpleULogger.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,305 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j.spi;
+
+import org.apache.log4j.ULogger;
+import org.apache.log4j.helpers.MessageFormatter;
+
+
+/**
+ * A simple implementation that logs messages of level INFO or higher on
+ * the console (<code>System.out</code>).
+ * <p>
+ * The output includes the relative time in milliseconds, thread name, level,
+ * logger name, and the message followed by the line separator for the host.
+ * In log4j terms it amounts to the "%r  [%t] %level %logger - %m%n" pattern.
+ * <pre>
+176 [main] INFO examples.Sort - Populating an array of 2 elements in reverse.
+225 [main] INFO examples.SortAlgo - Entered the sort method.
+304 [main] INFO SortAlgo.DUMP - Dump of interger array:
+317 [main] INFO SortAlgo.DUMP - Element [0] = 0
+331 [main] INFO SortAlgo.DUMP - Element [1] = 1
+343 [main] INFO examples.Sort - The next log statement should be an error msg.
+346 [main] ERROR SortAlgo.DUMP - Tried to dump an uninitialized array.
+        at org.log4j.examples.SortAlgo.dump(SortAlgo.java:58)
+        at org.log4j.examples.Sort.main(Sort.java:64)
+467 [main] INFO  examples.Sort - Exiting main method.
+</pre>
+ *
+ * @author Ceki G&uuml;lc&uuml;
+ */
+public final class SimpleULogger implements ULogger {
+
+    /**
+     * Logger name.
+     */
+  private final String loggerName;
+
+
+  /**
+   * Mark the time when this class gets loaded into memory.
+   */
+  private static long startTime = System.currentTimeMillis();
+
+    /**
+     * Line separator.
+     */
+  public static final String LINE_SEPARATOR
+            = System.getProperty("line.separator");
+
+    /**
+     * INFO string literal.
+     */
+  private static final String INFO_STR = "INFO";
+    /**
+     * WARN string literal.
+     */
+  private static final String WARN_STR = "WARN";
+    /**
+     * ERROR string literal.
+     */
+  private static final String ERROR_STR = "ERROR";
+
+  /**
+   * Constructor is private to force construction through getLogger.
+   * @param name logger name
+   */
+  private SimpleULogger(final String name) {
+    super();
+    this.loggerName = name;
+  }
+
+  /**
+   * Creates a new instance.
+   *
+   * @param name logger name
+   * @return  logger.
+   */
+  public static SimpleULogger getLogger(final String name) {
+      return new SimpleULogger(name);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public boolean isDebugEnabled() {
+    return false;
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void debug(final Object msg) {
+    // NOP
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void debug(final Object parameterizedMsg, final Object param1) {
+    // NOP
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void debug(final String parameterizedMsg,
+                    final Object param1,
+                    final Object param2) {
+    // NOP
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void debug(final Object msg, final Throwable t) {
+    // NOP
+  }
+
+  /**
+   * This is our internal implementation for logging regular (non-parameterized)
+   * log messages.
+   *
+   * @param level level
+   * @param message message
+   * @param t throwable
+   */
+  private void log(final String level,
+                   final String message,
+                   final Throwable t) {
+    StringBuffer buf = new StringBuffer();
+
+    long millis  = System.currentTimeMillis();
+    buf.append(millis - startTime);
+
+    buf.append(" [");
+    buf.append(Thread.currentThread().getName());
+    buf.append("] ");
+
+    buf.append(level);
+    buf.append(" ");
+
+    buf.append(loggerName);
+    buf.append(" - ");
+
+    buf.append(message);
+
+    buf.append(LINE_SEPARATOR);
+
+    System.out.print(buf.toString());
+    if (t != null) {
+      t.printStackTrace(System.out);
+    }
+    System.out.flush();
+  }
+  /**
+   * For parameterized messages, first substitute parameters and then log.
+   *
+   * @param level level
+   * @param parameterizedMsg message pattern
+   * @param param1 param1
+   * @param param2 param2
+   */
+  private void parameterizedLog(final String level,
+                                final Object parameterizedMsg,
+                                final Object param1,
+                                final Object param2) {
+    if (parameterizedMsg instanceof String) {
+      String msgStr = (String) parameterizedMsg;
+      msgStr = MessageFormatter.format(msgStr, param1, param2);
+      log(level, msgStr, null);
+    } else {
+      // To be failsafe, we handle the case where 'messagePattern' is not
+      // a String. Unless the user makes a mistake, this should not happen.
+      log(level, parameterizedMsg.toString(), null);
+    }
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public boolean isInfoEnabled() {
+    return true;
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void info(final Object msg) {
+    log(INFO_STR, msg.toString(), null);
+  }
+
+
+    /**
+     * {@inheritDoc}
+     */
+  public void info(final Object parameterizedMsg, final Object param1) {
+    parameterizedLog(INFO_STR, parameterizedMsg, param1, null);
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void info(final String parameterizedMsg,
+                   final Object param1,
+                   final Object param2) {
+    parameterizedLog(INFO_STR, parameterizedMsg, param1, param2);
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void info(final Object msg, final Throwable t) {
+    log(INFO_STR, msg.toString(), t);
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public boolean isWarnEnabled() {
+    return true;
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void warn(final Object msg) {
+    log(WARN_STR, msg.toString(), null);
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void warn(final Object parameterizedMsg, final Object param1) {
+    parameterizedLog(WARN_STR, parameterizedMsg, param1, null);
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void warn(final String parameterizedMsg,
+                   final Object param1,
+                   final Object param2) {
+    parameterizedLog(WARN_STR, parameterizedMsg, param1, param2);
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void warn(final Object msg, final Throwable t) {
+    log(WARN_STR, msg.toString(), t);
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public boolean isErrorEnabled() {
+    return true;
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void error(final Object msg) {
+    log(ERROR_STR, msg.toString(), null);
+  }
+
+
+    /**
+     * {@inheritDoc}
+     */
+  public void error(final Object parameterizedMsg, final Object param1) {
+    parameterizedLog(ERROR_STR, parameterizedMsg, param1, null);
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void error(final String parameterizedMsg,
+                    final Object param1,
+                    final Object param2) {
+    parameterizedLog(ERROR_STR, parameterizedMsg, param1, param2);
+  }
+
+    /**
+     * {@inheritDoc}
+     */
+  public void error(final Object msg, final Throwable t) {
+    log(ERROR_STR, msg.toString(), t);
+  }
+
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/spi/Thresholdable.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/spi/Thresholdable.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/spi/Thresholdable.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/spi/Thresholdable.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.spi;
+
+import org.apache.log4j.Level;
+
+/**
+ * An interface that defines the required methods for supporting the
+ * setting and getting of a level threshold.  Components should implement
+ * this interface if logging events they process should meet a certain
+ * threshold before being processed further.  Examples of this are
+ * Appenders and Receivers which will not process logging events unless
+ * the event level is at or greater than a set threshold level.
+ *
+ * @author Paul Smith (psmith@apache.org)
+ * @author Mark Womack
+ */
+public interface Thresholdable {
+    /**
+     * Sets the component theshold to the given level.
+     *
+     * @param level The threshold level events must equal or be greater
+     *              than before further processing can be done.
+     */
+    void setThreshold(Level level);
+
+    /**
+     * Gets the current threshold setting of the component.
+     *
+     * @return Level The current threshold level of the component.
+     */
+    Level getThreshold();
+
+    /**
+     * Returns true if the given level is equals or greater than the current
+     * threshold value of the component.
+     *
+     * @param level The level to test against the component threshold.
+     * @return boolean True if level is equal or greater than the
+     *         component threshold.
+     */
+    boolean isAsSevereAsThreshold(Level level);
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/ListModelAppender.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/ListModelAppender.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/ListModelAppender.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/ListModelAppender.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.varia;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.spi.LoggingEvent;
+
+import javax.swing.DefaultListModel;
+import javax.swing.ListModel;
+
+
+/**
+ * A very basic appender that takes the events and stores them in to a
+ * ListModel for late retrieval.
+ *
+ *
+ * @author Paul Smith (psmith@apache.org)
+ *
+ */
+public final class ListModelAppender extends AppenderSkeleton {
+    /**
+     * Default list model.
+     */
+  private final DefaultListModel model = new DefaultListModel();
+
+  /**
+   * Constructs a ListModelAppender.
+   */
+  public ListModelAppender() {
+      super(true);
+  }
+  /**
+   * Returns a reference to the ListModel that contains all the LoggingEvents
+   * that have been appended to this class.
+   *
+   * @return the list model
+   */
+  public ListModel getModel() {
+    return model;
+  }
+
+    /** {@inheritDoc} */
+  protected void append(final LoggingEvent event) {
+    model.addElement(event);
+  }
+
+    /** {@inheritDoc} */
+  public void close() {
+    clearModel();
+  }
+
+  /**
+   * Removes all the Events from the model.
+   */
+  public void clearModel() {
+    model.clear();
+  }
+
+    /** {@inheritDoc} */
+  public boolean requiresLayout() {
+      return false;
+  }
+
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiver.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,1043 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.varia;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.regex.MatchResult;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.Constants;
+import org.apache.log4j.plugins.Receiver;
+import org.apache.log4j.rule.ExpressionRule;
+import org.apache.log4j.rule.Rule;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
+
+/**
+ * LogFilePatternReceiver can parse and tail log files, converting entries into
+ * LoggingEvents.  If the file doesn't exist when the receiver is initialized, the
+ * receiver will look for the file once every 10 seconds.
+ * <p>
+ * This receiver relies on java.util.regex features to perform the parsing of text in the
+ * log file, however the only regular expression field explicitly supported is 
+ * a glob-style wildcard used to ignore fields in the log file if needed.  All other
+ * fields are parsed by using the supplied keywords.
+ * <p>
+ * <b>Features:</b><br>
+ * - specify the URL of the log file to be processed<br>
+ * - specify the timestamp format in the file (if one exists, using patterns from {@link java.text.SimpleDateFormat})<br>
+ * - specify the pattern (logFormat) used in the log file using keywords, a wildcard character (*) and fixed text<br>
+ * - 'tail' the file (allows the contents of the file to be continually read and new events processed)<br>
+ * - supports the parsing of multi-line messages and exceptions
+ * - 'hostname' property set to URL host (or 'file' if not available)
+ * - 'application' property set to URL path (or value of fileURL if not available) 
+ *<p>
+ * <b>Keywords:</b><br>
+ * TIMESTAMP<br>
+ * LOGGER<br>
+ * LEVEL<br>
+ * THREAD<br>
+ * CLASS<br>
+ * FILE<br>
+ * LINE<br>
+ * METHOD<br>
+ * RELATIVETIME<br>
+ * MESSAGE<br>
+ * NDC<br>
+ * PROP(key)<br>
+ * <p>
+ * Use a * to ignore portions of the log format that should be ignored
+ * <p>
+ * Example:<br>
+ * If your file's patternlayout is this:<br>
+ * <b>%d %-5p [%t] %C{2} (%F:%L) - %m%n</b>
+ *<p>
+ * specify this as the log format:<br>
+ * <b>TIMESTAMP LEVEL [THREAD] CLASS (FILE:LINE) - MESSAGE</b>
+ *<p>
+ * To define a PROPERTY field, use PROP(key)
+ * <p>
+ * Example:<br> 
+ * If you used the RELATIVETIME pattern layout character in the file, 
+ * you can use PROP(RELATIVETIME) in the logFormat definition to assign 
+ * the RELATIVETIME field as a property on the event.
+ * <p>
+ * If your file's patternlayout is this:<br>
+ * <b>%r [%t] %-5p %c %x - %m%n</b>
+ *<p>
+ * specify this as the log format:<br>
+ * <b>PROP(RELATIVETIME) [THREAD] LEVEL LOGGER * - MESSAGE</b>
+ * <p>
+ * Note the * - it can be used to ignore a single word or sequence of words in the log file
+ * (in order for the wildcard to ignore a sequence of words, the text being ignored must be
+ *  followed by some delimiter, like '-' or '[') - ndc is being ignored in the following example.
+ * <p>
+ * Assign a filterExpression in order to only process events which match a filter.
+ * If a filterExpression is not assigned, all events are processed.
+ *<p>
+ * <b>Limitations:</b><br>
+ * - no support for the single-line version of throwable supported by patternlayout<br>
+ *   (this version of throwable will be included as the last line of the message)<br>
+ * - the relativetime patternLayout character must be set as a property: PROP(RELATIVETIME)<br>
+ * - messages should appear as the last field of the logFormat because the variability in message content<br>
+ * - exceptions are converted if the exception stack trace (other than the first line of the exception)<br>
+ *   is stored in the log file with a tab followed by the word 'at' as the first characters in the line<br>
+ * - tailing may fail if the file rolls over. 
+ *<p>
+ * <b>Example receiver configuration settings</b> (add these as params, specifying a LogFilePatternReceiver 'plugin'):<br>
+ * param: "timestampFormat" value="yyyy-MM-d HH:mm:ss,SSS"<br>
+ * param: "logFormat" value="PROP(RELATIVETIME) [THREAD] LEVEL LOGGER * - MESSAGE"<br>
+ * param: "fileURL" value="file:///c:/events.log"<br>
+ * param: "tailing" value="true"
+ *<p>
+ * This configuration will be able to process these sample events:<br>
+ * 710    [       Thread-0] DEBUG                   first.logger first - <test>   <test2>something here</test2>   <test3 blah=something/>   <test4>       <test5>something else</test5>   </test4></test><br>
+ * 880    [       Thread-2] DEBUG                   first.logger third - <test>   <test2>something here</test2>   <test3 blah=something/>   <test4>       <test5>something else</test5>   </test4></test><br>
+ * 880    [       Thread-0] INFO                    first.logger first - infomsg-0<br>
+ * java.lang.Exception: someexception-first<br>
+ *     at Generator2.run(Generator2.java:102)<br>
+ *
+ *@author Scott Deboy
+ */
+public class LogFilePatternReceiver extends Receiver {
+  private final List keywords = new ArrayList();
+
+  private static final String PROP_START = "PROP(";
+  private static final String PROP_END = ")";
+
+  private static final String LOGGER = "LOGGER";
+  private static final String MESSAGE = "MESSAGE";
+  private static final String TIMESTAMP = "TIMESTAMP";
+  private static final String NDC = "NDC";
+  private static final String LEVEL = "LEVEL";
+  private static final String THREAD = "THREAD";
+  private static final String CLASS = "CLASS";
+  private static final String FILE = "FILE";
+  private static final String LINE = "LINE";
+  private static final String METHOD = "METHOD";
+  
+  private static final String DEFAULT_HOST = "file";
+  
+  //all lines other than first line of exception begin with tab followed by 'at' followed by text
+  private static final String EXCEPTION_PATTERN = "^\\s+at.*";
+  private static final String REGEXP_DEFAULT_WILDCARD = ".*?";
+  private static final String REGEXP_GREEDY_WILDCARD = ".*";
+  private static final String PATTERN_WILDCARD = "*";
+  private static final String NOSPACE_GROUP = "(\\S*\\s*?)";
+  private static final String DEFAULT_GROUP = "(" + REGEXP_DEFAULT_WILDCARD + ")";
+  private static final String GREEDY_GROUP = "(" + REGEXP_GREEDY_WILDCARD + ")";
+  private static final String MULTIPLE_SPACES_REGEXP = "[ ]+";
+  private final String newLine = System.getProperty("line.separator");
+
+  private final String[] emptyException = new String[] { "" };
+
+  private SimpleDateFormat dateFormat;
+  private String timestampFormat = "yyyy-MM-d HH:mm:ss,SSS";
+  private String logFormat;
+  private String customLevelDefinitions;
+  private String fileURL;
+  private String host;
+  private String path;
+  private boolean tailing;
+  private String filterExpression;
+  private long waitMillis = 2000; //default 2 seconds
+
+  private static final String VALID_DATEFORMAT_CHARS = "GyMwWDdFEaHkKhmsSzZ";
+  private static final String VALID_DATEFORMAT_CHAR_PATTERN = "[" + VALID_DATEFORMAT_CHARS + "]";
+
+  private Rule expressionRule;
+
+  private Map currentMap;
+  private List additionalLines;
+  private List matchingKeywords;
+
+  private String regexp;
+  private Reader reader;
+  private Pattern regexpPattern;
+  private Pattern exceptionPattern;
+  private String timestampPatternText;
+
+  private boolean useCurrentThread;
+  public static final int MISSING_FILE_RETRY_MILLIS = 10000;
+  private boolean appendNonMatches;
+  private final Map customLevelDefinitionMap = new HashMap();
+
+    public LogFilePatternReceiver() {
+    keywords.add(TIMESTAMP);
+    keywords.add(LOGGER);
+    keywords.add(LEVEL);
+    keywords.add(THREAD);
+    keywords.add(CLASS);
+    keywords.add(FILE);
+    keywords.add(LINE);
+    keywords.add(METHOD);
+    keywords.add(MESSAGE);
+    keywords.add(NDC);
+    try {
+        exceptionPattern = Pattern.compile(EXCEPTION_PATTERN);
+    } catch (PatternSyntaxException pse) {
+        //shouldn't happen
+    }
+  }
+
+  /**
+   * Accessor
+   * 
+   * @return file URL
+   */
+  public String getFileURL() {
+    return fileURL;
+  }
+
+  /**
+   * Mutator
+   * 
+   * @param fileURL
+   */
+  public void setFileURL(String fileURL) {
+    this.fileURL = fileURL;
+  }
+
+    /**
+     * If the log file contains non-log4j level strings, they can be mapped to log4j levels using the format (android example):
+     * V=TRACE,D=DEBUG,I=INFO,W=WARN,E=ERROR,F=FATAL,S=OFF
+     *
+     * @param customLevelDefinitions the level definition string
+     */
+  public void setCustomLevelDefinitions(String customLevelDefinitions) {
+    this.customLevelDefinitions = customLevelDefinitions;
+  }
+
+  public String getCustomLevelDefinitions() {
+    return customLevelDefinitions;
+  }
+
+  /**
+   * Accessor
+   * @return append non matches
+   */
+  public boolean isAppendNonMatches() {
+      return appendNonMatches;
+  }
+
+  /**
+   * Mutator
+   * @param appendNonMatches
+   */
+  public void setAppendNonMatches(boolean appendNonMatches) {
+      this.appendNonMatches = appendNonMatches;
+  }
+
+  /**
+   * Accessor
+   * 
+   * @return filter expression
+   */
+  public String getFilterExpression() {
+    return filterExpression;
+  }
+
+  /**
+   * Mutator
+   * 
+   * @param filterExpression
+   */
+  public void setFilterExpression(String filterExpression) {
+    this.filterExpression = filterExpression;
+  }
+
+  /**
+   * Accessor
+   * 
+   * @return tailing
+   */
+  public boolean isTailing() {
+    return tailing;
+  }
+
+  /**
+   * Mutator
+   * 
+   * @param tailing
+   */
+  public void setTailing(boolean tailing) {
+    this.tailing = tailing;
+  }
+
+  /**
+   * When true, this property uses the current Thread to perform the import,
+   * otherwise when false (the default), a new Thread is created and started to manage
+   * the import.
+   * @return
+   */ 
+ public final boolean isUseCurrentThread() {
+     return useCurrentThread;
+ }
+
+ /**
+  * Sets whether the current Thread or a new Thread is created to perform the import,
+  * the default being false (new Thread created).
+  * 
+  * @param useCurrentThread
+  */
+ public final void setUseCurrentThread(boolean useCurrentThread) {
+     this.useCurrentThread = useCurrentThread;
+ }
+
+  /**
+   * Accessor
+   * 
+   * @return log format
+   */
+  public String getLogFormat() {
+    return logFormat;
+  }
+
+    /**
+   * Mutator
+   *
+   * @param logFormat
+   *          the format
+   */
+  public void setLogFormat(String logFormat) {
+    this.logFormat = logFormat;
+  }
+
+    /**
+   * Mutator.  Specify a pattern from {@link java.text.SimpleDateFormat}
+   *
+   * @param timestampFormat
+   */
+  public void setTimestampFormat(String timestampFormat) {
+    this.timestampFormat = timestampFormat;
+  }
+
+    /**
+   * Accessor
+   *
+   * @return timestamp format
+   */
+  public String getTimestampFormat() {
+    return timestampFormat;
+  }
+
+  /**
+   * Accessor
+   * @return millis between retrieves of content
+   */
+  public long getWaitMillis() {
+    return waitMillis;
+  }
+
+  /**
+   * Mutator
+   * @param waitMillis
+   */
+  public void setWaitMillis(long waitMillis) {
+    this.waitMillis = waitMillis;
+  }
+
+    /**
+   * Walk the additionalLines list, looking for the EXCEPTION_PATTERN.
+   * <p>
+   * Return the index of the first matched line
+   * (the match may be the 1st line of an exception)
+   * <p>
+   * Assumptions: <br>
+   * - the additionalLines list may contain both message and exception lines<br>
+   * - message lines are added to the additionalLines list and then
+   * exception lines (all message lines occur in the list prior to all
+   * exception lines)
+   *
+   * @return -1 if no exception line exists, line number otherwise
+   */
+  private int getExceptionLine() {
+    for (int i = 0; i < additionalLines.size(); i++) {
+      Matcher exceptionMatcher = exceptionPattern.matcher((String)additionalLines.get(i));
+      if (exceptionMatcher.matches()) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+    /**
+   * Combine all message lines occuring in the additionalLines list, adding
+   * a newline character between each line
+   * <p>
+   * the event will already have a message - combine this message
+   * with the message lines in the additionalLines list
+   * (all entries prior to the exceptionLine index)
+   *
+   * @param firstMessageLine primary message line
+   * @param exceptionLine index of first exception line
+   * @return message
+   */
+  private String buildMessage(String firstMessageLine, int exceptionLine) {
+    if (additionalLines.size() == 0) {
+      return firstMessageLine;
+    }
+    StringBuffer message = new StringBuffer();
+    if (firstMessageLine != null) {
+      message.append(firstMessageLine);
+    }
+
+    int linesToProcess = (exceptionLine == -1?additionalLines.size(): exceptionLine);
+
+    for (int i = 0; i < linesToProcess; i++) {
+      message.append(newLine);
+      message.append(additionalLines.get(i));
+    }
+    return message.toString();
+  }
+
+    /**
+   * Combine all exception lines occuring in the additionalLines list into a
+   * String array
+   * <p>
+   * (all entries equal to or greater than the exceptionLine index)
+   *
+   * @param exceptionLine index of first exception line
+   * @return exception
+   */
+  private String[] buildException(int exceptionLine) {
+    if (exceptionLine == -1) {
+      return emptyException;
+    }
+    String[] exception = new String[additionalLines.size() - exceptionLine - 1];
+    for (int i = 0; i < exception.length; i++) {
+      exception[i] = (String) additionalLines.get(i + exceptionLine);
+    }
+    return exception;
+  }
+
+    /**
+   * Construct a logging event from currentMap and additionalLines
+   * (additionalLines contains multiple message lines and any exception lines)
+   * <p>
+   * CurrentMap and additionalLines are cleared in the process
+   *
+   * @return event
+   */
+  private LoggingEvent buildEvent() {
+    if (currentMap.size() == 0) {
+      if (additionalLines.size() > 0) {
+        for (Iterator iter = additionalLines.iterator();iter.hasNext();) {
+          getLogger().info("found non-matching line: " + iter.next());
+        }
+      }
+      additionalLines.clear();
+      return null;
+    }
+    //the current map contains fields - build an event
+    int exceptionLine = getExceptionLine();
+    String[] exception = buildException(exceptionLine);
+
+    //messages are listed before exceptions in additionallines
+    if (additionalLines.size() > 0 && exception.length > 0) {
+      currentMap.put(MESSAGE, buildMessage((String) currentMap.get(MESSAGE),
+          exceptionLine));
+    }
+    LoggingEvent event = convertToEvent(currentMap, exception);
+    currentMap.clear();
+    additionalLines.clear();
+    return event;
+  }
+
+    /**
+   * Read, parse and optionally tail the log file, converting entries into logging events.
+   *
+   * A runtimeException is thrown if the logFormat pattern is malformed.
+   *
+   * @param bufferedReader
+   * @throws IOException
+   */
+  protected void process(BufferedReader bufferedReader) throws IOException {
+        Matcher eventMatcher;
+        Matcher exceptionMatcher;
+        String line;
+        while ((line = bufferedReader.readLine()) != null) {
+            //skip empty line entries
+            eventMatcher = regexpPattern.matcher(line);
+            if (line.trim().equals("")) {continue;}
+            exceptionMatcher = exceptionPattern.matcher(line);
+            if (eventMatcher.matches()) {
+                //build an event from the previous match (held in current map)
+                LoggingEvent event = buildEvent();
+                if (event != null) {
+                    if (passesExpression(event)) {
+                        doPost(event);
+                    }
+                }
+                currentMap.putAll(processEvent(eventMatcher.toMatchResult()));
+            } else if (exceptionMatcher.matches()) {
+                //an exception line
+                additionalLines.add(line);
+            } else {
+                //neither...either post an event with the line or append as additional lines
+                //if this was a logging event with multiple lines, each line will show up as its own event instead of being
+                //appended as multiple lines on the same event..
+                //choice is to have each non-matching line show up as its own line, or append them all to a previous event
+                if (appendNonMatches) {
+                    //hold on to the previous time, so we can do our best to preserve time-based ordering if the event is a non-match
+                    String lastTime = (String)currentMap.get(TIMESTAMP);
+                    //build an event from the previous match (held in current map)
+                    if (currentMap.size() > 0) {
+                        LoggingEvent event = buildEvent();
+                        if (event != null) {
+                            if (passesExpression(event)) {
+                              doPost(event);
+                            }
+                        }
+                    }
+                    if (lastTime != null) {
+                        currentMap.put(TIMESTAMP, lastTime);
+                    }
+                    currentMap.put(MESSAGE, line);
+                } else {
+                    additionalLines.add(line);
+                }
+            }
+        }
+
+        //process last event if one exists
+        LoggingEvent event = buildEvent();
+        if (event != null) {
+            if (passesExpression(event)) {
+                doPost(event);
+            }
+        }
+    }
+
+    protected void createPattern() {
+        regexpPattern = Pattern.compile(regexp);
+    }
+
+    /**
+   * Helper method that supports the evaluation of the expression
+   *
+   * @param event
+   * @return true if expression isn't set, or the result of the evaluation otherwise
+   */
+  private boolean passesExpression(LoggingEvent event) {
+    if (event != null) {
+      if (expressionRule != null) {
+        return (expressionRule.evaluate(event, null));
+      }
+    }
+    return true;
+  }
+
+    /**
+   * Convert the match into a map.
+   * <p>
+   * Relies on the fact that the matchingKeywords list is in the same
+   * order as the groups in the regular expression
+   *
+   * @param result
+   * @return map
+   */
+  private Map processEvent(MatchResult result) {
+    Map map = new HashMap();
+    //group zero is the entire match - process all other groups
+    for (int i = 1; i < result.groupCount() + 1; i++) {
+      Object key = matchingKeywords.get(i - 1);
+      Object value = result.group(i);
+      map.put(key, value);
+
+    }
+    return map;
+  }
+
+    /**
+   * Helper method that will convert timestamp format to a pattern
+   *
+   *
+   * @return string
+   */
+  private String convertTimestamp() {
+    //some locales (for example, French) generate timestamp text with characters not included in \w -
+    // now using \S (all non-whitespace characters) instead of /w 
+    String result = timestampFormat.replaceAll(VALID_DATEFORMAT_CHAR_PATTERN + "+", "\\\\S+");
+    //make sure dots in timestamp are escaped
+    result = result.replaceAll(Pattern.quote("."), "\\\\.");
+    return result;
+  }
+
+    protected void setHost(String host) {
+	  this.host = host;
+  }
+
+    protected void setPath(String path) {
+	  this.path = path;
+  }
+
+  public String getPath() {
+      return path;
+  }
+
+    /**
+   * Build the regular expression needed to parse log entries
+   *
+   */
+  protected void initialize() {
+	if (host == null && path == null) {
+		try {
+			URL url = new URL(fileURL);
+			host = url.getHost();
+			path = url.getPath();
+		} catch (MalformedURLException e1) {
+			// TODO Auto-generated catch block
+			e1.printStackTrace();
+		}
+	}
+	if (host == null || host.trim().equals("")) {
+		host = DEFAULT_HOST;
+	}
+	if (path == null || path.trim().equals("")) {
+		path = fileURL;
+	}
+
+    currentMap = new HashMap();
+    additionalLines = new ArrayList();
+    matchingKeywords = new ArrayList();
+
+    if (timestampFormat != null) {
+      dateFormat = new SimpleDateFormat(quoteTimeStampChars(timestampFormat));
+      timestampPatternText = convertTimestamp();
+    }
+    //if custom level definitions exist, parse them
+    updateCustomLevelDefinitionMap();
+    try {
+      if (filterExpression != null) {
+        expressionRule = ExpressionRule.getRule(filterExpression);
+      }
+    } catch (Exception e) {
+      getLogger().warn("Invalid filter expression: " + filterExpression, e);
+    }
+
+    List buildingKeywords = new ArrayList();
+
+    String newPattern = logFormat;
+
+    int index = 0;
+    String current = newPattern;
+    //build a list of property names and temporarily replace the property with an empty string,
+    //we'll rebuild the pattern later
+    List propertyNames = new ArrayList();
+    while (index > -1) {
+        if (current.indexOf(PROP_START) > -1 && current.indexOf(PROP_END) > -1) {
+            index = current.indexOf(PROP_START);
+            String longPropertyName = current.substring(current.indexOf(PROP_START), current.indexOf(PROP_END) + 1);
+            String shortProp = getShortPropertyName(longPropertyName);
+            buildingKeywords.add(shortProp);
+            propertyNames.add(longPropertyName);
+            current = current.substring(longPropertyName.length() + 1 + index);
+            newPattern = singleReplace(newPattern, longPropertyName, new Integer(buildingKeywords.size() -1).toString());
+        } else {
+            //no properties
+            index = -1;
+        }
+    }
+
+    /*
+     * we're using a treemap, so the index will be used as the key to ensure
+     * keywords are ordered correctly
+     *
+     * examine pattern, adding keywords to an index-based map patterns can
+     * contain only one of these per entry...properties are the only 'keyword'
+     * that can occur multiple times in an entry
+     */
+    Iterator iter = keywords.iterator();
+    while (iter.hasNext()) {
+      String keyword = (String) iter.next();
+      int index2 = newPattern.indexOf(keyword);
+      if (index2 > -1) {
+        buildingKeywords.add(keyword);
+        newPattern = singleReplace(newPattern, keyword, new Integer(buildingKeywords.size() -1).toString());
+      }
+    }
+
+    String buildingInt = "";
+
+    for (int i=0;i<newPattern.length();i++) {
+        String thisValue = String.valueOf(newPattern.substring(i, i+1));
+        if (isInteger(thisValue)) {
+            buildingInt = buildingInt + thisValue;
+        } else {
+            if (isInteger(buildingInt)) {
+                matchingKeywords.add(buildingKeywords.get(Integer.parseInt(buildingInt)));
+            }
+            //reset
+            buildingInt = "";
+        }
+    }
+
+    //if the very last value is an int, make sure to add it
+    if (isInteger(buildingInt)) {
+        matchingKeywords.add(buildingKeywords.get(Integer.parseInt(buildingInt)));
+    }
+
+    newPattern = replaceMetaChars(newPattern);
+
+    //compress one or more spaces in the pattern into the [ ]+ regexp
+    //(supports padding of level in log files)
+    newPattern = newPattern.replaceAll(MULTIPLE_SPACES_REGEXP, MULTIPLE_SPACES_REGEXP);
+    newPattern = newPattern.replaceAll(Pattern.quote(PATTERN_WILDCARD), REGEXP_DEFAULT_WILDCARD);
+    //use buildingKeywords here to ensure correct order
+    for (int i = 0;i<buildingKeywords.size();i++) {
+      String keyword = (String) buildingKeywords.get(i);
+      //make the final keyword greedy (we're assuming it's the message)
+      if (i == (buildingKeywords.size() - 1)) {
+        newPattern = singleReplace(newPattern, String.valueOf(i), GREEDY_GROUP);
+      } else if (TIMESTAMP.equals(keyword)) {
+        newPattern = singleReplace(newPattern, String.valueOf(i), "(" + timestampPatternText + ")");
+      } else if (LOGGER.equals(keyword) || LEVEL.equals(keyword)) {
+        newPattern = singleReplace(newPattern, String.valueOf(i), NOSPACE_GROUP);
+      } else {
+        newPattern = singleReplace(newPattern, String.valueOf(i), DEFAULT_GROUP);
+      }
+    }
+
+    regexp = newPattern;
+    getLogger().debug("regexp is " + regexp);
+  }
+
+    private void updateCustomLevelDefinitionMap() {
+        if (customLevelDefinitions != null) {
+            StringTokenizer entryTokenizer = new StringTokenizer(customLevelDefinitions, ",");
+
+            customLevelDefinitionMap.clear();
+            while (entryTokenizer.hasMoreTokens()) {
+                StringTokenizer innerTokenizer = new StringTokenizer(entryTokenizer.nextToken(), "=");
+                customLevelDefinitionMap.put(innerTokenizer.nextToken(), Level.toLevel(innerTokenizer.nextToken()));
+            }
+        }
+    }
+
+    private boolean isInteger(String value) {
+        try {
+            Integer.parseInt(value);
+            return true;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+
+    private String quoteTimeStampChars(String input) {
+        //put single quotes around text that isn't a supported dateformat char
+        StringBuffer result = new StringBuffer();
+        //ok to default to false because we also check for index zero below
+        boolean lastCharIsDateFormat = false;
+        for (int i = 0;i<input.length();i++) {
+            String thisVal = input.substring(i, i + 1);
+            boolean thisCharIsDateFormat = VALID_DATEFORMAT_CHARS.contains(thisVal);
+            //we have encountered a non-dateformat char
+            if (!thisCharIsDateFormat && (i == 0 || lastCharIsDateFormat)) {
+                result.append("'");
+            }
+            //we have encountered a dateformat char after previously encountering a non-dateformat char
+            if (thisCharIsDateFormat && i > 0 && !lastCharIsDateFormat) {
+                result.append("'");
+            }
+            lastCharIsDateFormat = thisCharIsDateFormat;
+            result.append(thisVal);
+        }
+        //append an end single-quote if we ended with non-dateformat char
+        if (!lastCharIsDateFormat) {
+            result.append("'");
+        }
+        return result.toString();
+    }
+
+    private String singleReplace(String inputString, String oldString, String newString)
+    {
+        int propLength = oldString.length();
+        int startPos = inputString.indexOf(oldString);
+        if (startPos == -1)
+        {
+            getLogger().info("string: " + oldString + " not found in input: " + inputString + " - returning input");
+            return inputString;
+        }
+        if (startPos == 0)
+        {
+            inputString = inputString.substring(propLength);
+            inputString = newString + inputString;
+        } else {
+            inputString = inputString.substring(0, startPos) + newString + inputString.substring(startPos + propLength);
+        }
+        return inputString;
+    }
+
+    private String getShortPropertyName(String longPropertyName)
+  {
+      String currentProp = longPropertyName.substring(longPropertyName.indexOf(PROP_START));
+      String prop = currentProp.substring(0, currentProp.indexOf(PROP_END) + 1);
+      String shortProp = prop.substring(PROP_START.length(), prop.length() - 1);
+      return shortProp;
+  }
+
+    /**
+   * Some perl5 characters may occur in the log file format.
+   * Escape these characters to prevent parsing errors.
+   *
+   * @param input
+   * @return string
+   */
+  private String replaceMetaChars(String input) {
+    //escape backslash first since that character is used to escape the remaining meta chars
+    input = input.replaceAll("\\\\", "\\\\\\");
+
+    //don't escape star - it's used as the wildcard
+    input = input.replaceAll(Pattern.quote("]"), "\\\\]");
+    input = input.replaceAll(Pattern.quote("["), "\\\\[");
+    input = input.replaceAll(Pattern.quote("^"), "\\\\^");
+    input = input.replaceAll(Pattern.quote("$"), "\\\\$");
+    input = input.replaceAll(Pattern.quote("."), "\\\\.");
+    input = input.replaceAll(Pattern.quote("|"), "\\\\|");
+    input = input.replaceAll(Pattern.quote("?"), "\\\\?");
+    input = input.replaceAll(Pattern.quote("+"), "\\\\+");
+    input = input.replaceAll(Pattern.quote("("), "\\\\(");
+    input = input.replaceAll(Pattern.quote(")"), "\\\\)");
+    input = input.replaceAll(Pattern.quote("-"), "\\\\-");
+    input = input.replaceAll(Pattern.quote("{"), "\\\\{");
+    input = input.replaceAll(Pattern.quote("}"), "\\\\}");
+    input = input.replaceAll(Pattern.quote("#"), "\\\\#");
+    return input;
+  }
+
+    /**
+   * Convert a keyword-to-values map to a LoggingEvent
+   *
+   * @param fieldMap
+   * @param exception
+   *
+   * @return logging event
+   */
+  private LoggingEvent convertToEvent(Map fieldMap, String[] exception) {
+    if (fieldMap == null) {
+      return null;
+    }
+
+    //a logger must exist at a minimum for the event to be processed
+    if (!fieldMap.containsKey(LOGGER)) {
+      fieldMap.put(LOGGER, "Unknown");
+    }
+    if (exception == null) {
+      exception = emptyException;
+    }
+
+    Logger logger = null;
+    long timeStamp = 0L;
+    String level = null;
+    String threadName = null;
+    Object message = null;
+    String ndc = null;
+    String className = null;
+    String methodName = null;
+    String eventFileName = null;
+    String lineNumber = null;
+    Hashtable properties = new Hashtable();
+
+    logger = Logger.getLogger((String) fieldMap.remove(LOGGER));
+
+    if ((dateFormat != null) && fieldMap.containsKey(TIMESTAMP)) {
+      try {
+        timeStamp = dateFormat.parse((String) fieldMap.remove(TIMESTAMP))
+            .getTime();
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    }
+    //use current time if timestamp not parseable
+    if (timeStamp == 0L) {
+      timeStamp = System.currentTimeMillis();
+    }
+
+    message = fieldMap.remove(MESSAGE);
+    if (message == null) {
+      message = "";
+    }
+
+    level = (String) fieldMap.remove(LEVEL);
+    Level levelImpl;
+    if (level == null) {
+        levelImpl = Level.DEBUG;
+    } else {
+        //first try to resolve against custom level definition map, then fall back to regular levels
+        levelImpl = (Level) customLevelDefinitionMap.get(level);
+        if (levelImpl == null) {
+            levelImpl = Level.toLevel(level.trim());
+            if (!level.equals(levelImpl.toString())) {
+                //check custom level map
+                if (levelImpl == null) {
+                    levelImpl = Level.DEBUG;
+                    getLogger().debug("found unexpected level: " + level + ", logger: " + logger.getName() + ", msg: " + message);
+                    //make sure the text that couldn't match a level is added to the message
+                    message = level + " " + message;
+                }
+            }
+        }
+    }
+
+    threadName = (String) fieldMap.remove(THREAD);
+
+    ndc = (String) fieldMap.remove(NDC);
+
+    className = (String) fieldMap.remove(CLASS);
+
+    methodName = (String) fieldMap.remove(METHOD);
+
+    eventFileName = (String) fieldMap.remove(FILE);
+
+    lineNumber = (String) fieldMap.remove(LINE);
+
+    properties.put(Constants.HOSTNAME_KEY, host);
+    properties.put(Constants.APPLICATION_KEY, path);
+    properties.put(Constants.RECEIVER_NAME_KEY, getName());
+
+    //all remaining entries in fieldmap are properties
+    properties.putAll(fieldMap);
+
+    LocationInfo info = null;
+
+    if ((eventFileName != null) || (className != null) || (methodName != null)
+        || (lineNumber != null)) {
+      info = new LocationInfo(eventFileName, className, methodName, lineNumber);
+    } else {
+      info = LocationInfo.NA_LOCATION_INFO;
+    }
+
+    LoggingEvent event = new LoggingEvent(null,
+            logger, timeStamp, levelImpl, message,
+            threadName,
+            new ThrowableInformation(exception),
+            ndc,
+            info,
+            properties);
+
+    return event;
+  }
+
+//  public static void main(String[] args) {
+//    org.apache.log4j.Logger rootLogger = org.apache.log4j.Logger.getRootLogger();
+//    org.apache.log4j.ConsoleAppender appender = new org.apache.log4j.ConsoleAppender(new org.apache.log4j.SimpleLayout());
+//    appender.setName("console");
+//    rootLogger.addAppender(appender);
+//    LogFilePatternReceiver test = new LogFilePatternReceiver();
+//    org.apache.log4j.spi.LoggerRepository repo = new org.apache.log4j.LoggerRepositoryExImpl(org.apache.log4j.LogManager.getLoggerRepository());
+//    test.setLoggerRepository(repo);
+//    test.setLogFormat("PROP(RELATIVETIME) [THREAD] LEVEL LOGGER * - MESSAGE");
+//    test.setTailing(false);
+//    test.setAppendNonMatches(true);
+//    test.setTimestampFormat("yyyy-MM-d HH:mm:ss,SSS");
+//    test.setFileURL("file:///C:/log/test.log");
+//    test.initialize();
+//    test.activateOptions();
+//  }
+
+    /**
+   * Close the reader.
+   */
+  public void shutdown() {
+    getLogger().info(getPath() + " shutdown");
+    active = false;
+    try {
+      if (reader != null) {
+        reader.close();
+        reader = null;
+      }
+    } catch (IOException ioe) {
+      ioe.printStackTrace();
+    }
+  }
+
+    /**
+   * Read and process the log file.
+   */
+  public void activateOptions() {
+    getLogger().info("activateOptions");
+    active = true;
+	Runnable runnable = new Runnable() {
+	  public void run() {
+        initialize();
+            while (reader == null) {
+                getLogger().info("attempting to load file: " + getFileURL());
+                try {
+                    reader = new InputStreamReader(new URL(getFileURL()).openStream());
+                } catch (FileNotFoundException fnfe) {
+                    getLogger().info("file not available - will try again");
+                    synchronized (this) {
+                        try {
+                            wait(MISSING_FILE_RETRY_MILLIS);
+                        } catch (InterruptedException ie) {}
+                    }
+                } catch (IOException ioe) {
+                    getLogger().warn("unable to load file", ioe);
+                    return;
+                }
+            }
+            try {
+                BufferedReader bufferedReader = new BufferedReader(reader);
+                createPattern();
+                do {
+                    process(bufferedReader);
+                    try {
+                        synchronized (this) {
+                            wait(waitMillis);
+                        }
+                    } catch (InterruptedException ie) {}
+                    if (tailing) {
+                      getLogger().debug("tailing file");
+                    }
+                } while (tailing);
+
+            } catch (IOException ioe) {
+                //io exception - probably shut down
+                getLogger().info("stream closed");
+            }
+            getLogger().debug("processing " + path + " complete");
+            shutdown();
+          }
+        };
+        if(useCurrentThread) {
+            runnable.run();
+        }else {
+            new Thread(runnable, "LogFilePatternReceiver-"+getName()).start();
+        }
+    }
+}

Added: logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java (added)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/varia/LogFilePatternReceiverBeanInfo.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.varia;
+
+import java.beans.PropertyDescriptor;
+import java.beans.SimpleBeanInfo;
+
+
+/**
+ * BeanInfo class for the meta-data of the LogFilePatternReceiver.
+ *
+ */
+public class LogFilePatternReceiverBeanInfo extends SimpleBeanInfo {
+  /* (non-Javadoc)
+   * @see java.beans.BeanInfo#getPropertyDescriptors()
+   */
+  public PropertyDescriptor[] getPropertyDescriptors() {
+    try {
+      return new PropertyDescriptor[] {
+        new PropertyDescriptor("fileURL", LogFilePatternReceiver.class),
+        new PropertyDescriptor(
+          "timestampFormat", LogFilePatternReceiver.class),
+        new PropertyDescriptor("logFormat", LogFilePatternReceiver.class),
+        new PropertyDescriptor("name", LogFilePatternReceiver.class),
+        new PropertyDescriptor("tailing", LogFilePatternReceiver.class),
+        new PropertyDescriptor(
+          "filterExpression", LogFilePatternReceiver.class),
+        new PropertyDescriptor("waitMillis", LogFilePatternReceiver.class),
+        new PropertyDescriptor("appendNonMatches", LogFilePatternReceiver.class),
+        new PropertyDescriptor("customLevelDefinitions", LogFilePatternReceiver.class),
+        new PropertyDescriptor("useCurrentThread", LogFilePatternReceiver.class),
+      };
+    } catch (Exception e) {
+    }
+
+    return null;
+  }
+}

Modified: logging/chainsaw/trunk/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html?rev=1178304&r1=1178303&r2=1178304&view=diff
==============================================================================
--- logging/chainsaw/trunk/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html (original)
+++ logging/chainsaw/trunk/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html Mon Oct  3 06:15:40 2011
@@ -12,6 +12,7 @@
 <h1>2.1</h1>
 <h2>2 Oct 2011</h2>
 <ul>
+<li>Moved component and receivers companion sources to the Chainsaw source tree - those companions won't be released.</li>
 <li>Added persistent "always display" expression support (button below the logger tree).  The 'always display' expression overrides hidden loggers and the hidden expression but not the refine focus filtering mechanism.  Often used with expressions like 'exception exists || level > warn' to ensure errors and exceptions are not filtered out due to the hidden expression or hidden logger mechanism. </li>
 </ul>
 <h2>11 Nov 2010</h2>

Added: logging/chainsaw/trunk/src/test/java/org/apache/log4j/db/FullCycleDBTest.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/test/java/org/apache/log4j/db/FullCycleDBTest.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/test/java/org/apache/log4j/db/FullCycleDBTest.java (added)
+++ logging/chainsaw/trunk/src/test/java/org/apache/log4j/db/FullCycleDBTest.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,327 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j.db;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.log4j.Hierarchy;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.MDC;
+import org.apache.log4j.VectorAppender;
+import org.apache.log4j.LoggerRepositoryExImpl;
+import org.apache.log4j.helpers.Constants;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.RootLogger;
+import org.apache.log4j.spi.LoggerRepository;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+import java.util.HashMap;
+import java.io.InputStream;
+import java.io.IOException;
+
+
+/**
+ * This test case writes a few events into a databases and reads them
+ * back comparing the event written and read back.
+ * 
+ * <p>It relies heavily on the proper configuration of its environment
+ * in joran config files as well system properties.
+ * </p>
+ * 
+ * <p>See also the Ant build file in the tests/ directory.</p> 
+ * 
+ * @author Ceki G&uuml;lc&uuml
+ */
+public class FullCycleDBTest
+       extends TestCase {
+  
+  Vector witnessEvents;
+  Hierarchy lrWrite;
+  LoggerRepository lrRead;
+  String appendConfigFile = null;
+  String readConfigFile = null;
+  
+  
+  /*
+   * @see TestCase#setUp()
+   */
+  protected void setUp()
+         throws Exception {
+    super.setUp();
+    appendConfigFile = "append-with-drivermanager1.xml";
+    readConfigFile = "read-with-drivermanager1.xml";
+
+    witnessEvents = new Vector();
+    lrWrite = new Hierarchy(new RootLogger(Level.DEBUG));
+    lrRead = new LoggerRepositoryExImpl(new Hierarchy(new RootLogger(Level.DEBUG)));
+
+
+    //
+    //   attempt to define tables in in-memory database
+    //      will throw exception if already defined.
+    //
+        Class.forName("org.hsqldb.jdbcDriver");
+        Connection connection = DriverManager.getConnection("jdbc:hsqldb:mem:testdb");
+        try {
+            Statement s = connection.createStatement();
+            s.executeUpdate("CREATE TABLE logging_event " +
+              "( sequence_number   BIGINT NOT NULL, " +
+               " timestamp         BIGINT NOT NULL, " +
+               " rendered_message  LONGVARCHAR NOT NULL, " +
+               " logger_name       VARCHAR NOT NULL, " +
+               " level_string      VARCHAR NOT NULL, " +
+               " ndc               LONGVARCHAR, " +
+               " thread_name       VARCHAR, " +
+               " reference_flag    SMALLINT, " +
+               " caller_filename   VARCHAR, " +
+               " caller_class      VARCHAR, " +
+               " caller_method     VARCHAR, " +
+               " caller_line       CHAR(4), " +
+               " event_id          INT NOT NULL IDENTITY)");
+            s.executeUpdate("CREATE TABLE logging_event_property " +
+              "( event_id	      INT NOT NULL, " +
+               " mapped_key        VARCHAR(254) NOT NULL, " +
+               " mapped_value      LONGVARCHAR, " +
+               " PRIMARY KEY(event_id, mapped_key), " +
+               " FOREIGN KEY (event_id) REFERENCES logging_event(event_id))");
+            s.executeUpdate("CREATE TABLE logging_event_exception" +
+                    "  ( event_id         INT NOT NULL, " +
+                    "    i                SMALLINT NOT NULL," +
+                    "    trace_line       VARCHAR NOT NULL," +
+                    "    PRIMARY KEY(event_id, i)," +
+                    "    FOREIGN KEY (event_id) REFERENCES logging_event(event_id))");
+        } catch(SQLException ex) {
+            String s = ex.toString();
+        } finally {
+            connection.close();
+        }
+
+  }
+
+
+  /*
+   * @see TestCase#tearDown()
+   */
+  protected void tearDown()
+         throws Exception {
+    super.tearDown();
+    lrRead.shutdown();
+    witnessEvents = null;
+  }
+
+  /**
+   * Constructor for DBReeceiverTest.
+   * @param arg0
+   */
+  public FullCycleDBTest(String arg0) {
+    super(arg0);
+  }
+
+  
+  /**
+   * This test starts by writing a single event to a DB using DBAppender
+   * and then reads it back using DBReceiver.
+   * 
+   * DB related information is specified within the configuration files.
+   * @throws Exception
+   */
+  public void testSingleOutput()
+         throws Exception {
+    DOMConfigurator jc1 = new DOMConfigurator();
+    InputStream is = FullCycleDBTest.class.getResourceAsStream(appendConfigFile);
+    jc1.doConfigure(is, lrWrite);
+    is.close();
+  
+    long startTime = System.currentTimeMillis();
+    System.out.println("***startTime is  "+startTime);
+
+    // Write out just one log message
+    Logger out = lrWrite.getLogger("testSingleOutput.out");
+    out.debug("some message"+startTime);
+
+    VectorAppender witnessAppender = (VectorAppender) lrWrite.getRootLogger().getAppender("VECTOR");
+    witnessEvents = witnessAppender.getVector();
+    assertEquals(1, witnessEvents.size());    
+
+    // We have to close all appenders before starting to read
+    lrWrite.shutdown();
+
+    // now read it back
+    readBack(readConfigFile, startTime);
+
+  }
+
+  /**
+   * This test starts by writing a single event to a DB using DBAppender
+   * and then reads it back using DBReceiver.
+   * 
+   * The written event includes MDC and repository properties as well as
+   * exception info.
+   * 
+   * DB related information is specified within the configuration files.
+   * @throws Exception
+   */
+  public void testAllFields() throws IOException {
+    DOMConfigurator jc1 = new DOMConfigurator();
+    InputStream is = FullCycleDBTest.class.getResourceAsStream(appendConfigFile);
+    jc1.doConfigure(is, lrWrite);
+    is.close();
+  
+    long startTime = System.currentTimeMillis();
+    
+    // Write out just one log message
+    MDC.put("key1", "value1-"+startTime);
+    MDC.put("key2", "value2-"+startTime);
+    Map mdcMap = MDC.getContext();
+//    LogLog.info("**********"+mdcMap.size());
+    
+    // Write out just one log message
+    Logger out = lrWrite.getLogger("out"+startTime);
+
+    out.debug("some message"+startTime);
+    MDC.put("key3", "value2-"+startTime);
+    out.error("some error message"+startTime, new Exception("testing"));
+    
+    // we clear the MDC to avoid interference with the events read back from
+    // the db
+    MDC.remove("key1");
+    MDC.remove("key2");
+    MDC.remove("key3");
+
+    VectorAppender witnessAppender = (VectorAppender) lrWrite.getRootLogger().getAppender("VECTOR");
+    witnessEvents = witnessAppender.getVector();
+    assertEquals(2, witnessEvents.size());    
+
+    // We have to close all appenders just before starting to read
+    lrWrite.shutdown();
+    
+    readBack(readConfigFile, startTime);
+  }
+
+
+  void readBack(String configfile, long startTime) throws IOException {
+    DOMConfigurator jc2 = new DOMConfigurator();
+    InputStream is = FullCycleDBTest.class.getResourceAsStream(configfile);
+    jc2.doConfigure(is, lrRead);
+    is.close();
+    
+    // wait a little to allow events to be read
+    try { Thread.sleep(3100); } catch(Exception e) {}
+    VectorAppender va = (VectorAppender) lrRead.getRootLogger().getAppender("VECTOR");
+    Vector returnedEvents = getRelevantEventsFromVA(va, startTime);
+    
+    compareEvents(witnessEvents, returnedEvents);
+    
+  }
+  
+  void compareEvents(Vector l, Vector r) {
+    assertNotNull("left vector of events should not be null");
+    assertEquals(l.size(), r.size());
+    
+    for(int i = 0; i < r.size(); i++) {
+      LoggingEvent le = (LoggingEvent) l.get(i);
+      LoggingEvent re = (LoggingEvent) r.get(i);
+      assertEquals(le.getMessage(),        re.getMessage());
+      assertEquals(le.getLoggerName(),     re.getLoggerName());
+      assertEquals(le.getLevel(),          re.getLevel());
+      assertEquals(le.getThreadName(), re.getThreadName());
+      if(re.getTimeStamp() < le.getTimeStamp()) {
+        fail("Returned event cannot preceed witness timestamp");
+      }
+
+      Map sourceMap = re.getProperties();
+      Map remap;
+      if (sourceMap == null) {
+          remap = new HashMap();
+      } else {
+          remap = new HashMap(sourceMap);
+          if (remap.containsKey(Constants.LOG4J_ID_KEY)) {
+              remap.remove(Constants.LOG4J_ID_KEY);
+          }
+      }
+      if(le.getProperties() == null || le.getProperties().size() == 0) {
+        if(remap.size() != 0) {
+          System.out.println("properties are "+remap);
+          fail("Returned event should have been empty");
+        }
+      } else {
+        assertEquals(le.getProperties(), remap);
+      }
+      comprareStringArrays( le.getThrowableStrRep(),  re.getThrowableStrRep());
+      compareLocationInfo(le, re);
+    } 
+  }
+  
+  void comprareStringArrays(String[] la, String[] ra) {
+    if((la == null) && (ra == null)) {
+      return;
+    }
+    assertEquals(la.length, ra.length);
+    for(int i = 0; i < la.length; i++) {
+      assertEquals(la[i], ra[i]);
+    }
+  }
+  
+  void compareLocationInfo(LoggingEvent l, LoggingEvent r) {
+    if(l.locationInformationExists()) {
+      assertEquals(l.getLocationInformation().fullInfo, r.getLocationInformation().fullInfo);
+    } else {
+      assertEquals(LocationInfo.NA_LOCATION_INFO, r.getLocationInformation());
+    }
+  }
+  
+  Vector getRelevantEventsFromVA(VectorAppender va, long startTime) {
+    assertNotNull(va);
+    Vector v = va.getVector();
+    Vector r = new Vector();
+    // remove all elements older than startTime
+    for(Iterator i = v.iterator(); i.hasNext(); ) {
+      LoggingEvent event = (LoggingEvent) i.next();  
+      if(startTime > event.getTimeStamp()) {
+        System.out.println("***Removing event with timestamp "+event.getTimeStamp());
+      } else {
+        System.out.println("***Keeping event with timestamo"+event.getTimeStamp());
+        r.add(event);
+      }
+    }
+    return r;
+  }
+
+  void dump(Vector v) {
+    for(int i = 0; i < v.size(); i++) {
+      LoggingEvent le = (LoggingEvent) v.get(i);
+      System.out.println("---"+le.getLevel()+" "+le.getLoggerName()+" "+le.getMessage());
+    }
+  }
+  
+  public static Test XXsuite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new FullCycleDBTest("testSingleOutput"));
+    suite.addTest(new FullCycleDBTest("testAllFields"));
+    return suite;
+  }
+}

Added: logging/chainsaw/trunk/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java (added)
+++ logging/chainsaw/trunk/src/test/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.helpers;
+
+import junit.framework.*;
+
+
+/**
+ * Unit tests for UtilLoggingLevel.
+ */
+
+public class UtilLoggingLevelTest extends TestCase {
+
+    /**
+     * Create new instance of test.
+     *
+     * @param testName test name
+     */
+    public UtilLoggingLevelTest(final String testName) {
+        super(testName);
+    }
+
+    /**
+     * Test toLevel("fiNeSt").
+     */
+    public void testToLevelFINEST() {
+        assertSame(UtilLoggingLevel.FINEST, UtilLoggingLevel.toLevel("fiNeSt"));
+    }
+
+}
+

Added: logging/chainsaw/trunk/src/test/java/org/apache/log4j/rewrite/RewriteAppenderTest.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/test/java/org/apache/log4j/rewrite/RewriteAppenderTest.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/test/java/org/apache/log4j/rewrite/RewriteAppenderTest.java (added)
+++ logging/chainsaw/trunk/src/test/java/org/apache/log4j/rewrite/RewriteAppenderTest.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j.rewrite;
+
+import junit.framework.*;
+import org.apache.log4j.*;
+import org.apache.log4j.util.Compare;
+import org.apache.log4j.xml.*;
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.TreeMap;
+import java.util.Hashtable;
+import javax.xml.parsers.*;
+import org.w3c.dom.*;
+
+public class RewriteAppenderTest extends TestCase {
+    public RewriteAppenderTest(final String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        LogManager.getLoggerRepository().resetConfiguration();
+        Hashtable context = MDC.getContext();
+        if (context != null) {
+            context.clear();
+        }
+    }
+
+    public void tearDown() {
+        LogManager.getLoggerRepository().shutdown();
+    }
+
+    public void configure(final String resourceName) throws Exception {
+        InputStream is = RewriteAppenderTest.class.getResourceAsStream(resourceName);
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(false);
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        Document doc = builder.parse(is);
+        DOMConfigurator.configure(doc.getDocumentElement());
+    }
+
+
+    public void testMapPolicy() throws Exception {
+        configure("map.xml");
+        Logger logger = Logger.getLogger(RewriteAppenderTest.class);
+        logger.info("Message 0");
+        MDC.put("p1", "Hola");
+
+        Map msg = new TreeMap();
+        msg.put("p1", "Hello");
+        msg.put("p2", "World");
+        msg.put("x1", "Mundo");
+        logger.info(msg);
+        msg.put("message", "Message 1");
+        logger.info(msg);
+        assertTrue(Compare.compare(RewriteAppenderTest.class, "temp", "map.log"));
+    }
+
+    private static class BaseBean {
+        private final Object p2;
+        private final Object x1;
+
+        public BaseBean(final Object p2,
+                        final Object x1) {
+             this.p2 = p2;
+             this.x1 = x1;
+        }
+
+        public Object getP2() {
+            return p2;
+        }
+
+        public Object getX1() {
+            return x1;
+        }
+
+        public String toString() {
+            return "I am bean.";
+        }
+    }
+
+    private static class MessageBean extends BaseBean {
+        private final Object msg;
+
+        public MessageBean(final Object msg,
+                           final Object p2,
+                           final Object x1) {
+            super(p2, x1);
+            this.msg = msg;
+        }
+
+        public Object getMessage() {
+            return msg;
+        }
+    }
+
+    public void testReflectionPolicy() throws Exception {
+        configure("reflection.xml");
+        Logger logger = Logger.getLogger(RewriteAppenderTest.class);
+        logger.info("Message 0");
+        logger.info(new BaseBean("Hello", "World" ));
+        MDC.put("p1", "Hola");
+        MDC.put("p2", "p2");
+        logger.info(new MessageBean("Welcome to The Hub", "Hello", "World" ));
+        assertTrue(Compare.compare(RewriteAppenderTest.class, "temp", "reflection.log"));
+    }
+
+    public void testPropertyPolicy() throws Exception {
+        configure("property.xml");
+        Logger logger = Logger.getLogger(RewriteAppenderTest.class);
+        logger.info("Message 0");
+        MDC.put("p1", "Hola");
+        logger.info("Message 1");
+        assertTrue(Compare.compare(RewriteAppenderTest.class, "temp", "property.log"));
+    }
+}

Added: logging/chainsaw/trunk/src/test/java/org/apache/log4j/util/Compare.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/test/java/org/apache/log4j/util/Compare.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/test/java/org/apache/log4j/util/Compare.java (added)
+++ logging/chainsaw/trunk/src/test/java/org/apache/log4j/util/Compare.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.zip.GZIPInputStream;
+
+
+public class Compare {
+  static final int B1_NULL = -1;
+  static final int B2_NULL = -2;
+
+  private static final InputStream open(
+          final Class testClass,
+          final String fileName) throws IOException {
+      String resourceName = fileName;
+      if (fileName.startsWith("witness/")) {
+          resourceName = fileName.substring(fileName.lastIndexOf('/') + 1);
+      }
+      InputStream is = testClass.getResourceAsStream(resourceName);
+      if (is == null) {
+          File file = new File(fileName);
+          if (file.exists()) {
+              is = new FileInputStream(file);
+          } else {
+              throw new FileNotFoundException("Resource "
+                      + resourceName + " not found");
+          }
+      }
+      return is;
+  }
+
+  public static boolean compare(Class testClass,
+                                final String file1,
+                                final String file2)
+    throws IOException {
+    BufferedReader in1 = new BufferedReader(new FileReader(file1));
+    BufferedReader in2 = new BufferedReader(new InputStreamReader(
+            open(testClass, file2)));
+    try {
+      return compare(testClass, file1, file2, in1, in2);
+    } finally {
+      in1.close();
+      in2.close();
+    }
+  }
+    
+ public static boolean compare(
+         Class testClass, String file1, String file2, BufferedReader in1, BufferedReader in2) throws IOException {
+
+    String s1;
+    int lineCounter = 0;
+
+    while ((s1 = in1.readLine()) != null) {
+      lineCounter++;
+
+      String s2 = in2.readLine();
+
+      if (!s1.equals(s2)) {
+        System.out.println(
+          "Files [" + file1 + "] and [" + file2 + "] differ on line "
+          + lineCounter);
+        System.out.println("One reads:  [" + s1 + "].");
+        System.out.println("Other reads:[" + s2 + "].");
+        outputFile(testClass, file1);
+        outputFile(testClass, file2);
+
+        return false;
+      }
+    }
+
+    // the second file is longer
+    if (in2.read() != -1) {
+      System.out.println(
+        "File [" + file2 + "] longer than file [" + file1 + "].");
+      outputFile(testClass, file1);
+      outputFile(testClass, file2);
+
+      return false;
+    }
+
+    return true;
+  }
+
+  /** 
+   * 
+   * Prints file on the console.
+   *
+   */
+  private static void outputFile(Class testClass, String file)
+    throws IOException {
+    InputStream is = open(testClass, file);
+    BufferedReader in1 = new BufferedReader(new InputStreamReader(is));
+
+    String s1;
+    int lineCounter = 0;
+    System.out.println("--------------------------------");
+    System.out.println("Contents of " + file + ":");
+
+    while ((s1 = in1.readLine()) != null) {
+      lineCounter++;
+      System.out.print(lineCounter);
+
+      if (lineCounter < 10) {
+        System.out.print("   : ");
+      } else if (lineCounter < 100) {
+        System.out.print("  : ");
+      } else if (lineCounter < 1000) {
+        System.out.print(" : ");
+      } else {
+        System.out.print(": ");
+      }
+
+      System.out.println(s1);
+    }
+    in1.close();
+  }
+
+
+    public static boolean gzCompare(final Class testClass,
+                                    final String actual,
+                                    final String expected)
+      throws FileNotFoundException, IOException {
+      String resourceName = expected;
+      int lastSlash = expected.lastIndexOf("/");
+      if (lastSlash >= 0) {
+          resourceName = expected.substring(lastSlash + 1);
+      }
+      InputStream resourceStream = testClass.getResourceAsStream(resourceName);
+      if (resourceStream == null) {
+          throw new FileNotFoundException("Could not locate resource " + resourceName);
+      }
+
+      BufferedReader in1 = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(actual))));
+      BufferedReader in2 = new BufferedReader(new InputStreamReader(new GZIPInputStream(resourceStream)));
+      try {
+        return gzCompare(testClass, actual, expected, in1, in2);
+      } finally {
+        in1.close();
+        in2.close();
+      }
+    }
+
+    public static boolean gzCompare(Class testClass, String file1, String file2, BufferedReader in1, BufferedReader in2) throws IOException {
+
+      String s1;
+      int lineCounter = 0;
+
+      while ((s1 = in1.readLine()) != null) {
+        lineCounter++;
+
+        String s2 = in2.readLine();
+
+        if (!s1.equals(s2)) {
+          System.out.println(
+            "Files [" + file1 + "] and [" + file2 + "] differ on line "
+            + lineCounter);
+          System.out.println("One reads:  [" + s1 + "].");
+          System.out.println("Other reads:[" + s2 + "].");
+          outputFile(testClass, file1);
+          outputFile(testClass, file2);
+
+          return false;
+        }
+      }
+
+      // the second file is longer
+      if (in2.read() != -1) {
+        System.out.println(
+          "File [" + file2 + "] longer than file [" + file1 + "].");
+        outputFile(testClass, file1);
+        outputFile(testClass, file2);
+
+        return false;
+      }
+
+      return true;
+    }
+
+}

Added: logging/chainsaw/trunk/src/test/java/org/apache/log4j/xml/XMLDecoderTest.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/test/java/org/apache/log4j/xml/XMLDecoderTest.java?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/test/java/org/apache/log4j/xml/XMLDecoderTest.java (added)
+++ logging/chainsaw/trunk/src/test/java/org/apache/log4j/xml/XMLDecoderTest.java Mon Oct  3 06:15:40 2011
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.log4j.xml;
+
+import junit.framework.TestCase;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.CharBuffer;
+import java.util.Vector;
+import java.net.URL;
+
+/**
+ * Tests for XMLDecoder.
+ *
+ */
+public class XMLDecoderTest extends TestCase {
+
+
+  /**
+   * Constructor for XMLDecoderTest.
+   * @param arg0 test name.
+   */
+  public XMLDecoderTest(String arg0) {
+    super(arg0);
+  }
+
+  public String getStringFromResource(final String resourceName,
+                                      final int maxSize) throws Exception {
+      InputStream is = XMLDecoderTest.class.getResourceAsStream(resourceName);
+      if (is == null) {
+          throw new FileNotFoundException(resourceName);
+      }
+      InputStreamReader reader = new InputStreamReader(is, "UTF-8");
+      CharBuffer cb = CharBuffer.allocate(maxSize);
+      for(int chars = reader.read(cb);
+          chars != -1;
+          chars = reader.read(cb));
+      cb.flip();
+      return cb.toString();
+  }
+
+    public void testDecodeEventsString1() throws Exception {
+        String xmlStr = getStringFromResource("xmlLayout.1.xml", 10000);
+        XMLDecoder decoder = new XMLDecoder();
+        Vector events = decoder.decodeEvents(xmlStr);
+        assertEquals(17, events.size());
+    }
+
+  public void testDecodeEventsString2() throws Exception {
+      String xmlStr = getStringFromResource("xsltLayout.1.xml", 10000);
+      XMLDecoder decoder = new XMLDecoder();
+      Vector events = decoder.decodeEvents(xmlStr);
+      assertEquals(15, events.size());
+  }
+
+    public void testDecodeEventsURL1() throws Exception {
+        URL resource = XMLDecoderTest.class.getResource("xmlLayout.1.xml");
+        XMLDecoder decoder = new XMLDecoder();
+        Vector events = decoder.decode(resource);
+        assertEquals(17, events.size());
+    }
+
+    public void testDecodeEventsURL2() throws Exception {
+        URL resource = XMLDecoderTest.class.getResource("xsltLayout.1.xml");
+        XMLDecoder decoder = new XMLDecoder();
+        Vector events = decoder.decode(resource);
+        assertEquals(15, events.size());
+    }
+
+}

Added: logging/chainsaw/trunk/src/test/resources/org/apache/log4j/db/append-with-drivermanager1.xml
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/test/resources/org/apache/log4j/db/append-with-drivermanager1.xml?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/test/resources/org/apache/log4j/db/append-with-drivermanager1.xml (added)
+++ logging/chainsaw/trunk/src/test/resources/org/apache/log4j/db/append-with-drivermanager1.xml Mon Oct  3 06:15:40 2011
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE log4j:configuration SYSTEM 'http://logging.apache.org/log4j/1.2/log4j.dtd'>
+	  
+<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' debug="true">
+	  
+  <appender name="DB" class="org.apache.log4j.db.DBAppender">
+     <param name="locationInfo" value="true"/>
+     <connectionSource class="org.apache.log4j.db.DriverManagerConnectionSource">
+       <param name="driverClass" value="org.hsqldb.jdbcDriver"/>
+       <param name="url" value="jdbc:hsqldb:mem:testdb"/>
+       <param name="user" value="sa"/>
+       <param name="password" value=""/>
+     </connectionSource>
+  </appender>
+
+  <appender name="VECTOR" class="org.apache.log4j.VectorAppender">
+  </appender>
+  
+  <!-- Prevent internal log4j DEBUG messages from polluting the output. -->
+  <logger name="org.apache.log4j.joran"><level value="INFO" /></logger>
+  <logger name="org.apache.log4j.config"><level value="INFO" /></logger>
+  <logger name="org.apache.log4j.db.DBAppender"><level value="INFO" /></logger>
+  
+  <root>
+    <level value ="debug"/>
+    <appender-ref ref="DB" />
+    <appender-ref ref="VECTOR" />
+  </root>  
+</log4j:configuration>
+
+

Added: logging/chainsaw/trunk/src/test/resources/org/apache/log4j/db/read-with-drivermanager1.xml
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/test/resources/org/apache/log4j/db/read-with-drivermanager1.xml?rev=1178304&view=auto
==============================================================================
--- logging/chainsaw/trunk/src/test/resources/org/apache/log4j/db/read-with-drivermanager1.xml (added)
+++ logging/chainsaw/trunk/src/test/resources/org/apache/log4j/db/read-with-drivermanager1.xml Mon Oct  3 06:15:40 2011
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+<!DOCTYPE log4j:configuration SYSTEM 'http://logging.apache.org/log4j/1.2/log4j.dtd'>
+
+<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' debug="true">
+	  
+  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+     <layout class="org.apache.log4j.PatternLayout">
+       <param name="ConversionPattern" value="READING %relative %level %logger - %message%n"/>
+     </layout>
+  </appender>
+  
+  <appender name="VECTOR" class="org.apache.log4j.VectorAppender">
+  </appender>
+  
+  <plugin name="DB" class="org.apache.log4j.db.DBReceiver">
+     <connectionSource class="org.apache.log4j.db.DriverManagerConnectionSource">
+       <param name="driverClass" value="org.hsqldb.jdbcDriver"/>
+       <param name="url" value="jdbc:hsqldb:mem:testdb"/>
+       <param name="user" value="sa"/>
+       <param name="password" value=""/>
+     </connectionSource>
+  </plugin>
+  
+
+  <!-- Prevent internal log4j DEBUG messages from polluting the output. -->
+  <logger name="org.apache.log4j.joran"><level value="INFO" /></logger>
+  <logger name="org.apache.log4j.config"><level value="INFO" /></logger>
+  <logger name="org.apache.log4j.db"><level value="INFO" /></logger>
+    
+  <root>
+    <level value="debug"/>
+    <appender-ref ref="VECTOR" />
+    <appender-ref ref="CONSOLE" />
+  </root>  
+</log4j:configuration>
+
+
+



Mime
View raw message