Merge branch 'modulriseify' of https://github.com/astubbs/log4j into log4j12modules-svn.
Thanks to Antony Stubbs for the work.

Conflicts:
	core/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
	modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ControlPanel.java
	modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/DetailPanel.java
	modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/EventDetails.java
	modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ExitAction.java
	modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoadXMLAction.java
	modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoggingReceiver.java
	modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/Main.java
	modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/MyTableModel.java
	modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/XMLFileHandler.java
	modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/docFiles/screen_01.png
	modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/package.html
	modules/lf5/src/main/java/org/apache/log4j/lf5/AppenderFinalizer.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/DefaultLF5Configurator.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/LF5Appender.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/Log4JLogRecord.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/LogLevel.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/LogLevelFormatException.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/LogRecord.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/LogRecordFilter.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/PassingLogRecordFilter.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/StartLogFactor5.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/util/AdapterLogRecord.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/util/DateFormatManager.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/util/LogFileParser.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/util/LogMonitorAdapter.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/util/Resource.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/util/ResourceUtils.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/util/StreamUtils.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LF5SwingUtils.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTable.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableColumn.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableModel.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java
	modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java
	modules/lf5/src/main/resources/org/apache/log4j/lf5/config/defaultconfig.properties
	modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/channelexplorer_new.gif
	modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/channelexplorer_satellite.gif
	modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/lf5_small_icon.gif
	pom.xml
	src/main/java/org/apache/log4j/chainsaw/ExitAction.java
	tests/src/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java

git-svn-id: https://svn.apache.org/repos/asf/logging/log4j/branches/log4j12modules@1345492 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644
index 0000000..3f05d8c
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,30 @@
+<!--
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-parent</artifactId>
+      <version>1.4.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.log4j</groupId>
+  <artifactId>log4j-core</artifactId>
+  <name>Apache Log4j-Core</name>
+  <description>Apache Log4j core library.</description>
+  <packaging>bundle</packaging>
+</project>
\ No newline at end of file
diff --git a/src/assembly/bin.xml b/core/src/assembly/bin.xml
similarity index 100%
rename from src/assembly/bin.xml
rename to core/src/assembly/bin.xml
diff --git a/src/changes/changes.xml b/core/src/changes/changes.xml
similarity index 100%
rename from src/changes/changes.xml
rename to core/src/changes/changes.xml
diff --git a/src/main/java/org/apache/log4j/Appender.java b/core/src/main/java/org/apache/log4j/Appender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/Appender.java
rename to core/src/main/java/org/apache/log4j/Appender.java
diff --git a/src/main/java/org/apache/log4j/AppenderSkeleton.java b/core/src/main/java/org/apache/log4j/AppenderSkeleton.java
similarity index 100%
rename from src/main/java/org/apache/log4j/AppenderSkeleton.java
rename to core/src/main/java/org/apache/log4j/AppenderSkeleton.java
diff --git a/src/main/java/org/apache/log4j/AsyncAppender.java b/core/src/main/java/org/apache/log4j/AsyncAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/AsyncAppender.java
rename to core/src/main/java/org/apache/log4j/AsyncAppender.java
diff --git a/src/main/java/org/apache/log4j/BasicConfigurator.java b/core/src/main/java/org/apache/log4j/BasicConfigurator.java
similarity index 100%
rename from src/main/java/org/apache/log4j/BasicConfigurator.java
rename to core/src/main/java/org/apache/log4j/BasicConfigurator.java
diff --git a/src/main/java/org/apache/log4j/Category.java b/core/src/main/java/org/apache/log4j/Category.java
similarity index 100%
rename from src/main/java/org/apache/log4j/Category.java
rename to core/src/main/java/org/apache/log4j/Category.java
diff --git a/src/main/java/org/apache/log4j/CategoryKey.java b/core/src/main/java/org/apache/log4j/CategoryKey.java
similarity index 100%
rename from src/main/java/org/apache/log4j/CategoryKey.java
rename to core/src/main/java/org/apache/log4j/CategoryKey.java
diff --git a/src/main/java/org/apache/log4j/ConsoleAppender.java b/core/src/main/java/org/apache/log4j/ConsoleAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/ConsoleAppender.java
rename to core/src/main/java/org/apache/log4j/ConsoleAppender.java
diff --git a/src/main/java/org/apache/log4j/DailyRollingFileAppender.java b/core/src/main/java/org/apache/log4j/DailyRollingFileAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/DailyRollingFileAppender.java
rename to core/src/main/java/org/apache/log4j/DailyRollingFileAppender.java
diff --git a/src/main/java/org/apache/log4j/DefaultCategoryFactory.java b/core/src/main/java/org/apache/log4j/DefaultCategoryFactory.java
similarity index 100%
rename from src/main/java/org/apache/log4j/DefaultCategoryFactory.java
rename to core/src/main/java/org/apache/log4j/DefaultCategoryFactory.java
diff --git a/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java b/core/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
rename to core/src/main/java/org/apache/log4j/DefaultThrowableRenderer.java
diff --git a/src/main/java/org/apache/log4j/Dispatcher.java b/core/src/main/java/org/apache/log4j/Dispatcher.java
similarity index 100%
rename from src/main/java/org/apache/log4j/Dispatcher.java
rename to core/src/main/java/org/apache/log4j/Dispatcher.java
diff --git a/src/main/java/org/apache/log4j/EnhancedPatternLayout.java b/core/src/main/java/org/apache/log4j/EnhancedPatternLayout.java
similarity index 100%
rename from src/main/java/org/apache/log4j/EnhancedPatternLayout.java
rename to core/src/main/java/org/apache/log4j/EnhancedPatternLayout.java
diff --git a/src/main/java/org/apache/log4j/EnhancedThrowableRenderer.java b/core/src/main/java/org/apache/log4j/EnhancedThrowableRenderer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/EnhancedThrowableRenderer.java
rename to core/src/main/java/org/apache/log4j/EnhancedThrowableRenderer.java
diff --git a/src/main/java/org/apache/log4j/FileAppender.java b/core/src/main/java/org/apache/log4j/FileAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/FileAppender.java
rename to core/src/main/java/org/apache/log4j/FileAppender.java
diff --git a/src/main/java/org/apache/log4j/HTMLLayout.java b/core/src/main/java/org/apache/log4j/HTMLLayout.java
similarity index 100%
rename from src/main/java/org/apache/log4j/HTMLLayout.java
rename to core/src/main/java/org/apache/log4j/HTMLLayout.java
diff --git a/src/main/java/org/apache/log4j/Hierarchy.java b/core/src/main/java/org/apache/log4j/Hierarchy.java
similarity index 100%
rename from src/main/java/org/apache/log4j/Hierarchy.java
rename to core/src/main/java/org/apache/log4j/Hierarchy.java
diff --git a/src/main/java/org/apache/log4j/Layout.java b/core/src/main/java/org/apache/log4j/Layout.java
similarity index 100%
rename from src/main/java/org/apache/log4j/Layout.java
rename to core/src/main/java/org/apache/log4j/Layout.java
diff --git a/src/main/java/org/apache/log4j/Level.java b/core/src/main/java/org/apache/log4j/Level.java
similarity index 100%
rename from src/main/java/org/apache/log4j/Level.java
rename to core/src/main/java/org/apache/log4j/Level.java
diff --git a/src/main/java/org/apache/log4j/LogMF.java b/core/src/main/java/org/apache/log4j/LogMF.java
similarity index 100%
rename from src/main/java/org/apache/log4j/LogMF.java
rename to core/src/main/java/org/apache/log4j/LogMF.java
diff --git a/src/main/java/org/apache/log4j/LogManager.java b/core/src/main/java/org/apache/log4j/LogManager.java
similarity index 100%
rename from src/main/java/org/apache/log4j/LogManager.java
rename to core/src/main/java/org/apache/log4j/LogManager.java
diff --git a/src/main/java/org/apache/log4j/LogSF.java b/core/src/main/java/org/apache/log4j/LogSF.java
similarity index 100%
rename from src/main/java/org/apache/log4j/LogSF.java
rename to core/src/main/java/org/apache/log4j/LogSF.java
diff --git a/src/main/java/org/apache/log4j/LogXF.java b/core/src/main/java/org/apache/log4j/LogXF.java
similarity index 100%
rename from src/main/java/org/apache/log4j/LogXF.java
rename to core/src/main/java/org/apache/log4j/LogXF.java
diff --git a/src/main/java/org/apache/log4j/Logger.java b/core/src/main/java/org/apache/log4j/Logger.java
similarity index 100%
rename from src/main/java/org/apache/log4j/Logger.java
rename to core/src/main/java/org/apache/log4j/Logger.java
diff --git a/src/main/java/org/apache/log4j/MDC.java b/core/src/main/java/org/apache/log4j/MDC.java
similarity index 100%
rename from src/main/java/org/apache/log4j/MDC.java
rename to core/src/main/java/org/apache/log4j/MDC.java
diff --git a/src/main/java/org/apache/log4j/NDC.java b/core/src/main/java/org/apache/log4j/NDC.java
similarity index 100%
rename from src/main/java/org/apache/log4j/NDC.java
rename to core/src/main/java/org/apache/log4j/NDC.java
diff --git a/src/main/java/org/apache/log4j/PatternLayout.java b/core/src/main/java/org/apache/log4j/PatternLayout.java
similarity index 100%
rename from src/main/java/org/apache/log4j/PatternLayout.java
rename to core/src/main/java/org/apache/log4j/PatternLayout.java
diff --git a/src/main/java/org/apache/log4j/Priority.java b/core/src/main/java/org/apache/log4j/Priority.java
similarity index 100%
rename from src/main/java/org/apache/log4j/Priority.java
rename to core/src/main/java/org/apache/log4j/Priority.java
diff --git a/src/main/java/org/apache/log4j/PropertyConfigurator.java b/core/src/main/java/org/apache/log4j/PropertyConfigurator.java
similarity index 100%
rename from src/main/java/org/apache/log4j/PropertyConfigurator.java
rename to core/src/main/java/org/apache/log4j/PropertyConfigurator.java
diff --git a/src/main/java/org/apache/log4j/ProvisionNode.java b/core/src/main/java/org/apache/log4j/ProvisionNode.java
similarity index 100%
rename from src/main/java/org/apache/log4j/ProvisionNode.java
rename to core/src/main/java/org/apache/log4j/ProvisionNode.java
diff --git a/src/main/java/org/apache/log4j/RollingFileAppender.java b/core/src/main/java/org/apache/log4j/RollingFileAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/RollingFileAppender.java
rename to core/src/main/java/org/apache/log4j/RollingFileAppender.java
diff --git a/src/main/java/org/apache/log4j/SimpleLayout.java b/core/src/main/java/org/apache/log4j/SimpleLayout.java
similarity index 100%
rename from src/main/java/org/apache/log4j/SimpleLayout.java
rename to core/src/main/java/org/apache/log4j/SimpleLayout.java
diff --git a/src/main/java/org/apache/log4j/TTCCLayout.java b/core/src/main/java/org/apache/log4j/TTCCLayout.java
similarity index 100%
rename from src/main/java/org/apache/log4j/TTCCLayout.java
rename to core/src/main/java/org/apache/log4j/TTCCLayout.java
diff --git a/src/main/java/org/apache/log4j/WriterAppender.java b/core/src/main/java/org/apache/log4j/WriterAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/WriterAppender.java
rename to core/src/main/java/org/apache/log4j/WriterAppender.java
diff --git a/src/main/java/org/apache/log4j/config/PropertyGetter.java b/core/src/main/java/org/apache/log4j/config/PropertyGetter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/config/PropertyGetter.java
rename to core/src/main/java/org/apache/log4j/config/PropertyGetter.java
diff --git a/src/main/java/org/apache/log4j/config/PropertyPrinter.java b/core/src/main/java/org/apache/log4j/config/PropertyPrinter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/config/PropertyPrinter.java
rename to core/src/main/java/org/apache/log4j/config/PropertyPrinter.java
diff --git a/src/main/java/org/apache/log4j/config/PropertySetter.java b/core/src/main/java/org/apache/log4j/config/PropertySetter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/config/PropertySetter.java
rename to core/src/main/java/org/apache/log4j/config/PropertySetter.java
diff --git a/src/main/java/org/apache/log4j/config/PropertySetterException.java b/core/src/main/java/org/apache/log4j/config/PropertySetterException.java
similarity index 100%
rename from src/main/java/org/apache/log4j/config/PropertySetterException.java
rename to core/src/main/java/org/apache/log4j/config/PropertySetterException.java
diff --git a/src/main/java/org/apache/log4j/config/package.html b/core/src/main/java/org/apache/log4j/config/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/config/package.html
rename to core/src/main/java/org/apache/log4j/config/package.html
diff --git a/src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java b/core/src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java
rename to core/src/main/java/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java
diff --git a/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java b/core/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
rename to core/src/main/java/org/apache/log4j/helpers/AppenderAttachableImpl.java
diff --git a/src/main/java/org/apache/log4j/helpers/BoundedFIFO.java b/core/src/main/java/org/apache/log4j/helpers/BoundedFIFO.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/BoundedFIFO.java
rename to core/src/main/java/org/apache/log4j/helpers/BoundedFIFO.java
diff --git a/src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java b/core/src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java
rename to core/src/main/java/org/apache/log4j/helpers/CountingQuietWriter.java
diff --git a/src/main/java/org/apache/log4j/helpers/CyclicBuffer.java b/core/src/main/java/org/apache/log4j/helpers/CyclicBuffer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/CyclicBuffer.java
rename to core/src/main/java/org/apache/log4j/helpers/CyclicBuffer.java
diff --git a/src/main/java/org/apache/log4j/helpers/DateLayout.java b/core/src/main/java/org/apache/log4j/helpers/DateLayout.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/DateLayout.java
rename to core/src/main/java/org/apache/log4j/helpers/DateLayout.java
diff --git a/src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java b/core/src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java
rename to core/src/main/java/org/apache/log4j/helpers/DateTimeDateFormat.java
diff --git a/src/main/java/org/apache/log4j/helpers/FileWatchdog.java b/core/src/main/java/org/apache/log4j/helpers/FileWatchdog.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/FileWatchdog.java
rename to core/src/main/java/org/apache/log4j/helpers/FileWatchdog.java
diff --git a/src/main/java/org/apache/log4j/helpers/FormattingInfo.java b/core/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/FormattingInfo.java
rename to core/src/main/java/org/apache/log4j/helpers/FormattingInfo.java
diff --git a/src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java b/core/src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java
rename to core/src/main/java/org/apache/log4j/helpers/ISO8601DateFormat.java
diff --git a/src/main/java/org/apache/log4j/helpers/Loader.java b/core/src/main/java/org/apache/log4j/helpers/Loader.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/Loader.java
rename to core/src/main/java/org/apache/log4j/helpers/Loader.java
diff --git a/src/main/java/org/apache/log4j/helpers/LogLog.java b/core/src/main/java/org/apache/log4j/helpers/LogLog.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/LogLog.java
rename to core/src/main/java/org/apache/log4j/helpers/LogLog.java
diff --git a/src/main/java/org/apache/log4j/helpers/MDCKeySetExtractor.java b/core/src/main/java/org/apache/log4j/helpers/MDCKeySetExtractor.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/MDCKeySetExtractor.java
rename to core/src/main/java/org/apache/log4j/helpers/MDCKeySetExtractor.java
diff --git a/src/main/java/org/apache/log4j/helpers/NullEnumeration.java b/core/src/main/java/org/apache/log4j/helpers/NullEnumeration.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/NullEnumeration.java
rename to core/src/main/java/org/apache/log4j/helpers/NullEnumeration.java
diff --git a/src/main/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java b/core/src/main/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java
rename to core/src/main/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java
diff --git a/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/core/src/main/java/org/apache/log4j/helpers/OptionConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/OptionConverter.java
rename to core/src/main/java/org/apache/log4j/helpers/OptionConverter.java
diff --git a/src/main/java/org/apache/log4j/helpers/PatternConverter.java b/core/src/main/java/org/apache/log4j/helpers/PatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/PatternConverter.java
rename to core/src/main/java/org/apache/log4j/helpers/PatternConverter.java
diff --git a/src/main/java/org/apache/log4j/helpers/PatternParser.java b/core/src/main/java/org/apache/log4j/helpers/PatternParser.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/PatternParser.java
rename to core/src/main/java/org/apache/log4j/helpers/PatternParser.java
diff --git a/src/main/java/org/apache/log4j/helpers/QuietWriter.java b/core/src/main/java/org/apache/log4j/helpers/QuietWriter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/QuietWriter.java
rename to core/src/main/java/org/apache/log4j/helpers/QuietWriter.java
diff --git a/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java b/core/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java
rename to core/src/main/java/org/apache/log4j/helpers/RelativeTimeDateFormat.java
diff --git a/src/main/java/org/apache/log4j/helpers/SyslogQuietWriter.java b/core/src/main/java/org/apache/log4j/helpers/SyslogQuietWriter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/SyslogQuietWriter.java
rename to core/src/main/java/org/apache/log4j/helpers/SyslogQuietWriter.java
diff --git a/src/main/java/org/apache/log4j/helpers/SyslogWriter.java b/core/src/main/java/org/apache/log4j/helpers/SyslogWriter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/SyslogWriter.java
rename to core/src/main/java/org/apache/log4j/helpers/SyslogWriter.java
diff --git a/src/main/java/org/apache/log4j/helpers/ThreadLocalMap.java b/core/src/main/java/org/apache/log4j/helpers/ThreadLocalMap.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/ThreadLocalMap.java
rename to core/src/main/java/org/apache/log4j/helpers/ThreadLocalMap.java
diff --git a/src/main/java/org/apache/log4j/helpers/Transform.java b/core/src/main/java/org/apache/log4j/helpers/Transform.java
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/Transform.java
rename to core/src/main/java/org/apache/log4j/helpers/Transform.java
diff --git a/src/main/java/org/apache/log4j/helpers/package.html b/core/src/main/java/org/apache/log4j/helpers/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/helpers/package.html
rename to core/src/main/java/org/apache/log4j/helpers/package.html
diff --git a/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java b/core/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/jdbc/JDBCAppender.java
rename to core/src/main/java/org/apache/log4j/jdbc/JDBCAppender.java
diff --git a/src/main/java/org/apache/log4j/jdbc/package.html b/core/src/main/java/org/apache/log4j/jdbc/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/jdbc/package.html
rename to core/src/main/java/org/apache/log4j/jdbc/package.html
diff --git a/src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java b/core/src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java
similarity index 100%
rename from src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java
rename to core/src/main/java/org/apache/log4j/jmx/AbstractDynamicMBean.java
diff --git a/src/main/java/org/apache/log4j/jmx/Agent.java b/core/src/main/java/org/apache/log4j/jmx/Agent.java
similarity index 100%
rename from src/main/java/org/apache/log4j/jmx/Agent.java
rename to core/src/main/java/org/apache/log4j/jmx/Agent.java
diff --git a/src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java b/core/src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java
similarity index 100%
rename from src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java
rename to core/src/main/java/org/apache/log4j/jmx/AppenderDynamicMBean.java
diff --git a/src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java b/core/src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java
similarity index 100%
rename from src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java
rename to core/src/main/java/org/apache/log4j/jmx/HierarchyDynamicMBean.java
diff --git a/src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java b/core/src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java
similarity index 100%
rename from src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java
rename to core/src/main/java/org/apache/log4j/jmx/LayoutDynamicMBean.java
diff --git a/src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java b/core/src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java
similarity index 100%
rename from src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java
rename to core/src/main/java/org/apache/log4j/jmx/LoggerDynamicMBean.java
diff --git a/src/main/java/org/apache/log4j/jmx/MethodUnion.java b/core/src/main/java/org/apache/log4j/jmx/MethodUnion.java
similarity index 100%
rename from src/main/java/org/apache/log4j/jmx/MethodUnion.java
rename to core/src/main/java/org/apache/log4j/jmx/MethodUnion.java
diff --git a/src/main/java/org/apache/log4j/jmx/package.html b/core/src/main/java/org/apache/log4j/jmx/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/jmx/package.html
rename to core/src/main/java/org/apache/log4j/jmx/package.html
diff --git a/src/main/java/org/apache/log4j/or/DefaultRenderer.java b/core/src/main/java/org/apache/log4j/or/DefaultRenderer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/or/DefaultRenderer.java
rename to core/src/main/java/org/apache/log4j/or/DefaultRenderer.java
diff --git a/src/main/java/org/apache/log4j/or/ObjectRenderer.java b/core/src/main/java/org/apache/log4j/or/ObjectRenderer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/or/ObjectRenderer.java
rename to core/src/main/java/org/apache/log4j/or/ObjectRenderer.java
diff --git a/src/main/java/org/apache/log4j/or/RendererMap.java b/core/src/main/java/org/apache/log4j/or/RendererMap.java
similarity index 100%
rename from src/main/java/org/apache/log4j/or/RendererMap.java
rename to core/src/main/java/org/apache/log4j/or/RendererMap.java
diff --git a/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java b/core/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
rename to core/src/main/java/org/apache/log4j/or/ThreadGroupRenderer.java
diff --git a/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java b/core/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/or/jms/MessageRenderer.java
rename to core/src/main/java/org/apache/log4j/or/jms/MessageRenderer.java
diff --git a/src/main/java/org/apache/log4j/or/jms/package.html b/core/src/main/java/org/apache/log4j/or/jms/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/or/jms/package.html
rename to core/src/main/java/org/apache/log4j/or/jms/package.html
diff --git a/src/main/java/org/apache/log4j/or/package.html b/core/src/main/java/org/apache/log4j/or/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/or/package.html
rename to core/src/main/java/org/apache/log4j/or/package.html
diff --git a/src/main/java/org/apache/log4j/or/sax/AttributesRenderer.java b/core/src/main/java/org/apache/log4j/or/sax/AttributesRenderer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/or/sax/AttributesRenderer.java
rename to core/src/main/java/org/apache/log4j/or/sax/AttributesRenderer.java
diff --git a/src/main/java/org/apache/log4j/or/sax/package.html b/core/src/main/java/org/apache/log4j/or/sax/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/or/sax/package.html
rename to core/src/main/java/org/apache/log4j/or/sax/package.html
diff --git a/src/main/java/org/apache/log4j/package.html b/core/src/main/java/org/apache/log4j/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/package.html
rename to core/src/main/java/org/apache/log4j/package.html
diff --git a/src/main/java/org/apache/log4j/pattern/BridgePatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/BridgePatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/BridgePatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/BridgePatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/BridgePatternParser.java b/core/src/main/java/org/apache/log4j/pattern/BridgePatternParser.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/BridgePatternParser.java
rename to core/src/main/java/org/apache/log4j/pattern/BridgePatternParser.java
diff --git a/src/main/java/org/apache/log4j/pattern/CachedDateFormat.java b/core/src/main/java/org/apache/log4j/pattern/CachedDateFormat.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/CachedDateFormat.java
rename to core/src/main/java/org/apache/log4j/pattern/CachedDateFormat.java
diff --git a/src/main/java/org/apache/log4j/pattern/ClassNamePatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/ClassNamePatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/ClassNamePatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/ClassNamePatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/DatePatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/DatePatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/DatePatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/DatePatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/FileDatePatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/FileDatePatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/FileDatePatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/FileDatePatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/FileLocationPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/FileLocationPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/FileLocationPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/FileLocationPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/FormattingInfo.java b/core/src/main/java/org/apache/log4j/pattern/FormattingInfo.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/FormattingInfo.java
rename to core/src/main/java/org/apache/log4j/pattern/FormattingInfo.java
diff --git a/src/main/java/org/apache/log4j/pattern/FullLocationPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/FullLocationPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/FullLocationPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/FullLocationPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/IntegerPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/IntegerPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/IntegerPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/IntegerPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/LevelPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/LevelPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/LevelPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/LevelPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/LineLocationPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/LineLocationPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/LineLocationPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/LineLocationPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/LineSeparatorPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/LineSeparatorPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/LineSeparatorPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/LineSeparatorPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/LiteralPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/LiteralPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/LiteralPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/LiteralPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/LogEvent.java b/core/src/main/java/org/apache/log4j/pattern/LogEvent.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/LogEvent.java
rename to core/src/main/java/org/apache/log4j/pattern/LogEvent.java
diff --git a/src/main/java/org/apache/log4j/pattern/LoggerPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/LoggerPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/LoggerPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/LoggerPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/LoggingEventPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/LoggingEventPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/LoggingEventPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/LoggingEventPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/MessagePatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/MessagePatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/MessagePatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/MessagePatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/MethodLocationPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/MethodLocationPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/MethodLocationPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/MethodLocationPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/NDCPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/NDCPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/NDCPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/NDCPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java b/core/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
rename to core/src/main/java/org/apache/log4j/pattern/NameAbbreviator.java
diff --git a/src/main/java/org/apache/log4j/pattern/NamePatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/NamePatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/NamePatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/NamePatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/PatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/PatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/PatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/PatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/PatternParser.java b/core/src/main/java/org/apache/log4j/pattern/PatternParser.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/PatternParser.java
rename to core/src/main/java/org/apache/log4j/pattern/PatternParser.java
diff --git a/src/main/java/org/apache/log4j/pattern/PropertiesPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/PropertiesPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/PropertiesPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/PropertiesPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/RelativeTimePatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/RelativeTimePatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/RelativeTimePatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/RelativeTimePatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/SequenceNumberPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/SequenceNumberPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/SequenceNumberPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/SequenceNumberPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/ThreadPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/ThreadPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/ThreadPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/ThreadPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java b/core/src/main/java/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java
rename to core/src/main/java/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java
diff --git a/src/main/java/org/apache/log4j/pattern/package.html b/core/src/main/java/org/apache/log4j/pattern/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/pattern/package.html
rename to core/src/main/java/org/apache/log4j/pattern/package.html
diff --git a/src/main/java/org/apache/log4j/spi/AppenderAttachable.java b/core/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/AppenderAttachable.java
rename to core/src/main/java/org/apache/log4j/spi/AppenderAttachable.java
diff --git a/src/main/java/org/apache/log4j/spi/Configurator.java b/core/src/main/java/org/apache/log4j/spi/Configurator.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/Configurator.java
rename to core/src/main/java/org/apache/log4j/spi/Configurator.java
diff --git a/src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java b/core/src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java
rename to core/src/main/java/org/apache/log4j/spi/DefaultRepositorySelector.java
diff --git a/src/main/java/org/apache/log4j/spi/ErrorCode.java b/core/src/main/java/org/apache/log4j/spi/ErrorCode.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/ErrorCode.java
rename to core/src/main/java/org/apache/log4j/spi/ErrorCode.java
diff --git a/src/main/java/org/apache/log4j/spi/ErrorHandler.java b/core/src/main/java/org/apache/log4j/spi/ErrorHandler.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/ErrorHandler.java
rename to core/src/main/java/org/apache/log4j/spi/ErrorHandler.java
diff --git a/src/main/java/org/apache/log4j/spi/Filter.java b/core/src/main/java/org/apache/log4j/spi/Filter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/Filter.java
rename to core/src/main/java/org/apache/log4j/spi/Filter.java
diff --git a/src/main/java/org/apache/log4j/spi/HierarchyEventListener.java b/core/src/main/java/org/apache/log4j/spi/HierarchyEventListener.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/HierarchyEventListener.java
rename to core/src/main/java/org/apache/log4j/spi/HierarchyEventListener.java
diff --git a/src/main/java/org/apache/log4j/spi/LocationInfo.java b/core/src/main/java/org/apache/log4j/spi/LocationInfo.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/LocationInfo.java
rename to core/src/main/java/org/apache/log4j/spi/LocationInfo.java
diff --git a/src/main/java/org/apache/log4j/spi/LoggerFactory.java b/core/src/main/java/org/apache/log4j/spi/LoggerFactory.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/LoggerFactory.java
rename to core/src/main/java/org/apache/log4j/spi/LoggerFactory.java
diff --git a/src/main/java/org/apache/log4j/spi/LoggerRepository.java b/core/src/main/java/org/apache/log4j/spi/LoggerRepository.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/LoggerRepository.java
rename to core/src/main/java/org/apache/log4j/spi/LoggerRepository.java
diff --git a/src/main/java/org/apache/log4j/spi/LoggingEvent.java b/core/src/main/java/org/apache/log4j/spi/LoggingEvent.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/LoggingEvent.java
rename to core/src/main/java/org/apache/log4j/spi/LoggingEvent.java
diff --git a/src/main/java/org/apache/log4j/spi/NOPLogger.java b/core/src/main/java/org/apache/log4j/spi/NOPLogger.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/NOPLogger.java
rename to core/src/main/java/org/apache/log4j/spi/NOPLogger.java
diff --git a/src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java b/core/src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java
rename to core/src/main/java/org/apache/log4j/spi/NOPLoggerRepository.java
diff --git a/src/main/java/org/apache/log4j/spi/NullWriter.java b/core/src/main/java/org/apache/log4j/spi/NullWriter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/NullWriter.java
rename to core/src/main/java/org/apache/log4j/spi/NullWriter.java
diff --git a/src/main/java/org/apache/log4j/spi/OptionHandler.java b/core/src/main/java/org/apache/log4j/spi/OptionHandler.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/OptionHandler.java
rename to core/src/main/java/org/apache/log4j/spi/OptionHandler.java
diff --git a/src/main/java/org/apache/log4j/spi/RendererSupport.java b/core/src/main/java/org/apache/log4j/spi/RendererSupport.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/RendererSupport.java
rename to core/src/main/java/org/apache/log4j/spi/RendererSupport.java
diff --git a/src/main/java/org/apache/log4j/spi/RepositorySelector.java b/core/src/main/java/org/apache/log4j/spi/RepositorySelector.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/RepositorySelector.java
rename to core/src/main/java/org/apache/log4j/spi/RepositorySelector.java
diff --git a/src/main/java/org/apache/log4j/spi/RootCategory.java b/core/src/main/java/org/apache/log4j/spi/RootCategory.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/RootCategory.java
rename to core/src/main/java/org/apache/log4j/spi/RootCategory.java
diff --git a/src/main/java/org/apache/log4j/spi/RootLogger.java b/core/src/main/java/org/apache/log4j/spi/RootLogger.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/RootLogger.java
rename to core/src/main/java/org/apache/log4j/spi/RootLogger.java
diff --git a/src/main/java/org/apache/log4j/spi/ThrowableInformation.java b/core/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/ThrowableInformation.java
rename to core/src/main/java/org/apache/log4j/spi/ThrowableInformation.java
diff --git a/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java b/core/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/ThrowableRenderer.java
rename to core/src/main/java/org/apache/log4j/spi/ThrowableRenderer.java
diff --git a/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java b/core/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java
rename to core/src/main/java/org/apache/log4j/spi/ThrowableRendererSupport.java
diff --git a/src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java b/core/src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java
rename to core/src/main/java/org/apache/log4j/spi/TriggeringEventEvaluator.java
diff --git a/src/main/java/org/apache/log4j/spi/VectorWriter.java b/core/src/main/java/org/apache/log4j/spi/VectorWriter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/VectorWriter.java
rename to core/src/main/java/org/apache/log4j/spi/VectorWriter.java
diff --git a/src/main/java/org/apache/log4j/spi/package.html b/core/src/main/java/org/apache/log4j/spi/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/spi/package.html
rename to core/src/main/java/org/apache/log4j/spi/package.html
diff --git a/src/main/java/org/apache/log4j/varia/DenyAllFilter.java b/core/src/main/java/org/apache/log4j/varia/DenyAllFilter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/DenyAllFilter.java
rename to core/src/main/java/org/apache/log4j/varia/DenyAllFilter.java
diff --git a/src/main/java/org/apache/log4j/varia/ExternallyRolledFileAppender.java b/core/src/main/java/org/apache/log4j/varia/ExternallyRolledFileAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/ExternallyRolledFileAppender.java
rename to core/src/main/java/org/apache/log4j/varia/ExternallyRolledFileAppender.java
diff --git a/src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java b/core/src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java
rename to core/src/main/java/org/apache/log4j/varia/FallbackErrorHandler.java
diff --git a/src/main/java/org/apache/log4j/varia/LevelMatchFilter.java b/core/src/main/java/org/apache/log4j/varia/LevelMatchFilter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/LevelMatchFilter.java
rename to core/src/main/java/org/apache/log4j/varia/LevelMatchFilter.java
diff --git a/src/main/java/org/apache/log4j/varia/LevelRangeFilter.java b/core/src/main/java/org/apache/log4j/varia/LevelRangeFilter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/LevelRangeFilter.java
rename to core/src/main/java/org/apache/log4j/varia/LevelRangeFilter.java
diff --git a/src/main/java/org/apache/log4j/varia/NullAppender.java b/core/src/main/java/org/apache/log4j/varia/NullAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/NullAppender.java
rename to core/src/main/java/org/apache/log4j/varia/NullAppender.java
diff --git a/src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java b/core/src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java
rename to core/src/main/java/org/apache/log4j/varia/ReloadingPropertyConfigurator.java
diff --git a/src/main/java/org/apache/log4j/varia/Roller.java b/core/src/main/java/org/apache/log4j/varia/Roller.java
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/Roller.java
rename to core/src/main/java/org/apache/log4j/varia/Roller.java
diff --git a/src/main/java/org/apache/log4j/varia/StringMatchFilter.java b/core/src/main/java/org/apache/log4j/varia/StringMatchFilter.java
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/StringMatchFilter.java
rename to core/src/main/java/org/apache/log4j/varia/StringMatchFilter.java
diff --git a/src/main/java/org/apache/log4j/varia/package.html b/core/src/main/java/org/apache/log4j/varia/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/varia/package.html
rename to core/src/main/java/org/apache/log4j/varia/package.html
diff --git a/src/main/java/org/apache/log4j/xml/DOMConfigurator.java b/core/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
similarity index 100%
rename from src/main/java/org/apache/log4j/xml/DOMConfigurator.java
rename to core/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
diff --git a/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java b/core/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
similarity index 100%
rename from src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
rename to core/src/main/java/org/apache/log4j/xml/Log4jEntityResolver.java
diff --git a/src/main/java/org/apache/log4j/xml/SAXErrorHandler.java b/core/src/main/java/org/apache/log4j/xml/SAXErrorHandler.java
similarity index 100%
rename from src/main/java/org/apache/log4j/xml/SAXErrorHandler.java
rename to core/src/main/java/org/apache/log4j/xml/SAXErrorHandler.java
diff --git a/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java b/core/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java
similarity index 100%
rename from src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java
rename to core/src/main/java/org/apache/log4j/xml/UnrecognizedElementHandler.java
diff --git a/src/main/java/org/apache/log4j/xml/XMLLayout.java b/core/src/main/java/org/apache/log4j/xml/XMLLayout.java
similarity index 100%
rename from src/main/java/org/apache/log4j/xml/XMLLayout.java
rename to core/src/main/java/org/apache/log4j/xml/XMLLayout.java
diff --git a/src/main/java/org/apache/log4j/xml/package.html b/core/src/main/java/org/apache/log4j/xml/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/xml/package.html
rename to core/src/main/java/org/apache/log4j/xml/package.html
diff --git a/src/main/javadoc/org/apache/log4j/xml/doc-files/log4j.dtd b/core/src/main/javadoc/org/apache/log4j/xml/docFiles/log4j.dtd
similarity index 100%
rename from src/main/javadoc/org/apache/log4j/xml/doc-files/log4j.dtd
rename to core/src/main/javadoc/org/apache/log4j/xml/docFiles/log4j.dtd
diff --git a/src/main/javadoc/org/apache/log4j/xml/examples/XMLSample.java b/core/src/main/javadoc/org/apache/log4j/xml/examples/XMLSample.java
similarity index 100%
rename from src/main/javadoc/org/apache/log4j/xml/examples/XMLSample.java
rename to core/src/main/javadoc/org/apache/log4j/xml/examples/XMLSample.java
diff --git a/src/main/javadoc/org/apache/log4j/xml/examples/doc-files/XMLSample.java b/core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/XMLSample.java
similarity index 100%
rename from src/main/javadoc/org/apache/log4j/xml/examples/doc-files/XMLSample.java
rename to core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/XMLSample.java
diff --git a/src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample1.xml b/core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample1.xml
similarity index 100%
rename from src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample1.xml
rename to core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample1.xml
diff --git a/src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample2.xml b/core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample2.xml
similarity index 100%
rename from src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample2.xml
rename to core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample2.xml
diff --git a/src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample3.xml b/core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample3.xml
similarity index 100%
rename from src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample3.xml
rename to core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample3.xml
diff --git a/src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample4.xml b/core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample4.xml
similarity index 100%
rename from src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample4.xml
rename to core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample4.xml
diff --git a/src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample5.xml b/core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample5.xml
similarity index 100%
rename from src/main/javadoc/org/apache/log4j/xml/examples/doc-files/sample5.xml
rename to core/src/main/javadoc/org/apache/log4j/xml/examples/docFiles/sample5.xml
diff --git a/src/main/resources/META-INF/LICENSE b/core/src/main/resources/META-INF/LICENSE
similarity index 100%
rename from src/main/resources/META-INF/LICENSE
rename to core/src/main/resources/META-INF/LICENSE
diff --git a/src/main/resources/META-INF/NOTICE b/core/src/main/resources/META-INF/NOTICE
similarity index 100%
rename from src/main/resources/META-INF/NOTICE
rename to core/src/main/resources/META-INF/NOTICE
diff --git a/src/main/resources/org/apache/log4j/xml/log4j.dtd b/core/src/main/resources/org/apache/log4j/xml/log4j.dtd
similarity index 100%
rename from src/main/resources/org/apache/log4j/xml/log4j.dtd
rename to core/src/main/resources/org/apache/log4j/xml/log4j.dtd
diff --git a/src/site/apt/download.apt b/core/src/site/apt/download.apt
similarity index 100%
rename from src/site/apt/download.apt
rename to core/src/site/apt/download.apt
diff --git a/src/site/apt/index.apt b/core/src/site/apt/index.apt
similarity index 100%
rename from src/site/apt/index.apt
rename to core/src/site/apt/index.apt
diff --git a/src/site/apt/publications.apt b/core/src/site/apt/publications.apt
similarity index 100%
rename from src/site/apt/publications.apt
rename to core/src/site/apt/publications.apt
diff --git a/src/site/apt/roadmap.apt b/core/src/site/apt/roadmap.apt
similarity index 100%
rename from src/site/apt/roadmap.apt
rename to core/src/site/apt/roadmap.apt
diff --git a/src/site/fml/faq.fml b/core/src/site/fml/faq.fml
similarity index 100%
rename from src/site/fml/faq.fml
rename to core/src/site/fml/faq.fml
diff --git a/src/site/resources/css/site.css b/core/src/site/resources/css/site.css
similarity index 100%
rename from src/site/resources/css/site.css
rename to core/src/site/resources/css/site.css
diff --git a/src/site/resources/images/logo.jpg b/core/src/site/resources/images/logo.jpg
similarity index 100%
rename from src/site/resources/images/logo.jpg
rename to core/src/site/resources/images/logo.jpg
Binary files differ
diff --git a/src/site/resources/images/ls-logo.jpg b/core/src/site/resources/images/ls-logo.jpg
similarity index 100%
rename from src/site/resources/images/ls-logo.jpg
rename to core/src/site/resources/images/ls-logo.jpg
Binary files differ
diff --git a/src/site/resources/images/od.gif b/core/src/site/resources/images/od.gif
similarity index 100%
rename from src/site/resources/images/od.gif
rename to core/src/site/resources/images/od.gif
Binary files differ
diff --git a/src/site/site.xml b/core/src/site/site.xml
similarity index 100%
rename from src/site/site.xml
rename to core/src/site/site.xml
diff --git a/src/site/xdoc/manual.xml b/core/src/site/xdoc/manual.xml
similarity index 100%
rename from src/site/xdoc/manual.xml
rename to core/src/site/xdoc/manual.xml
diff --git a/tests/README b/core/src/test/README
similarity index 100%
rename from tests/README
rename to core/src/test/README
diff --git a/tests/build.properties.sample b/core/src/test/build.properties.sample
similarity index 100%
rename from tests/build.properties.sample
rename to core/src/test/build.properties.sample
diff --git a/tests/src/java/org/apache/log4j/AsyncAppenderTestCase.java b/core/src/test/java/org/apache/log4j/AsyncAppenderTestCase.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/AsyncAppenderTestCase.java
rename to core/src/test/java/org/apache/log4j/AsyncAppenderTestCase.java
diff --git a/tests/src/java/org/apache/log4j/CategoryTest.java b/core/src/test/java/org/apache/log4j/CategoryTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/CategoryTest.java
rename to core/src/test/java/org/apache/log4j/CategoryTest.java
diff --git a/tests/src/java/org/apache/log4j/CoreTestSuite.java b/core/src/test/java/org/apache/log4j/CoreTestSuite.java
similarity index 95%
rename from tests/src/java/org/apache/log4j/CoreTestSuite.java
rename to core/src/test/java/org/apache/log4j/CoreTestSuite.java
index 82ab97d..f72d9b5 100644
--- a/tests/src/java/org/apache/log4j/CoreTestSuite.java
+++ b/core/src/test/java/org/apache/log4j/CoreTestSuite.java
@@ -49,8 +49,6 @@
         s.addTestSuite(org.apache.log4j.spi.ThrowableInformationTest.class);
         s.addTestSuite(org.apache.log4j.spi.LocationInfoTest.class);
         s.addTestSuite(org.apache.log4j.PropertyConfiguratorTest.class);
-        s.addTestSuite(org.apache.log4j.net.SMTPAppenderTest.class);
-        s.addTestSuite(org.apache.log4j.net.TelnetAppenderTest.class);
         s.addTestSuite(org.apache.log4j.DefaultThrowableRendererTest.class);
         s.addTestSuite(org.apache.log4j.EnhancedThrowableRendererTest.class);
         s.addTestSuite(org.apache.log4j.TestLogXF.class);
diff --git a/tests/src/java/org/apache/log4j/DRFATestCase.java b/core/src/test/java/org/apache/log4j/DRFATestCase.java
similarity index 99%
rename from tests/src/java/org/apache/log4j/DRFATestCase.java
rename to core/src/test/java/org/apache/log4j/DRFATestCase.java
index 8d04174..68b72ac 100644
--- a/tests/src/java/org/apache/log4j/DRFATestCase.java
+++ b/core/src/test/java/org/apache/log4j/DRFATestCase.java
@@ -494,7 +494,7 @@
         append(combined, new FileInputStream(filename), buf);
         combined.close();
         assertTrue(Compare.compare(combinedFilename,
-                "witness/drfa_blockedRollover.log"));
+                "target/test-classes/witness/drfa_blockedRollover.log"));
     }
 
     /** Check that the computed rollover period for a pattern containing a week as the finest unit is set to be
diff --git a/tests/src/java/org/apache/log4j/DefaultThrowableRendererTest.java b/core/src/test/java/org/apache/log4j/DefaultThrowableRendererTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/DefaultThrowableRendererTest.java
rename to core/src/test/java/org/apache/log4j/DefaultThrowableRendererTest.java
diff --git a/tests/src/java/org/apache/log4j/EnhancedMyPatternLayout.java b/core/src/test/java/org/apache/log4j/EnhancedMyPatternLayout.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/EnhancedMyPatternLayout.java
rename to core/src/test/java/org/apache/log4j/EnhancedMyPatternLayout.java
diff --git a/tests/src/java/org/apache/log4j/EnhancedPatternLayoutTest.java b/core/src/test/java/org/apache/log4j/EnhancedPatternLayoutTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/EnhancedPatternLayoutTest.java
rename to core/src/test/java/org/apache/log4j/EnhancedPatternLayoutTest.java
diff --git a/tests/src/java/org/apache/log4j/EnhancedPatternLayoutTestCase.java b/core/src/test/java/org/apache/log4j/EnhancedPatternLayoutTestCase.java
similarity index 85%
rename from tests/src/java/org/apache/log4j/EnhancedPatternLayoutTestCase.java
rename to core/src/test/java/org/apache/log4j/EnhancedPatternLayoutTestCase.java
index e97c42f..34d186f 100644
--- a/tests/src/java/org/apache/log4j/EnhancedPatternLayoutTestCase.java
+++ b/core/src/test/java/org/apache/log4j/EnhancedPatternLayoutTestCase.java
@@ -40,6 +40,8 @@
 
 
 public class EnhancedPatternLayoutTestCase extends TestCase {
+  static final String FILE_PREFIX = "target/test-classes/";
+  
   static String TEMP = "output/temp";
   static String FILTERED = "output/filtered";
   static String EXCEPTION1 = "java.lang.Exception: Just testing";
@@ -111,7 +113,7 @@
   }
 
   public void test1() throws Exception {
-    configure("input/pattern/enhancedPatternLayout1.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout1.properties");
     common();
     Transformer.transform(
       TEMP, FILTERED,
@@ -119,11 +121,11 @@
         new EnhancedLineNumberFilter(), new SunReflectFilter(),
         new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.1"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.1"));
   }
 
   public void test2() throws Exception {
-    configure("input/pattern/enhancedPatternLayout2.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout2.properties");
     common();
 
     ControlFilter cf1 =
@@ -135,11 +137,11 @@
         cf1, new EnhancedLineNumberFilter(), new ISO8601Filter(),
         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.2"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.2"));
   }
 
   public void test3() throws Exception {
-    configure("input/pattern/enhancedPatternLayout3.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout3.properties");
     common();
 
     ControlFilter cf1 =
@@ -151,13 +153,13 @@
         cf1, new EnhancedLineNumberFilter(), new ISO8601Filter(),
         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.3"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.3"));
   }
 
   // Output format:
   // 06 avr. 2002 18:30:58,937 [main] DEBUG atternLayoutTest - Message 0  
   public void test4() throws Exception {
-    configure("input/pattern/enhancedPatternLayout4.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout4.properties");
     common();
 
     ControlFilter cf1 =
@@ -169,11 +171,11 @@
         cf1, new EnhancedLineNumberFilter(), new AbsoluteDateAndTimeFilter(),
         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.4"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.4"));
   }
 
   public void test5() throws Exception {
-    configure("input/pattern/enhancedPatternLayout5.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout5.properties");
     common();
 
     ControlFilter cf1 =
@@ -185,12 +187,12 @@
         cf1, new EnhancedLineNumberFilter(), new AbsoluteDateAndTimeFilter(),
         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.5"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.5"));
   }
 
   // 18:54:19,201 [main] DEBUG atternLayoutTest - Message 0
   public void test6() throws Exception {
-    configure("input/pattern/enhancedPatternLayout6.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout6.properties");
     common();
 
     ControlFilter cf1 =
@@ -202,11 +204,11 @@
         cf1, new EnhancedLineNumberFilter(), new AbsoluteTimeFilter(),
         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.6"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.6"));
   }
 
   public void test7() throws Exception {
-    configure("input/pattern/enhancedPatternLayout7.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout7.properties");
     common();
 
     ControlFilter cf1 =
@@ -218,11 +220,11 @@
         cf1, new EnhancedLineNumberFilter(), new AbsoluteTimeFilter(),
         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.7"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.7"));
   }
 
   public void test8() throws Exception {
-    configure("input/pattern/enhancedPatternLayout8.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout8.properties");
     common();
 
     ControlFilter cf1 =
@@ -234,11 +236,11 @@
         cf1, new EnhancedLineNumberFilter(), new RelativeTimeFilter(),
         new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.8"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.8"));
   }
 
   public void test9() throws Exception {
-    configure("input/pattern/enhancedPatternLayout9.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout9.properties");
     common();
 
     ControlFilter cf1 =
@@ -250,11 +252,11 @@
         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
         new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.9"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.9"));
   }
 
   public void test10() throws Exception {
-    configure("input/pattern/enhancedPatternLayout10.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout10.properties");
     common();
 
     ControlFilter cf1 =
@@ -266,11 +268,11 @@
         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
         new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.10"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.10"));
   }
 
   public void test11() throws Exception {
-    configure("input/pattern/enhancedPatternLayout11.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout11.properties");
     common();
 
     ControlFilter cf1 =
@@ -282,11 +284,11 @@
         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
         new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.11"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.11"));
   }
 
   public void test12() throws Exception {
-    configure("input/pattern/enhancedPatternLayout12.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout12.properties");
     common();
 
     ControlFilter cf1 =
@@ -298,11 +300,11 @@
         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
         new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.12"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.12"));
   }
 
   public void test13() throws Exception {
-    configure("input/pattern/enhancedPatternLayout13.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout13.properties");
     common();
 
     ControlFilter cf1 =
@@ -314,7 +316,7 @@
         cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
         new EnhancedJunitTestRunnerFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.13"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.13"));
   }
 
     /**
@@ -323,7 +325,7 @@
      * @throws Exception
      */
     public void test14() throws Exception {
-      configure("input/pattern/enhancedPatternLayout14.properties");
+      configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout14.properties");
       common();
 
       Transformer.transform(
@@ -332,7 +334,7 @@
           new EnhancedLineNumberFilter(), new SunReflectFilter(),
           new EnhancedJunitTestRunnerFilter()
         });
-      assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.14"));
+      assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.14"));
     }
 
 
@@ -344,7 +346,7 @@
     }
 
   public void testMDC1() throws Exception {
-    configure("input/pattern/enhancedPatternLayout.mdc.1.properties");
+    configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout.mdc.1.properties");
     clearMDC();
     MDC.put("key1", "va11");
     MDC.put("key2", "va12");
@@ -359,7 +361,7 @@
         new EnhancedJunitTestRunnerFilter(),
         new MDCOrderFilter()
       });
-    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.mdc.1"));
+    assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.mdc.1"));
   }
     /**
      * Tests log4j 1.2 style extension of EnhancedPatternLayout.
@@ -367,7 +369,7 @@
      * @throws Exception
      */
     public void test15() throws Exception {
-      configure("input/pattern/enhancedPatternLayout15.properties");
+      configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout15.properties");
       common();
       ControlFilter cf1 = new ControlFilter(new String[]{PAT14, EXCEPTION1,
                                  EXCEPTION2, EXCEPTION3, EXCEPTION4});
@@ -377,7 +379,7 @@
           cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
           new EnhancedJunitTestRunnerFilter()
         });
-      assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.15"));
+      assertTrue(compare(FILTERED, FILE_PREFIX + "witness/pattern/enhancedPatternLayout.15"));
     }
     /**
      * Tests explicit UTC time zone in pattern.
@@ -385,7 +387,7 @@
      */
     public void test16() throws Exception {
       final long start = new Date().getTime();
-      configure("input/pattern/enhancedPatternLayout16.properties");
+      configure(FILE_PREFIX + "input/pattern/enhancedPatternLayout16.properties");
       common();
       final long end = new Date().getTime();
       FileReader reader = new FileReader("output/patternLayout16.log");
@@ -435,7 +437,7 @@
     Test case for MDC conversion pattern. */
   public void testMDC2() throws Exception {
     String OUTPUT_FILE   = "output/patternLayout.mdc.2";
-    String WITNESS_FILE  = "witness/pattern/enhancedPatternLayout.mdc.2";
+    String WITNESS_FILE  = FILE_PREFIX + "witness/pattern/enhancedPatternLayout.mdc.2";
     
     String mdcMsgPattern1 = "%m : %X%n";
     String mdcMsgPattern2 = "%m : %X{key1}%n";
@@ -520,7 +522,7 @@
     Test case for throwable conversion pattern. */
   public void testThrowable() throws Exception {
     String OUTPUT_FILE   = "output/patternLayout.throwable";
-    String WITNESS_FILE  = "witness/pattern/enhancedPatternLayout.throwable";
+    String WITNESS_FILE  = FILE_PREFIX + "witness/pattern/enhancedPatternLayout.throwable";
     
     
     // set up appender
diff --git a/tests/src/java/org/apache/log4j/EnhancedThrowableRendererTest.java b/core/src/test/java/org/apache/log4j/EnhancedThrowableRendererTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/EnhancedThrowableRendererTest.java
rename to core/src/test/java/org/apache/log4j/EnhancedThrowableRendererTest.java
diff --git a/tests/src/java/org/apache/log4j/FileAppenderTest.java b/core/src/test/java/org/apache/log4j/FileAppenderTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/FileAppenderTest.java
rename to core/src/test/java/org/apache/log4j/FileAppenderTest.java
diff --git a/tests/src/java/org/apache/log4j/HTMLLayoutTest.java b/core/src/test/java/org/apache/log4j/HTMLLayoutTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/HTMLLayoutTest.java
rename to core/src/test/java/org/apache/log4j/HTMLLayoutTest.java
diff --git a/tests/src/java/org/apache/log4j/HierarchyThresholdTestCase.java b/core/src/test/java/org/apache/log4j/HierarchyThresholdTestCase.java
similarity index 66%
rename from tests/src/java/org/apache/log4j/HierarchyThresholdTestCase.java
rename to core/src/test/java/org/apache/log4j/HierarchyThresholdTestCase.java
index 3e885bd..7ae45c3 100644
--- a/tests/src/java/org/apache/log4j/HierarchyThresholdTestCase.java
+++ b/core/src/test/java/org/apache/log4j/HierarchyThresholdTestCase.java
@@ -30,6 +30,10 @@
    @author  Ceki G&uuml;lc&uuml;
 */
 public class HierarchyThresholdTestCase extends TestCase {
+
+  static final String FILE_PREFIX = "target/test-classes";
+  static final String INPUT_DIR = FILE_PREFIX + "/input";
+  static final String WITNESS_DIR = FILE_PREFIX + "/witness";
   
   static String TEMP = "output/temp";
   static Logger logger = Logger.getLogger(HierarchyThresholdTestCase.class);
@@ -47,51 +51,51 @@
   }
   
   public void test1() throws Exception {
-    PropertyConfigurator.configure("input/hierarchyThreshold1.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/hierarchyThreshold1.properties");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.1"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/hierarchyThreshold.1"));
   }
 
   public void test2() throws Exception {
-    PropertyConfigurator.configure("input/hierarchyThreshold2.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/hierarchyThreshold2.properties");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.2"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/hierarchyThreshold.2"));
   }
 
   public void test3() throws Exception {
-    PropertyConfigurator.configure("input/hierarchyThreshold3.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/hierarchyThreshold3.properties");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.3"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/hierarchyThreshold.3"));
   }
 
   public void test4() throws Exception {
-    PropertyConfigurator.configure("input/hierarchyThreshold4.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/hierarchyThreshold4.properties");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.4"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/hierarchyThreshold.4"));
   }
 
   public void test5() throws Exception {
-    PropertyConfigurator.configure("input/hierarchyThreshold5.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/hierarchyThreshold5.properties");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.5"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/hierarchyThreshold.5"));
   }
 
   public void test6() throws Exception {
-    PropertyConfigurator.configure("input/hierarchyThreshold6.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/hierarchyThreshold6.properties");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.6"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/hierarchyThreshold.6"));
   }
 
   public void test7() throws Exception {
-    PropertyConfigurator.configure("input/hierarchyThreshold7.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/hierarchyThreshold7.properties");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.7"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/hierarchyThreshold.7"));
   }
 
   public void test8() throws Exception {
-    PropertyConfigurator.configure("input/hierarchyThreshold8.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/hierarchyThreshold8.properties");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.8"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/hierarchyThreshold.8"));
   }
 
 
diff --git a/tests/src/java/org/apache/log4j/Last.java b/core/src/test/java/org/apache/log4j/Last.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/Last.java
rename to core/src/test/java/org/apache/log4j/Last.java
diff --git a/tests/src/java/org/apache/log4j/LayoutTest.java b/core/src/test/java/org/apache/log4j/LayoutTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/LayoutTest.java
rename to core/src/test/java/org/apache/log4j/LayoutTest.java
diff --git a/tests/src/java/org/apache/log4j/LevelTest.java b/core/src/test/java/org/apache/log4j/LevelTest.java
similarity index 97%
rename from tests/src/java/org/apache/log4j/LevelTest.java
rename to core/src/test/java/org/apache/log4j/LevelTest.java
index 8ed550c..4374e5d 100644
--- a/tests/src/java/org/apache/log4j/LevelTest.java
+++ b/core/src/test/java/org/apache/log4j/LevelTest.java
@@ -46,7 +46,7 @@
   public void testSerializeINFO() throws Exception {
     int[] skip = new int[] {  };
     SerializationTestHelper.assertSerializationEquals(
-      "witness/serialization/info.bin", Level.INFO, skip, Integer.MAX_VALUE);
+      "target/test-classes/witness/serialization/info.bin", Level.INFO, skip, Integer.MAX_VALUE);
   }
 
   /**
@@ -56,7 +56,7 @@
   public void testDeserializeINFO() throws Exception {
     Object obj =
       SerializationTestHelper.deserializeStream(
-        "witness/serialization/info.bin");
+        "target/test-classes/witness/serialization/info.bin");
     assertTrue(obj instanceof Level);
     Level info = (Level) obj;
     assertEquals("INFO", info.toString());
diff --git a/tests/src/java/org/apache/log4j/LogCapture.java b/core/src/test/java/org/apache/log4j/LogCapture.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/LogCapture.java
rename to core/src/test/java/org/apache/log4j/LogCapture.java
diff --git a/tests/src/java/org/apache/log4j/LogManagerTest.java b/core/src/test/java/org/apache/log4j/LogManagerTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/LogManagerTest.java
rename to core/src/test/java/org/apache/log4j/LogManagerTest.java
diff --git a/tests/src/java/org/apache/log4j/LoggerTestCase.java b/core/src/test/java/org/apache/log4j/LoggerTestCase.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/LoggerTestCase.java
rename to core/src/test/java/org/apache/log4j/LoggerTestCase.java
diff --git a/tests/src/java/org/apache/log4j/MinimumTestCase.java b/core/src/test/java/org/apache/log4j/MinimumTestCase.java
similarity index 95%
rename from tests/src/java/org/apache/log4j/MinimumTestCase.java
rename to core/src/test/java/org/apache/log4j/MinimumTestCase.java
index 50d1503..568b22a 100644
--- a/tests/src/java/org/apache/log4j/MinimumTestCase.java
+++ b/core/src/test/java/org/apache/log4j/MinimumTestCase.java
@@ -29,6 +29,10 @@
  */
 public class MinimumTestCase extends TestCase {
 
+  static final String FILE_PREFIX = "target/test-classes";
+  static final String INPUT_DIR = FILE_PREFIX + "/input";
+  static final String WITNESS_DIR = FILE_PREFIX + "/witness";
+  
   static String FILTERED = "output/filtered";
 
   static String EXCEPTION1 = "java.lang.Exception: Just testing";
@@ -75,7 +79,7 @@
       new Filter[] { new LineNumberFilter(), 
                      new SunReflectFilter(), 
                      new JunitTestRunnerFilter() });
-    assertTrue(Compare.compare(FILTERED, "witness/simple"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/simple"));
   }
 
   public void ttcc() throws Exception {
@@ -101,7 +105,7 @@
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
 
-    assertTrue(Compare.compare(FILTERED, "witness/ttcc"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/ttcc"));
   }
 
 
diff --git a/tests/src/java/org/apache/log4j/MyPatternLayout.java b/core/src/test/java/org/apache/log4j/MyPatternLayout.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/MyPatternLayout.java
rename to core/src/test/java/org/apache/log4j/MyPatternLayout.java
diff --git a/tests/src/java/org/apache/log4j/MyPatternParser.java b/core/src/test/java/org/apache/log4j/MyPatternParser.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/MyPatternParser.java
rename to core/src/test/java/org/apache/log4j/MyPatternParser.java
diff --git a/tests/src/java/org/apache/log4j/PatternLayoutTest.java b/core/src/test/java/org/apache/log4j/PatternLayoutTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/PatternLayoutTest.java
rename to core/src/test/java/org/apache/log4j/PatternLayoutTest.java
diff --git a/tests/src/java/org/apache/log4j/PatternLayoutTestCase.java b/core/src/test/java/org/apache/log4j/PatternLayoutTestCase.java
similarity index 79%
rename from tests/src/java/org/apache/log4j/PatternLayoutTestCase.java
rename to core/src/test/java/org/apache/log4j/PatternLayoutTestCase.java
index 9578523..10ed06a 100644
--- a/tests/src/java/org/apache/log4j/PatternLayoutTestCase.java
+++ b/core/src/test/java/org/apache/log4j/PatternLayoutTestCase.java
@@ -32,6 +32,10 @@
 
 public class PatternLayoutTestCase extends TestCase {
 
+  static final String FILE_PREFIX = "target/test-classes";
+  static final String INPUT_DIR = FILE_PREFIX + "/input";
+  static final String WITNESS_DIR = FILE_PREFIX + "/witness";
+  
   static String TEMP = "output/temp";
   static String FILTERED = "output/filtered";
 
@@ -80,7 +84,7 @@
   }
 
   public void test1() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout1.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout1.properties");
     common();
     Transformer.transform(
       TEMP, FILTERED,
@@ -88,11 +92,11 @@
         new LineNumberFilter(), new SunReflectFilter(),
         new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.1"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.1"));
   }
 
   public void test2() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout2.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout2.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT1, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -102,11 +106,11 @@
         cf1, new LineNumberFilter(), new ISO8601Filter(),
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.2"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.2"));
   }
 
   public void test3() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout3.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout3.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT1, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -116,13 +120,13 @@
         cf1, new LineNumberFilter(), new ISO8601Filter(),
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.3"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.3"));
   }
 
   // Output format:
   // 06 avr. 2002 18:30:58,937 [main] DEBUG rnLayoutTestCase - Message 0  
   public void test4() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout4.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout4.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT2, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -132,11 +136,11 @@
         cf1, new LineNumberFilter(), new AbsoluteDateAndTimeFilter(),
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.4"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.4"));
   }
 
   public void test5() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout5.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout5.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT2, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -146,12 +150,12 @@
         cf1, new LineNumberFilter(), new AbsoluteDateAndTimeFilter(),
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.5"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.5"));
   }
 
   // 18:54:19,201 [main] DEBUG rnLayoutTestCase - Message 0
   public void test6() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout6.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout6.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT3, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -161,12 +165,12 @@
         cf1, new LineNumberFilter(), new AbsoluteTimeFilter(),
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.6"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.6"));
   }
 
 
   public void test7() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout7.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout7.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT3, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -176,11 +180,11 @@
         cf1, new LineNumberFilter(), new AbsoluteTimeFilter(),
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.7"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.7"));
   }
 
   public void test8() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout8.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout8.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT4, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -190,11 +194,11 @@
         cf1, new LineNumberFilter(), new RelativeTimeFilter(),
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.8"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.8"));
   }
 
   public void test9() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout9.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout9.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT5, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -204,11 +208,11 @@
         cf1, new LineNumberFilter(), new SunReflectFilter(),
         new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.9"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.9"));
   }
 
   public void test10() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout10.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout10.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT6, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -218,11 +222,11 @@
         cf1, new LineNumberFilter(), new SunReflectFilter(),
         new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.10"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.10"));
   }
 
   public void test11() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout11.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout11.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT11a, PAT11b, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -232,11 +236,11 @@
         cf1, new LineNumberFilter(), new SunReflectFilter(),
         new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.11"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.11"));
   }
 
   public void test12() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout12.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout12.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT12, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -246,11 +250,11 @@
         cf1, new LineNumberFilter(), new SunReflectFilter(),
         new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.12"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.12"));
   }
 
   public void test13() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout13.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout13.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT13, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -260,11 +264,11 @@
         cf1, new LineNumberFilter(), new SunReflectFilter(),
         new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.13"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.13"));
   }
 
   public void test14() throws Exception {
-    PropertyConfigurator.configure("input/patternLayout14.properties");
+    PropertyConfigurator.configure(INPUT_DIR + "/patternLayout14.properties");
     common();
     ControlFilter cf1 = new ControlFilter(new String[]{PAT14, EXCEPTION1, 
 						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
@@ -274,29 +278,29 @@
         cf1, new LineNumberFilter(), new SunReflectFilter(),
         new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.14"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/patternLayout.14"));
   }
 
     public void testMDC1() throws Exception {
-      PropertyConfigurator.configure("input/patternLayout.mdc.1.properties");
+      PropertyConfigurator.configure(INPUT_DIR + "/patternLayout.mdc.1.properties");
       MDC.put("key1", "va11");
       MDC.put("key2", "va12");
       logger.debug("Hello World");
       MDC.remove("key1");
       MDC.remove("key2");
 
-      assertTrue(Compare.compare(TEMP, "witness/patternLayout.mdc.1"));
+      assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/patternLayout.mdc.1"));
     }
 
     public void testMDCClear() throws Exception {
-      PropertyConfigurator.configure("input/patternLayout.mdc.1.properties");
+      PropertyConfigurator.configure(INPUT_DIR + "/patternLayout.mdc.1.properties");
       MDC.put("key1", "va11");
       MDC.put("key2", "va12");
       logger.debug("Hello World");
       MDC.clear();
       logger.debug("Hello World");
 
-      assertTrue(Compare.compare(TEMP, "witness/patternLayout.mdc.clear"));
+      assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/patternLayout.mdc.clear"));
     }
 
 
diff --git a/tests/src/java/org/apache/log4j/PriorityTest.java b/core/src/test/java/org/apache/log4j/PriorityTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/PriorityTest.java
rename to core/src/test/java/org/apache/log4j/PriorityTest.java
diff --git a/tests/src/java/org/apache/log4j/PropertyConfiguratorTest.java b/core/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
similarity index 90%
rename from tests/src/java/org/apache/log4j/PropertyConfiguratorTest.java
rename to core/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
index 4b5e030..c8b4eef 100644
--- a/tests/src/java/org/apache/log4j/PropertyConfiguratorTest.java
+++ b/core/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
@@ -40,6 +40,11 @@
  *

  */

 public class PropertyConfiguratorTest extends TestCase {

+

+    static final String FILE_PREFIX = "target/test-classes";

+    static final String INPUT_DIR = FILE_PREFIX + "/input";

+    static final String WITNESS_DIR = FILE_PREFIX + "/witness";

+

     public PropertyConfiguratorTest(final String testName) {

         super(testName);

     }

@@ -311,12 +316,20 @@
      * Tests processing of nested objects, see bug 36384.

      */

     public void testNested() {

-        try {

-            PropertyConfigurator.configure("input/filter1.properties");

-            this.validateNested();

-        } finally {

-            LogManager.resetConfiguration();

-        }

+        PropertyConfigurator.configure(INPUT_DIR + "/filter1.properties");

+        RollingFileAppender rfa = (RollingFileAppender)

+                Logger.getLogger("org.apache.log4j.PropertyConfiguratorTest")

+                   .getAppender("ROLLING");

+        FixedWindowRollingPolicy rollingPolicy = (FixedWindowRollingPolicy) rfa.getRollingPolicy();

+        assertEquals("filterBase-test1.log", rollingPolicy.getActiveFileName());

+        assertEquals("filterBased-test1.%i", rollingPolicy.getFileNamePattern());

+        assertEquals(0, rollingPolicy.getMinIndex());

+        assertTrue(rollingPolicy.isActivated());

+        FilterBasedTriggeringPolicy triggeringPolicy =

+                (FilterBasedTriggeringPolicy) rfa.getTriggeringPolicy();

+        LevelRangeFilter filter = (LevelRangeFilter) triggeringPolicy.getFilter();

+        assertTrue(Level.INFO.equals(filter.getLevelMin()));

+        LogManager.resetConfiguration();

     }

 

 

diff --git a/tests/src/java/org/apache/log4j/RFATestCase.java b/core/src/test/java/org/apache/log4j/RFATestCase.java
similarity index 96%
rename from tests/src/java/org/apache/log4j/RFATestCase.java
rename to core/src/test/java/org/apache/log4j/RFATestCase.java
index 0d11fc2..d1537ab 100644
--- a/tests/src/java/org/apache/log4j/RFATestCase.java
+++ b/core/src/test/java/org/apache/log4j/RFATestCase.java
@@ -30,6 +30,10 @@
  */
 public class RFATestCase extends TestCase {
 
+  static final String FILE_PREFIX = "target/test-classes";
+  static final String INPUT_DIR = FILE_PREFIX + "/input";
+  static final String WITNESS_DIR = FILE_PREFIX + "/witness";
+  
   public RFATestCase(String name) {
     super(name);
   }
@@ -43,7 +47,7 @@
      */
     public void test1() throws Exception {
      Logger logger = Logger.getLogger(RFATestCase.class);
-      PropertyConfigurator.configure("input/RFA1.properties");
+      PropertyConfigurator.configure(INPUT_DIR + "/RFA1.properties");
 
       // Write exactly 10 bytes with each log
       for (int i = 0; i < 25; i++) {
diff --git a/tests/src/java/org/apache/log4j/StressCategory.java b/core/src/test/java/org/apache/log4j/StressCategory.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/StressCategory.java
rename to core/src/test/java/org/apache/log4j/StressCategory.java
diff --git a/tests/src/java/org/apache/log4j/TTCCLayoutTest.java b/core/src/test/java/org/apache/log4j/TTCCLayoutTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/TTCCLayoutTest.java
rename to core/src/test/java/org/apache/log4j/TTCCLayoutTest.java
diff --git a/tests/src/java/org/apache/log4j/TestLogMF.java b/core/src/test/java/org/apache/log4j/TestLogMF.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/TestLogMF.java
rename to core/src/test/java/org/apache/log4j/TestLogMF.java
diff --git a/tests/src/java/org/apache/log4j/TestLogSF.java b/core/src/test/java/org/apache/log4j/TestLogSF.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/TestLogSF.java
rename to core/src/test/java/org/apache/log4j/TestLogSF.java
diff --git a/tests/src/java/org/apache/log4j/TestLogXF.java b/core/src/test/java/org/apache/log4j/TestLogXF.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/TestLogXF.java
rename to core/src/test/java/org/apache/log4j/TestLogXF.java
diff --git a/tests/src/java/org/apache/log4j/VectorAppender.java b/core/src/test/java/org/apache/log4j/VectorAppender.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/VectorAppender.java
rename to core/src/test/java/org/apache/log4j/VectorAppender.java
diff --git a/tests/src/java/org/apache/log4j/VectorErrorHandler.java b/core/src/test/java/org/apache/log4j/VectorErrorHandler.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/VectorErrorHandler.java
rename to core/src/test/java/org/apache/log4j/VectorErrorHandler.java
diff --git a/tests/src/java/org/apache/log4j/customLogger/XLogger.java b/core/src/test/java/org/apache/log4j/customLogger/XLogger.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/customLogger/XLogger.java
rename to core/src/test/java/org/apache/log4j/customLogger/XLogger.java
diff --git a/tests/src/java/org/apache/log4j/customLogger/XLoggerTestCase.java b/core/src/test/java/org/apache/log4j/customLogger/XLoggerTestCase.java
similarity index 92%
rename from tests/src/java/org/apache/log4j/customLogger/XLoggerTestCase.java
rename to core/src/test/java/org/apache/log4j/customLogger/XLoggerTestCase.java
index ac05b76..f30d356 100644
--- a/tests/src/java/org/apache/log4j/customLogger/XLoggerTestCase.java
+++ b/core/src/test/java/org/apache/log4j/customLogger/XLoggerTestCase.java
@@ -46,7 +46,7 @@
   public void test2()  throws Exception  { common(2); }
 
   void common(int number) throws Exception {
-    DOMConfigurator.configure("input/xml/customLogger"+number+".xml");
+    DOMConfigurator.configure("target/test-classes/input/xml/customLogger"+number+".xml");
 
     int i = -1;
 
@@ -64,7 +64,7 @@
         new LineNumberFilter(), new SunReflectFilter(),
         new JunitTestRunnerFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/customLogger."+number));
+    assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/customLogger."+number));
 
   }
 
diff --git a/tests/src/java/org/apache/log4j/defaultInit/TestCase1.java b/core/src/test/java/org/apache/log4j/defaultInit/TestCase1.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/defaultInit/TestCase1.java
rename to core/src/test/java/org/apache/log4j/defaultInit/TestCase1.java
diff --git a/tests/src/java/org/apache/log4j/defaultInit/TestCase2.java b/core/src/test/java/org/apache/log4j/defaultInit/TestCase2.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/defaultInit/TestCase2.java
rename to core/src/test/java/org/apache/log4j/defaultInit/TestCase2.java
diff --git a/tests/src/java/org/apache/log4j/defaultInit/TestCase3.java b/core/src/test/java/org/apache/log4j/defaultInit/TestCase3.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/defaultInit/TestCase3.java
rename to core/src/test/java/org/apache/log4j/defaultInit/TestCase3.java
diff --git a/tests/src/java/org/apache/log4j/defaultInit/TestCase4.java b/core/src/test/java/org/apache/log4j/defaultInit/TestCase4.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/defaultInit/TestCase4.java
rename to core/src/test/java/org/apache/log4j/defaultInit/TestCase4.java
diff --git a/tests/src/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java b/core/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
rename to core/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
diff --git a/tests/src/java/org/apache/log4j/helpers/CyclicBufferTestCase.java b/core/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
rename to core/src/test/java/org/apache/log4j/helpers/CyclicBufferTestCase.java
diff --git a/tests/src/java/org/apache/log4j/helpers/DateLayoutTest.java b/core/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/helpers/DateLayoutTest.java
rename to core/src/test/java/org/apache/log4j/helpers/DateLayoutTest.java
diff --git a/tests/src/java/org/apache/log4j/helpers/LogLogTest.java b/core/src/test/java/org/apache/log4j/helpers/LogLogTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/helpers/LogLogTest.java
rename to core/src/test/java/org/apache/log4j/helpers/LogLogTest.java
diff --git a/tests/src/java/org/apache/log4j/helpers/OptionConverterTestCase.java b/core/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/helpers/OptionConverterTestCase.java
rename to core/src/test/java/org/apache/log4j/helpers/OptionConverterTestCase.java
diff --git a/tests/src/java/org/apache/log4j/helpers/PatternParserTestCase.java b/core/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
similarity index 97%
rename from tests/src/java/org/apache/log4j/helpers/PatternParserTestCase.java
rename to core/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
index 872e15f..f0d113b 100644
--- a/tests/src/java/org/apache/log4j/helpers/PatternParserTestCase.java
+++ b/core/src/test/java/org/apache/log4j/helpers/PatternParserTestCase.java
@@ -39,7 +39,7 @@
 public class PatternParserTestCase extends TestCase {
   
   static String OUTPUT_FILE   = "output/PatternParser";
-  static String WITNESS_FILE  = "witness/PatternParser";
+  static String WITNESS_FILE  = "target/test-classes/witness/PatternParser";
 
   static String msgPattern = "%m%n";
   
diff --git a/tests/src/java/org/apache/log4j/or/ORTestCase.java b/core/src/test/java/org/apache/log4j/or/ORTestCase.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/or/ORTestCase.java
rename to core/src/test/java/org/apache/log4j/or/ORTestCase.java
diff --git a/tests/src/java/org/apache/log4j/pattern/CachedDateFormatTest.java b/core/src/test/java/org/apache/log4j/pattern/CachedDateFormatTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/pattern/CachedDateFormatTest.java
rename to core/src/test/java/org/apache/log4j/pattern/CachedDateFormatTest.java
diff --git a/tests/src/java/org/apache/log4j/pattern/FormattingInfoTest.java b/core/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/pattern/FormattingInfoTest.java
rename to core/src/test/java/org/apache/log4j/pattern/FormattingInfoTest.java
diff --git a/tests/src/java/org/apache/log4j/pattern/NameAbbreviatorTest.java b/core/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
rename to core/src/test/java/org/apache/log4j/pattern/NameAbbreviatorTest.java
diff --git a/tests/src/java/org/apache/log4j/pattern/Num343PatternConverter.java b/core/src/test/java/org/apache/log4j/pattern/Num343PatternConverter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/pattern/Num343PatternConverter.java
rename to core/src/test/java/org/apache/log4j/pattern/Num343PatternConverter.java
diff --git a/tests/src/java/org/apache/log4j/pattern/PatternParserTest.java b/core/src/test/java/org/apache/log4j/pattern/PatternParserTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/pattern/PatternParserTest.java
rename to core/src/test/java/org/apache/log4j/pattern/PatternParserTest.java
diff --git a/tests/src/java/org/apache/log4j/spi/LocationInfoTest.java b/core/src/test/java/org/apache/log4j/spi/LocationInfoTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/spi/LocationInfoTest.java
rename to core/src/test/java/org/apache/log4j/spi/LocationInfoTest.java
diff --git a/tests/src/java/org/apache/log4j/spi/LoggingEventTest.java b/core/src/test/java/org/apache/log4j/spi/LoggingEventTest.java
similarity index 91%
rename from tests/src/java/org/apache/log4j/spi/LoggingEventTest.java
rename to core/src/test/java/org/apache/log4j/spi/LoggingEventTest.java
index 9dbec3a..5b7e49a 100644
--- a/tests/src/java/org/apache/log4j/spi/LoggingEventTest.java
+++ b/core/src/test/java/org/apache/log4j/spi/LoggingEventTest.java
@@ -34,6 +34,11 @@
  * @author Curt Arnold
  */
 public class LoggingEventTest extends TestCase {
+
+  static final String FILE_PREFIX = "target/test-classes";
+  static final String INPUT_DIR = FILE_PREFIX + "/input";
+  static final String WITNESS_DIR = FILE_PREFIX + "/witness";
+  
   /**
    * Create LoggingEventTest.
    *
@@ -57,7 +62,7 @@
 
     int[] skip = new int[] { 352, 353, 354, 355, 356 };
     SerializationTestHelper.assertSerializationEquals(
-      "witness/serialization/simple.bin", event, skip, 237);
+      WITNESS_DIR + "/serialization/simple.bin", event, skip, 237);
   }
 
   /**
@@ -76,7 +81,7 @@
 
     int[] skip = new int[] { 352, 353, 354, 355, 356 };
     SerializationTestHelper.assertSerializationEquals(
-      "witness/serialization/exception.bin", event, skip, 237);
+      WITNESS_DIR + "/serialization/exception.bin", event, skip, 237);
   }
 
   /**
@@ -95,7 +100,7 @@
 
     int[] skip = new int[] { 352, 353, 354, 355, 356 };
     SerializationTestHelper.assertSerializationEquals(
-      "witness/serialization/location.bin", event, skip, 237);
+      WITNESS_DIR + "/serialization/location.bin", event, skip, 237);
   }
 
   /**
@@ -114,7 +119,7 @@
 
     int[] skip = new int[] { 352, 353, 354, 355, 356 };
     SerializationTestHelper.assertSerializationEquals(
-      "witness/serialization/ndc.bin", event, skip, 237);
+      WITNESS_DIR + "/serialization/ndc.bin", event, skip, 237);
     }
 
   /**
@@ -133,7 +138,7 @@
 
     int[] skip = new int[] { 352, 353, 354, 355, 356 };
     SerializationTestHelper.assertSerializationEquals(
-      "witness/serialization/mdc.bin", event, skip, 237);
+      WITNESS_DIR + "/serialization/mdc.bin", event, skip, 237);
   }
 
   /**
@@ -144,7 +149,7 @@
   public void testDeserializationSimple() throws Exception {
     Object obj =
       SerializationTestHelper.deserializeStream(
-        "witness/serialization/simple.bin");
+        WITNESS_DIR + "/serialization/simple.bin");
     assertTrue(obj instanceof LoggingEvent);
 
     LoggingEvent event = (LoggingEvent) obj;
@@ -160,7 +165,7 @@
   public void testDeserializationWithException() throws Exception {
     Object obj =
       SerializationTestHelper.deserializeStream(
-        "witness/serialization/exception.bin");
+        WITNESS_DIR + "/serialization/exception.bin");
     assertTrue(obj instanceof LoggingEvent);
 
     LoggingEvent event = (LoggingEvent) obj;
@@ -176,7 +181,7 @@
   public void testDeserializationWithLocation() throws Exception {
     Object obj =
       SerializationTestHelper.deserializeStream(
-        "witness/serialization/location.bin");
+        WITNESS_DIR + "/serialization/location.bin");
     assertTrue(obj instanceof LoggingEvent);
 
     LoggingEvent event = (LoggingEvent) obj;
diff --git a/tests/src/java/org/apache/log4j/spi/ThrowableInformationTest.java b/core/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/spi/ThrowableInformationTest.java
rename to core/src/test/java/org/apache/log4j/spi/ThrowableInformationTest.java
diff --git a/tests/src/java/org/apache/log4j/stressCategory b/core/src/test/java/org/apache/log4j/stressCategory
similarity index 100%
rename from tests/src/java/org/apache/log4j/stressCategory
rename to core/src/test/java/org/apache/log4j/stressCategory
diff --git a/tests/src/java/org/apache/log4j/stressCategory.pl b/core/src/test/java/org/apache/log4j/stressCategory.pl
similarity index 100%
rename from tests/src/java/org/apache/log4j/stressCategory.pl
rename to core/src/test/java/org/apache/log4j/stressCategory.pl
diff --git a/tests/src/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java b/core/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java
rename to core/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java
diff --git a/tests/src/java/org/apache/log4j/util/AbsoluteTimeFilter.java b/core/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/AbsoluteTimeFilter.java
rename to core/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java
diff --git a/tests/src/java/org/apache/log4j/util/Compare.java b/core/src/test/java/org/apache/log4j/util/Compare.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/Compare.java
rename to core/src/test/java/org/apache/log4j/util/Compare.java
diff --git a/tests/src/java/org/apache/log4j/util/ControlFilter.java b/core/src/test/java/org/apache/log4j/util/ControlFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/ControlFilter.java
rename to core/src/test/java/org/apache/log4j/util/ControlFilter.java
diff --git a/tests/src/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java b/core/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
rename to core/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
diff --git a/tests/src/java/org/apache/log4j/util/EnhancedLineNumberFilter.java b/core/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/EnhancedLineNumberFilter.java
rename to core/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java
diff --git a/tests/src/java/org/apache/log4j/util/Filter.java b/core/src/test/java/org/apache/log4j/util/Filter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/Filter.java
rename to core/src/test/java/org/apache/log4j/util/Filter.java
diff --git a/tests/src/java/org/apache/log4j/util/ISO8601Filter.java b/core/src/test/java/org/apache/log4j/util/ISO8601Filter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/ISO8601Filter.java
rename to core/src/test/java/org/apache/log4j/util/ISO8601Filter.java
diff --git a/tests/src/java/org/apache/log4j/util/JunitTestRunnerFilter.java b/core/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/JunitTestRunnerFilter.java
rename to core/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java
diff --git a/tests/src/java/org/apache/log4j/util/LineNumberFilter.java b/core/src/test/java/org/apache/log4j/util/LineNumberFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/LineNumberFilter.java
rename to core/src/test/java/org/apache/log4j/util/LineNumberFilter.java
diff --git a/tests/src/java/org/apache/log4j/MDCOrderFilter.java b/core/src/test/java/org/apache/log4j/util/MDCOrderFilter.java
similarity index 97%
rename from tests/src/java/org/apache/log4j/MDCOrderFilter.java
rename to core/src/test/java/org/apache/log4j/util/MDCOrderFilter.java
index 06f85b2..c8a206c 100644
--- a/tests/src/java/org/apache/log4j/MDCOrderFilter.java
+++ b/core/src/test/java/org/apache/log4j/util/MDCOrderFilter.java
@@ -19,6 +19,8 @@
 
 import org.apache.log4j.util.Filter;
 
+import org.apache.log4j.util.Filter;
+
 /**
  * This class switches MDC values into the order
  * (unreasonably) expected by the witness files.
diff --git a/tests/src/java/org/apache/log4j/util/RelativeTimeFilter.java b/core/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/RelativeTimeFilter.java
rename to core/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java
diff --git a/tests/src/java/org/apache/log4j/util/SerializationTestHelper.java b/core/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/SerializationTestHelper.java
rename to core/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
diff --git a/tests/src/java/org/apache/log4j/util/SunReflectFilter.java b/core/src/test/java/org/apache/log4j/util/SunReflectFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/SunReflectFilter.java
rename to core/src/test/java/org/apache/log4j/util/SunReflectFilter.java
diff --git a/tests/src/java/org/apache/log4j/util/Transformer.java b/core/src/test/java/org/apache/log4j/util/Transformer.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/Transformer.java
rename to core/src/test/java/org/apache/log4j/util/Transformer.java
diff --git a/tests/src/java/org/apache/log4j/util/UnexpectedFormatException.java b/core/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/UnexpectedFormatException.java
rename to core/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java
diff --git a/tests/src/java/org/apache/log4j/util/XMLLineAttributeFilter.java b/core/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/XMLLineAttributeFilter.java
rename to core/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java
diff --git a/tests/src/java/org/apache/log4j/util/XMLTimestampFilter.java b/core/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/util/XMLTimestampFilter.java
rename to core/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java
diff --git a/tests/src/java/org/apache/log4j/varia/ERFATestCase.java b/core/src/test/java/org/apache/log4j/varia/ERFATestCase.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/varia/ERFATestCase.java
rename to core/src/test/java/org/apache/log4j/varia/ERFATestCase.java
diff --git a/tests/src/java/org/apache/log4j/varia/ErrorHandlerTestCase.java b/core/src/test/java/org/apache/log4j/varia/ErrorHandlerTestCase.java
similarity index 91%
rename from tests/src/java/org/apache/log4j/varia/ErrorHandlerTestCase.java
rename to core/src/test/java/org/apache/log4j/varia/ErrorHandlerTestCase.java
index b0001f8..e218a71 100644
--- a/tests/src/java/org/apache/log4j/varia/ErrorHandlerTestCase.java
+++ b/core/src/test/java/org/apache/log4j/varia/ErrorHandlerTestCase.java
@@ -63,7 +63,7 @@
   }
 
   public void test1() throws Exception {
-    DOMConfigurator.configure("input/xml/fallback1.xml");
+    DOMConfigurator.configure("target/test-classes/input/xml/fallback1.xml");
     Appender primary = root.getAppender("PRIMARY");
     ErrorHandler eh = primary.getErrorHandler();
     assertNotNull(eh);
@@ -79,11 +79,11 @@
                             new SunReflectFilter()});
 
 
-    assertTrue(Compare.compare(FILTERED, "witness/fallback1"));
+    assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/fallback1"));
   }
   
   public void test2() throws Exception {
-    PropertyConfigurator.configure("input/fallback1.properties");
+    PropertyConfigurator.configure("target/test-classes/input/fallback1.properties");
     Appender primary = root.getAppender("PRIMARY");
     ErrorHandler eh = primary.getErrorHandler();
     assertNotNull(eh);
@@ -99,7 +99,7 @@
                             new SunReflectFilter()});
 
 
-    assertTrue(Compare.compare(FILTERED, "witness/fallback1"));
+    assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/fallback1"));
   }
 
   void common() {
diff --git a/tests/src/java/org/apache/log4j/varia/LevelMatchFilterTestCase.java b/core/src/test/java/org/apache/log4j/varia/LevelMatchFilterTestCase.java
similarity index 96%
rename from tests/src/java/org/apache/log4j/varia/LevelMatchFilterTestCase.java
rename to core/src/test/java/org/apache/log4j/varia/LevelMatchFilterTestCase.java
index 1afc0d5..fbf901b 100644
--- a/tests/src/java/org/apache/log4j/varia/LevelMatchFilterTestCase.java
+++ b/core/src/test/java/org/apache/log4j/varia/LevelMatchFilterTestCase.java
@@ -41,11 +41,11 @@
   
   static String ACCEPT_FILE     = "output/LevelMatchFilter_accept";
   static String ACCEPT_FILTERED = "output/LevelMatchFilter_accept_filtered";
-  static String ACCEPT_WITNESS  = "witness/LevelMatchFilter_accept";
+  static String ACCEPT_WITNESS  = "target/test-classes/witness/LevelMatchFilter_accept";
 
   static String DENY_FILE       = "output/LevelMatchFilter_deny";
   static String DENY_FILTERED   = "output/LevelMatchFilter_deny_filtered";
-  static String DENY_WITNESS    = "witness/LevelMatchFilter_deny";
+  static String DENY_WITNESS    = "target/test-classes/witness/LevelMatchFilter_deny";
 
   Logger root; 
   Logger logger;
diff --git a/tests/src/java/org/apache/log4j/xml/CustomLevelTestCase.java b/core/src/test/java/org/apache/log4j/xml/CustomLevelTestCase.java
similarity index 74%
rename from tests/src/java/org/apache/log4j/xml/CustomLevelTestCase.java
rename to core/src/test/java/org/apache/log4j/xml/CustomLevelTestCase.java
index 52c366a..825a40b 100644
--- a/tests/src/java/org/apache/log4j/xml/CustomLevelTestCase.java
+++ b/core/src/test/java/org/apache/log4j/xml/CustomLevelTestCase.java
@@ -25,6 +25,10 @@
 
 public class CustomLevelTestCase extends TestCase {
 
+  static final String FILE_PREFIX = "target/test-classes";
+  static final String INPUT_DIR = FILE_PREFIX + "/input";
+  static final String WITNESS_DIR = FILE_PREFIX + "/witness";
+  
   static String TEMP = "output/temp";
 
   Logger root; 
@@ -44,27 +48,27 @@
   }
 
   public void test1() throws Exception {
-    DOMConfigurator.configure("input/xml/customLevel1.xml");
+    DOMConfigurator.configure(INPUT_DIR + "/xml/customLevel1.xml");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/customLevel.1"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/customLevel.1"));
   }
 
   public void test2() throws Exception {
-    DOMConfigurator.configure("input/xml/customLevel2.xml");
+    DOMConfigurator.configure(INPUT_DIR + "/xml/customLevel2.xml");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/customLevel.2"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/customLevel.2"));
   }
 
   public void test3() throws Exception {
-    DOMConfigurator.configure("input/xml/customLevel3.xml");
+    DOMConfigurator.configure(INPUT_DIR + "/xml/customLevel3.xml");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/customLevel.3"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/customLevel.3"));
   }
 
   public void test4() throws Exception {
-    DOMConfigurator.configure("input/xml/customLevel4.xml");
+    DOMConfigurator.configure(INPUT_DIR + "/xml/customLevel4.xml");
     common();
-    assertTrue(Compare.compare(TEMP, "witness/customLevel.4"));
+    assertTrue(Compare.compare(TEMP, WITNESS_DIR + "/customLevel.4"));
   }
 
 
diff --git a/tests/src/java/org/apache/log4j/xml/DOMTestCase.java b/core/src/test/java/org/apache/log4j/xml/DOMTestCase.java
similarity index 91%
rename from tests/src/java/org/apache/log4j/xml/DOMTestCase.java
rename to core/src/test/java/org/apache/log4j/xml/DOMTestCase.java
index e664c43..314e42d 100644
--- a/tests/src/java/org/apache/log4j/xml/DOMTestCase.java
+++ b/core/src/test/java/org/apache/log4j/xml/DOMTestCase.java
@@ -49,7 +49,8 @@
 import java.util.zip.ZipOutputStream;
 
 public class DOMTestCase extends TestCase {
-
+  static final String FILE_PREFIX = "target/test-classes/";
+  
   static String TEMP_A1 = "output/temp.A1";
   static String TEMP_A2 = "output/temp.A2";
   static String FILTERED_A1 = "output/filtered.A1";
@@ -90,7 +91,7 @@
   }
 
   public void test1() throws Exception {
-    DOMConfigurator.configure("input/xml/DOMTestCase1.xml");
+    DOMConfigurator.configure(FILE_PREFIX + "input/xml/DOMTestCase1.xml");
     common();
 
     ControlFilter cf1 = new ControlFilter(new String[]{TEST1_1A_PAT, TEST1_1B_PAT, 
@@ -113,15 +114,15 @@
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
 
-    assertTrue(Compare.compare(FILTERED_A1, "witness/dom.A1.1"));
-    assertTrue(Compare.compare(FILTERED_A2, "witness/dom.A2.1"));
+    assertTrue(Compare.compare(FILTERED_A1, FILE_PREFIX + "witness/dom.A1.1"));
+    assertTrue(Compare.compare(FILTERED_A2, FILE_PREFIX + "witness/dom.A2.1"));
   }
   
   /**
    *   Tests processing of external entities in XML file.
    */
   public void test4() throws Exception {
-    DOMConfigurator.configure("input/xml/DOMTest4.xml");
+    DOMConfigurator.configure(FILE_PREFIX + "input/xml/DOMTest4.xml");
     common();
 
     ControlFilter cf1 = new ControlFilter(new String[]{TEST1_1A_PAT, TEST1_1B_PAT, 
@@ -144,8 +145,8 @@
         new SunReflectFilter(), new JunitTestRunnerFilter()
       });
 
-    assertTrue(Compare.compare(FILTERED_A1 + ".4", "witness/dom.A1.4"));
-    assertTrue(Compare.compare(FILTERED_A2 + ".4", "witness/dom.A2.4"));
+    assertTrue(Compare.compare(FILTERED_A1 + ".4", FILE_PREFIX + "witness/dom.A1.4"));
+    assertTrue(Compare.compare(FILTERED_A2 + ".4", FILE_PREFIX + "witness/dom.A2.4"));
   }
 
   void common() {
@@ -251,7 +252,7 @@
      *    use the specified categoryFactory.  See bug 33708.
      */
   public void testCategoryFactory1() {
-      DOMConfigurator.configure("input/xml/categoryfactory1.xml");
+      DOMConfigurator.configure(FILE_PREFIX + "input/xml/categoryfactory1.xml");
       //
       //   logger explicitly mentioned in configuration,
       //         should be a CustomLogger
@@ -269,7 +270,7 @@
      *    use the specified categoryFactory.  See bug 33708.
      */
     public void testCategoryFactory2() {
-        DOMConfigurator.configure("input/xml/categoryfactory2.xml");
+        DOMConfigurator.configure(FILE_PREFIX + "input/xml/categoryfactory2.xml");
         //
         //   logger explicitly mentioned in configuration,
         //         should be a CustomLogger
@@ -287,7 +288,7 @@
      *    use the specified loggerFactory.  See bug 33708.
      */
   public void testLoggerFactory1() {
-      DOMConfigurator.configure("input/xml/loggerfactory1.xml");
+      DOMConfigurator.configure(FILE_PREFIX + "input/xml/loggerfactory1.xml");
       //
       //   logger explicitly mentioned in configuration,
       //         should be a CustomLogger
@@ -309,7 +310,7 @@
       VectorAppender appender = new VectorAppender();
       appender.setName("V1");
       Logger.getRootLogger().addAppender(appender);
-      DOMConfigurator.configure("input/xml/testReset.xml");
+      DOMConfigurator.configure(FILE_PREFIX + "input/xml/testReset.xml");
       assertNull(Logger.getRootLogger().getAppender("V1"));
   }
 
@@ -319,7 +320,7 @@
       * @throws Exception if IO error.
      */
   public void testConfigureAndWatch() throws Exception {
-    DOMConfigurator.configureAndWatch("input/xml/DOMTestCase1.xml");
+    DOMConfigurator.configureAndWatch(FILE_PREFIX + "input/xml/DOMTestCase1.xml");
     assertNotNull(Logger.getRootLogger().getAppender("A1"));
   }
 
@@ -338,7 +339,7 @@
               return value;
           }
       };
-      configurator.doConfigure("input/xml/DOMTestCase1.xml", LogManager.getLoggerRepository());
+      configurator.doConfigure(FILE_PREFIX + "input/xml/DOMTestCase1.xml", LogManager.getLoggerRepository());
       FileAppender a1 = (FileAppender) Logger.getRootLogger().getAppender("A1");
       String file = a1.getFile();
       assertEquals("output/subst-test.A1", file);
@@ -379,7 +380,7 @@
      * Test of log4j.throwableRenderer support.  See bug 45721.
      */
     public void testThrowableRenderer1() {
-        DOMConfigurator.configure("input/xml/throwableRenderer1.xml");
+        DOMConfigurator.configure(FILE_PREFIX + "input/xml/throwableRenderer1.xml");
         ThrowableRendererSupport repo = (ThrowableRendererSupport) LogManager.getLoggerRepository();
         MockThrowableRenderer renderer = (MockThrowableRenderer) repo.getThrowableRenderer();
         LogManager.resetConfiguration();
@@ -394,7 +395,7 @@
      * @throws IOException if IOException creating properties jar.
      */
     public void testJarURL() throws IOException {
-        File input = new File("input/xml/defaultInit.xml");
+        File input = new File(FILE_PREFIX + "input/xml/defaultInit.xml");
         System.out.println(input.getAbsolutePath());
         InputStream is = new FileInputStream(input);
         File dir = new File("output");
diff --git a/tests/src/java/org/apache/log4j/xml/XLevel.java b/core/src/test/java/org/apache/log4j/xml/XLevel.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/xml/XLevel.java
rename to core/src/test/java/org/apache/log4j/xml/XLevel.java
diff --git a/tests/src/java/org/apache/log4j/xml/XMLLayoutTest.java b/core/src/test/java/org/apache/log4j/xml/XMLLayoutTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/xml/XMLLayoutTest.java
rename to core/src/test/java/org/apache/log4j/xml/XMLLayoutTest.java
diff --git a/tests/src/java/org/apache/log4j/xml/XMLLayoutTestCase.java b/core/src/test/java/org/apache/log4j/xml/XMLLayoutTestCase.java
similarity index 91%
rename from tests/src/java/org/apache/log4j/xml/XMLLayoutTestCase.java
rename to core/src/test/java/org/apache/log4j/xml/XMLLayoutTestCase.java
index e76acd6..f45f788 100644
--- a/tests/src/java/org/apache/log4j/xml/XMLLayoutTestCase.java
+++ b/core/src/test/java/org/apache/log4j/xml/XMLLayoutTestCase.java
@@ -37,6 +37,10 @@
 
 public class XMLLayoutTestCase extends TestCase {
 
+  static final String FILE_PREFIX = "target/test-classes";
+  static final String INPUT_DIR = FILE_PREFIX + "/input";
+  static final String WITNESS_DIR = FILE_PREFIX + "/witness";
+  
   static String TEMP = "output/temp";
   static String FILTERED = "output/filtered";
 
@@ -70,7 +74,7 @@
         new JunitTestRunnerFilter(),
         new SunReflectFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/xmlLayout.1"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/xmlLayout.1"));
   }
 
   public void locationInfo() throws Exception {
@@ -87,7 +91,7 @@
         new JunitTestRunnerFilter(),
         new SunReflectFilter()
       });
-    assertTrue(Compare.compare(FILTERED, "witness/xmlLayout.2"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/xmlLayout.2"));
   }
 
   public void testCDATA() throws Exception {
@@ -116,7 +120,7 @@
     Transformer.transform(TEMP, FILTERED, new Filter[] {new LineNumberFilter(),
     						  new XMLTimestampFilter(),
     						  new XMLLineAttributeFilter()});
-    assertTrue(Compare.compare(FILTERED, "witness/xmlLayout.3"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/xmlLayout.3"));
   }
 
   public void testNull() throws Exception {
@@ -139,7 +143,7 @@
           new XMLTimestampFilter(),  
           new JunitTestRunnerFilter(),
           new SunReflectFilter()});
-    assertTrue(Compare.compare(FILTERED, "witness/xmlLayout.null"));
+    assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/xmlLayout.null"));
   }
 
     /**
@@ -165,7 +169,7 @@
         new Filter[] { new LineNumberFilter(),
             new JunitTestRunnerFilter(),
             new XMLTimestampFilter()});
-      assertTrue(Compare.compare(FILTERED, "witness/xmlLayout.mdc.1"));
+      assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/xmlLayout.mdc.1"));
     }
 
     public void testMDCEscaped() throws Exception {
@@ -186,7 +190,7 @@
         new Filter[] { new LineNumberFilter(),
             new JunitTestRunnerFilter(),
             new XMLTimestampFilter() });
-      assertTrue(Compare.compare(FILTERED, "witness/xmlLayout.mdc.2"));
+      assertTrue(Compare.compare(FILTERED, WITNESS_DIR + "/xmlLayout.mdc.2"));
     }
 
   
diff --git a/tests/log4j-coding-convention.xml b/core/src/test/log4j-coding-convention.xml
similarity index 100%
rename from tests/log4j-coding-convention.xml
rename to core/src/test/log4j-coding-convention.xml
diff --git a/tests/resources/L7D_en_US.properties b/core/src/test/resources/L7D_en_US.properties
similarity index 100%
rename from tests/resources/L7D_en_US.properties
rename to core/src/test/resources/L7D_en_US.properties
diff --git a/tests/resources/L7D_fr.properties b/core/src/test/resources/L7D_fr.properties
similarity index 100%
rename from tests/resources/L7D_fr.properties
rename to core/src/test/resources/L7D_fr.properties
diff --git a/tests/resources/L7D_fr_CH.properties b/core/src/test/resources/L7D_fr_CH.properties
similarity index 100%
rename from tests/resources/L7D_fr_CH.properties
rename to core/src/test/resources/L7D_fr_CH.properties
diff --git a/tests/resources/TestLogSFPatterns.properties b/core/src/test/resources/TestLogSFPatterns.properties
similarity index 100%
rename from tests/resources/TestLogSFPatterns.properties
rename to core/src/test/resources/TestLogSFPatterns.properties
diff --git a/tests/input/RFA1.properties b/core/src/test/resources/input/RFA1.properties
similarity index 100%
rename from tests/input/RFA1.properties
rename to core/src/test/resources/input/RFA1.properties
diff --git a/tests/input/defaultInit3.properties b/core/src/test/resources/input/defaultInit3.properties
similarity index 100%
rename from tests/input/defaultInit3.properties
rename to core/src/test/resources/input/defaultInit3.properties
diff --git a/tests/input/fallback1.properties b/core/src/test/resources/input/fallback1.properties
similarity index 100%
rename from tests/input/fallback1.properties
rename to core/src/test/resources/input/fallback1.properties
diff --git a/tests/input/filter1.properties b/core/src/test/resources/input/filter1.properties
similarity index 100%
rename from tests/input/filter1.properties
rename to core/src/test/resources/input/filter1.properties
diff --git a/tests/input/hierarchyThreshold1.properties b/core/src/test/resources/input/hierarchyThreshold1.properties
similarity index 100%
rename from tests/input/hierarchyThreshold1.properties
rename to core/src/test/resources/input/hierarchyThreshold1.properties
diff --git a/tests/input/hierarchyThreshold2.properties b/core/src/test/resources/input/hierarchyThreshold2.properties
similarity index 100%
rename from tests/input/hierarchyThreshold2.properties
rename to core/src/test/resources/input/hierarchyThreshold2.properties
diff --git a/tests/input/hierarchyThreshold3.properties b/core/src/test/resources/input/hierarchyThreshold3.properties
similarity index 100%
rename from tests/input/hierarchyThreshold3.properties
rename to core/src/test/resources/input/hierarchyThreshold3.properties
diff --git a/tests/input/hierarchyThreshold4.properties b/core/src/test/resources/input/hierarchyThreshold4.properties
similarity index 100%
rename from tests/input/hierarchyThreshold4.properties
rename to core/src/test/resources/input/hierarchyThreshold4.properties
diff --git a/tests/input/hierarchyThreshold5.properties b/core/src/test/resources/input/hierarchyThreshold5.properties
similarity index 100%
rename from tests/input/hierarchyThreshold5.properties
rename to core/src/test/resources/input/hierarchyThreshold5.properties
diff --git a/tests/input/hierarchyThreshold6.properties b/core/src/test/resources/input/hierarchyThreshold6.properties
similarity index 100%
rename from tests/input/hierarchyThreshold6.properties
rename to core/src/test/resources/input/hierarchyThreshold6.properties
diff --git a/tests/input/hierarchyThreshold7.properties b/core/src/test/resources/input/hierarchyThreshold7.properties
similarity index 100%
rename from tests/input/hierarchyThreshold7.properties
rename to core/src/test/resources/input/hierarchyThreshold7.properties
diff --git a/tests/input/hierarchyThreshold8.properties b/core/src/test/resources/input/hierarchyThreshold8.properties
similarity index 100%
rename from tests/input/hierarchyThreshold8.properties
rename to core/src/test/resources/input/hierarchyThreshold8.properties
diff --git a/tests/input/pattern/enhancedPatternLayout.mdc.1.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout.mdc.1.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout.mdc.1.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout.mdc.1.properties
diff --git a/tests/input/pattern/enhancedPatternLayout1.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout1.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout1.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout1.properties
diff --git a/tests/input/pattern/enhancedPatternLayout10.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout10.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout10.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout10.properties
diff --git a/tests/input/pattern/enhancedPatternLayout11.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout11.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout11.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout11.properties
diff --git a/tests/input/pattern/enhancedPatternLayout12.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout12.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout12.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout12.properties
diff --git a/tests/input/pattern/enhancedPatternLayout13.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout13.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout13.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout13.properties
diff --git a/tests/input/pattern/enhancedPatternLayout14.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout14.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout14.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout14.properties
diff --git a/tests/input/pattern/enhancedPatternLayout15.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout15.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout15.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout15.properties
diff --git a/tests/input/pattern/enhancedPatternLayout16.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout16.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout16.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout16.properties
diff --git a/tests/input/pattern/enhancedPatternLayout2.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout2.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout2.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout2.properties
diff --git a/tests/input/pattern/enhancedPatternLayout3.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout3.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout3.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout3.properties
diff --git a/tests/input/pattern/enhancedPatternLayout4.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout4.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout4.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout4.properties
diff --git a/tests/input/pattern/enhancedPatternLayout5.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout5.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout5.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout5.properties
diff --git a/tests/input/pattern/enhancedPatternLayout6.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout6.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout6.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout6.properties
diff --git a/tests/input/pattern/enhancedPatternLayout7.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout7.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout7.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout7.properties
diff --git a/tests/input/pattern/enhancedPatternLayout8.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout8.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout8.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout8.properties
diff --git a/tests/input/pattern/enhancedPatternLayout9.properties b/core/src/test/resources/input/pattern/enhancedPatternLayout9.properties
similarity index 100%
rename from tests/input/pattern/enhancedPatternLayout9.properties
rename to core/src/test/resources/input/pattern/enhancedPatternLayout9.properties
diff --git a/tests/input/patternLayout.mdc.1.properties b/core/src/test/resources/input/patternLayout.mdc.1.properties
similarity index 100%
rename from tests/input/patternLayout.mdc.1.properties
rename to core/src/test/resources/input/patternLayout.mdc.1.properties
diff --git a/tests/input/patternLayout1.properties b/core/src/test/resources/input/patternLayout1.properties
similarity index 100%
rename from tests/input/patternLayout1.properties
rename to core/src/test/resources/input/patternLayout1.properties
diff --git a/tests/input/patternLayout10.properties b/core/src/test/resources/input/patternLayout10.properties
similarity index 100%
rename from tests/input/patternLayout10.properties
rename to core/src/test/resources/input/patternLayout10.properties
diff --git a/tests/input/patternLayout11.properties b/core/src/test/resources/input/patternLayout11.properties
similarity index 100%
rename from tests/input/patternLayout11.properties
rename to core/src/test/resources/input/patternLayout11.properties
diff --git a/tests/input/patternLayout12.properties b/core/src/test/resources/input/patternLayout12.properties
similarity index 100%
rename from tests/input/patternLayout12.properties
rename to core/src/test/resources/input/patternLayout12.properties
diff --git a/tests/input/patternLayout13.properties b/core/src/test/resources/input/patternLayout13.properties
similarity index 100%
rename from tests/input/patternLayout13.properties
rename to core/src/test/resources/input/patternLayout13.properties
diff --git a/tests/input/patternLayout14.properties b/core/src/test/resources/input/patternLayout14.properties
similarity index 100%
rename from tests/input/patternLayout14.properties
rename to core/src/test/resources/input/patternLayout14.properties
diff --git a/tests/input/patternLayout2.properties b/core/src/test/resources/input/patternLayout2.properties
similarity index 100%
rename from tests/input/patternLayout2.properties
rename to core/src/test/resources/input/patternLayout2.properties
diff --git a/tests/input/patternLayout3.properties b/core/src/test/resources/input/patternLayout3.properties
similarity index 100%
rename from tests/input/patternLayout3.properties
rename to core/src/test/resources/input/patternLayout3.properties
diff --git a/tests/input/patternLayout4.properties b/core/src/test/resources/input/patternLayout4.properties
similarity index 100%
rename from tests/input/patternLayout4.properties
rename to core/src/test/resources/input/patternLayout4.properties
diff --git a/tests/input/patternLayout5.properties b/core/src/test/resources/input/patternLayout5.properties
similarity index 100%
rename from tests/input/patternLayout5.properties
rename to core/src/test/resources/input/patternLayout5.properties
diff --git a/tests/input/patternLayout6.properties b/core/src/test/resources/input/patternLayout6.properties
similarity index 100%
rename from tests/input/patternLayout6.properties
rename to core/src/test/resources/input/patternLayout6.properties
diff --git a/tests/input/patternLayout7.properties b/core/src/test/resources/input/patternLayout7.properties
similarity index 100%
rename from tests/input/patternLayout7.properties
rename to core/src/test/resources/input/patternLayout7.properties
diff --git a/tests/input/patternLayout8.properties b/core/src/test/resources/input/patternLayout8.properties
similarity index 100%
rename from tests/input/patternLayout8.properties
rename to core/src/test/resources/input/patternLayout8.properties
diff --git a/tests/input/patternLayout9.properties b/core/src/test/resources/input/patternLayout9.properties
similarity index 100%
rename from tests/input/patternLayout9.properties
rename to core/src/test/resources/input/patternLayout9.properties
diff --git a/tests/input/xml/DOMTest4.xml b/core/src/test/resources/input/xml/DOMTest4.xml
similarity index 100%
rename from tests/input/xml/DOMTest4.xml
rename to core/src/test/resources/input/xml/DOMTest4.xml
diff --git a/tests/input/xml/DOMTest4_A1.xml b/core/src/test/resources/input/xml/DOMTest4_A1.xml
similarity index 100%
rename from tests/input/xml/DOMTest4_A1.xml
rename to core/src/test/resources/input/xml/DOMTest4_A1.xml
diff --git a/tests/input/xml/DOMTest4_A2.xml b/core/src/test/resources/input/xml/DOMTest4_A2.xml
similarity index 100%
rename from tests/input/xml/DOMTest4_A2.xml
rename to core/src/test/resources/input/xml/DOMTest4_A2.xml
diff --git a/tests/input/xml/DOMTestCase1.xml b/core/src/test/resources/input/xml/DOMTestCase1.xml
similarity index 100%
rename from tests/input/xml/DOMTestCase1.xml
rename to core/src/test/resources/input/xml/DOMTestCase1.xml
diff --git a/tests/input/xml/categoryfactory1.xml b/core/src/test/resources/input/xml/categoryfactory1.xml
similarity index 100%
rename from tests/input/xml/categoryfactory1.xml
rename to core/src/test/resources/input/xml/categoryfactory1.xml
diff --git a/tests/input/xml/categoryfactory2.xml b/core/src/test/resources/input/xml/categoryfactory2.xml
similarity index 100%
rename from tests/input/xml/categoryfactory2.xml
rename to core/src/test/resources/input/xml/categoryfactory2.xml
diff --git a/tests/input/xml/customLevel1.xml b/core/src/test/resources/input/xml/customLevel1.xml
similarity index 100%
rename from tests/input/xml/customLevel1.xml
rename to core/src/test/resources/input/xml/customLevel1.xml
diff --git a/tests/input/xml/customLevel2.xml b/core/src/test/resources/input/xml/customLevel2.xml
similarity index 100%
rename from tests/input/xml/customLevel2.xml
rename to core/src/test/resources/input/xml/customLevel2.xml
diff --git a/tests/input/xml/customLevel3.xml b/core/src/test/resources/input/xml/customLevel3.xml
similarity index 100%
rename from tests/input/xml/customLevel3.xml
rename to core/src/test/resources/input/xml/customLevel3.xml
diff --git a/tests/input/xml/customLevel4.xml b/core/src/test/resources/input/xml/customLevel4.xml
similarity index 100%
rename from tests/input/xml/customLevel4.xml
rename to core/src/test/resources/input/xml/customLevel4.xml
diff --git a/tests/input/xml/customLogger1.xml b/core/src/test/resources/input/xml/customLogger1.xml
similarity index 100%
rename from tests/input/xml/customLogger1.xml
rename to core/src/test/resources/input/xml/customLogger1.xml
diff --git a/tests/input/xml/customLogger2.xml b/core/src/test/resources/input/xml/customLogger2.xml
similarity index 100%
rename from tests/input/xml/customLogger2.xml
rename to core/src/test/resources/input/xml/customLogger2.xml
diff --git a/tests/input/xml/customLogger3.xml b/core/src/test/resources/input/xml/customLogger3.xml
similarity index 100%
rename from tests/input/xml/customLogger3.xml
rename to core/src/test/resources/input/xml/customLogger3.xml
diff --git a/tests/input/xml/defaultInit.xml b/core/src/test/resources/input/xml/defaultInit.xml
similarity index 100%
rename from tests/input/xml/defaultInit.xml
rename to core/src/test/resources/input/xml/defaultInit.xml
diff --git a/tests/input/xml/fallback1.xml b/core/src/test/resources/input/xml/fallback1.xml
similarity index 100%
rename from tests/input/xml/fallback1.xml
rename to core/src/test/resources/input/xml/fallback1.xml
diff --git a/tests/input/xml/loggerfactory1.xml b/core/src/test/resources/input/xml/loggerfactory1.xml
similarity index 100%
rename from tests/input/xml/loggerfactory1.xml
rename to core/src/test/resources/input/xml/loggerfactory1.xml
diff --git a/tests/input/xml/testReset.xml b/core/src/test/resources/input/xml/testReset.xml
similarity index 100%
rename from tests/input/xml/testReset.xml
rename to core/src/test/resources/input/xml/testReset.xml
diff --git a/tests/input/xml/throwableRenderer1.xml b/core/src/test/resources/input/xml/throwableRenderer1.xml
similarity index 100%
rename from tests/input/xml/throwableRenderer1.xml
rename to core/src/test/resources/input/xml/throwableRenderer1.xml
diff --git a/tests/resources/org/apache/log4j/TestLogMFPatterns.properties b/core/src/test/resources/org/apache/log4j/TestLogMFPatterns.properties
similarity index 100%
rename from tests/resources/org/apache/log4j/TestLogMFPatterns.properties
rename to core/src/test/resources/org/apache/log4j/TestLogMFPatterns.properties
diff --git a/tests/resources/org/apache/log4j/TestLogSFPatterns.properties b/core/src/test/resources/org/apache/log4j/TestLogSFPatterns.properties
similarity index 100%
rename from tests/resources/org/apache/log4j/TestLogSFPatterns.properties
rename to core/src/test/resources/org/apache/log4j/TestLogSFPatterns.properties
diff --git a/tests/witness/LevelMatchFilter_accept b/core/src/test/resources/witness/LevelMatchFilter_accept
similarity index 100%
rename from tests/witness/LevelMatchFilter_accept
rename to core/src/test/resources/witness/LevelMatchFilter_accept
diff --git a/tests/witness/LevelMatchFilter_deny b/core/src/test/resources/witness/LevelMatchFilter_deny
similarity index 100%
rename from tests/witness/LevelMatchFilter_deny
rename to core/src/test/resources/witness/LevelMatchFilter_deny
diff --git a/tests/witness/PatternParser_mdc b/core/src/test/resources/witness/PatternParser_mdc
similarity index 100%
rename from tests/witness/PatternParser_mdc
rename to core/src/test/resources/witness/PatternParser_mdc
diff --git a/tests/witness/customLevel.1 b/core/src/test/resources/witness/customLevel.1
similarity index 100%
rename from tests/witness/customLevel.1
rename to core/src/test/resources/witness/customLevel.1
diff --git a/tests/witness/customLevel.2 b/core/src/test/resources/witness/customLevel.2
similarity index 100%
rename from tests/witness/customLevel.2
rename to core/src/test/resources/witness/customLevel.2
diff --git a/tests/witness/customLevel.3 b/core/src/test/resources/witness/customLevel.3
similarity index 100%
rename from tests/witness/customLevel.3
rename to core/src/test/resources/witness/customLevel.3
diff --git a/tests/witness/customLevel.4 b/core/src/test/resources/witness/customLevel.4
similarity index 100%
rename from tests/witness/customLevel.4
rename to core/src/test/resources/witness/customLevel.4
diff --git a/tests/witness/customLogger.1 b/core/src/test/resources/witness/customLogger.1
similarity index 100%
rename from tests/witness/customLogger.1
rename to core/src/test/resources/witness/customLogger.1
diff --git a/tests/witness/customLogger.2 b/core/src/test/resources/witness/customLogger.2
similarity index 100%
rename from tests/witness/customLogger.2
rename to core/src/test/resources/witness/customLogger.2
diff --git a/tests/witness/dom.A1.1 b/core/src/test/resources/witness/dom.A1.1
similarity index 100%
rename from tests/witness/dom.A1.1
rename to core/src/test/resources/witness/dom.A1.1
diff --git a/tests/witness/dom.A1.4 b/core/src/test/resources/witness/dom.A1.4
similarity index 100%
rename from tests/witness/dom.A1.4
rename to core/src/test/resources/witness/dom.A1.4
diff --git a/tests/witness/dom.A2.1 b/core/src/test/resources/witness/dom.A2.1
similarity index 100%
rename from tests/witness/dom.A2.1
rename to core/src/test/resources/witness/dom.A2.1
diff --git a/tests/witness/dom.A2.4 b/core/src/test/resources/witness/dom.A2.4
similarity index 100%
rename from tests/witness/dom.A2.4
rename to core/src/test/resources/witness/dom.A2.4
diff --git a/tests/witness/drfa_blockedRollover.log b/core/src/test/resources/witness/drfa_blockedRollover.log
similarity index 100%
rename from tests/witness/drfa_blockedRollover.log
rename to core/src/test/resources/witness/drfa_blockedRollover.log
diff --git a/tests/witness/fallback1 b/core/src/test/resources/witness/fallback1
similarity index 100%
rename from tests/witness/fallback1
rename to core/src/test/resources/witness/fallback1
diff --git a/tests/witness/hierarchyThreshold.1 b/core/src/test/resources/witness/hierarchyThreshold.1
similarity index 100%
rename from tests/witness/hierarchyThreshold.1
rename to core/src/test/resources/witness/hierarchyThreshold.1
diff --git a/tests/witness/hierarchyThreshold.2 b/core/src/test/resources/witness/hierarchyThreshold.2
similarity index 100%
rename from tests/witness/hierarchyThreshold.2
rename to core/src/test/resources/witness/hierarchyThreshold.2
diff --git a/tests/witness/hierarchyThreshold.3 b/core/src/test/resources/witness/hierarchyThreshold.3
similarity index 100%
rename from tests/witness/hierarchyThreshold.3
rename to core/src/test/resources/witness/hierarchyThreshold.3
diff --git a/tests/witness/hierarchyThreshold.4 b/core/src/test/resources/witness/hierarchyThreshold.4
similarity index 100%
rename from tests/witness/hierarchyThreshold.4
rename to core/src/test/resources/witness/hierarchyThreshold.4
diff --git a/tests/witness/hierarchyThreshold.5 b/core/src/test/resources/witness/hierarchyThreshold.5
similarity index 100%
rename from tests/witness/hierarchyThreshold.5
rename to core/src/test/resources/witness/hierarchyThreshold.5
diff --git a/tests/witness/hierarchyThreshold.6 b/core/src/test/resources/witness/hierarchyThreshold.6
similarity index 100%
rename from tests/witness/hierarchyThreshold.6
rename to core/src/test/resources/witness/hierarchyThreshold.6
diff --git a/tests/witness/hierarchyThreshold.7 b/core/src/test/resources/witness/hierarchyThreshold.7
similarity index 100%
rename from tests/witness/hierarchyThreshold.7
rename to core/src/test/resources/witness/hierarchyThreshold.7
diff --git a/tests/witness/hierarchyThreshold.8 b/core/src/test/resources/witness/hierarchyThreshold.8
similarity index 100%
rename from tests/witness/hierarchyThreshold.8
rename to core/src/test/resources/witness/hierarchyThreshold.8
diff --git a/tests/witness/pattern/enhancedPatternLayout.1 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.1
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.1
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.1
diff --git a/tests/witness/pattern/enhancedPatternLayout.10 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.10
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.10
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.10
diff --git a/tests/witness/pattern/enhancedPatternLayout.11 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.11
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.11
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.11
diff --git a/tests/witness/pattern/enhancedPatternLayout.12 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.12
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.12
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.12
diff --git a/tests/witness/pattern/enhancedPatternLayout.13 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.13
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.13
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.13
diff --git a/tests/witness/pattern/enhancedPatternLayout.14 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.14
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.14
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.14
diff --git a/tests/witness/pattern/enhancedPatternLayout.15 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.15
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.15
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.15
diff --git a/tests/witness/pattern/enhancedPatternLayout.2 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.2
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.2
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.2
diff --git a/tests/witness/pattern/enhancedPatternLayout.3 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.3
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.3
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.3
diff --git a/tests/witness/pattern/enhancedPatternLayout.4 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.4
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.4
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.4
diff --git a/tests/witness/pattern/enhancedPatternLayout.5 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.5
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.5
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.5
diff --git a/tests/witness/pattern/enhancedPatternLayout.6 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.6
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.6
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.6
diff --git a/tests/witness/pattern/enhancedPatternLayout.7 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.7
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.7
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.7
diff --git a/tests/witness/pattern/enhancedPatternLayout.8 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.8
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.8
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.8
diff --git a/tests/witness/pattern/enhancedPatternLayout.9 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.9
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.9
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.9
diff --git a/tests/witness/pattern/enhancedPatternLayout.mdc.1 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.mdc.1
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.mdc.1
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.mdc.1
diff --git a/tests/witness/pattern/enhancedPatternLayout.mdc.2 b/core/src/test/resources/witness/pattern/enhancedPatternLayout.mdc.2
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.mdc.2
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.mdc.2
diff --git a/tests/witness/pattern/enhancedPatternLayout.throwable b/core/src/test/resources/witness/pattern/enhancedPatternLayout.throwable
similarity index 100%
rename from tests/witness/pattern/enhancedPatternLayout.throwable
rename to core/src/test/resources/witness/pattern/enhancedPatternLayout.throwable
diff --git a/tests/witness/patternLayout.1 b/core/src/test/resources/witness/patternLayout.1
similarity index 100%
rename from tests/witness/patternLayout.1
rename to core/src/test/resources/witness/patternLayout.1
diff --git a/tests/witness/patternLayout.10 b/core/src/test/resources/witness/patternLayout.10
similarity index 100%
rename from tests/witness/patternLayout.10
rename to core/src/test/resources/witness/patternLayout.10
diff --git a/tests/witness/patternLayout.11 b/core/src/test/resources/witness/patternLayout.11
similarity index 100%
rename from tests/witness/patternLayout.11
rename to core/src/test/resources/witness/patternLayout.11
diff --git a/tests/witness/patternLayout.12 b/core/src/test/resources/witness/patternLayout.12
similarity index 100%
rename from tests/witness/patternLayout.12
rename to core/src/test/resources/witness/patternLayout.12
diff --git a/tests/witness/patternLayout.13 b/core/src/test/resources/witness/patternLayout.13
similarity index 100%
rename from tests/witness/patternLayout.13
rename to core/src/test/resources/witness/patternLayout.13
diff --git a/tests/witness/patternLayout.14 b/core/src/test/resources/witness/patternLayout.14
similarity index 100%
rename from tests/witness/patternLayout.14
rename to core/src/test/resources/witness/patternLayout.14
diff --git a/tests/witness/patternLayout.2 b/core/src/test/resources/witness/patternLayout.2
similarity index 100%
rename from tests/witness/patternLayout.2
rename to core/src/test/resources/witness/patternLayout.2
diff --git a/tests/witness/patternLayout.3 b/core/src/test/resources/witness/patternLayout.3
similarity index 100%
rename from tests/witness/patternLayout.3
rename to core/src/test/resources/witness/patternLayout.3
diff --git a/tests/witness/patternLayout.4 b/core/src/test/resources/witness/patternLayout.4
similarity index 100%
rename from tests/witness/patternLayout.4
rename to core/src/test/resources/witness/patternLayout.4
diff --git a/tests/witness/patternLayout.5 b/core/src/test/resources/witness/patternLayout.5
similarity index 100%
rename from tests/witness/patternLayout.5
rename to core/src/test/resources/witness/patternLayout.5
diff --git a/tests/witness/patternLayout.6 b/core/src/test/resources/witness/patternLayout.6
similarity index 100%
rename from tests/witness/patternLayout.6
rename to core/src/test/resources/witness/patternLayout.6
diff --git a/tests/witness/patternLayout.7 b/core/src/test/resources/witness/patternLayout.7
similarity index 100%
rename from tests/witness/patternLayout.7
rename to core/src/test/resources/witness/patternLayout.7
diff --git a/tests/witness/patternLayout.8 b/core/src/test/resources/witness/patternLayout.8
similarity index 100%
rename from tests/witness/patternLayout.8
rename to core/src/test/resources/witness/patternLayout.8
diff --git a/tests/witness/patternLayout.9 b/core/src/test/resources/witness/patternLayout.9
similarity index 100%
rename from tests/witness/patternLayout.9
rename to core/src/test/resources/witness/patternLayout.9
diff --git a/tests/witness/patternLayout.mdc.1 b/core/src/test/resources/witness/patternLayout.mdc.1
similarity index 100%
rename from tests/witness/patternLayout.mdc.1
rename to core/src/test/resources/witness/patternLayout.mdc.1
diff --git a/tests/witness/patternLayout.mdc.clear b/core/src/test/resources/witness/patternLayout.mdc.clear
similarity index 100%
rename from tests/witness/patternLayout.mdc.clear
rename to core/src/test/resources/witness/patternLayout.mdc.clear
diff --git a/tests/witness/prudent b/core/src/test/resources/witness/prudent
similarity index 100%
rename from tests/witness/prudent
rename to core/src/test/resources/witness/prudent
diff --git a/tests/witness/serialization/exception.bin b/core/src/test/resources/witness/serialization/exception.bin
similarity index 100%
rename from tests/witness/serialization/exception.bin
rename to core/src/test/resources/witness/serialization/exception.bin
Binary files differ
diff --git a/tests/witness/serialization/info.bin b/core/src/test/resources/witness/serialization/info.bin
similarity index 100%
rename from tests/witness/serialization/info.bin
rename to core/src/test/resources/witness/serialization/info.bin
Binary files differ
diff --git a/tests/witness/serialization/location.bin b/core/src/test/resources/witness/serialization/location.bin
similarity index 100%
rename from tests/witness/serialization/location.bin
rename to core/src/test/resources/witness/serialization/location.bin
Binary files differ
diff --git a/tests/witness/serialization/mdc.bin b/core/src/test/resources/witness/serialization/mdc.bin
similarity index 100%
rename from tests/witness/serialization/mdc.bin
rename to core/src/test/resources/witness/serialization/mdc.bin
Binary files differ
diff --git a/tests/witness/serialization/ndc.bin b/core/src/test/resources/witness/serialization/ndc.bin
similarity index 100%
rename from tests/witness/serialization/ndc.bin
rename to core/src/test/resources/witness/serialization/ndc.bin
Binary files differ
diff --git a/tests/witness/serialization/simple.bin b/core/src/test/resources/witness/serialization/simple.bin
similarity index 100%
rename from tests/witness/serialization/simple.bin
rename to core/src/test/resources/witness/serialization/simple.bin
Binary files differ
diff --git a/tests/witness/simple b/core/src/test/resources/witness/simple
similarity index 100%
rename from tests/witness/simple
rename to core/src/test/resources/witness/simple
diff --git a/tests/witness/socketServer.1 b/core/src/test/resources/witness/socketServer.1
similarity index 100%
rename from tests/witness/socketServer.1
rename to core/src/test/resources/witness/socketServer.1
diff --git a/tests/witness/socketServer.2 b/core/src/test/resources/witness/socketServer.2
similarity index 100%
rename from tests/witness/socketServer.2
rename to core/src/test/resources/witness/socketServer.2
diff --git a/tests/witness/socketServer.3 b/core/src/test/resources/witness/socketServer.3
similarity index 100%
rename from tests/witness/socketServer.3
rename to core/src/test/resources/witness/socketServer.3
diff --git a/tests/witness/socketServer.4 b/core/src/test/resources/witness/socketServer.4
similarity index 100%
rename from tests/witness/socketServer.4
rename to core/src/test/resources/witness/socketServer.4
diff --git a/tests/witness/socketServer.5 b/core/src/test/resources/witness/socketServer.5
similarity index 100%
rename from tests/witness/socketServer.5
rename to core/src/test/resources/witness/socketServer.5
diff --git a/tests/witness/socketServer.6 b/core/src/test/resources/witness/socketServer.6
similarity index 100%
rename from tests/witness/socketServer.6
rename to core/src/test/resources/witness/socketServer.6
diff --git a/tests/witness/socketServer.7 b/core/src/test/resources/witness/socketServer.7
similarity index 100%
rename from tests/witness/socketServer.7
rename to core/src/test/resources/witness/socketServer.7
diff --git a/tests/witness/socketServer.8 b/core/src/test/resources/witness/socketServer.8
similarity index 100%
rename from tests/witness/socketServer.8
rename to core/src/test/resources/witness/socketServer.8
diff --git a/tests/witness/ttcc b/core/src/test/resources/witness/ttcc
similarity index 100%
rename from tests/witness/ttcc
rename to core/src/test/resources/witness/ttcc
diff --git a/tests/witness/xmlLayout.1 b/core/src/test/resources/witness/xmlLayout.1
similarity index 100%
rename from tests/witness/xmlLayout.1
rename to core/src/test/resources/witness/xmlLayout.1
diff --git a/tests/witness/xmlLayout.2 b/core/src/test/resources/witness/xmlLayout.2
similarity index 100%
rename from tests/witness/xmlLayout.2
rename to core/src/test/resources/witness/xmlLayout.2
diff --git a/tests/witness/xmlLayout.3 b/core/src/test/resources/witness/xmlLayout.3
similarity index 100%
rename from tests/witness/xmlLayout.3
rename to core/src/test/resources/witness/xmlLayout.3
diff --git a/tests/witness/xmlLayout.mdc.1 b/core/src/test/resources/witness/xmlLayout.mdc.1
similarity index 100%
rename from tests/witness/xmlLayout.mdc.1
rename to core/src/test/resources/witness/xmlLayout.mdc.1
diff --git a/tests/witness/xmlLayout.mdc.2 b/core/src/test/resources/witness/xmlLayout.mdc.2
similarity index 100%
rename from tests/witness/xmlLayout.mdc.2
rename to core/src/test/resources/witness/xmlLayout.mdc.2
diff --git a/tests/witness/xmlLayout.null b/core/src/test/resources/witness/xmlLayout.null
similarity index 100%
rename from tests/witness/xmlLayout.null
rename to core/src/test/resources/witness/xmlLayout.null
diff --git a/tests/run-tests.bat b/core/src/test/run-tests.bat
similarity index 100%
rename from tests/run-tests.bat
rename to core/src/test/run-tests.bat
diff --git a/tests/sun_checks.xml b/core/src/test/sun_checks.xml
similarity index 100%
rename from tests/sun_checks.xml
rename to core/src/test/sun_checks.xml
diff --git a/modules/chainsaw/pom.xml b/modules/chainsaw/pom.xml
new file mode 100644
index 0000000..d4497ea
--- /dev/null
+++ b/modules/chainsaw/pom.xml
@@ -0,0 +1,38 @@
+<!--
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-modules</artifactId>
+      <version>1.4.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.log4j</groupId>
+  <artifactId>log4j-chainsaw</artifactId>
+  <name>Apache Log4j-Chainsaw V1</name>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ControlPanel.java b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ControlPanel.java
new file mode 100644
index 0000000..53b0b1b
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ControlPanel.java
@@ -0,0 +1,222 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+import org.apache.log4j.Level;
+
+/**
+ * Represents the controls for filtering, pausing, exiting, etc.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ */
+class ControlPanel extends JPanel {
+    /** use the log messages **/
+    private static final Logger LOG = 
+                                  Logger.getLogger(ControlPanel.class);
+
+    /**
+     * Creates a new <code>ControlPanel</code> instance.
+     *
+     * @param aModel the model to control
+     */
+    ControlPanel(final MyTableModel aModel) {
+        setBorder(BorderFactory.createTitledBorder("Controls: "));
+        final GridBagLayout gridbag = new GridBagLayout();
+        final GridBagConstraints c = new GridBagConstraints();
+        setLayout(gridbag);
+
+        // Pad everything
+        c.ipadx = 5;
+        c.ipady = 5;
+
+        // Add the 1st column of labels
+        c.gridx = 0;
+        c.anchor = GridBagConstraints.EAST;
+
+        c.gridy = 0;
+        JLabel label = new JLabel("Filter Level:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        c.gridy++;
+        label = new JLabel("Filter Thread:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        c.gridy++;
+        label = new JLabel("Filter Logger:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        c.gridy++;
+        label = new JLabel("Filter NDC:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        c.gridy++;
+        label = new JLabel("Filter Message:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        // Add the 2nd column of filters
+        c.weightx = 1;
+        //c.weighty = 1;
+        c.gridx = 1;
+        c.anchor = GridBagConstraints.WEST;
+
+        c.gridy = 0;
+        final Level[] allPriorities = new Level[] {Level.FATAL, 
+               Level.ERROR, 
+               Level.WARN, 
+			   Level.INFO, 
+			   Level.DEBUG, 
+			   Level.TRACE };
+        
+        final JComboBox priorities = new JComboBox(allPriorities);
+        final Level lowest = allPriorities[allPriorities.length - 1];
+        priorities.setSelectedItem(lowest);
+        aModel.setPriorityFilter(lowest);
+        gridbag.setConstraints(priorities, c);
+        add(priorities);
+        priorities.setEditable(false);
+        priorities.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent aEvent) {
+                    aModel.setPriorityFilter(
+                        (Priority) priorities.getSelectedItem());
+                }
+            });
+
+
+        c.fill = GridBagConstraints.HORIZONTAL;
+        c.gridy++;
+        final JTextField threadField = new JTextField("");
+        threadField.getDocument().addDocumentListener(new DocumentListener () {
+                public void insertUpdate(DocumentEvent aEvent) {
+                    aModel.setThreadFilter(threadField.getText());
+                }
+                public void removeUpdate(DocumentEvent aEvente) {
+                    aModel.setThreadFilter(threadField.getText());
+                }
+                public void changedUpdate(DocumentEvent aEvent) {
+                    aModel.setThreadFilter(threadField.getText());
+                }
+            });
+        gridbag.setConstraints(threadField, c);
+        add(threadField);
+
+        c.gridy++;
+        final JTextField catField = new JTextField("");
+        catField.getDocument().addDocumentListener(new DocumentListener () {
+                public void insertUpdate(DocumentEvent aEvent) {
+                    aModel.setCategoryFilter(catField.getText());
+                }
+                public void removeUpdate(DocumentEvent aEvent) {
+                    aModel.setCategoryFilter(catField.getText());
+                }
+                public void changedUpdate(DocumentEvent aEvent) {
+                    aModel.setCategoryFilter(catField.getText());
+                }
+            });
+        gridbag.setConstraints(catField, c);
+        add(catField);
+
+        c.gridy++;
+        final JTextField ndcField = new JTextField("");
+        ndcField.getDocument().addDocumentListener(new DocumentListener () {
+                public void insertUpdate(DocumentEvent aEvent) {
+                    aModel.setNDCFilter(ndcField.getText());
+                }
+                public void removeUpdate(DocumentEvent aEvent) {
+                    aModel.setNDCFilter(ndcField.getText());
+                }
+                public void changedUpdate(DocumentEvent aEvent) {
+                    aModel.setNDCFilter(ndcField.getText());
+                }
+            });
+        gridbag.setConstraints(ndcField, c);
+        add(ndcField);
+
+        c.gridy++;
+        final JTextField msgField = new JTextField("");
+        msgField.getDocument().addDocumentListener(new DocumentListener () {
+                public void insertUpdate(DocumentEvent aEvent) {
+                    aModel.setMessageFilter(msgField.getText());
+                }
+                public void removeUpdate(DocumentEvent aEvent) {
+                    aModel.setMessageFilter(msgField.getText());
+                }
+                public void changedUpdate(DocumentEvent aEvent) {
+                    aModel.setMessageFilter(msgField.getText());
+                }
+            });
+
+
+        gridbag.setConstraints(msgField, c);
+        add(msgField);
+
+        // Add the 3rd column of buttons
+        c.weightx = 0;
+        c.fill = GridBagConstraints.HORIZONTAL;
+        c.anchor = GridBagConstraints.EAST;
+        c.gridx = 2;
+
+        c.gridy = 0;
+        final JButton exitButton = new JButton("Exit");
+        exitButton.setMnemonic('x');
+        exitButton.addActionListener(ExitAction.INSTANCE);
+        gridbag.setConstraints(exitButton, c);
+        add(exitButton);
+
+        c.gridy++;
+        final JButton clearButton = new JButton("Clear");
+        clearButton.setMnemonic('c');
+        clearButton.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent aEvent) {
+                    aModel.clear();
+                }
+            });
+        gridbag.setConstraints(clearButton, c);
+        add(clearButton);
+
+        c.gridy++;
+        final JButton toggleButton = new JButton("Pause");
+        toggleButton.setMnemonic('p');
+        toggleButton.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent aEvent) {
+                    aModel.toggle();
+                    toggleButton.setText(
+                        aModel.isPaused() ? "Resume" : "Pause");
+                }
+            });
+        gridbag.setConstraints(toggleButton, c);
+        add(toggleButton);
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ControlPanel.java.orig b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ControlPanel.java.orig
new file mode 100644
index 0000000..53b0b1b
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ControlPanel.java.orig
@@ -0,0 +1,222 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+import org.apache.log4j.Level;
+
+/**
+ * Represents the controls for filtering, pausing, exiting, etc.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ */
+class ControlPanel extends JPanel {
+    /** use the log messages **/
+    private static final Logger LOG = 
+                                  Logger.getLogger(ControlPanel.class);
+
+    /**
+     * Creates a new <code>ControlPanel</code> instance.
+     *
+     * @param aModel the model to control
+     */
+    ControlPanel(final MyTableModel aModel) {
+        setBorder(BorderFactory.createTitledBorder("Controls: "));
+        final GridBagLayout gridbag = new GridBagLayout();
+        final GridBagConstraints c = new GridBagConstraints();
+        setLayout(gridbag);
+
+        // Pad everything
+        c.ipadx = 5;
+        c.ipady = 5;
+
+        // Add the 1st column of labels
+        c.gridx = 0;
+        c.anchor = GridBagConstraints.EAST;
+
+        c.gridy = 0;
+        JLabel label = new JLabel("Filter Level:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        c.gridy++;
+        label = new JLabel("Filter Thread:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        c.gridy++;
+        label = new JLabel("Filter Logger:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        c.gridy++;
+        label = new JLabel("Filter NDC:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        c.gridy++;
+        label = new JLabel("Filter Message:");
+        gridbag.setConstraints(label, c);
+        add(label);
+
+        // Add the 2nd column of filters
+        c.weightx = 1;
+        //c.weighty = 1;
+        c.gridx = 1;
+        c.anchor = GridBagConstraints.WEST;
+
+        c.gridy = 0;
+        final Level[] allPriorities = new Level[] {Level.FATAL, 
+               Level.ERROR, 
+               Level.WARN, 
+			   Level.INFO, 
+			   Level.DEBUG, 
+			   Level.TRACE };
+        
+        final JComboBox priorities = new JComboBox(allPriorities);
+        final Level lowest = allPriorities[allPriorities.length - 1];
+        priorities.setSelectedItem(lowest);
+        aModel.setPriorityFilter(lowest);
+        gridbag.setConstraints(priorities, c);
+        add(priorities);
+        priorities.setEditable(false);
+        priorities.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent aEvent) {
+                    aModel.setPriorityFilter(
+                        (Priority) priorities.getSelectedItem());
+                }
+            });
+
+
+        c.fill = GridBagConstraints.HORIZONTAL;
+        c.gridy++;
+        final JTextField threadField = new JTextField("");
+        threadField.getDocument().addDocumentListener(new DocumentListener () {
+                public void insertUpdate(DocumentEvent aEvent) {
+                    aModel.setThreadFilter(threadField.getText());
+                }
+                public void removeUpdate(DocumentEvent aEvente) {
+                    aModel.setThreadFilter(threadField.getText());
+                }
+                public void changedUpdate(DocumentEvent aEvent) {
+                    aModel.setThreadFilter(threadField.getText());
+                }
+            });
+        gridbag.setConstraints(threadField, c);
+        add(threadField);
+
+        c.gridy++;
+        final JTextField catField = new JTextField("");
+        catField.getDocument().addDocumentListener(new DocumentListener () {
+                public void insertUpdate(DocumentEvent aEvent) {
+                    aModel.setCategoryFilter(catField.getText());
+                }
+                public void removeUpdate(DocumentEvent aEvent) {
+                    aModel.setCategoryFilter(catField.getText());
+                }
+                public void changedUpdate(DocumentEvent aEvent) {
+                    aModel.setCategoryFilter(catField.getText());
+                }
+            });
+        gridbag.setConstraints(catField, c);
+        add(catField);
+
+        c.gridy++;
+        final JTextField ndcField = new JTextField("");
+        ndcField.getDocument().addDocumentListener(new DocumentListener () {
+                public void insertUpdate(DocumentEvent aEvent) {
+                    aModel.setNDCFilter(ndcField.getText());
+                }
+                public void removeUpdate(DocumentEvent aEvent) {
+                    aModel.setNDCFilter(ndcField.getText());
+                }
+                public void changedUpdate(DocumentEvent aEvent) {
+                    aModel.setNDCFilter(ndcField.getText());
+                }
+            });
+        gridbag.setConstraints(ndcField, c);
+        add(ndcField);
+
+        c.gridy++;
+        final JTextField msgField = new JTextField("");
+        msgField.getDocument().addDocumentListener(new DocumentListener () {
+                public void insertUpdate(DocumentEvent aEvent) {
+                    aModel.setMessageFilter(msgField.getText());
+                }
+                public void removeUpdate(DocumentEvent aEvent) {
+                    aModel.setMessageFilter(msgField.getText());
+                }
+                public void changedUpdate(DocumentEvent aEvent) {
+                    aModel.setMessageFilter(msgField.getText());
+                }
+            });
+
+
+        gridbag.setConstraints(msgField, c);
+        add(msgField);
+
+        // Add the 3rd column of buttons
+        c.weightx = 0;
+        c.fill = GridBagConstraints.HORIZONTAL;
+        c.anchor = GridBagConstraints.EAST;
+        c.gridx = 2;
+
+        c.gridy = 0;
+        final JButton exitButton = new JButton("Exit");
+        exitButton.setMnemonic('x');
+        exitButton.addActionListener(ExitAction.INSTANCE);
+        gridbag.setConstraints(exitButton, c);
+        add(exitButton);
+
+        c.gridy++;
+        final JButton clearButton = new JButton("Clear");
+        clearButton.setMnemonic('c');
+        clearButton.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent aEvent) {
+                    aModel.clear();
+                }
+            });
+        gridbag.setConstraints(clearButton, c);
+        add(clearButton);
+
+        c.gridy++;
+        final JButton toggleButton = new JButton("Pause");
+        toggleButton.setMnemonic('p');
+        toggleButton.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent aEvent) {
+                    aModel.toggle();
+                    toggleButton.setText(
+                        aModel.isPaused() ? "Resume" : "Pause");
+                }
+            });
+        gridbag.setConstraints(toggleButton, c);
+        add(toggleButton);
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/DetailPanel.java b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/DetailPanel.java
new file mode 100644
index 0000000..1f5dfe2
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/DetailPanel.java
@@ -0,0 +1,170 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.BorderLayout;
+import java.text.MessageFormat;
+import java.util.Date;
+import javax.swing.BorderFactory;
+import javax.swing.JEditorPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import org.apache.log4j.Logger;
+
+/**
+ * A panel for showing a stack trace.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ */
+class DetailPanel
+    extends JPanel
+    implements ListSelectionListener
+{
+    /** used to log events **/
+    private static final Logger LOG =
+        Logger.getLogger(DetailPanel.class);
+
+    /** used to format the logging event **/
+    private static final MessageFormat FORMATTER = new MessageFormat(
+        "<b>Time:</b> <code>{0,time,medium}</code>" +
+        "&nbsp;&nbsp;<b>Priority:</b> <code>{1}</code>" +
+        "&nbsp;&nbsp;<b>Thread:</b> <code>{2}</code>" +
+        "&nbsp;&nbsp;<b>NDC:</b> <code>{3}</code>" +
+        "<br><b>Logger:</b> <code>{4}</code>" +
+        "<br><b>Location:</b> <code>{5}</code>" +
+        "<br><b>Message:</b>" +
+        "<pre>{6}</pre>" +
+        "<b>Throwable:</b>" +
+        "<pre>{7}</pre>");
+
+    /** the model for the data to render **/
+    private final MyTableModel mModel;
+    /** pane for rendering detail **/
+    private final JEditorPane mDetails;
+
+    /**
+     * Creates a new <code>DetailPanel</code> instance.
+     *
+     * @param aTable the table to listen for selections on
+     * @param aModel the model backing the table
+     */
+    DetailPanel(JTable aTable, final MyTableModel aModel) {
+        mModel = aModel;
+        setLayout(new BorderLayout());
+        setBorder(BorderFactory.createTitledBorder("Details: "));
+
+        mDetails = new JEditorPane();
+        mDetails.setEditable(false);
+        mDetails.setContentType("text/html");
+        add(new JScrollPane(mDetails), BorderLayout.CENTER);
+
+        final ListSelectionModel rowSM = aTable.getSelectionModel();
+        rowSM.addListSelectionListener(this);
+    }
+
+    /** @see ListSelectionListener **/
+    public void valueChanged(ListSelectionEvent aEvent) {
+        //Ignore extra messages.
+        if (aEvent.getValueIsAdjusting()) {
+            return;
+        }
+
+        final ListSelectionModel lsm = (ListSelectionModel) aEvent.getSource();
+        if (lsm.isSelectionEmpty()) {
+            mDetails.setText("Nothing selected");
+        } else {
+            final int selectedRow = lsm.getMinSelectionIndex();
+            final EventDetails e = mModel.getEventDetails(selectedRow);
+            final Object[] args =
+            {
+                new Date(e.getTimeStamp()),
+                e.getPriority(),
+                escape(e.getThreadName()),
+                escape(e.getNDC()),
+                escape(e.getCategoryName()),
+                escape(e.getLocationDetails()),
+                escape(e.getMessage()),
+                escape(getThrowableStrRep(e))
+            };
+            mDetails.setText(FORMATTER.format(args));
+            mDetails.setCaretPosition(0);
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Private methods
+    ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Returns a string representation of a throwable.
+     *
+     * @param aEvent contains the throwable information
+     * @return a <code>String</code> value
+     */
+    private static String getThrowableStrRep(EventDetails aEvent) {
+        final String[] strs = aEvent.getThrowableStrRep();
+        if (strs == null) {
+            return null;
+        }
+
+        final StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < strs.length; i++) {
+            sb.append(strs[i]).append("\n");
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Escape &lt;, &gt; &amp; and &quot; as their entities. It is very
+     * dumb about &amp; handling.
+     * @param aStr the String to escape.
+     * @return the escaped String
+     */
+    private String escape(String aStr) {
+        if (aStr == null) {
+            return null;
+        }
+
+        final StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < aStr.length(); i++) {
+            char c = aStr.charAt(i);
+            switch (c) {
+            case '<':
+                buf.append("&lt;");
+                break;
+            case '>':
+                buf.append("&gt;");
+                break;
+            case '\"':
+                buf.append("&quot;");
+                break;
+            case '&':
+                buf.append("&amp;");
+                break;
+            default:
+                buf.append(c);
+                break;
+            }
+        }
+        return buf.toString();
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/DetailPanel.java.orig b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/DetailPanel.java.orig
new file mode 100644
index 0000000..1f5dfe2
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/DetailPanel.java.orig
@@ -0,0 +1,170 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.BorderLayout;
+import java.text.MessageFormat;
+import java.util.Date;
+import javax.swing.BorderFactory;
+import javax.swing.JEditorPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import org.apache.log4j.Logger;
+
+/**
+ * A panel for showing a stack trace.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ */
+class DetailPanel
+    extends JPanel
+    implements ListSelectionListener
+{
+    /** used to log events **/
+    private static final Logger LOG =
+        Logger.getLogger(DetailPanel.class);
+
+    /** used to format the logging event **/
+    private static final MessageFormat FORMATTER = new MessageFormat(
+        "<b>Time:</b> <code>{0,time,medium}</code>" +
+        "&nbsp;&nbsp;<b>Priority:</b> <code>{1}</code>" +
+        "&nbsp;&nbsp;<b>Thread:</b> <code>{2}</code>" +
+        "&nbsp;&nbsp;<b>NDC:</b> <code>{3}</code>" +
+        "<br><b>Logger:</b> <code>{4}</code>" +
+        "<br><b>Location:</b> <code>{5}</code>" +
+        "<br><b>Message:</b>" +
+        "<pre>{6}</pre>" +
+        "<b>Throwable:</b>" +
+        "<pre>{7}</pre>");
+
+    /** the model for the data to render **/
+    private final MyTableModel mModel;
+    /** pane for rendering detail **/
+    private final JEditorPane mDetails;
+
+    /**
+     * Creates a new <code>DetailPanel</code> instance.
+     *
+     * @param aTable the table to listen for selections on
+     * @param aModel the model backing the table
+     */
+    DetailPanel(JTable aTable, final MyTableModel aModel) {
+        mModel = aModel;
+        setLayout(new BorderLayout());
+        setBorder(BorderFactory.createTitledBorder("Details: "));
+
+        mDetails = new JEditorPane();
+        mDetails.setEditable(false);
+        mDetails.setContentType("text/html");
+        add(new JScrollPane(mDetails), BorderLayout.CENTER);
+
+        final ListSelectionModel rowSM = aTable.getSelectionModel();
+        rowSM.addListSelectionListener(this);
+    }
+
+    /** @see ListSelectionListener **/
+    public void valueChanged(ListSelectionEvent aEvent) {
+        //Ignore extra messages.
+        if (aEvent.getValueIsAdjusting()) {
+            return;
+        }
+
+        final ListSelectionModel lsm = (ListSelectionModel) aEvent.getSource();
+        if (lsm.isSelectionEmpty()) {
+            mDetails.setText("Nothing selected");
+        } else {
+            final int selectedRow = lsm.getMinSelectionIndex();
+            final EventDetails e = mModel.getEventDetails(selectedRow);
+            final Object[] args =
+            {
+                new Date(e.getTimeStamp()),
+                e.getPriority(),
+                escape(e.getThreadName()),
+                escape(e.getNDC()),
+                escape(e.getCategoryName()),
+                escape(e.getLocationDetails()),
+                escape(e.getMessage()),
+                escape(getThrowableStrRep(e))
+            };
+            mDetails.setText(FORMATTER.format(args));
+            mDetails.setCaretPosition(0);
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Private methods
+    ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Returns a string representation of a throwable.
+     *
+     * @param aEvent contains the throwable information
+     * @return a <code>String</code> value
+     */
+    private static String getThrowableStrRep(EventDetails aEvent) {
+        final String[] strs = aEvent.getThrowableStrRep();
+        if (strs == null) {
+            return null;
+        }
+
+        final StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < strs.length; i++) {
+            sb.append(strs[i]).append("\n");
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Escape &lt;, &gt; &amp; and &quot; as their entities. It is very
+     * dumb about &amp; handling.
+     * @param aStr the String to escape.
+     * @return the escaped String
+     */
+    private String escape(String aStr) {
+        if (aStr == null) {
+            return null;
+        }
+
+        final StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < aStr.length(); i++) {
+            char c = aStr.charAt(i);
+            switch (c) {
+            case '<':
+                buf.append("&lt;");
+                break;
+            case '>':
+                buf.append("&gt;");
+                break;
+            case '\"':
+                buf.append("&quot;");
+                break;
+            case '&':
+                buf.append("&amp;");
+                break;
+            default:
+                buf.append(c);
+                break;
+            }
+        }
+        return buf.toString();
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/EventDetails.java b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/EventDetails.java
new file mode 100644
index 0000000..4b3ad94
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/EventDetails.java
@@ -0,0 +1,135 @@
+/*
+ * 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.chainsaw;
+
+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 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 aThreadName,
+                 String aMessage,
+                 String[] aThrowableStrRep,
+                 String aLocationDetails)
+    {
+        mTimeStamp = aTimeStamp;
+        mPriority = aPriority;
+        mCategoryName = aCategoryName;
+        mNDC = aNDC;
+        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(),
+             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 #mThreadName **/
+    String getThreadName() {
+        return mThreadName;
+    }
+
+    /** @see #mMessage **/
+    String getMessage() {
+        return mMessage;
+    }
+
+    /** @see #mLocationDetails **/
+    String getLocationDetails(){
+        return mLocationDetails;
+    }
+
+    /** @see #mThrowableStrRep **/
+    String[] getThrowableStrRep() {
+        return mThrowableStrRep;
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/EventDetails.java.orig b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/EventDetails.java.orig
new file mode 100644
index 0000000..4b3ad94
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/EventDetails.java.orig
@@ -0,0 +1,135 @@
+/*
+ * 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.chainsaw;
+
+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 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 aThreadName,
+                 String aMessage,
+                 String[] aThrowableStrRep,
+                 String aLocationDetails)
+    {
+        mTimeStamp = aTimeStamp;
+        mPriority = aPriority;
+        mCategoryName = aCategoryName;
+        mNDC = aNDC;
+        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(),
+             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 #mThreadName **/
+    String getThreadName() {
+        return mThreadName;
+    }
+
+    /** @see #mMessage **/
+    String getMessage() {
+        return mMessage;
+    }
+
+    /** @see #mLocationDetails **/
+    String getLocationDetails(){
+        return mLocationDetails;
+    }
+
+    /** @see #mThrowableStrRep **/
+    String[] getThrowableStrRep() {
+        return mThrowableStrRep;
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ExitAction.java b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ExitAction.java
new file mode 100644
index 0000000..55a100e
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ExitAction.java
@@ -0,0 +1,48 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.event.ActionEvent;
+import javax.swing.AbstractAction;
+import org.apache.log4j.Logger;
+
+/**
+ * Encapsulates the action to exit.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ * @version 1.0
+ */
+class ExitAction
+    extends AbstractAction
+{
+    /** use to log messages **/
+    private static final Logger LOG = Logger.getLogger(ExitAction.class);
+    /** The instance to share **/
+    public static final ExitAction INSTANCE = new ExitAction();
+
+    /** Stop people creating instances **/
+    private ExitAction() {}
+
+    /**
+     * Will shutdown the application.
+     * @param aIgnore ignored
+     */
+    public void actionPerformed(ActionEvent aIgnore) {
+        LOG.info("shutting down");
+        System.exit(0);
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ExitAction.java.orig b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ExitAction.java.orig
new file mode 100644
index 0000000..55a100e
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/ExitAction.java.orig
@@ -0,0 +1,48 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.event.ActionEvent;
+import javax.swing.AbstractAction;
+import org.apache.log4j.Logger;
+
+/**
+ * Encapsulates the action to exit.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ * @version 1.0
+ */
+class ExitAction
+    extends AbstractAction
+{
+    /** use to log messages **/
+    private static final Logger LOG = Logger.getLogger(ExitAction.class);
+    /** The instance to share **/
+    public static final ExitAction INSTANCE = new ExitAction();
+
+    /** Stop people creating instances **/
+    private ExitAction() {}
+
+    /**
+     * Will shutdown the application.
+     * @param aIgnore ignored
+     */
+    public void actionPerformed(ActionEvent aIgnore) {
+        LOG.info("shutting down");
+        System.exit(0);
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoadXMLAction.java b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoadXMLAction.java
new file mode 100644
index 0000000..33e5d13
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoadXMLAction.java
@@ -0,0 +1,139 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import javax.swing.AbstractAction;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.apache.log4j.Logger;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Encapsulates the action to load an XML file.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ * @version 1.0
+ */
+class LoadXMLAction
+    extends AbstractAction
+{
+    /** use to log messages **/
+    private static final Logger LOG = Logger.getLogger(LoadXMLAction.class);
+
+    /** the parent frame **/
+    private final JFrame mParent;
+
+    /**
+     * the file chooser - configured to allow only the selection of a
+     * single file.
+     */
+    private final JFileChooser mChooser = new JFileChooser();
+    {
+        mChooser.setMultiSelectionEnabled(false);
+        mChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+    }
+
+    /** parser to read XML files **/
+    private final XMLReader mParser;
+    /** the content handler **/
+    private final XMLFileHandler mHandler;
+
+
+    /**
+     * Creates a new <code>LoadXMLAction</code> instance.
+     *
+     * @param aParent the parent frame
+     * @param aModel the model to add events to
+     * @exception SAXException if an error occurs
+     * @throws ParserConfigurationException if an error occurs
+     */
+    LoadXMLAction(JFrame aParent, MyTableModel aModel)
+        throws SAXException, ParserConfigurationException
+    {
+        mParent = aParent;
+        mHandler = new XMLFileHandler(aModel);
+        mParser = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
+        mParser.setContentHandler(mHandler);
+    }
+
+    /**
+     * Prompts the user for a file to load events from.
+     * @param aIgnore an <code>ActionEvent</code> value
+     */
+    public void actionPerformed(ActionEvent aIgnore) {
+        LOG.info("load file called");
+        if (mChooser.showOpenDialog(mParent) == JFileChooser.APPROVE_OPTION) {
+            LOG.info("Need to load a file");
+            final File chosen = mChooser.getSelectedFile();
+            LOG.info("loading the contents of " + chosen.getAbsolutePath());
+            try {
+                final int num = loadFile(chosen.getAbsolutePath());
+                JOptionPane.showMessageDialog(
+                    mParent,
+                    "Loaded " + num + " events.",
+                    "CHAINSAW",
+                    JOptionPane.INFORMATION_MESSAGE);
+            } catch (Exception e) {
+                LOG.warn("caught an exception loading the file", e);
+                JOptionPane.showMessageDialog(
+                    mParent,
+                    "Error parsing file - " + e.getMessage(),
+                    "CHAINSAW",
+                    JOptionPane.ERROR_MESSAGE);
+            }
+        }
+    }
+
+    /**
+     * Loads the contents of file into the model
+     *
+     * @param aFile the file to extract events from
+     * @return the number of events loaded
+     * @throws SAXException if an error occurs
+     * @throws IOException if an error occurs
+     */
+    private int loadFile(String aFile)
+        throws SAXException, IOException
+    {
+        synchronized (mParser) {
+            // Create a dummy document to parse the file
+            final StringBuffer buf = new StringBuffer();
+            buf.append("<?xml version=\"1.0\" standalone=\"yes\"?>\n");
+            buf.append("<!DOCTYPE log4j:eventSet ");
+            buf.append("[<!ENTITY data SYSTEM \"file:///");
+            buf.append(aFile);
+            buf.append("\">]>\n");
+            buf.append("<log4j:eventSet xmlns:log4j=\"Claira\">\n");
+            buf.append("&data;\n");
+            buf.append("</log4j:eventSet>\n");
+
+            final InputSource is =
+                new InputSource(new StringReader(buf.toString()));
+            mParser.parse(is);
+            return mHandler.getNumEvents();
+        }
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoadXMLAction.java.orig b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoadXMLAction.java.orig
new file mode 100644
index 0000000..33e5d13
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoadXMLAction.java.orig
@@ -0,0 +1,139 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import javax.swing.AbstractAction;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.apache.log4j.Logger;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Encapsulates the action to load an XML file.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ * @version 1.0
+ */
+class LoadXMLAction
+    extends AbstractAction
+{
+    /** use to log messages **/
+    private static final Logger LOG = Logger.getLogger(LoadXMLAction.class);
+
+    /** the parent frame **/
+    private final JFrame mParent;
+
+    /**
+     * the file chooser - configured to allow only the selection of a
+     * single file.
+     */
+    private final JFileChooser mChooser = new JFileChooser();
+    {
+        mChooser.setMultiSelectionEnabled(false);
+        mChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+    }
+
+    /** parser to read XML files **/
+    private final XMLReader mParser;
+    /** the content handler **/
+    private final XMLFileHandler mHandler;
+
+
+    /**
+     * Creates a new <code>LoadXMLAction</code> instance.
+     *
+     * @param aParent the parent frame
+     * @param aModel the model to add events to
+     * @exception SAXException if an error occurs
+     * @throws ParserConfigurationException if an error occurs
+     */
+    LoadXMLAction(JFrame aParent, MyTableModel aModel)
+        throws SAXException, ParserConfigurationException
+    {
+        mParent = aParent;
+        mHandler = new XMLFileHandler(aModel);
+        mParser = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
+        mParser.setContentHandler(mHandler);
+    }
+
+    /**
+     * Prompts the user for a file to load events from.
+     * @param aIgnore an <code>ActionEvent</code> value
+     */
+    public void actionPerformed(ActionEvent aIgnore) {
+        LOG.info("load file called");
+        if (mChooser.showOpenDialog(mParent) == JFileChooser.APPROVE_OPTION) {
+            LOG.info("Need to load a file");
+            final File chosen = mChooser.getSelectedFile();
+            LOG.info("loading the contents of " + chosen.getAbsolutePath());
+            try {
+                final int num = loadFile(chosen.getAbsolutePath());
+                JOptionPane.showMessageDialog(
+                    mParent,
+                    "Loaded " + num + " events.",
+                    "CHAINSAW",
+                    JOptionPane.INFORMATION_MESSAGE);
+            } catch (Exception e) {
+                LOG.warn("caught an exception loading the file", e);
+                JOptionPane.showMessageDialog(
+                    mParent,
+                    "Error parsing file - " + e.getMessage(),
+                    "CHAINSAW",
+                    JOptionPane.ERROR_MESSAGE);
+            }
+        }
+    }
+
+    /**
+     * Loads the contents of file into the model
+     *
+     * @param aFile the file to extract events from
+     * @return the number of events loaded
+     * @throws SAXException if an error occurs
+     * @throws IOException if an error occurs
+     */
+    private int loadFile(String aFile)
+        throws SAXException, IOException
+    {
+        synchronized (mParser) {
+            // Create a dummy document to parse the file
+            final StringBuffer buf = new StringBuffer();
+            buf.append("<?xml version=\"1.0\" standalone=\"yes\"?>\n");
+            buf.append("<!DOCTYPE log4j:eventSet ");
+            buf.append("[<!ENTITY data SYSTEM \"file:///");
+            buf.append(aFile);
+            buf.append("\">]>\n");
+            buf.append("<log4j:eventSet xmlns:log4j=\"Claira\">\n");
+            buf.append("&data;\n");
+            buf.append("</log4j:eventSet>\n");
+
+            final InputSource is =
+                new InputSource(new StringReader(buf.toString()));
+            mParser.parse(is);
+            return mHandler.getNumEvents();
+        }
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoggingReceiver.java b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoggingReceiver.java
new file mode 100644
index 0000000..ca087ad
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoggingReceiver.java
@@ -0,0 +1,121 @@
+/*
+ * 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.chainsaw;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A daemon thread the processes connections from a
+ * <code>org.apache.log4j.net.SocketAppender.html</code>.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ */
+class LoggingReceiver extends Thread {
+    /** used to log messages **/
+    private static final Logger LOG = Logger.getLogger(LoggingReceiver.class);
+
+    /**
+     * Helper that actually processes a client connection. It receives events
+     * and adds them to the supplied model.
+     *
+     * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+     */
+    private class Slurper implements Runnable {
+        /** socket connection to read events from **/
+        private final Socket mClient;
+
+        /**
+         * Creates a new <code>Slurper</code> instance.
+         *
+         * @param aClient socket to receive events from
+         */
+        Slurper(Socket aClient) {
+            mClient = aClient;
+        }
+
+        /** loops getting the events **/
+        public void run() {
+            LOG.debug("Starting to get data");
+            try {
+                final ObjectInputStream ois =
+                    new ObjectInputStream(mClient.getInputStream());
+                while (true) {
+                    final LoggingEvent event = (LoggingEvent) ois.readObject();
+                    mModel.addEvent(new EventDetails(event));
+                }
+            } catch (EOFException e) {
+                LOG.info("Reached EOF, closing connection");
+            } catch (SocketException e) {
+                LOG.info("Caught SocketException, closing connection");
+            } catch (IOException e) {
+                LOG.warn("Got IOException, closing connection", e);
+            } catch (ClassNotFoundException e) {
+                LOG.warn("Got ClassNotFoundException, closing connection", e);
+            }
+
+            try {
+                mClient.close();
+            } catch (IOException e) {
+                LOG.warn("Error closing connection", e);
+            }
+        }
+    }
+
+    /** where to put the events **/
+    private MyTableModel mModel;
+
+    /** server for listening for connections **/
+    private ServerSocket mSvrSock;
+    
+    /**
+     * Creates a new <code>LoggingReceiver</code> instance.
+     *
+     * @param aModel model to place put received into
+     * @param aPort port to listen on
+     * @throws IOException if an error occurs
+     */
+    LoggingReceiver(MyTableModel aModel, int aPort) throws IOException {
+        setDaemon(true);
+        mModel = aModel;
+        mSvrSock = new ServerSocket(aPort);
+    }
+
+    /** Listens for client connections **/
+    public void run() {
+        LOG.info("Thread started");
+        try {
+            while (true) {
+                LOG.debug("Waiting for a connection");
+                final Socket client = mSvrSock.accept();
+                LOG.debug("Got a connection from " +
+                          client.getInetAddress().getHostName());
+                final Thread t = new Thread(new Slurper(client));
+                t.setDaemon(true);
+                t.start();
+            }
+        } catch (IOException e) {
+            LOG.error("Error in accepting connections, stopping.", e);
+        }
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoggingReceiver.java.orig b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoggingReceiver.java.orig
new file mode 100644
index 0000000..ca087ad
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/LoggingReceiver.java.orig
@@ -0,0 +1,121 @@
+/*
+ * 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.chainsaw;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A daemon thread the processes connections from a
+ * <code>org.apache.log4j.net.SocketAppender.html</code>.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ */
+class LoggingReceiver extends Thread {
+    /** used to log messages **/
+    private static final Logger LOG = Logger.getLogger(LoggingReceiver.class);
+
+    /**
+     * Helper that actually processes a client connection. It receives events
+     * and adds them to the supplied model.
+     *
+     * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+     */
+    private class Slurper implements Runnable {
+        /** socket connection to read events from **/
+        private final Socket mClient;
+
+        /**
+         * Creates a new <code>Slurper</code> instance.
+         *
+         * @param aClient socket to receive events from
+         */
+        Slurper(Socket aClient) {
+            mClient = aClient;
+        }
+
+        /** loops getting the events **/
+        public void run() {
+            LOG.debug("Starting to get data");
+            try {
+                final ObjectInputStream ois =
+                    new ObjectInputStream(mClient.getInputStream());
+                while (true) {
+                    final LoggingEvent event = (LoggingEvent) ois.readObject();
+                    mModel.addEvent(new EventDetails(event));
+                }
+            } catch (EOFException e) {
+                LOG.info("Reached EOF, closing connection");
+            } catch (SocketException e) {
+                LOG.info("Caught SocketException, closing connection");
+            } catch (IOException e) {
+                LOG.warn("Got IOException, closing connection", e);
+            } catch (ClassNotFoundException e) {
+                LOG.warn("Got ClassNotFoundException, closing connection", e);
+            }
+
+            try {
+                mClient.close();
+            } catch (IOException e) {
+                LOG.warn("Error closing connection", e);
+            }
+        }
+    }
+
+    /** where to put the events **/
+    private MyTableModel mModel;
+
+    /** server for listening for connections **/
+    private ServerSocket mSvrSock;
+    
+    /**
+     * Creates a new <code>LoggingReceiver</code> instance.
+     *
+     * @param aModel model to place put received into
+     * @param aPort port to listen on
+     * @throws IOException if an error occurs
+     */
+    LoggingReceiver(MyTableModel aModel, int aPort) throws IOException {
+        setDaemon(true);
+        mModel = aModel;
+        mSvrSock = new ServerSocket(aPort);
+    }
+
+    /** Listens for client connections **/
+    public void run() {
+        LOG.info("Thread started");
+        try {
+            while (true) {
+                LOG.debug("Waiting for a connection");
+                final Socket client = mSvrSock.accept();
+                LOG.debug("Got a connection from " +
+                          client.getInetAddress().getHostName());
+                final Thread t = new Thread(new Slurper(client));
+                t.setDaemon(true);
+                t.start();
+            }
+        } catch (IOException e) {
+            LOG.error("Error in accepting connections, stopping.", e);
+        }
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/Main.java b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/Main.java
new file mode 100644
index 0000000..c0c9aad
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/Main.java
@@ -0,0 +1,192 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.util.Properties;
+import javax.swing.BorderFactory;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+/**
+ * The main application.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ */
+public class Main
+    extends JFrame
+{
+    /** the default port number to listen on **/
+    private static final int DEFAULT_PORT = 4445;
+
+    /** name of property for port name **/
+    public static final String PORT_PROP_NAME = "chainsaw.port";
+
+    /** use to log messages **/
+    private static final Logger LOG = Logger.getLogger(Main.class);
+
+
+    /**
+     * Creates a new <code>Main</code> instance.
+     */
+    private Main() {
+        super("CHAINSAW - Log4J Log Viewer");
+        // create the all important model
+        final MyTableModel model = new MyTableModel();
+
+        //Create the menu bar.
+        final JMenuBar menuBar = new JMenuBar();
+        setJMenuBar(menuBar);
+        final JMenu menu = new JMenu("File");
+        menuBar.add(menu);
+
+        try {
+            final LoadXMLAction lxa = new LoadXMLAction(this, model);
+            final JMenuItem loadMenuItem = new JMenuItem("Load file...");
+            menu.add(loadMenuItem);
+            loadMenuItem.addActionListener(lxa);
+        } catch (NoClassDefFoundError e) {
+            LOG.info("Missing classes for XML parser", e);
+            JOptionPane.showMessageDialog(
+                this,
+                "XML parser not in classpath - unable to load XML events.",
+                "CHAINSAW",
+                JOptionPane.ERROR_MESSAGE);
+        } catch (Exception e) {
+            LOG.info("Unable to create the action to load XML files", e);
+            JOptionPane.showMessageDialog(
+                this,
+                "Unable to create a XML parser - unable to load XML events.",
+                "CHAINSAW",
+                JOptionPane.ERROR_MESSAGE);
+        }
+
+        final JMenuItem exitMenuItem = new JMenuItem("Exit");
+        menu.add(exitMenuItem);
+        exitMenuItem.addActionListener(ExitAction.INSTANCE);
+
+        // Add control panel
+        final ControlPanel cp = new ControlPanel(model);
+        getContentPane().add(cp, BorderLayout.NORTH);
+
+        // Create the table
+        final JTable table = new JTable(model);
+        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        final JScrollPane scrollPane = new JScrollPane(table);
+        scrollPane.setBorder(BorderFactory.createTitledBorder("Events: "));
+        scrollPane.setPreferredSize(new Dimension(900, 300));
+
+        // Create the details
+        final JPanel details = new DetailPanel(table, model);
+        details.setPreferredSize(new Dimension(900, 300));
+
+        // Add the table and stack trace into a splitter
+        final JSplitPane jsp =
+            new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, details);
+        getContentPane().add(jsp, BorderLayout.CENTER);
+
+        addWindowListener(new WindowAdapter() {
+                public void windowClosing(WindowEvent aEvent) {
+                    ExitAction.INSTANCE.actionPerformed(null);
+                }
+            });
+
+        pack();
+        setVisible(true);
+
+        setupReceiver(model);
+    }
+
+    /**
+     * Setup recieving messages.
+     *
+     * @param aModel a <code>MyTableModel</code> value
+     */
+    private void setupReceiver(MyTableModel aModel) {
+        int port = DEFAULT_PORT;
+        final String strRep = System.getProperty(PORT_PROP_NAME);
+        if (strRep != null) {
+            try {
+                port = Integer.parseInt(strRep);
+            } catch (NumberFormatException nfe) {
+                LOG.fatal("Unable to parse " + PORT_PROP_NAME +
+                          " property with value " + strRep + ".");
+                JOptionPane.showMessageDialog(
+                    this,
+                    "Unable to parse port number from '" + strRep +
+                    "', quitting.",
+                    "CHAINSAW",
+                    JOptionPane.ERROR_MESSAGE);
+                System.exit(1);
+            }
+        }
+
+        try {
+            final LoggingReceiver lr = new LoggingReceiver(aModel, port);
+            lr.start();
+        } catch (IOException e) {
+            LOG.fatal("Unable to connect to socket server, quiting", e);
+            JOptionPane.showMessageDialog(
+                this,
+                "Unable to create socket on port " + port + ", quitting.",
+                "CHAINSAW",
+                JOptionPane.ERROR_MESSAGE);
+            System.exit(1);
+        }
+    }
+
+
+    ////////////////////////////////////////////////////////////////////////////
+    // static methods
+    ////////////////////////////////////////////////////////////////////////////
+
+
+    /** initialise log4j **/
+    private static void initLog4J() {
+        final Properties props = new Properties();
+        props.setProperty("log4j.rootLogger", "DEBUG, A1");
+        props.setProperty("log4j.appender.A1",
+                          "org.apache.log4j.ConsoleAppender");
+        props.setProperty("log4j.appender.A1.layout",
+                          "org.apache.log4j.TTCCLayout");
+        PropertyConfigurator.configure(props);
+    }
+
+    /**
+     * The main method.
+     *
+     * @param aArgs ignored
+     */
+    public static void main(String[] aArgs) {
+        initLog4J();
+        new Main();
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/Main.java.orig b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/Main.java.orig
new file mode 100644
index 0000000..c0c9aad
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/Main.java.orig
@@ -0,0 +1,192 @@
+/*
+ * 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.chainsaw;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.util.Properties;
+import javax.swing.BorderFactory;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+/**
+ * The main application.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ */
+public class Main
+    extends JFrame
+{
+    /** the default port number to listen on **/
+    private static final int DEFAULT_PORT = 4445;
+
+    /** name of property for port name **/
+    public static final String PORT_PROP_NAME = "chainsaw.port";
+
+    /** use to log messages **/
+    private static final Logger LOG = Logger.getLogger(Main.class);
+
+
+    /**
+     * Creates a new <code>Main</code> instance.
+     */
+    private Main() {
+        super("CHAINSAW - Log4J Log Viewer");
+        // create the all important model
+        final MyTableModel model = new MyTableModel();
+
+        //Create the menu bar.
+        final JMenuBar menuBar = new JMenuBar();
+        setJMenuBar(menuBar);
+        final JMenu menu = new JMenu("File");
+        menuBar.add(menu);
+
+        try {
+            final LoadXMLAction lxa = new LoadXMLAction(this, model);
+            final JMenuItem loadMenuItem = new JMenuItem("Load file...");
+            menu.add(loadMenuItem);
+            loadMenuItem.addActionListener(lxa);
+        } catch (NoClassDefFoundError e) {
+            LOG.info("Missing classes for XML parser", e);
+            JOptionPane.showMessageDialog(
+                this,
+                "XML parser not in classpath - unable to load XML events.",
+                "CHAINSAW",
+                JOptionPane.ERROR_MESSAGE);
+        } catch (Exception e) {
+            LOG.info("Unable to create the action to load XML files", e);
+            JOptionPane.showMessageDialog(
+                this,
+                "Unable to create a XML parser - unable to load XML events.",
+                "CHAINSAW",
+                JOptionPane.ERROR_MESSAGE);
+        }
+
+        final JMenuItem exitMenuItem = new JMenuItem("Exit");
+        menu.add(exitMenuItem);
+        exitMenuItem.addActionListener(ExitAction.INSTANCE);
+
+        // Add control panel
+        final ControlPanel cp = new ControlPanel(model);
+        getContentPane().add(cp, BorderLayout.NORTH);
+
+        // Create the table
+        final JTable table = new JTable(model);
+        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        final JScrollPane scrollPane = new JScrollPane(table);
+        scrollPane.setBorder(BorderFactory.createTitledBorder("Events: "));
+        scrollPane.setPreferredSize(new Dimension(900, 300));
+
+        // Create the details
+        final JPanel details = new DetailPanel(table, model);
+        details.setPreferredSize(new Dimension(900, 300));
+
+        // Add the table and stack trace into a splitter
+        final JSplitPane jsp =
+            new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, details);
+        getContentPane().add(jsp, BorderLayout.CENTER);
+
+        addWindowListener(new WindowAdapter() {
+                public void windowClosing(WindowEvent aEvent) {
+                    ExitAction.INSTANCE.actionPerformed(null);
+                }
+            });
+
+        pack();
+        setVisible(true);
+
+        setupReceiver(model);
+    }
+
+    /**
+     * Setup recieving messages.
+     *
+     * @param aModel a <code>MyTableModel</code> value
+     */
+    private void setupReceiver(MyTableModel aModel) {
+        int port = DEFAULT_PORT;
+        final String strRep = System.getProperty(PORT_PROP_NAME);
+        if (strRep != null) {
+            try {
+                port = Integer.parseInt(strRep);
+            } catch (NumberFormatException nfe) {
+                LOG.fatal("Unable to parse " + PORT_PROP_NAME +
+                          " property with value " + strRep + ".");
+                JOptionPane.showMessageDialog(
+                    this,
+                    "Unable to parse port number from '" + strRep +
+                    "', quitting.",
+                    "CHAINSAW",
+                    JOptionPane.ERROR_MESSAGE);
+                System.exit(1);
+            }
+        }
+
+        try {
+            final LoggingReceiver lr = new LoggingReceiver(aModel, port);
+            lr.start();
+        } catch (IOException e) {
+            LOG.fatal("Unable to connect to socket server, quiting", e);
+            JOptionPane.showMessageDialog(
+                this,
+                "Unable to create socket on port " + port + ", quitting.",
+                "CHAINSAW",
+                JOptionPane.ERROR_MESSAGE);
+            System.exit(1);
+        }
+    }
+
+
+    ////////////////////////////////////////////////////////////////////////////
+    // static methods
+    ////////////////////////////////////////////////////////////////////////////
+
+
+    /** initialise log4j **/
+    private static void initLog4J() {
+        final Properties props = new Properties();
+        props.setProperty("log4j.rootLogger", "DEBUG, A1");
+        props.setProperty("log4j.appender.A1",
+                          "org.apache.log4j.ConsoleAppender");
+        props.setProperty("log4j.appender.A1.layout",
+                          "org.apache.log4j.TTCCLayout");
+        PropertyConfigurator.configure(props);
+    }
+
+    /**
+     * The main method.
+     *
+     * @param aArgs ignored
+     */
+    public static void main(String[] aArgs) {
+        initLog4J();
+        new Main();
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/MyTableModel.java b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/MyTableModel.java
new file mode 100644
index 0000000..0d43272
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/MyTableModel.java
@@ -0,0 +1,390 @@
+/*
+ * 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.chainsaw;
+
+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;
+import org.apache.log4j.Priority;
+import org.apache.log4j.Logger;
+
+/**
+ * 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
+{
+
+    /** used to log messages **/
+    private static final Logger LOG = Logger.getLogger(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;
+        }
+        };
+
+    /**
+     * 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);
+                    }
+                }
+            }
+
+        }
+    }
+
+
+    /** names of the columns in the table **/
+    private static final String[] COL_NAMES = {
+        "Time", "Priority", "Trace", "Category", "NDC", "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 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();
+            }
+            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 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))))
+        {
+            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
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/MyTableModel.java.orig b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/MyTableModel.java.orig
new file mode 100644
index 0000000..0d43272
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/MyTableModel.java.orig
@@ -0,0 +1,390 @@
+/*
+ * 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.chainsaw;
+
+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;
+import org.apache.log4j.Priority;
+import org.apache.log4j.Logger;
+
+/**
+ * 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
+{
+
+    /** used to log messages **/
+    private static final Logger LOG = Logger.getLogger(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;
+        }
+        };
+
+    /**
+     * 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);
+                    }
+                }
+            }
+
+        }
+    }
+
+
+    /** names of the columns in the table **/
+    private static final String[] COL_NAMES = {
+        "Time", "Priority", "Trace", "Category", "NDC", "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 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();
+            }
+            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 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))))
+        {
+            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
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/XMLFileHandler.java b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/XMLFileHandler.java
new file mode 100644
index 0000000..2f9af51
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/XMLFileHandler.java
@@ -0,0 +1,170 @@
+/*
+ * 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.chainsaw;
+
+import java.util.StringTokenizer;
+import org.apache.log4j.Level;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A content handler for document containing Log4J events logged using the
+ * XMLLayout class. It will create events and add them to a supplied model.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ * @version 1.0
+ */
+class XMLFileHandler
+    extends DefaultHandler
+{
+    /** represents the event tag **/
+    private static final String TAG_EVENT = "log4j:event";
+    /** represents the message tag **/
+    private static final String TAG_MESSAGE = "log4j:message";
+    /** represents the ndc tag **/
+    private static final String TAG_NDC = "log4j:NDC";
+    /** represents the throwable tag **/
+    private static final String TAG_THROWABLE = "log4j:throwable";
+    /** represents the location info tag **/
+    private static final String TAG_LOCATION_INFO = "log4j:locationInfo";
+
+    /** where to put the events **/
+    private final MyTableModel mModel;
+    /** the number of events in the document **/
+    private int mNumEvents;
+
+    /** the time of the event **/
+    private long mTimeStamp;
+    /** the priority (level) of the event **/
+    private Level mLevel;
+    /** the category of the event **/
+    private String mCategoryName;
+    /** the NDC for the event **/
+    private String mNDC;
+    /** the thread for the event **/
+    private String mThreadName;
+    /** the msg for the event **/
+    private String mMessage;
+    /** the throwable details the event **/
+    private String[] mThrowableStrRep;
+    /** the location details for the event **/
+    private String mLocationDetails;
+    /** buffer for collecting text **/
+    private final StringBuffer mBuf = new StringBuffer();
+
+    /**
+     * Creates a new <code>XMLFileHandler</code> instance.
+     *
+     * @param aModel where to add the events
+     */
+    XMLFileHandler(MyTableModel aModel) {
+        mModel = aModel;
+    }
+
+    /** @see DefaultHandler **/
+    public void startDocument()
+        throws SAXException
+    {
+        mNumEvents = 0;
+    }
+
+    /** @see DefaultHandler **/
+    public void characters(char[] aChars, int aStart, int aLength) {
+        mBuf.append(String.valueOf(aChars, aStart, aLength));
+    }
+
+    /** @see DefaultHandler **/
+    public void endElement(String aNamespaceURI,
+                           String aLocalName,
+                           String aQName)
+    {
+        if (TAG_EVENT.equals(aQName)) {
+            addEvent();
+            resetData();
+        } else if (TAG_NDC.equals(aQName)) {
+            mNDC = mBuf.toString();
+        } else if (TAG_MESSAGE.equals(aQName)) {
+            mMessage = mBuf.toString();
+        } else if (TAG_THROWABLE.equals(aQName)) {
+            final StringTokenizer st =
+                new StringTokenizer(mBuf.toString(), "\n\t");
+            mThrowableStrRep = new String[st.countTokens()];
+            if (mThrowableStrRep.length > 0) {
+                mThrowableStrRep[0] = st.nextToken();
+                for (int i = 1; i < mThrowableStrRep.length; i++) {
+                    mThrowableStrRep[i] = "\t" + st.nextToken();
+                }
+            }
+        }
+    }
+
+    /** @see DefaultHandler **/
+    public void startElement(String aNamespaceURI,
+                             String aLocalName,
+                             String aQName,
+                             Attributes aAtts)
+    {
+        mBuf.setLength(0);
+
+        if (TAG_EVENT.equals(aQName)) {
+            mThreadName = aAtts.getValue("thread");
+            mTimeStamp = Long.parseLong(aAtts.getValue("timestamp"));
+            mCategoryName = aAtts.getValue("logger");
+            mLevel = Level.toLevel(aAtts.getValue("level"));
+        } else if (TAG_LOCATION_INFO.equals(aQName)) {
+            mLocationDetails = aAtts.getValue("class") + "."
+                + aAtts.getValue("method")
+                + "(" + aAtts.getValue("file") + ":" + aAtts.getValue("line")
+                + ")";
+        }
+    }
+
+    /** @return the number of events in the document **/
+    int getNumEvents() {
+        return mNumEvents;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Private methods
+    ////////////////////////////////////////////////////////////////////////////
+
+    /** Add an event to the model **/
+    private void addEvent() {
+        mModel.addEvent(new EventDetails(mTimeStamp,
+                                         mLevel,
+                                         mCategoryName,
+                                         mNDC,
+                                         mThreadName,
+                                         mMessage,
+                                         mThrowableStrRep,
+                                         mLocationDetails));
+        mNumEvents++;
+    }
+
+    /** Reset the data for an event **/
+    private void resetData() {
+        mTimeStamp = 0;
+        mLevel = null;
+        mCategoryName = null;
+        mNDC = null;
+        mThreadName = null;
+        mMessage = null;
+        mThrowableStrRep = null;
+        mLocationDetails = null;
+    }
+}
diff --git a/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/XMLFileHandler.java.orig b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/XMLFileHandler.java.orig
new file mode 100644
index 0000000..2f9af51
--- /dev/null
+++ b/modules/chainsaw/src/main/java/org/apache/log4j/chainsaw/XMLFileHandler.java.orig
@@ -0,0 +1,170 @@
+/*
+ * 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.chainsaw;
+
+import java.util.StringTokenizer;
+import org.apache.log4j.Level;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A content handler for document containing Log4J events logged using the
+ * XMLLayout class. It will create events and add them to a supplied model.
+ *
+ * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
+ * @version 1.0
+ */
+class XMLFileHandler
+    extends DefaultHandler
+{
+    /** represents the event tag **/
+    private static final String TAG_EVENT = "log4j:event";
+    /** represents the message tag **/
+    private static final String TAG_MESSAGE = "log4j:message";
+    /** represents the ndc tag **/
+    private static final String TAG_NDC = "log4j:NDC";
+    /** represents the throwable tag **/
+    private static final String TAG_THROWABLE = "log4j:throwable";
+    /** represents the location info tag **/
+    private static final String TAG_LOCATION_INFO = "log4j:locationInfo";
+
+    /** where to put the events **/
+    private final MyTableModel mModel;
+    /** the number of events in the document **/
+    private int mNumEvents;
+
+    /** the time of the event **/
+    private long mTimeStamp;
+    /** the priority (level) of the event **/
+    private Level mLevel;
+    /** the category of the event **/
+    private String mCategoryName;
+    /** the NDC for the event **/
+    private String mNDC;
+    /** the thread for the event **/
+    private String mThreadName;
+    /** the msg for the event **/
+    private String mMessage;
+    /** the throwable details the event **/
+    private String[] mThrowableStrRep;
+    /** the location details for the event **/
+    private String mLocationDetails;
+    /** buffer for collecting text **/
+    private final StringBuffer mBuf = new StringBuffer();
+
+    /**
+     * Creates a new <code>XMLFileHandler</code> instance.
+     *
+     * @param aModel where to add the events
+     */
+    XMLFileHandler(MyTableModel aModel) {
+        mModel = aModel;
+    }
+
+    /** @see DefaultHandler **/
+    public void startDocument()
+        throws SAXException
+    {
+        mNumEvents = 0;
+    }
+
+    /** @see DefaultHandler **/
+    public void characters(char[] aChars, int aStart, int aLength) {
+        mBuf.append(String.valueOf(aChars, aStart, aLength));
+    }
+
+    /** @see DefaultHandler **/
+    public void endElement(String aNamespaceURI,
+                           String aLocalName,
+                           String aQName)
+    {
+        if (TAG_EVENT.equals(aQName)) {
+            addEvent();
+            resetData();
+        } else if (TAG_NDC.equals(aQName)) {
+            mNDC = mBuf.toString();
+        } else if (TAG_MESSAGE.equals(aQName)) {
+            mMessage = mBuf.toString();
+        } else if (TAG_THROWABLE.equals(aQName)) {
+            final StringTokenizer st =
+                new StringTokenizer(mBuf.toString(), "\n\t");
+            mThrowableStrRep = new String[st.countTokens()];
+            if (mThrowableStrRep.length > 0) {
+                mThrowableStrRep[0] = st.nextToken();
+                for (int i = 1; i < mThrowableStrRep.length; i++) {
+                    mThrowableStrRep[i] = "\t" + st.nextToken();
+                }
+            }
+        }
+    }
+
+    /** @see DefaultHandler **/
+    public void startElement(String aNamespaceURI,
+                             String aLocalName,
+                             String aQName,
+                             Attributes aAtts)
+    {
+        mBuf.setLength(0);
+
+        if (TAG_EVENT.equals(aQName)) {
+            mThreadName = aAtts.getValue("thread");
+            mTimeStamp = Long.parseLong(aAtts.getValue("timestamp"));
+            mCategoryName = aAtts.getValue("logger");
+            mLevel = Level.toLevel(aAtts.getValue("level"));
+        } else if (TAG_LOCATION_INFO.equals(aQName)) {
+            mLocationDetails = aAtts.getValue("class") + "."
+                + aAtts.getValue("method")
+                + "(" + aAtts.getValue("file") + ":" + aAtts.getValue("line")
+                + ")";
+        }
+    }
+
+    /** @return the number of events in the document **/
+    int getNumEvents() {
+        return mNumEvents;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Private methods
+    ////////////////////////////////////////////////////////////////////////////
+
+    /** Add an event to the model **/
+    private void addEvent() {
+        mModel.addEvent(new EventDetails(mTimeStamp,
+                                         mLevel,
+                                         mCategoryName,
+                                         mNDC,
+                                         mThreadName,
+                                         mMessage,
+                                         mThrowableStrRep,
+                                         mLocationDetails));
+        mNumEvents++;
+    }
+
+    /** Reset the data for an event **/
+    private void resetData() {
+        mTimeStamp = 0;
+        mLevel = null;
+        mCategoryName = null;
+        mNDC = null;
+        mThreadName = null;
+        mMessage = null;
+        mThrowableStrRep = null;
+        mLocationDetails = null;
+    }
+}
diff --git a/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/docFiles/screen_01.png b/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/docFiles/screen_01.png
new file mode 100644
index 0000000..1a9c26d
--- /dev/null
+++ b/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/docFiles/screen_01.png
Binary files differ
diff --git a/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/docFiles/screen_01.png.orig b/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/docFiles/screen_01.png.orig
new file mode 100644
index 0000000..1a9c26d
--- /dev/null
+++ b/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/docFiles/screen_01.png.orig
Binary files differ
diff --git a/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/package.html b/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/package.html
new file mode 100644
index 0000000..5b01480
--- /dev/null
+++ b/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/package.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ 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.
+
+-->
+
+<HTML>
+  <HEAD>
+    <TITLE>Chainsaw Tool</TITLE>
+  </head>
+    
+  <BODY>
+
+    <P>Chainsaw is a GUI log viewer and filter for the log4j
+package. By default it listens for <a
+href="../spi/LoggingEvent.html">LoggingEvent</A> objects sent using
+the <A href="../net/SocketAppender.html">SocketAppender</A> and
+displays them in a table. The events can be filtered based on:</P>
+
+    <UL>
+      <LI>Level </li>
+
+      <LI>Thread name</li>
+
+      <LI>Logger</li>
+      <LI>Message</li>
+
+      <LI>NDC</LI>
+    </UL>
+
+    <P>All the details for each event can be displayed by selecting
+      the event in the table.</P> 
+
+    <P>Chainsaw also supports loading a events logged to a file using
+      the <A href="../xml/XMLLayout.html">XMLLayout</A> format. This
+      is great for analysing log files, and means you do not need to
+      keep Chainsaw running continously. It is easy to add support
+      for loading events from other sources like JDBC.</P> 
+
+    <P>A picture is worth a thousand words: </P>
+
+    <P align=center><A
+      href="doc-files/screen_01.png"><IMG
+      height="50%" alt="Screen shot of chainsaw"
+      src="doc-files/screen_01.png"
+      width="50%"></A>.</P> 
+
+    <P>Finally, why is it called chainsaw?
+      Because it cuts your log (file) down to size. :-)
+    </P>
+
+
+    <H2>Requirements</H2> 
+
+    <P>Chainsaw is based on the Swing API which requires JDK 1.2 or later.</P>
+
+
+    <H2>Running chainsaw</H2>
+
+    <H3>Setup</H3>
+    <P>You need to include the <code>log4j.jar</code> in the classpath.
+
+    <H3>Usage</H3>
+
+    <P>The command line usage is:</P>
+
+    <PRE>  java -D&lt;property&gt;=&lt;value&gt; org.apache.log4j.chainsaw.Main </PRE>
+
+    <P>The default behaviour of chainsaw can be changed by setting system properties 
+      using the <CODE>-D&lt;property&gt;=&lt;value&gt;</CODE> arguments to java. The 
+      following table describes what properties can be set:</P>
+
+    <TABLE cellSpacing=0 cellPadding=2 border=1>
+
+      <TR>
+	<TD vAlign=top><B>Property</B></TD>
+	<TD vAlign=top><B>Description</B></TD></TR>
+      <TR>
+	<TD vAlign=top>chainsaw.port</TD>
+	<TD vAlign=top>Indicates which port to listen for connections on. Defaults 
+	  to <SPAN class=default>"4445"</SPAN>.
+	</TD>
+      </TR>
+    </TBODY>
+    </TABLE>
+
+    <H2>Configuring Log4J</H2>
+
+    <P>You will need to configure log4j to send logging events to
+      Chainsaw.  Here is a sample <CODE>log4j.properties</CODE> file
+      for sending logging events to Chainsaw.</P>
+
+<PRE>
+log4j.rootLogger=DEBUG, CHAINSAW_CLIENT
+
+log4j.appender.CHAINSAW_CLIENT=org.apache.log4j.net.SocketAppender
+log4j.appender.CHAINSAW_CLIENT.RemoteHost=localhost
+log4j.appender.CHAINSAW_CLIENT.Port=4445
+log4j.appender.CHAINSAW_CLIENT.LocationInfo=true
+</PRE>
+
+
+
+  </body>
+</html>
\ No newline at end of file
diff --git a/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/package.html.orig b/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/package.html.orig
new file mode 100644
index 0000000..5b01480
--- /dev/null
+++ b/modules/chainsaw/src/main/resources/org/apache/log4j/chainsaw/package.html.orig
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<!--
+ 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.
+
+-->
+
+<HTML>
+  <HEAD>
+    <TITLE>Chainsaw Tool</TITLE>
+  </head>
+    
+  <BODY>
+
+    <P>Chainsaw is a GUI log viewer and filter for the log4j
+package. By default it listens for <a
+href="../spi/LoggingEvent.html">LoggingEvent</A> objects sent using
+the <A href="../net/SocketAppender.html">SocketAppender</A> and
+displays them in a table. The events can be filtered based on:</P>
+
+    <UL>
+      <LI>Level </li>
+
+      <LI>Thread name</li>
+
+      <LI>Logger</li>
+      <LI>Message</li>
+
+      <LI>NDC</LI>
+    </UL>
+
+    <P>All the details for each event can be displayed by selecting
+      the event in the table.</P> 
+
+    <P>Chainsaw also supports loading a events logged to a file using
+      the <A href="../xml/XMLLayout.html">XMLLayout</A> format. This
+      is great for analysing log files, and means you do not need to
+      keep Chainsaw running continously. It is easy to add support
+      for loading events from other sources like JDBC.</P> 
+
+    <P>A picture is worth a thousand words: </P>
+
+    <P align=center><A
+      href="doc-files/screen_01.png"><IMG
+      height="50%" alt="Screen shot of chainsaw"
+      src="doc-files/screen_01.png"
+      width="50%"></A>.</P> 
+
+    <P>Finally, why is it called chainsaw?
+      Because it cuts your log (file) down to size. :-)
+    </P>
+
+
+    <H2>Requirements</H2> 
+
+    <P>Chainsaw is based on the Swing API which requires JDK 1.2 or later.</P>
+
+
+    <H2>Running chainsaw</H2>
+
+    <H3>Setup</H3>
+    <P>You need to include the <code>log4j.jar</code> in the classpath.
+
+    <H3>Usage</H3>
+
+    <P>The command line usage is:</P>
+
+    <PRE>  java -D&lt;property&gt;=&lt;value&gt; org.apache.log4j.chainsaw.Main </PRE>
+
+    <P>The default behaviour of chainsaw can be changed by setting system properties 
+      using the <CODE>-D&lt;property&gt;=&lt;value&gt;</CODE> arguments to java. The 
+      following table describes what properties can be set:</P>
+
+    <TABLE cellSpacing=0 cellPadding=2 border=1>
+
+      <TR>
+	<TD vAlign=top><B>Property</B></TD>
+	<TD vAlign=top><B>Description</B></TD></TR>
+      <TR>
+	<TD vAlign=top>chainsaw.port</TD>
+	<TD vAlign=top>Indicates which port to listen for connections on. Defaults 
+	  to <SPAN class=default>"4445"</SPAN>.
+	</TD>
+      </TR>
+    </TBODY>
+    </TABLE>
+
+    <H2>Configuring Log4J</H2>
+
+    <P>You will need to configure log4j to send logging events to
+      Chainsaw.  Here is a sample <CODE>log4j.properties</CODE> file
+      for sending logging events to Chainsaw.</P>
+
+<PRE>
+log4j.rootLogger=DEBUG, CHAINSAW_CLIENT
+
+log4j.appender.CHAINSAW_CLIENT=org.apache.log4j.net.SocketAppender
+log4j.appender.CHAINSAW_CLIENT.RemoteHost=localhost
+log4j.appender.CHAINSAW_CLIENT.Port=4445
+log4j.appender.CHAINSAW_CLIENT.LocationInfo=true
+</PRE>
+
+
+
+  </body>
+</html>
\ No newline at end of file
diff --git a/contribs/CONTENTS b/modules/contribs/CONTENTS
similarity index 100%
rename from contribs/CONTENTS
rename to modules/contribs/CONTENTS
diff --git a/modules/contribs/pom.xml b/modules/contribs/pom.xml
new file mode 100644
index 0000000..1820b5c
--- /dev/null
+++ b/modules/contribs/pom.xml
@@ -0,0 +1,60 @@
+<!--
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-modules</artifactId>
+      <version>1.4.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.log4j</groupId>
+  <artifactId>log4j-contribs</artifactId>
+  <name>Apache Log4j-Contribtutions</name>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+    	<groupId>org.apache.log4j</groupId>
+    	<artifactId>log4j-net</artifactId>
+    	<version>1.4.0-SNAPSHOT</version>
+    	<type>bundle</type>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.0.1</version>
+        <extensions>true</extensions>
+        <configuration>
+            <instructions>
+                <Export-Package>org.apache.log4j.contribs.*</Export-Package>
+            </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
\ No newline at end of file
diff --git a/contribs/JimMoore/LoggingOutputStream.java b/modules/contribs/src/main/java/org/apache/log4j/LoggingOutputStream.java
similarity index 99%
rename from contribs/JimMoore/LoggingOutputStream.java
rename to modules/contribs/src/main/java/org/apache/log4j/LoggingOutputStream.java
index bc084b3..e4712ae 100644
--- a/contribs/JimMoore/LoggingOutputStream.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/LoggingOutputStream.java
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+package org.apache.log4j;
 
 import java.io.*;
 import org.apache.log4j.*;
diff --git a/contribs/EirikLygre/mail-2001-01-18 b/modules/contribs/src/main/java/org/apache/log4j/contribs/EirikLygre/mail-2001-01-18
similarity index 100%
rename from contribs/EirikLygre/mail-2001-01-18
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/EirikLygre/mail-2001-01-18
diff --git a/contribs/JamesHouse/mail-2001-01-23 b/modules/contribs/src/main/java/org/apache/log4j/contribs/JamesHouse/mail-2001-01-23
similarity index 100%
rename from contribs/JamesHouse/mail-2001-01-23
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/JamesHouse/mail-2001-01-23
diff --git a/contribs/Jamie Tsao/mail-2001-06-20 b/modules/contribs/src/main/java/org/apache/log4j/contribs/JamieTsao/mail-2001-06-20
similarity index 100%
rename from contribs/Jamie Tsao/mail-2001-06-20
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/JamieTsao/mail-2001-06-20
diff --git a/contribs/JimMoore/mail-2001-03-12T1326 b/modules/contribs/src/main/java/org/apache/log4j/contribs/JimMoore/mail-2001-03-12T1326
similarity index 100%
rename from contribs/JimMoore/mail-2001-03-12T1326
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/JimMoore/mail-2001-03-12T1326
diff --git a/contribs/JimMoore/mail-2001-03-12T1454 b/modules/contribs/src/main/java/org/apache/log4j/contribs/JimMoore/mail-2001-03-12T1454
similarity index 100%
rename from contribs/JimMoore/mail-2001-03-12T1454
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/JimMoore/mail-2001-03-12T1454
diff --git a/contribs/JimMoore/mail-2001-03-13T0646 b/modules/contribs/src/main/java/org/apache/log4j/contribs/JimMoore/mail-2001-03-13T0646
similarity index 100%
rename from contribs/JimMoore/mail-2001-03-13T0646
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/JimMoore/mail-2001-03-13T0646
diff --git a/contribs/KevinSteppe/mail-2001-02-01 b/modules/contribs/src/main/java/org/apache/log4j/contribs/KevinSteppe/mail-2001-02-01
similarity index 100%
rename from contribs/KevinSteppe/mail-2001-02-01
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/KevinSteppe/mail-2001-02-01
diff --git a/contribs/KevinSteppe/mail-2002-03-27.txt b/modules/contribs/src/main/java/org/apache/log4j/contribs/KevinSteppe/mail-2002-03-27.txt
similarity index 100%
rename from contribs/KevinSteppe/mail-2002-03-27.txt
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/KevinSteppe/mail-2002-03-27.txt
diff --git a/contribs/KitchingSimon/mail-2001-02-07 b/modules/contribs/src/main/java/org/apache/log4j/contribs/KitchingSimon/mail-2001-02-07
similarity index 100%
rename from contribs/KitchingSimon/mail-2001-02-07
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/KitchingSimon/mail-2001-02-07
diff --git a/contribs/LeosLiterak/mail b/modules/contribs/src/main/java/org/apache/log4j/contribs/LeosLiterak/mail
similarity index 100%
rename from contribs/LeosLiterak/mail
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/LeosLiterak/mail
diff --git a/contribs/MarkDouglas/Log.txt b/modules/contribs/src/main/java/org/apache/log4j/contribs/MarkDouglas/Log.txt
similarity index 100%
rename from contribs/MarkDouglas/Log.txt
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/MarkDouglas/Log.txt
diff --git a/contribs/MarkDouglas/mail-2001-01-17 b/modules/contribs/src/main/java/org/apache/log4j/contribs/MarkDouglas/mail-2001-01-17
similarity index 100%
rename from contribs/MarkDouglas/mail-2001-01-17
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/MarkDouglas/mail-2001-01-17
diff --git a/contribs/VolkerMentzner/mail-03-05-2001 b/modules/contribs/src/main/java/org/apache/log4j/contribs/VolkerMentzner/mail-03-05-2001
similarity index 100%
rename from contribs/VolkerMentzner/mail-03-05-2001
rename to modules/contribs/src/main/java/org/apache/log4j/contribs/VolkerMentzner/mail-03-05-2001
diff --git a/contribs/CekiGulcu/AppenderTable.java b/modules/contribs/src/main/java/org/apache/log4j/gui/AppenderTable.java
similarity index 95%
rename from contribs/CekiGulcu/AppenderTable.java
rename to modules/contribs/src/main/java/org/apache/log4j/gui/AppenderTable.java
index fbb18f5..d5820b9 100644
--- a/contribs/CekiGulcu/AppenderTable.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/gui/AppenderTable.java
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+package org.apache.log4j.gui;
 
 import org.apache.log4j.helpers.CyclicBuffer;
 import org.apache.log4j.Level;
@@ -57,7 +58,9 @@
 
    <p>On windows NT the test will run about twice as fast if you give
    the focus to the window that runs "java AppenderTable" and not the
-   window that contains the Swing JFrame.  */
+   window that contains the Swing JFrame.  
+   
+   @author CekiGulcu */
 public class AppenderTable extends JTable {
 
 
@@ -95,7 +98,7 @@
     // add one new logging event to the table.
     JButton button = new JButton("ADD");
     container.add(button);
-    button.addActionListener(new JTableAddAction(tableAppender));
+    button.addActionListener(new AppTableJTableAddAction(tableAppender));
 
     frame.setSize(new Dimension(500,300));
     frame.setVisible(true);
@@ -225,13 +228,13 @@
    The JTableAddAction is called when the user clicks on the "ADD"
    button.
 */
-class JTableAddAction implements ActionListener {
+class AppTableJTableAddAction implements ActionListener {
     
   AppenderTable appenderTable;
   Logger dummy = Logger.getLogger("x");
   int counter = 0;
   public
-  JTableAddAction(AppenderTable appenderTable) {
+  AppTableJTableAddAction(AppenderTable appenderTable) {
     this.appenderTable = appenderTable;
   }
     
diff --git a/contribs/SvenReimers/gui/JListView.java b/modules/contribs/src/main/java/org/apache/log4j/gui/JListView.java
similarity index 98%
rename from contribs/SvenReimers/gui/JListView.java
rename to modules/contribs/src/main/java/org/apache/log4j/gui/JListView.java
index 6a93ed0..d762574 100644
--- a/contribs/SvenReimers/gui/JListView.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/gui/JListView.java
@@ -51,6 +51,9 @@
 import java.net.URL;
 import java.awt.Rectangle;
 
+/**
+ * @author Sven Reimers
+ */
 public class JListView extends JList {
 
 
@@ -255,7 +258,7 @@
       if(value instanceof LoggingEvent) {
 	LoggingEvent event = (LoggingEvent) value;
 	String str = layout.format(event);
-	String t = event.getThrowableInformation();
+	String t = event.getThrowableInformation().toString();
 
 	if(t != null) {
 	  setText(str + Layout.LINE_SEP + t);
diff --git a/contribs/SvenReimers/gui/JTableAppender.java b/modules/contribs/src/main/java/org/apache/log4j/gui/JTableAppender.java
similarity index 94%
rename from contribs/SvenReimers/gui/JTableAppender.java
rename to modules/contribs/src/main/java/org/apache/log4j/gui/JTableAppender.java
index 32cc23a..530b96c 100644
--- a/contribs/SvenReimers/gui/JTableAppender.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/gui/JTableAppender.java
@@ -52,9 +52,11 @@
 import java.net.URL;
 import java.awt.Rectangle;
 
+/**
+ * @author Sven Reimers
+ */
 public class JTableAppender extends JTable {
 
-
   static Category cat = Category.getInstance(JTableAppender.class.getName());
 
   PatternLayout layout;
@@ -101,7 +103,7 @@
     container.add(button);
     
 
-    button.addActionListener(new JTableAddAction(appender));
+    button.addActionListener(new JTableAppenderJTableAddAction(appender));
 
     frame.setVisible(true);
     frame.setSize(new Dimension(700,700));
@@ -148,7 +150,7 @@
       if(value instanceof LoggingEvent) {
 	LoggingEvent event = (LoggingEvent) value;
 	String str = layout.format(event);
-	String t = event.getThrowableInformation();
+	String t = event.getThrowableInformation().toString();
 	
 	if(t != null) {
 	  System.out.println("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
@@ -208,7 +210,7 @@
 }
 
 
-class JTableAddAction implements ActionListener {
+class JTableAppenderJTableAddAction implements ActionListener {
     
   int j;
   JTableAppender appender;
@@ -216,7 +218,7 @@
   Category cat = Category.getInstance("x");
   
   public
-  JTableAddAction(JTableAppender appender) {
+  JTableAppenderJTableAddAction(JTableAppender appender) {
     this.appender = appender;
     j = 0;
   }
diff --git a/contribs/KitchingSimon/DatagramStringWriter.java b/modules/contribs/src/main/java/org/apache/log4j/net/DatagramStringWriter.java
similarity index 100%
rename from contribs/KitchingSimon/DatagramStringWriter.java
rename to modules/contribs/src/main/java/org/apache/log4j/net/DatagramStringWriter.java
diff --git a/contribs/VolkerMentzner/HTTPRequestHandler.java b/modules/contribs/src/main/java/org/apache/log4j/net/HTTPRequestHandler.java
similarity index 98%
rename from contribs/VolkerMentzner/HTTPRequestHandler.java
rename to modules/contribs/src/main/java/org/apache/log4j/net/HTTPRequestHandler.java
index d426e86..b5f87b9 100644
--- a/contribs/VolkerMentzner/HTTPRequestHandler.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/net/HTTPRequestHandler.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.psibt.framework.net;
+package org.apache.log4j.net;
 
 import java.io.*;
 import java.net.*;
diff --git a/contribs/ThomasFenner/JDBCAppender.java b/modules/contribs/src/main/java/org/apache/log4j/net/JDBCAppender.java
similarity index 99%
rename from contribs/ThomasFenner/JDBCAppender.java
rename to modules/contribs/src/main/java/org/apache/log4j/net/JDBCAppender.java
index 841aaad..ed7919d 100644
--- a/contribs/ThomasFenner/JDBCAppender.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/net/JDBCAppender.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.klopotek.utils.log;
+package org.apache.log4j.net;
 
 import java.sql.*;
 import java.util.*;
diff --git a/contribs/ThomasFenner/JDBCConnectionHandler.java b/modules/contribs/src/main/java/org/apache/log4j/net/JDBCConnectionHandler.java
similarity index 97%
rename from contribs/ThomasFenner/JDBCConnectionHandler.java
rename to modules/contribs/src/main/java/org/apache/log4j/net/JDBCConnectionHandler.java
index da90942..0164eec 100644
--- a/contribs/ThomasFenner/JDBCConnectionHandler.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/net/JDBCConnectionHandler.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.klopotek.utils.log;
+package org.apache.log4j.net;
 
 import java.sql.*;
 
diff --git a/contribs/ThomasFenner/JDBCIDHandler.java b/modules/contribs/src/main/java/org/apache/log4j/net/JDBCIDHandler.java
similarity index 96%
rename from contribs/ThomasFenner/JDBCIDHandler.java
rename to modules/contribs/src/main/java/org/apache/log4j/net/JDBCIDHandler.java
index 9e6a765..0de23ba 100644
--- a/contribs/ThomasFenner/JDBCIDHandler.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/net/JDBCIDHandler.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.klopotek.utils.log;
+package org.apache.log4j.net;
 
 
 /**
diff --git a/contribs/ThomasFenner/JDBCLogger.java b/modules/contribs/src/main/java/org/apache/log4j/net/JDBCLogger.java
similarity index 90%
rename from contribs/ThomasFenner/JDBCLogger.java
rename to modules/contribs/src/main/java/org/apache/log4j/net/JDBCLogger.java
index 5edf2e8..3973511 100644
--- a/contribs/ThomasFenner/JDBCLogger.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/net/JDBCLogger.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.klopotek.utils.log;
+package org.apache.log4j.net;
 
 import java.sql.*;
 import java.util.*;
@@ -418,51 +418,4 @@
 	int logtype = LogType.EMPTY;
 	Object value = null;				//Generic storage for typewrapper-classes Long, String, etc...
 	JDBCIDHandler idhandler = null;
-}
-
-
-/**
-This class contains all constants which are necessary to define a columns log-type.
-*/
-class LogType
-{
-	//A column of this type will receive the message.
-	public static final int MSG = 1;
-
-	//A column of this type will be a unique identifier of the logged row.
-	public static final int ID = 2;
-
-	//A column of this type will contain a static, one-time-defined value.
-	public static final int STATIC = 3;
-
-  	//A column of this type will be filled with an actual timestamp depending by the time the logging will be done.
-	public static final int TIMESTAMP = 4;
-
-	//A column of this type will contain no value and will not be included in logging insert-statement.
-   //This could be a column which will be filled not by creation but otherwhere...
-	public static final int EMPTY = 5;
-
-
-	public static boolean isLogType(int _lt)
-	{
-		if(_lt == MSG || _lt == STATIC || _lt == ID || _lt == TIMESTAMP || _lt == EMPTY) return true;
-
-		return false;
-	}
-
-   public static int parseLogType(String _lt)
-   {
-		if(_lt.equals("MSG")) return MSG;
-		if(_lt.equals("ID")) return ID;
-		if(_lt.equals("STATIC")) return STATIC;
-		if(_lt.equals("TIMESTAMP")) return TIMESTAMP;
-		if(_lt.equals("EMPTY")) return EMPTY;
-
-      return -1;
-   }
-}
-
-
-
-
-
+}
\ No newline at end of file
diff --git a/contribs/Jamie Tsao/JMSQueueAppender.java b/modules/contribs/src/main/java/org/apache/log4j/net/JMSQueueAppender.java
similarity index 99%
rename from contribs/Jamie Tsao/JMSQueueAppender.java
rename to modules/contribs/src/main/java/org/apache/log4j/net/JMSQueueAppender.java
index 9e762f5..47b865c 100644
--- a/contribs/Jamie Tsao/JMSQueueAppender.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/net/JMSQueueAppender.java
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+package org.apache.log4j.net;
+
 import org.apache.log4j.AppenderSkeleton;
 import org.apache.log4j.spi.LoggingEvent;
 import org.apache.log4j.spi.ErrorHandler;
diff --git a/contribs/VolkerMentzner/Log4jRequestHandler.java b/modules/contribs/src/main/java/org/apache/log4j/net/Log4jRequestHandler.java
similarity index 98%
rename from contribs/VolkerMentzner/Log4jRequestHandler.java
rename to modules/contribs/src/main/java/org/apache/log4j/net/Log4jRequestHandler.java
index bf231a0..c0de2d6 100644
--- a/contribs/VolkerMentzner/Log4jRequestHandler.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/net/Log4jRequestHandler.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.psibt.framework.net;
+package org.apache.log4j.net;
 
 import java.io.*;
 import java.net.*;
diff --git a/modules/contribs/src/main/java/org/apache/log4j/net/LogType.java b/modules/contribs/src/main/java/org/apache/log4j/net/LogType.java
new file mode 100644
index 0000000..5d62178
--- /dev/null
+++ b/modules/contribs/src/main/java/org/apache/log4j/net/LogType.java
@@ -0,0 +1,42 @@
+package org.apache.log4j.net;
+
+/**
+This class contains all constants which are necessary to define a columns log-type.
+*/
+public class LogType
+{
+  //A column of this type will receive the message.
+  public static final int MSG = 1;
+
+  //A column of this type will be a unique identifier of the logged row.
+  public static final int ID = 2;
+
+  //A column of this type will contain a static, one-time-defined value.
+  public static final int STATIC = 3;
+
+    //A column of this type will be filled with an actual timestamp depending by the time the logging will be done.
+  public static final int TIMESTAMP = 4;
+
+  //A column of this type will contain no value and will not be included in logging insert-statement.
+   //This could be a column which will be filled not by creation but otherwhere...
+  public static final int EMPTY = 5;
+
+
+  public static boolean isLogType(int _lt)
+  {
+    if(_lt == MSG || _lt == STATIC || _lt == ID || _lt == TIMESTAMP || _lt == EMPTY) return true;
+
+    return false;
+  }
+
+   public static int parseLogType(String _lt)
+   {
+    if(_lt.equals("MSG")) return MSG;
+    if(_lt.equals("ID")) return ID;
+    if(_lt.equals("STATIC")) return STATIC;
+    if(_lt.equals("TIMESTAMP")) return TIMESTAMP;
+    if(_lt.equals("EMPTY")) return EMPTY;
+
+      return -1;
+   }
+}
\ No newline at end of file
diff --git a/contribs/VolkerMentzner/RootRequestHandler.java b/modules/contribs/src/main/java/org/apache/log4j/net/RootRequestHandler.java
similarity index 98%
rename from contribs/VolkerMentzner/RootRequestHandler.java
rename to modules/contribs/src/main/java/org/apache/log4j/net/RootRequestHandler.java
index 121d39b..41b4946 100644
--- a/contribs/VolkerMentzner/RootRequestHandler.java
+++ b/modules/contribs/src/main/java/org/apache/log4j/net/RootRequestHandler.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.psibt.framework.net;
+package org.apache.log4j.net;
 
 import java.io.*;
 import java.net.*;
diff --git a/contribs/ThomasFenner/code_example1.java b/modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/JDBCAppenderLog4JTest.java
similarity index 90%
rename from contribs/ThomasFenner/code_example1.java
rename to modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/JDBCAppenderLog4JTest.java
index 8d6f7d8..062b922 100644
--- a/contribs/ThomasFenner/code_example1.java
+++ b/modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/JDBCAppenderLog4JTest.java
@@ -1,3 +1,4 @@
+package org.apache.log4j.contribs.ThomasFenner;
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -18,14 +19,13 @@
 // Here is a code example to configure the JDBCAppender with a configuration-file
 
 import org.apache.log4j.*;
-import java.sql.*;
-import java.lang.*;
-import java.util.*;
 
-public class Log4JTest
+import java.sql.*;
+
+public class JDBCAppenderLog4JTest
 {
    // Create a category instance for this class
-   static Category cat = Category.getInstance(Log4JTest.class.getName());
+   static Category cat = Category.getInstance(JDBCAppenderLog4JTest.class.getName());
 
    public static void main(String[] args)
    {
diff --git a/contribs/ThomasFenner/code_example2.java b/modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/JDBCAppenderNoConfigFileLog4JTest.java
similarity index 91%
rename from contribs/ThomasFenner/code_example2.java
rename to modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/JDBCAppenderNoConfigFileLog4JTest.java
index 40bb71d..9a9511a 100644
--- a/contribs/ThomasFenner/code_example2.java
+++ b/modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/JDBCAppenderNoConfigFileLog4JTest.java
@@ -1,3 +1,4 @@
+package org.apache.log4j.contribs.ThomasFenner;
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -18,14 +19,18 @@
 // Here is a code example to configure the JDBCAppender without a configuration-file
 
 import org.apache.log4j.*;
+import org.apache.log4j.net.JDBCAppender;
+import org.apache.log4j.net.JDBCConnectionHandler;
+import org.apache.log4j.net.JDBCIDHandler;
+import org.apache.log4j.net.LogType;
+
 import java.sql.*;
-import java.lang.*;
 import java.util.*;
 
-public class Log4JTest
+public class JDBCAppenderNoConfigFileLog4JTest
 {
 	// Create a category instance for this class
-	static Category cat = Category.getInstance(Log4JTest.class.getName());
+	static Category cat = Category.getInstance(JDBCAppenderNoConfigFileLog4JTest.class.getName());
 
 	public static void main(String[] args)
 	{
diff --git a/contribs/ThomasFenner/Log4JTest.java b/modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/JDBCAppenderWithConfigFileLog4JTest.java
similarity index 95%
rename from contribs/ThomasFenner/Log4JTest.java
rename to modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/JDBCAppenderWithConfigFileLog4JTest.java
index d7bb30f..5fe1a53 100644
--- a/contribs/ThomasFenner/Log4JTest.java
+++ b/modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/JDBCAppenderWithConfigFileLog4JTest.java
@@ -1,3 +1,4 @@
+package org.apache.log4j.contribs.ThomasFenner;
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -123,14 +124,16 @@
 // Here is a code example to configure the JDBCAppender with a configuration-file :
 
 import org.apache.log4j.*;
+import org.apache.log4j.net.JDBCConnectionHandler;
+import org.apache.log4j.net.JDBCIDHandler;
+
 import java.sql.*;
-import java.lang.*;
 import java.util.*;
 
-public class Log4JTest
+public class JDBCAppenderWithConfigFileLog4JTest
 {
 	// Create a category instance for this class
-   static Category cat = Category.getInstance(Log4JTest.class.getName());
+   static Category cat = Category.getInstance(JDBCAppenderWithConfigFileLog4JTest.class.getName());
 
    public static void main(String[] args)
    {
@@ -229,7 +232,7 @@
 */
 
 // Implement a sample JDBCConnectionHandler
-class MyConnectionHandler implements JDBCConnectionHandler
+class JDBCAppenderWithConfigFileLog4JTestConnectionHandler implements JDBCConnectionHandler
 {
 	Connection con = null;
    //Default connection
@@ -258,7 +261,7 @@
 
 
 // Implement a sample JDBCIDHandler
-class MyIDHandler implements JDBCIDHandler
+class JDBCAppenderWithConfigFileIDHandler implements JDBCIDHandler
 {
 	private static long id = 0;
 
diff --git a/contribs/ThomasFenner/configfile_example.txt b/modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/configfile_example.txt
similarity index 100%
rename from contribs/ThomasFenner/configfile_example.txt
rename to modules/contribs/src/stale/java/org/apache/log4j/contribs/ThomasFenner/configfile_example.txt
diff --git a/contribs/KevinSteppe/CompositeRollingAppender.java b/modules/contribs/src/stale/org/apache/log4j/CompositeRollingAppender.java
similarity index 100%
rename from contribs/KevinSteppe/CompositeRollingAppender.java
rename to modules/contribs/src/stale/org/apache/log4j/CompositeRollingAppender.java
diff --git a/contribs/EirikLygre/DailyFileAppender1.java b/modules/contribs/src/stale/org/apache/log4j/DailyFileAppender.java
similarity index 100%
rename from contribs/EirikLygre/DailyFileAppender1.java
rename to modules/contribs/src/stale/org/apache/log4j/DailyFileAppender.java
diff --git a/contribs/LeosLiterak/TempFileAppender.java b/modules/contribs/src/stale/org/apache/log4j/TempFileAppender.java
similarity index 99%
rename from contribs/LeosLiterak/TempFileAppender.java
rename to modules/contribs/src/stale/org/apache/log4j/TempFileAppender.java
index 0d8562a..f0516f1 100644
--- a/contribs/LeosLiterak/TempFileAppender.java
+++ b/modules/contribs/src/stale/org/apache/log4j/TempFileAppender.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
- package org.apache.log4j;
+package org.apache.log4j;
  
 import java.io.File;
 import java.io.Writer;
diff --git a/contribs/JamesHouse/LogTextPanel.java b/modules/contribs/src/stale/org/apache/log4j/gui/LogTextPanel.java
similarity index 98%
rename from contribs/JamesHouse/LogTextPanel.java
rename to modules/contribs/src/stale/org/apache/log4j/gui/LogTextPanel.java
index 56b989a..a477e42 100644
--- a/contribs/JamesHouse/LogTextPanel.java
+++ b/modules/contribs/src/stale/org/apache/log4j/gui/LogTextPanel.java
@@ -34,6 +34,9 @@
 
 import org.apache.log4j.*;
 
+/**
+ * @author James House
+ */
 public class LogTextPanel extends JPanel {
 
   private JScrollBar scrollBar;
diff --git a/contribs/SvenReimers/gui/TextPaneAppender.java b/modules/contribs/src/stale/org/apache/log4j/gui/TextPaneAppender.java
similarity index 99%
rename from contribs/SvenReimers/gui/TextPaneAppender.java
rename to modules/contribs/src/stale/org/apache/log4j/gui/TextPaneAppender.java
index 6b8fce5..ffb1372 100644
--- a/contribs/SvenReimers/gui/TextPaneAppender.java
+++ b/modules/contribs/src/stale/org/apache/log4j/gui/TextPaneAppender.java
@@ -56,7 +56,6 @@
  *
  * @author Sven Reimers
  */
-
 public class TextPaneAppender extends AppenderSkeleton {
     
   JTextPane textpane;
diff --git a/contribs/JamesHouse/TextPanelAppender.java b/modules/contribs/src/stale/org/apache/log4j/gui/TextPanelAppender.java
similarity index 99%
rename from contribs/JamesHouse/TextPanelAppender.java
rename to modules/contribs/src/stale/org/apache/log4j/gui/TextPanelAppender.java
index bb46064..532a34a 100644
--- a/contribs/JamesHouse/TextPanelAppender.java
+++ b/modules/contribs/src/stale/org/apache/log4j/gui/TextPanelAppender.java
@@ -37,12 +37,10 @@
 import org.apache.log4j.helpers.TracerPrintWriter;
 import org.apache.log4j.helpers.OptionConverter;
 
-
 /**
  *
  * @author James House
  */
-
 public class TextPanelAppender extends AppenderSkeleton {
 
   TracerPrintWriter tp;
diff --git a/contribs/JamesHouse/LogTextPanelExample.java b/modules/contribs/src/stale/org/apache/log4j/gui/examples/LogTextPanelExample.java
similarity index 98%
rename from contribs/JamesHouse/LogTextPanelExample.java
rename to modules/contribs/src/stale/org/apache/log4j/gui/examples/LogTextPanelExample.java
index c8898ee..a28ef7f 100644
--- a/contribs/JamesHouse/LogTextPanelExample.java
+++ b/modules/contribs/src/stale/org/apache/log4j/gui/examples/LogTextPanelExample.java
@@ -23,6 +23,9 @@
 import org.apache.log4j.*;
 import org.apache.log4j.gui.TextPanelAppender;
 
+/**
+ * @author James House
+ */
 public class LogTextPanelExample {
   boolean packFrame = false;
 
diff --git a/contribs/SvenReimers/gui/examples/TextPaneAppenderExample.java b/modules/contribs/src/stale/org/apache/log4j/gui/examples/TextPaneAppenderExample.java
similarity index 98%
rename from contribs/SvenReimers/gui/examples/TextPaneAppenderExample.java
rename to modules/contribs/src/stale/org/apache/log4j/gui/examples/TextPaneAppenderExample.java
index bd5f9d3..1a96250 100644
--- a/contribs/SvenReimers/gui/examples/TextPaneAppenderExample.java
+++ b/modules/contribs/src/stale/org/apache/log4j/gui/examples/TextPaneAppenderExample.java
@@ -17,14 +17,14 @@
 
 package org.apache.log4j.gui.examples;
 
-import java.awt.BorderLayout;
 import java.awt.event.*;
 import javax.swing.*;
 import org.apache.log4j.*;
 import org.apache.log4j.gui.*;
 
-
-
+/**
+ * @author Sven Reimers
+ */
 public class TextPaneAppenderExample implements ActionListener {
 
     JFrame mainframe;
diff --git a/contribs/KitchingSimon/SingleLineTracerPrintWriter.java b/modules/contribs/src/stale/org/apache/log4j/helpers/SingleLineTracerPrintWriter.java
similarity index 100%
rename from contribs/KitchingSimon/SingleLineTracerPrintWriter.java
rename to modules/contribs/src/stale/org/apache/log4j/helpers/SingleLineTracerPrintWriter.java
diff --git a/contribs/KitchingSimon/DatagramStringAppender.java b/modules/contribs/src/stale/org/apache/log4j/net/DatagramStringAppender.java
similarity index 100%
rename from contribs/KitchingSimon/DatagramStringAppender.java
rename to modules/contribs/src/stale/org/apache/log4j/net/DatagramStringAppender.java
diff --git a/contribs/VolkerMentzner/PluggableHTTPServer.java b/modules/contribs/src/stale/org/apache/log4j/net/PluggableHTTPServer.java
similarity index 99%
rename from contribs/VolkerMentzner/PluggableHTTPServer.java
rename to modules/contribs/src/stale/org/apache/log4j/net/PluggableHTTPServer.java
index 12aa063..d895315 100644
--- a/contribs/VolkerMentzner/PluggableHTTPServer.java
+++ b/modules/contribs/src/stale/org/apache/log4j/net/PluggableHTTPServer.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.psibt.framework.net;
+package org.apache.log4j.net;
 
 import java.net.*;
 import java.io.*;
diff --git a/contribs/MarkDouglas/SocketNode2.java b/modules/contribs/src/stale/org/apache/log4j/net/SocketNode2.java
similarity index 98%
rename from contribs/MarkDouglas/SocketNode2.java
rename to modules/contribs/src/stale/org/apache/log4j/net/SocketNode2.java
index 60f63e7..7acaaa9 100644
--- a/contribs/MarkDouglas/SocketNode2.java
+++ b/modules/contribs/src/stale/org/apache/log4j/net/SocketNode2.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package com.systemsunion.LoggingServer;
+package org.apache.log4j.net;
 
 import java.net.InetAddress;
 import java.net.Socket;
diff --git a/contribs/MarkDouglas/SocketServer2.java b/modules/contribs/src/stale/org/apache/log4j/net/SocketServer2.java
similarity index 97%
rename from contribs/MarkDouglas/SocketServer2.java
rename to modules/contribs/src/stale/org/apache/log4j/net/SocketServer2.java
index 96caf0e..8d66f7c 100644
--- a/contribs/MarkDouglas/SocketServer2.java
+++ b/modules/contribs/src/stale/org/apache/log4j/net/SocketServer2.java
@@ -16,7 +16,7 @@
  */
 
 
-package com.systemsunion.LoggingServer;
+package org.apache.log4j.net;
 
 import java.net.Socket;
 import java.net.ServerSocket;
diff --git a/contribs/VolkerMentzner/UserDialogRequestHandler.java b/modules/contribs/src/stale/org/apache/log4j/net/UserDialogRequestHandler.java
similarity index 97%
rename from contribs/VolkerMentzner/UserDialogRequestHandler.java
rename to modules/contribs/src/stale/org/apache/log4j/net/UserDialogRequestHandler.java
index 4a5124d..4d34cd7 100644
--- a/contribs/VolkerMentzner/UserDialogRequestHandler.java
+++ b/modules/contribs/src/stale/org/apache/log4j/net/UserDialogRequestHandler.java
@@ -22,7 +22,12 @@
  *   16.04.2001  VMentzner     Created
  */
 
-package com.psibt.framework.net;
+package org.apache.log4j.net;
+
+import java.awt.Component;
+import java.io.Writer;
+import java.net.URL;
+
 /**
  * This class implements a RequestHandler for the path "/userdialog/" in the PluggableHTTPServer.
  * A simple input form is presented in the browser where you can enter a message. This message will be sent
diff --git a/contribs/KevinSteppe/JDBCTest.java b/modules/contribs/src/stale/org/apache/log4j/varia/test/JDBCTest.java
similarity index 84%
rename from contribs/KevinSteppe/JDBCTest.java
rename to modules/contribs/src/stale/org/apache/log4j/varia/test/JDBCTest.java
index 8df3d18..37ea663 100644
--- a/contribs/KevinSteppe/JDBCTest.java
+++ b/modules/contribs/src/stale/org/apache/log4j/varia/test/JDBCTest.java
@@ -16,11 +16,12 @@
  */
 package org.apache.log4j.varia.test;
 
-
-import org.apache.log4j.varia.JDBCAppender;
 import org.apache.log4j.*;
+import org.apache.log4j.net.JDBCAppender;
 
-
+/**
+ * @author Kevin Sceppe
+ */
 public class JDBCTest
 {
     public static void main (String argv[])
@@ -39,11 +40,11 @@
         Layout layout = new PatternLayout("%p [%t] %c - %m%n");
         JDBCAppender appender = new JDBCAppender();
         appender.setLayout(layout);
-        appender.setOption(JDBCAppender.URL_OPTION, "jdbc:odbc:someDB");
+        appender.setOption(org.apache.log4j.net.URL_OPTION, "jdbc:odbc:someDB");
 
 
-        appender.setOption(JDBCAppender.USER_OPTION, "auser");
-        appender.setOption(JDBCAppender.PASSWORD_OPTION, "thepassword");
+        appender.setOption(org.apache.log4j.net.USER_OPTION, "auser");
+        appender.setOption(org.apache.log4j.net.PASSWORD_OPTION, "thepassword");
 
 
 
@@ -64,7 +65,7 @@
             Thread.sleep(500);
 
 
-            appender.setOption(JDBCAppender.BUFFER_OPTION, "5");
+            appender.setOption(org.apache.log4j.net.BUFFER_OPTION, "5");
             log.debug("Debug 2");
             Thread.sleep(500);
             log.info("info 2");
@@ -77,7 +78,7 @@
             Thread.sleep(500);
 
 
-            appender.setOption(JDBCAppender.BUFFER_OPTION, "2");
+            appender.setOption(org.apache.log4j.net.BUFFER_OPTION, "2");
             appender.setThreshold(Priority.WARN);
             log.debug("Debug 3");
             Thread.sleep(500);
diff --git a/contribs/CekiGulcu/Transform.java b/modules/contribs/src/stale/org/apache/log4j/xml/Transform.java
similarity index 99%
rename from contribs/CekiGulcu/Transform.java
rename to modules/contribs/src/stale/org/apache/log4j/xml/Transform.java
index dcd9c15..bd0fb1c 100644
--- a/contribs/CekiGulcu/Transform.java
+++ b/modules/contribs/src/stale/org/apache/log4j/xml/Transform.java
@@ -52,7 +52,9 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 
-
+/**
+ * @author CekiGulcu
+ */
 public class Transform {
 
   public static void main(String[] args) throws Exception {
diff --git a/contribs/KitchingSimon/logconfig.xml b/modules/contribs/src/test/resources/org/apache/log4j/contribs/KitchingSimon/logconfig.xml
similarity index 100%
rename from contribs/KitchingSimon/logconfig.xml
rename to modules/contribs/src/test/resources/org/apache/log4j/contribs/KitchingSimon/logconfig.xml
diff --git a/contribs/KitchingSimon/udpserver.pl b/modules/contribs/src/test/resources/org/apache/log4j/contribs/KitchingSimon/udpserver.pl
similarity index 100%
rename from contribs/KitchingSimon/udpserver.pl
rename to modules/contribs/src/test/resources/org/apache/log4j/contribs/KitchingSimon/udpserver.pl
diff --git a/examples/lf5/InitUsingDefaultConfigurator/InitUsingDefaultConfigurator.java b/modules/lf5/examples/InitUsingDefaultConfigurator/InitUsingDefaultConfigurator.java
similarity index 100%
rename from examples/lf5/InitUsingDefaultConfigurator/InitUsingDefaultConfigurator.java
rename to modules/lf5/examples/InitUsingDefaultConfigurator/InitUsingDefaultConfigurator.java
diff --git a/examples/lf5/InitUsingLog4JProperties/InitUsingLog4JProperties.java b/modules/lf5/examples/InitUsingLog4JProperties/InitUsingLog4JProperties.java
similarity index 100%
rename from examples/lf5/InitUsingLog4JProperties/InitUsingLog4JProperties.java
rename to modules/lf5/examples/InitUsingLog4JProperties/InitUsingLog4JProperties.java
diff --git a/examples/lf5/InitUsingLog4JProperties/log4j.properties b/modules/lf5/examples/InitUsingLog4JProperties/log4j.properties
similarity index 100%
rename from examples/lf5/InitUsingLog4JProperties/log4j.properties
rename to modules/lf5/examples/InitUsingLog4JProperties/log4j.properties
diff --git a/examples/lf5/InitUsingMultipleAppenders/InitUsingMultipleAppenders.java b/modules/lf5/examples/InitUsingMultipleAppenders/InitUsingMultipleAppenders.java
similarity index 100%
rename from examples/lf5/InitUsingMultipleAppenders/InitUsingMultipleAppenders.java
rename to modules/lf5/examples/InitUsingMultipleAppenders/InitUsingMultipleAppenders.java
diff --git a/examples/lf5/InitUsingMultipleAppenders/example.properties b/modules/lf5/examples/InitUsingMultipleAppenders/example.properties
similarity index 100%
rename from examples/lf5/InitUsingMultipleAppenders/example.properties
rename to modules/lf5/examples/InitUsingMultipleAppenders/example.properties
diff --git a/examples/lf5/InitUsingPropertiesFile/InitUsingPropertiesFile.java b/modules/lf5/examples/InitUsingPropertiesFile/InitUsingPropertiesFile.java
similarity index 100%
rename from examples/lf5/InitUsingPropertiesFile/InitUsingPropertiesFile.java
rename to modules/lf5/examples/InitUsingPropertiesFile/InitUsingPropertiesFile.java
diff --git a/examples/lf5/InitUsingPropertiesFile/example.properties b/modules/lf5/examples/InitUsingPropertiesFile/example.properties
similarity index 100%
rename from examples/lf5/InitUsingPropertiesFile/example.properties
rename to modules/lf5/examples/InitUsingPropertiesFile/example.properties
diff --git a/examples/lf5/InitUsingXMLPropertiesFile/InitUsingXMLPropertiesFile.java b/modules/lf5/examples/InitUsingXMLPropertiesFile/InitUsingXMLPropertiesFile.java
similarity index 100%
rename from examples/lf5/InitUsingXMLPropertiesFile/InitUsingXMLPropertiesFile.java
rename to modules/lf5/examples/InitUsingXMLPropertiesFile/InitUsingXMLPropertiesFile.java
diff --git a/examples/lf5/InitUsingXMLPropertiesFile/example.xml b/modules/lf5/examples/InitUsingXMLPropertiesFile/example.xml
similarity index 100%
rename from examples/lf5/InitUsingXMLPropertiesFile/example.xml
rename to modules/lf5/examples/InitUsingXMLPropertiesFile/example.xml
diff --git a/examples/lf5/UsingLogMonitorAdapter/CustomizedLogLevels.java b/modules/lf5/examples/UsingLogMonitorAdapter/CustomizedLogLevels.java
similarity index 100%
rename from examples/lf5/UsingLogMonitorAdapter/CustomizedLogLevels.java
rename to modules/lf5/examples/UsingLogMonitorAdapter/CustomizedLogLevels.java
diff --git a/examples/lf5/UsingLogMonitorAdapter/UsingLogMonitorAdapter.java b/modules/lf5/examples/UsingLogMonitorAdapter/UsingLogMonitorAdapter.java
similarity index 100%
rename from examples/lf5/UsingLogMonitorAdapter/UsingLogMonitorAdapter.java
rename to modules/lf5/examples/UsingLogMonitorAdapter/UsingLogMonitorAdapter.java
diff --git a/examples/lf5/UsingSocketAppenders/UsingSocketAppenders.java b/modules/lf5/examples/UsingSocketAppenders/UsingSocketAppenders.java
similarity index 100%
rename from examples/lf5/UsingSocketAppenders/UsingSocketAppenders.java
rename to modules/lf5/examples/UsingSocketAppenders/UsingSocketAppenders.java
diff --git a/examples/lf5/UsingSocketAppenders/socketclient.properties b/modules/lf5/examples/UsingSocketAppenders/socketclient.properties
similarity index 100%
rename from examples/lf5/UsingSocketAppenders/socketclient.properties
rename to modules/lf5/examples/UsingSocketAppenders/socketclient.properties
diff --git a/examples/lf5/UsingSocketAppenders/socketserver.properties b/modules/lf5/examples/UsingSocketAppenders/socketserver.properties
similarity index 100%
rename from examples/lf5/UsingSocketAppenders/socketserver.properties
rename to modules/lf5/examples/UsingSocketAppenders/socketserver.properties
diff --git a/examples/lf5/index.html b/modules/lf5/examples/index.html
similarity index 100%
rename from examples/lf5/index.html
rename to modules/lf5/examples/index.html
diff --git a/modules/lf5/pom.xml b/modules/lf5/pom.xml
new file mode 100644
index 0000000..1c3f71d
--- /dev/null
+++ b/modules/lf5/pom.xml
@@ -0,0 +1,38 @@
+<!--
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-modules</artifactId>
+      <version>1.4.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.log4j</groupId>
+  <artifactId>log4j-lf5</artifactId>
+  <name>Apache Log4j-LF5</name>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/AppenderFinalizer.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/AppenderFinalizer.java
new file mode 100644
index 0000000..a2a7019
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/AppenderFinalizer.java
@@ -0,0 +1,78 @@
+/*
+ * 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.lf5;
+
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+
+/**
+ * <code>AppenderFinalizer</code> has a single method that will finalize
+ * resources associated with a <code>LogBrokerMonitor</code> in the event
+ * that the <code>LF5Appender</code> class is destroyed, and the class loader
+ * is garbage collected.
+ *
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class AppenderFinalizer {
+  //--------------------------------------------------------------------------
+  // Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Protected Variables:
+  //--------------------------------------------------------------------------
+
+  protected LogBrokerMonitor _defaultMonitor = null;
+
+  //--------------------------------------------------------------------------
+  // Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Constructors:
+  //--------------------------------------------------------------------------
+
+  public AppenderFinalizer(LogBrokerMonitor defaultMonitor) {
+    _defaultMonitor = defaultMonitor;
+  }
+  //--------------------------------------------------------------------------
+  // Public Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Protected Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * @throws java.lang.Throwable
+   */
+  protected void finalize() throws Throwable {
+    System.out.println("Disposing of the default LogBrokerMonitor instance");
+    _defaultMonitor.dispose();
+  }
+
+  //--------------------------------------------------------------------------
+  // Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
\ No newline at end of file
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/DefaultLF5Configurator.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/DefaultLF5Configurator.java
new file mode 100644
index 0000000..d5c80a5
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/DefaultLF5Configurator.java
@@ -0,0 +1,118 @@
+/*
+ * 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.lf5;
+
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.LoggerRepository;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * The <code>DefaultLF5Configurator</code> provides a default
+ * configuration for the <code>LF5Appender</code>.
+ *
+ * Note: The preferred method for configuring a <code>LF5Appender</code>
+ * is to use the <code>LF5Manager</code> class. This class ensures
+ * that configuration does not occur multiple times, and improves system
+ * performance. Reconfiguring the monitor multiple times can result in
+ * unexpected behavior.
+ *
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class DefaultLF5Configurator implements Configurator {
+  //--------------------------------------------------------------------------
+  // Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Constructors:
+  //--------------------------------------------------------------------------
+  /**
+   * This class should never be instantiated! It implements the <code>
+   * Configurator</code>
+   * interface, but does not provide the same functionality as full
+   * configurator class.
+   */
+  private DefaultLF5Configurator() {
+
+  }
+
+  //--------------------------------------------------------------------------
+  // Public Methods:
+  //--------------------------------------------------------------------------
+  /**
+   * This method configures the <code>LF5Appender</code> using a
+   * default configuration file. The default configuration file is
+   * <bold>defaultconfig.properties</bold>.
+   * @throws java.io.IOException
+   */
+  public static void configure() throws IOException {
+    String resource =
+        "/org/apache/log4j/lf5/config/defaultconfig.properties";
+    URL configFileResource =
+        DefaultLF5Configurator.class.getResource(resource);
+
+    if (configFileResource != null) {
+      PropertyConfigurator.configure(configFileResource);
+    } else {
+      throw new IOException("Error: Unable to open the resource" +
+          resource);
+    }
+
+  }
+
+    public void doConfigure(InputStream inputStream, LoggerRepository repository) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    /**
+   * This is a dummy method that will throw an
+   * <code>IllegalStateException</code> if used.
+   */
+  public void doConfigure(URL configURL, LoggerRepository repository) {
+    throw new IllegalStateException("This class should NOT be" +
+        " instantiated!");
+  }
+
+  //--------------------------------------------------------------------------
+  // Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
\ No newline at end of file
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/LF5Appender.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/LF5Appender.java
new file mode 100644
index 0000000..e98e9bd
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/LF5Appender.java
@@ -0,0 +1,266 @@
+/*
+ * 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.lf5;
+
+import java.awt.Toolkit;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * <code>LF5Appender</code> logs events to a swing based logging
+ * console. The swing console supports turning categories on and off,
+ * multiple detail level views, as well as full text searching and many
+ * other capabilties.
+ *
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LF5Appender extends AppenderSkeleton {
+  //--------------------------------------------------------------------------
+  // Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Protected Variables:
+  //--------------------------------------------------------------------------
+
+  protected LogBrokerMonitor _logMonitor;
+  protected static LogBrokerMonitor _defaultLogMonitor;
+  protected static AppenderFinalizer _finalizer;
+
+  //--------------------------------------------------------------------------
+  // Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  // Constructors:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Constructs a <code>LF5Appender</code> using the default instance of
+   * the <code>LogBrokerMonitor</code>. This constructor should <bold>always
+   * </bold> be  preferred over the
+   * <code>LF5Appender(LogBrokerMonitor monitor)</code>
+   * constructor, unless you need to spawn additional log monitoring
+   * windows.
+   */
+  public LF5Appender() {
+    this(getDefaultInstance());
+  }
+
+  /**
+   * Constructs a <code>LF5Appender<code> using an instance of
+   * a <code>LogBrokerMonitor<code> supplied by the user. This
+   * constructor should only be used when you need to spawn
+   * additional log monitoring windows.
+   *
+   * @param monitor An instance of a <code>LogBrokerMonitor<code>
+   * created by the user.
+   */
+  public LF5Appender(LogBrokerMonitor monitor) {
+
+    if (monitor != null) {
+      _logMonitor = monitor;
+    }
+  }
+
+  //--------------------------------------------------------------------------
+  // Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Appends a <code>LoggingEvent</code> record to the
+   * <code>LF5Appender</code>.
+   * @param event The <code>LoggingEvent</code>
+   * to be appended.
+   */
+  public void append(LoggingEvent event) {
+    // Retrieve the information from the log4j LoggingEvent.
+    String category = event.getLoggerName();
+    String logMessage = event.getRenderedMessage();
+    String nestedDiagnosticContext = event.getNDC();
+    String threadDescription = event.getThreadName();
+    String level = event.getLevel().toString();
+    long time = event.timeStamp;
+    LocationInfo locationInfo = event.getLocationInformation();
+
+    // Add the logging event information to a LogRecord
+    Log4JLogRecord record = new Log4JLogRecord();
+
+    record.setCategory(category);
+    record.setMessage(logMessage);
+    record.setLocation(locationInfo.fullInfo);
+    record.setMillis(time);
+    record.setThreadDescription(threadDescription);
+
+    if (nestedDiagnosticContext != null) {
+      record.setNDC(nestedDiagnosticContext);
+    } else {
+      record.setNDC("");
+    }
+
+    if (event.getThrowableInformation() != null) {
+      record.setThrownStackTrace(event.getThrowableInformation());
+    }
+
+    try {
+      record.setLevel(LogLevel.valueOf(level));
+    } catch (LogLevelFormatException e) {
+      // If the priority level doesn't match one of the predefined
+      // log levels, then set the level to warning.
+      record.setLevel(LogLevel.WARN);
+    }
+
+    if (_logMonitor != null) {
+      _logMonitor.addMessage(record);
+    }
+  }
+
+  /**
+   * This method is an empty implementation of the close() method inherited
+   * from the <code>org.apache.log4j.Appender</code> interface.
+   */
+  public void close() {
+  }
+
+  /**
+   * Returns a value that indicates whether this appender requires a
+   * <code>Layout</code>. This method always returns false.
+   * No layout is required for the <code>LF5Appender</code>.
+   */
+  public boolean requiresLayout() {
+    return false;
+  }
+
+  /**
+   * This method is used to set the property that controls whether
+   * the <code>LogBrokerMonitor</code> is hidden or closed when a user
+   * exits
+   * the monitor. By default, the <code>LogBrokerMonitor</code> will hide
+   * itself when the log window is exited, and the swing thread will
+   * continue to run in the background. If this property is
+   * set to true, the <code>LogBrokerMonitor</code> will call System.exit(0)
+   * and will shut down swing thread and the virtual machine.
+   *
+   * @param callSystemExitOnClose A boolean value indicating whether
+   * to call System.exit(0) when closing the log window.
+   */
+  public void setCallSystemExitOnClose(boolean callSystemExitOnClose) {
+    _logMonitor.setCallSystemExitOnClose(callSystemExitOnClose);
+  }
+
+  /**
+   * The equals method compares two LF5Appenders and determines whether
+   * they are equal. Two <code>Appenders</code> will be considered equal
+   * if, and only if, they both contain references to the same <code>
+   * LogBrokerMonitor</code>.
+   *
+   * @param compareTo A boolean value indicating whether
+   * the two LF5Appenders are equal.
+   */
+  public boolean equals(LF5Appender compareTo) {
+    // If both reference the same LogBrokerMonitor, they are equal.
+    return _logMonitor == compareTo.getLogBrokerMonitor();
+  }
+
+  public LogBrokerMonitor getLogBrokerMonitor() {
+    return _logMonitor;
+  }
+
+  public static void main(String[] args) {
+    new LF5Appender();
+  }
+
+  public void setMaxNumberOfRecords(int maxNumberOfRecords) {
+    _defaultLogMonitor.setMaxNumberOfLogRecords(maxNumberOfRecords);
+  }
+  //--------------------------------------------------------------------------
+  // Protected Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * @return The default instance of the <code>LogBrokerMonitor</code>.
+   */
+  protected static synchronized LogBrokerMonitor getDefaultInstance() {
+    if (_defaultLogMonitor == null) {
+      try {
+        _defaultLogMonitor =
+            new LogBrokerMonitor(LogLevel.getLog4JLevels());
+        _finalizer = new AppenderFinalizer(_defaultLogMonitor);
+
+        _defaultLogMonitor.setFrameSize(getDefaultMonitorWidth(),
+            getDefaultMonitorHeight());
+        _defaultLogMonitor.setFontSize(12);
+        _defaultLogMonitor.show();
+
+      } catch (SecurityException e) {
+        _defaultLogMonitor = null;
+      }
+    }
+
+    return _defaultLogMonitor;
+  }
+
+  /**
+   * @return the screen width from Toolkit.getScreenSize()
+   * if possible, otherwise returns 800
+   * @see java.awt.Toolkit
+   */
+  protected static int getScreenWidth() {
+    try {
+      return Toolkit.getDefaultToolkit().getScreenSize().width;
+    } catch (Throwable t) {
+      return 800;
+    }
+  }
+
+  /**
+   * @return the screen height from Toolkit.getScreenSize()
+   * if possible, otherwise returns 600
+   * @see java.awt.Toolkit
+   */
+  protected static int getScreenHeight() {
+    try {
+      return Toolkit.getDefaultToolkit().getScreenSize().height;
+    } catch (Throwable t) {
+      return 600;
+    }
+  }
+
+  protected static int getDefaultMonitorWidth() {
+    return (3 * getScreenWidth()) / 4;
+  }
+
+  protected static int getDefaultMonitorHeight() {
+    return (3 * getScreenHeight()) / 4;
+  }
+  //--------------------------------------------------------------------------
+  // Private Methods:
+  //--------------------------------------------------------------------------
+
+
+  //--------------------------------------------------------------------------
+  // Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/Log4JLogRecord.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/Log4JLogRecord.java
new file mode 100644
index 0000000..4393eb5
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/Log4JLogRecord.java
@@ -0,0 +1,116 @@
+/*
+ * 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.lf5;
+
+import org.apache.log4j.spi.ThrowableInformation;
+
+/**
+ * A <code>Log4JLogRecord</code> encapsulates
+ * the details of your log4j <code>LoggingEvent</code> in a format usable
+ * by the <code>LogBrokerMonitor</code>.
+ *
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class Log4JLogRecord extends LogRecord {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Constructs an instance of a <code>Log4JLogRecord</code>.
+   */
+  public Log4JLogRecord() {
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+  /**
+   * Determines which <code>Priority</code> levels will
+   * be displayed in colored font when the <code>LogMonitorAppender</code>
+   * renders this log message. By default, messages will be colored
+   * red if they are of <code>Priority</code> ERROR or FATAL.
+   *
+   * @return true if the log level is ERROR or FATAL.
+   */
+  public boolean isSevereLevel() {
+    boolean isSevere = false;
+
+    if (LogLevel.ERROR.equals(getLevel()) ||
+        LogLevel.FATAL.equals(getLevel())) {
+      isSevere = true;
+    }
+
+    return isSevere;
+  }
+
+  /**
+   * Set stack trace information associated with this Log4JLogRecord.
+   * When this method is called, the stack trace in a
+   * String-based format is made
+   * available via the getThrownStackTrace() method.
+   *
+   * @param throwableInfo An org.apache.log4j.spi.ThrowableInformation to
+   * associate with this Log4JLogRecord.
+   * @see #getThrownStackTrace()
+   */
+  public void setThrownStackTrace(ThrowableInformation throwableInfo) {
+    String[] stackTraceArray = throwableInfo.getThrowableStrRep();
+
+    StringBuffer stackTrace = new StringBuffer();
+    String nextLine;
+
+    for (int i = 0; i < stackTraceArray.length; i++) {
+      nextLine = stackTraceArray[i] + "\n";
+      stackTrace.append(nextLine);
+    }
+
+    _thrownStackTrace = stackTrace.toString();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/LogLevel.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/LogLevel.java
new file mode 100644
index 0000000..080ed68
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/LogLevel.java
@@ -0,0 +1,278 @@
+/*
+ * 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.lf5;
+
+import java.awt.Color;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The LogLevel class defines a set of standard logging levels.
+ *
+ * The logging Level objects are ordered and are specified by ordered
+ * integers. Enabling logging at a given level also enables logging at all
+ * higher levels.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brent Sprecher
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogLevel implements java.io.Serializable {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  // log4j log levels.
+  public final static LogLevel FATAL = new LogLevel("FATAL", 0);
+  public final static LogLevel ERROR = new LogLevel("ERROR", 1);
+  public final static LogLevel WARN = new LogLevel("WARN", 2);
+  public final static LogLevel INFO = new LogLevel("INFO", 3);
+  public final static LogLevel DEBUG = new LogLevel("DEBUG", 4);
+
+  // jdk1.4 log levels NOTE: also includes INFO
+  public final static LogLevel SEVERE = new LogLevel("SEVERE", 1);
+  public final static LogLevel WARNING = new LogLevel("WARNING", 2);
+  public final static LogLevel CONFIG = new LogLevel("CONFIG", 4);
+  public final static LogLevel FINE = new LogLevel("FINE", 5);
+  public final static LogLevel FINER = new LogLevel("FINER", 6);
+  public final static LogLevel FINEST = new LogLevel("FINEST", 7);
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected String _label;
+  protected int _precedence;
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+  private static LogLevel[] _log4JLevels;
+  private static LogLevel[] _jdk14Levels;
+  private static LogLevel[] _allDefaultLevels;
+  private static Map _logLevelMap;
+  private static Map _logLevelColorMap;
+  private static Map _registeredLogLevelMap = new HashMap();
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  static {
+    _log4JLevels = new LogLevel[]{FATAL, ERROR, WARN, INFO, DEBUG};
+    _jdk14Levels = new LogLevel[]{SEVERE, WARNING, INFO,
+                                  CONFIG, FINE, FINER, FINEST};
+    _allDefaultLevels = new LogLevel[]{FATAL, ERROR, WARN, INFO, DEBUG,
+                                       SEVERE, WARNING, CONFIG, FINE, FINER, FINEST};
+
+    _logLevelMap = new HashMap();
+    for (int i = 0; i < _allDefaultLevels.length; i++) {
+      _logLevelMap.put(_allDefaultLevels[i].getLabel(), _allDefaultLevels[i]);
+    }
+
+    // prepopulate map with levels and text color of black
+    _logLevelColorMap = new HashMap();
+    for (int i = 0; i < _allDefaultLevels.length; i++) {
+      _logLevelColorMap.put(_allDefaultLevels[i], Color.black);
+    }
+  }
+
+  public LogLevel(String label, int precedence) {
+    _label = label;
+    _precedence = precedence;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Return the Label of the LogLevel.
+   */
+  public String getLabel() {
+    return _label;
+  }
+
+  /**
+   * Returns true if the level supplied is encompassed by this level.
+   * For example, LogLevel.SEVERE encompasses no other LogLevels and
+   * LogLevel.FINE encompasses all other LogLevels.  By definition,
+   * a LogLevel encompasses itself.
+   */
+  public boolean encompasses(LogLevel level) {
+    if (level.getPrecedence() <= getPrecedence()) {
+      return true;
+    }
+
+    return false;
+  }
+
+  /**
+   * Convert a log level label into a LogLevel object.
+   *
+   * @param level The label of a level to be converted into a LogLevel.
+   * @return LogLevel The LogLevel with a label equal to level.
+   * @throws LogLevelFormatException Is thrown when the level can not be
+   *         converted into a LogLevel.
+   */
+  public static LogLevel valueOf(String level)
+      throws LogLevelFormatException {
+    LogLevel logLevel = null;
+    if (level != null) {
+      level = level.trim().toUpperCase();
+      logLevel = (LogLevel) _logLevelMap.get(level);
+    }
+
+    // Didn't match, Check for registered LogLevels
+    if (logLevel == null && _registeredLogLevelMap.size() > 0) {
+      logLevel = (LogLevel) _registeredLogLevelMap.get(level);
+    }
+
+    if (logLevel == null) {
+      StringBuffer buf = new StringBuffer();
+      buf.append("Error while trying to parse (" + level + ") into");
+      buf.append(" a LogLevel.");
+      throw new LogLevelFormatException(buf.toString());
+    }
+    return logLevel;
+  }
+
+  /**
+   * Registers a used defined LogLevel.
+   *
+   * @param logLevel The log level to be registered. Cannot be a default LogLevel
+   * @return LogLevel The replaced log level.
+   */
+  public static LogLevel register(LogLevel logLevel) {
+    if (logLevel == null) return null;
+
+    // ensure that this is not a default log level
+    if (_logLevelMap.get(logLevel.getLabel()) == null) {
+      return (LogLevel) _registeredLogLevelMap.put(logLevel.getLabel(), logLevel);
+    }
+
+    return null;
+  }
+
+  public static void register(LogLevel[] logLevels) {
+    if (logLevels != null) {
+      for (int i = 0; i < logLevels.length; i++) {
+        register(logLevels[i]);
+      }
+    }
+  }
+
+  public static void register(List logLevels) {
+    if (logLevels != null) {
+      Iterator it = logLevels.iterator();
+      while (it.hasNext()) {
+        register((LogLevel) it.next());
+      }
+    }
+  }
+
+  public boolean equals(Object o) {
+    boolean equals = false;
+
+    if (o instanceof LogLevel) {
+      if (this.getPrecedence() ==
+          ((LogLevel) o).getPrecedence()) {
+        equals = true;
+      }
+
+    }
+
+    return equals;
+  }
+
+  public int hashCode() {
+    return _label.hashCode();
+  }
+
+  public String toString() {
+    return _label;
+  }
+
+  // set a text color for a specific log level
+  public void setLogLevelColorMap(LogLevel level, Color color) {
+    // remove the old entry
+    _logLevelColorMap.remove(level);
+    // add the new color entry
+    if (color == null) {
+      color = Color.black;
+    }
+    _logLevelColorMap.put(level, color);
+  }
+
+  public static void resetLogLevelColorMap() {
+    // empty the map
+    _logLevelColorMap.clear();
+
+    // repopulate map and reset text color black
+    for (int i = 0; i < _allDefaultLevels.length; i++) {
+      _logLevelColorMap.put(_allDefaultLevels[i], Color.black);
+    }
+  }
+
+  /**
+   * @return A <code>List</code> of <code>LogLevel</code> objects that map
+   * to log4j <code>Priority</code> objects.
+   */
+  public static List getLog4JLevels() {
+    return Arrays.asList(_log4JLevels);
+  }
+
+  public static List getJdk14Levels() {
+    return Arrays.asList(_jdk14Levels);
+  }
+
+  public static List getAllDefaultLevels() {
+    return Arrays.asList(_allDefaultLevels);
+  }
+
+  public static Map getLogLevelColorMap() {
+    return _logLevelColorMap;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected int getPrecedence() {
+    return _precedence;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/LogLevelFormatException.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/LogLevelFormatException.java
new file mode 100644
index 0000000..1109e23
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/LogLevelFormatException.java
@@ -0,0 +1,73 @@
+/*
+ * 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.lf5;
+
+/**
+ * Thrown to indicate that the client has attempted to convert a string
+ * to one the LogLevel types, but the string does not have the appropriate
+ * format.
+ *
+ * @author Michael J. Sikorsky<
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogLevelFormatException extends Exception {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public LogLevelFormatException(String message) {
+    super(message);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/LogRecord.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/LogRecord.java
new file mode 100644
index 0000000..4f4097f
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/LogRecord.java
@@ -0,0 +1,395 @@
+/*
+ * 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.lf5;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * LogRecord.  A LogRecord encapsulates the details of your desired log
+ * request.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public abstract class LogRecord implements java.io.Serializable {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected static long _seqCount = 0;
+
+  protected LogLevel _level;
+  protected String _message;
+  protected long _sequenceNumber;
+  protected long _millis;
+  protected String _category;
+  protected String _thread;
+  protected String _thrownStackTrace;
+  protected Throwable _thrown;
+  protected String _ndc;
+  protected String _location;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public LogRecord() {
+    super();
+
+    _millis = System.currentTimeMillis();
+    _category = "Debug";
+    _message = "";
+    _level = LogLevel.INFO;
+    _sequenceNumber = getNextId();
+    _thread = Thread.currentThread().toString();
+    _ndc = "";
+    _location = "";
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Get the level of this LogRecord.
+   *
+   * @return The LogLevel of this record.
+   * @see #setLevel(LogLevel)
+   * @see LogLevel
+   */
+  public LogLevel getLevel() {
+    return (_level);
+  }
+
+  /**
+   * Set the level of this LogRecord.
+   *
+   * @param level The LogLevel for this record.
+   * @see #getLevel()
+   * @see LogLevel
+   */
+  public void setLevel(LogLevel level) {
+    _level = level;
+  }
+
+  /**
+   * Abstract method. Must be overridden to indicate what log level
+   * to show in red.
+   */
+  public abstract boolean isSevereLevel();
+
+  /**
+   * @return true if getThrown().toString() is a non-empty string.
+   */
+  public boolean hasThrown() {
+    Throwable thrown = getThrown();
+    if (thrown == null) {
+      return false;
+    }
+    String thrownString = thrown.toString();
+    return thrownString != null && thrownString.trim().length() != 0;
+  }
+
+  /**
+   * @return true if isSevereLevel() or hasThrown() returns true.
+   */
+  public boolean isFatal() {
+    return isSevereLevel() || hasThrown();
+  }
+
+  /**
+   * Get the category asscociated with this LogRecord.  For a more detailed
+   * description of what a category is see setCategory().
+   *
+   * @return The category of this record.
+   * @see #setCategory(String)
+   */
+  public String getCategory() {
+    return (_category);
+  }
+
+  /**
+   * Set the category associated with this LogRecord. A category represents
+   * a hierarchical dot (".") separated namespace for messages.
+   * The definition of a category is application specific, but a common convention
+   * is as follows:
+   *
+   * <p>
+   * When logging messages
+   * for a particluar class you can use its class name:
+   * com.thoughtworks.framework.servlet.ServletServiceBroker.<br><br>
+   * Futhermore, to log a message for a particular method in a class
+   * add the method name:
+   * com.thoughtworks.framework.servlet.ServletServiceBroker.init().
+   * </p>
+   *
+   * @param category The category for this record.
+   * @see #getCategory()
+   */
+  public void setCategory(String category) {
+    _category = category;
+  }
+
+  /**
+   * Get the message asscociated with this LogRecord.
+   *
+   * @return The message of this record.
+   * @see #setMessage(String)
+   */
+  public String getMessage() {
+    return (_message);
+  }
+
+  /**
+   * Set the message associated with this LogRecord.
+   *
+   * @param message The message for this record.
+   * @see #getMessage()
+   */
+  public void setMessage(String message) {
+    _message = message;
+  }
+
+  /**
+   * Get the sequence number associated with this LogRecord.  Sequence numbers
+   * are generally assigned when a LogRecord is constructed.  Sequence numbers
+   * start at 0 and increase with each newly constructed LogRocord.
+   *
+   * @return The sequence number of this record.
+   * @see #setSequenceNumber(long)
+   */
+  public long getSequenceNumber() {
+    return (_sequenceNumber);
+  }
+
+  /**
+   * Set the sequence number assocsiated with this LogRecord.  A sequence number
+   * will automatically be assigned to evey newly constructed LogRecord, however,
+   * this method can override the value.
+   *
+   * @param number The sequence number.
+   * @see #getSequenceNumber()
+   */
+  public void setSequenceNumber(long number) {
+    _sequenceNumber = number;
+  }
+
+  /**
+   * Get the event time of this record in milliseconds from 1970.
+   * When a LogRecord is constructed the event time is set but may be
+   * overridden by calling setMillis();
+   *
+   * @return The event time of this record in milliseconds from 1970.
+   * @see #setMillis(long)
+   */
+  public long getMillis() {
+    return _millis;
+  }
+
+  /**
+   * Set the event time of this record.  When a LogRecord is constructed
+   * the event time is set but may be overridden by calling this method.
+   *
+   * @param millis The time in milliseconds from 1970.
+   * @see #getMillis()
+   */
+  public void setMillis(long millis) {
+    _millis = millis;
+  }
+
+  /**
+   * Get the thread description asscociated with this LogRecord.  When a
+   * LogRecord is constructed, the thread description is set by calling:
+   * Thread.currentThread().toString().  You may supply a thread description
+   * of your own by calling the setThreadDescription(String) method.
+   *
+   * @return The thread description of this record.
+   * @see #setThreadDescription(String)
+   */
+  public String getThreadDescription() {
+    return (_thread);
+  }
+
+  /**
+   * Set the thread description associated with this LogRecord.  When a
+   * LogRecord is constructed, the thread description is set by calling:
+   * Thread.currentThread().toString().  You may supply a thread description
+   * of your own by calling this method.
+   *
+   * @param threadDescription The description of the thread for this record.
+   * @see #getThreadDescription()
+   */
+  public void setThreadDescription(String threadDescription) {
+    _thread = threadDescription;
+  }
+
+  /**
+   * Get the stack trace in a String-based format for the associated Throwable
+   * of this LogRecord.  The stack trace in a String-based format is set
+   * when the setThrown(Throwable) method is called.
+   *
+   * <p>
+   * Why do we need this method considering that we
+   * have the getThrown() and setThrown() methods?
+   * A Throwable object may not be serializable, however, a String representation
+   * of it is.  Users of LogRecords should generally call this method over
+   * getThrown() for the reasons of serialization.
+   * </p>
+   *
+   * @return The Stack Trace for the asscoiated Throwable of this LogRecord.
+   * @see #setThrown(Throwable)
+   * @see #getThrown()
+   */
+  public String getThrownStackTrace() {
+    return (_thrownStackTrace);
+  }
+
+  /**
+   * Set the ThrownStackTrace for the log record.
+   *
+   * @param trace A String to associate with this LogRecord
+   * @see #getThrownStackTrace()
+   */
+  public void setThrownStackTrace(String trace) {
+    _thrownStackTrace = trace;
+  }
+
+  /**
+   * Get the Throwable associated with this LogRecord.
+   *
+   * @return The LogLevel of this record.
+   * @see #setThrown(Throwable)
+   * @see #getThrownStackTrace()
+   */
+  public Throwable getThrown() {
+    return (_thrown);
+  }
+
+  /**
+   * Set the Throwable associated with this LogRecord.  When this method
+   * is called, the stack trace in a String-based format is made
+   * available via the getThrownStackTrace() method.
+   *
+   * @param thrown A Throwable to associate with this LogRecord.
+   * @see #getThrown()
+   * @see #getThrownStackTrace()
+   */
+  public void setThrown(Throwable thrown) {
+    if (thrown == null) {
+      return;
+    }
+    _thrown = thrown;
+    StringWriter sw = new StringWriter();
+    PrintWriter out = new PrintWriter(sw);
+    thrown.printStackTrace(out);
+    out.flush();
+    _thrownStackTrace = sw.toString();
+    try {
+      out.close();
+      sw.close();
+    } catch (IOException e) {
+      // Do nothing, this should not happen as it is StringWriter.
+    }
+    out = null;
+    sw = null;
+  }
+
+  /**
+   * Return a String representation of this LogRecord.
+   */
+  public String toString() {
+    StringBuffer buf = new StringBuffer();
+    buf.append("LogRecord: [" + _level + ", " + _message + "]");
+    return (buf.toString());
+  }
+
+  /**
+   * Get the NDC (nested diagnostic context) for this record.
+   *
+   * @return The string representing the NDC.
+   */
+  public String getNDC() {
+    return _ndc;
+  }
+
+  /**
+   * Set the NDC (nested diagnostic context) for this record.
+   *
+   * @param ndc A string representing the NDC.
+   */
+  public void setNDC(String ndc) {
+    _ndc = ndc;
+  }
+
+  /**
+   * Get the location in code where this LogRecord originated.
+   *
+   * @return The string containing the location information.
+   */
+  public String getLocation() {
+    return _location;
+  }
+
+  /**
+   * Set the location in code where this LogRecord originated.
+   *
+   * @param location A string containing location information.
+   */
+  public void setLocation(String location) {
+    _location = location;
+  }
+
+  /**
+   * Resets that sequence number to 0.
+   *
+   */
+  public static synchronized void resetSequenceNumber() {
+    _seqCount = 0;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected static synchronized long getNextId() {
+    _seqCount++;
+    return _seqCount;
+  }
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/LogRecordFilter.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/LogRecordFilter.java
new file mode 100644
index 0000000..25a3e53
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/LogRecordFilter.java
@@ -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.lf5;
+
+
+/**
+ * An interface for classes which filters LogRecords.  Implementations
+ * represent a rule or condition which LogRecords may pass or fail.
+ * @see LogRecord
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public interface LogRecordFilter {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * @return true if the specified LogRecord satisfies whatever condition
+   * implementing class tests for.
+   */
+  public boolean passes(LogRecord record);
+
+}
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/PassingLogRecordFilter.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/PassingLogRecordFilter.java
new file mode 100644
index 0000000..178f6cb
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/PassingLogRecordFilter.java
@@ -0,0 +1,74 @@
+/*
+ * 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.lf5;
+
+
+/**
+ * An implementation of LogRecordFilter which always returns true.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class PassingLogRecordFilter implements LogRecordFilter {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * @return true;
+   */
+  public boolean passes(LogRecord record) {
+    return true;
+  }
+
+  /**
+   * Does nothing.
+   */
+  public void reset() {
+    // do nothing
+  }
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/StartLogFactor5.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/StartLogFactor5.java
new file mode 100644
index 0000000..1ea28c3
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/StartLogFactor5.java
@@ -0,0 +1,81 @@
+/*
+ * 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.lf5;
+
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+
+/**
+ * Starts an instance of the LogFactor5 console for off-line viewing.
+ *
+ * @author Brad Marlborough
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class StartLogFactor5 {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Main - starts a an instance of the LogFactor5 console and configures
+   * the console settings.
+   */
+  public final static void main(String[] args) {
+
+    LogBrokerMonitor monitor = new LogBrokerMonitor(
+        LogLevel.getLog4JLevels());
+
+    monitor.setFrameSize(LF5Appender.getDefaultMonitorWidth(),
+        LF5Appender.getDefaultMonitorHeight());
+    monitor.setFontSize(12);
+    monitor.show();
+
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+
+}
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/util/AdapterLogRecord.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/AdapterLogRecord.java
new file mode 100644
index 0000000..2e4ee6f
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/AdapterLogRecord.java
@@ -0,0 +1,114 @@
+/*
+ * 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.lf5.util;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogRecord;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * <p>A LogRecord to be used with the LogMonitorAdapter</p>
+ *
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class AdapterLogRecord extends LogRecord {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+  private static LogLevel severeLevel = null;
+
+  private static StringWriter sw = new StringWriter();
+  private static PrintWriter pw = new PrintWriter(sw);
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  public AdapterLogRecord() {
+    super();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+  public void setCategory(String category) {
+    super.setCategory(category);
+    super.setLocation(getLocationInfo(category));
+  }
+
+  public boolean isSevereLevel() {
+    if (severeLevel == null) return false;
+    return severeLevel.equals(getLevel());
+  }
+
+  public static void setSevereLevel(LogLevel level) {
+    severeLevel = level;
+  }
+
+  public static LogLevel getSevereLevel() {
+    return severeLevel;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+  protected String getLocationInfo(String category) {
+    String stackTrace = stackTraceToString(new Throwable());
+    String line = parseLine(stackTrace, category);
+    return line;
+  }
+
+  protected String stackTraceToString(Throwable t) {
+    String s = null;
+
+    synchronized (sw) {
+      t.printStackTrace(pw);
+      s = sw.toString();
+      sw.getBuffer().setLength(0);
+    }
+
+    return s;
+  }
+
+  protected String parseLine(String trace, String category) {
+    int index = trace.indexOf(category);
+    if (index == -1) return null;
+    trace = trace.substring(index);
+    trace = trace.substring(0, trace.indexOf(")") + 1);
+    return trace;
+  }
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/util/DateFormatManager.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/DateFormatManager.java
new file mode 100644
index 0000000..6bed9cc
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/DateFormatManager.java
@@ -0,0 +1,243 @@
+/*
+ * 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.lf5.util;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Date format manager.
+ * Utility class to help manage consistent date formatting and parsing.
+ * It may be advantageous to have multiple DateFormatManagers per
+ * application.  For example, one for handling the output (formatting) of
+ * dates, and another one for handling the input (parsing) of dates.
+ *
+ * @author Robert Shaw
+ * @author Michael J. Sikorsky
+ */
+
+// Contributed by ThoughtWorks Inc.
+public class DateFormatManager {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+  private TimeZone _timeZone = null;
+  private Locale _locale = null;
+
+  private String _pattern = null;
+  private DateFormat _dateFormat = null;
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  public DateFormatManager() {
+    super();
+    configure();
+  }
+
+  public DateFormatManager(TimeZone timeZone) {
+    super();
+
+    _timeZone = timeZone;
+    configure();
+  }
+
+  public DateFormatManager(Locale locale) {
+    super();
+
+    _locale = locale;
+    configure();
+  }
+
+  public DateFormatManager(String pattern) {
+    super();
+
+    _pattern = pattern;
+    configure();
+  }
+
+  public DateFormatManager(TimeZone timeZone, Locale locale) {
+    super();
+
+    _timeZone = timeZone;
+    _locale = locale;
+    configure();
+  }
+
+  public DateFormatManager(TimeZone timeZone, String pattern) {
+    super();
+
+    _timeZone = timeZone;
+    _pattern = pattern;
+    configure();
+  }
+
+  public DateFormatManager(Locale locale, String pattern) {
+    super();
+
+    _locale = locale;
+    _pattern = pattern;
+    configure();
+  }
+
+  public DateFormatManager(TimeZone timeZone, Locale locale, String pattern) {
+    super();
+
+    _timeZone = timeZone;
+    _locale = locale;
+    _pattern = pattern;
+    configure();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public synchronized TimeZone getTimeZone() {
+    if (_timeZone == null) {
+      return TimeZone.getDefault();
+    } else {
+      return _timeZone;
+    }
+  }
+
+  public synchronized void setTimeZone(TimeZone timeZone) {
+    _timeZone = timeZone;
+    configure();
+  }
+
+  public synchronized Locale getLocale() {
+    if (_locale == null) {
+      return Locale.getDefault();
+    } else {
+      return _locale;
+    }
+  }
+
+  public synchronized void setLocale(Locale locale) {
+    _locale = locale;
+    configure();
+  }
+
+  public synchronized String getPattern() {
+    return _pattern;
+  }
+
+  /**
+   * Set the pattern. i.e. "EEEEE, MMMMM d, yyyy hh:mm aaa"
+   */
+  public synchronized void setPattern(String pattern) {
+    _pattern = pattern;
+    configure();
+  }
+
+
+  /**
+   * This method has been deprecated in favour of getPattern().
+   * @deprecated Use getPattern().
+   */
+  public synchronized String getOutputFormat() {
+    return _pattern;
+  }
+
+  /**
+   * This method has been deprecated in favour of setPattern().
+   * @deprecated Use setPattern().
+   */
+  public synchronized void setOutputFormat(String pattern) {
+    _pattern = pattern;
+    configure();
+  }
+
+  public synchronized DateFormat getDateFormatInstance() {
+    return _dateFormat;
+  }
+
+  public synchronized void setDateFormatInstance(DateFormat dateFormat) {
+    _dateFormat = dateFormat;
+    // No reconfiguration necessary!
+  }
+
+  public String format(Date date) {
+    return getDateFormatInstance().format(date);
+  }
+
+  public String format(Date date, String pattern) {
+    DateFormat formatter = null;
+    formatter = getDateFormatInstance();
+    if (formatter instanceof SimpleDateFormat) {
+      formatter = (SimpleDateFormat) (formatter.clone());
+      ((SimpleDateFormat) formatter).applyPattern(pattern);
+    }
+    return formatter.format(date);
+  }
+
+  /**
+   * @throws java.text.ParseException
+   */
+  public Date parse(String date) throws ParseException {
+    return getDateFormatInstance().parse(date);
+  }
+
+  /**
+   * @throws java.text.ParseException
+   */
+  public Date parse(String date, String pattern) throws ParseException {
+    DateFormat formatter = null;
+    formatter = getDateFormatInstance();
+    if (formatter instanceof SimpleDateFormat) {
+      formatter = (SimpleDateFormat) (formatter.clone());
+      ((SimpleDateFormat) formatter).applyPattern(pattern);
+    }
+    return formatter.parse(date);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+  private synchronized void configure() {
+    _dateFormat = SimpleDateFormat.getDateTimeInstance(DateFormat.FULL,
+        DateFormat.FULL,
+        getLocale());
+    _dateFormat.setTimeZone(getTimeZone());
+
+    if (_pattern != null) {
+      ((SimpleDateFormat) _dateFormat).applyPattern(_pattern);
+    }
+  }
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/util/LogFileParser.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/LogFileParser.java
new file mode 100644
index 0000000..656d4b6
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/LogFileParser.java
@@ -0,0 +1,301 @@
+/*
+ * 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.lf5.util;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.swing.SwingUtilities;
+
+import org.apache.log4j.lf5.Log4JLogRecord;
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogLevelFormatException;
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+import org.apache.log4j.lf5.viewer.LogFactor5ErrorDialog;
+import org.apache.log4j.lf5.viewer.LogFactor5LoadingDialog;
+
+/**
+ * Provides utility methods for input and output streams.
+ *
+ * @author Brad Marlborough
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogFileParser implements Runnable {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+  public static final String RECORD_DELIMITER = "[slf5s.start]";
+  public static final String ATTRIBUTE_DELIMITER = "[slf5s.";
+  public static final String DATE_DELIMITER = ATTRIBUTE_DELIMITER + "DATE]";
+  public static final String THREAD_DELIMITER = ATTRIBUTE_DELIMITER + "THREAD]";
+  public static final String CATEGORY_DELIMITER = ATTRIBUTE_DELIMITER + "CATEGORY]";
+  public static final String LOCATION_DELIMITER = ATTRIBUTE_DELIMITER + "LOCATION]";
+  public static final String MESSAGE_DELIMITER = ATTRIBUTE_DELIMITER + "MESSAGE]";
+  public static final String PRIORITY_DELIMITER = ATTRIBUTE_DELIMITER + "PRIORITY]";
+  public static final String NDC_DELIMITER = ATTRIBUTE_DELIMITER + "NDC]";
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+  private static SimpleDateFormat _sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss,S");
+  private LogBrokerMonitor _monitor;
+  LogFactor5LoadingDialog _loadDialog;
+  private InputStream _in = null;
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  public LogFileParser(File file) throws IOException,
+      FileNotFoundException {
+    this(new FileInputStream(file));
+  }
+
+  public LogFileParser(InputStream stream) throws IOException {
+    _in = stream;
+  }
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Starts a new thread to parse the log file and create a LogRecord.
+   * See run().
+   * @param monitor LogBrokerMonitor
+   */
+  public void parse(LogBrokerMonitor monitor) throws RuntimeException {
+    _monitor = monitor;
+    Thread t = new Thread(this);
+    t.start();
+  }
+
+  /**
+   * Parses the file and creates new log records and adds the record
+   * to the monitor.
+   */
+  public void run() {
+
+    int index = 0;
+    int counter = 0;
+    LogRecord temp;
+    boolean isLogFile = false;
+
+    _loadDialog = new LogFactor5LoadingDialog(
+        _monitor.getBaseFrame(), "Loading file...");
+
+
+    try {
+      String logRecords = loadLogFile(_in);
+
+      while ((counter = logRecords.indexOf(RECORD_DELIMITER, index)) != -1) {
+        temp = createLogRecord(logRecords.substring(index, counter));
+        isLogFile = true;
+
+        if (temp != null) {
+          _monitor.addMessage(temp);
+        }
+
+        index = counter + RECORD_DELIMITER.length();
+      }
+
+      if (index < logRecords.length() && isLogFile) {
+        temp = createLogRecord(logRecords.substring(index));
+
+        if (temp != null) {
+          _monitor.addMessage(temp);
+        }
+      }
+
+      if (isLogFile == false) {
+        throw new RuntimeException("Invalid log file format");
+      }
+      SwingUtilities.invokeLater(new Runnable() {
+        public void run() {
+          destroyDialog();
+        }
+      });
+
+    } catch (RuntimeException e) {
+      destroyDialog();
+      displayError("Error - Invalid log file format.\nPlease see documentation"
+          + " on how to load log files.");
+    } catch (IOException e) {
+      destroyDialog();
+      displayError("Error - Unable to load log file!");
+    }
+
+    _in = null;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+  protected void displayError(String message) {
+    LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+        _monitor.getBaseFrame(), message);
+
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+  private void destroyDialog() {
+    _loadDialog.hide();
+    _loadDialog.dispose();
+  }
+
+  /**
+   * Loads a log file from a web server into the LogFactor5 GUI.
+   */
+  private String loadLogFile(InputStream stream) throws IOException {
+    BufferedInputStream br = new BufferedInputStream(stream);
+
+    int count = 0;
+    int size = br.available();
+
+    StringBuffer sb = null;
+    if (size > 0) {
+      sb = new StringBuffer(size);
+    } else {
+      sb = new StringBuffer(1024);
+    }
+
+    while ((count = br.read()) != -1) {
+      sb.append((char) count);
+    }
+
+    br.close();
+    br = null;
+    return sb.toString();
+
+  }
+
+  private String parseAttribute(String name, String record) {
+
+    int index = record.indexOf(name);
+
+    if (index == -1) {
+      return null;
+    }
+
+    return getAttribute(index, record);
+  }
+
+  private long parseDate(String record) {
+    try {
+      String s = parseAttribute(DATE_DELIMITER, record);
+
+      if (s == null) {
+        return 0;
+      }
+
+      Date d = _sdf.parse(s);
+
+      return d.getTime();
+    } catch (ParseException e) {
+      return 0;
+    }
+  }
+
+  private LogLevel parsePriority(String record) {
+    String temp = parseAttribute(PRIORITY_DELIMITER, record);
+
+    if (temp != null) {
+      try {
+        return LogLevel.valueOf(temp);
+      } catch (LogLevelFormatException e) {
+        return LogLevel.DEBUG;
+      }
+
+    }
+
+    return LogLevel.DEBUG;
+  }
+
+  private String parseThread(String record) {
+    return parseAttribute(THREAD_DELIMITER, record);
+  }
+
+  private String parseCategory(String record) {
+    return parseAttribute(CATEGORY_DELIMITER, record);
+  }
+
+  private String parseLocation(String record) {
+    return parseAttribute(LOCATION_DELIMITER, record);
+  }
+
+  private String parseMessage(String record) {
+    return parseAttribute(MESSAGE_DELIMITER, record);
+  }
+
+  private String parseNDC(String record) {
+    return parseAttribute(NDC_DELIMITER, record);
+  }
+
+  private String parseThrowable(String record) {
+    return getAttribute(record.length(), record);
+  }
+
+  private LogRecord createLogRecord(String record) {
+    if (record == null || record.trim().length() == 0) {
+      return null;
+    }
+
+    LogRecord lr = new Log4JLogRecord();
+    lr.setMillis(parseDate(record));
+    lr.setLevel(parsePriority(record));
+    lr.setCategory(parseCategory(record));
+    lr.setLocation(parseLocation(record));
+    lr.setThreadDescription(parseThread(record));
+    lr.setNDC(parseNDC(record));
+    lr.setMessage(parseMessage(record));
+    lr.setThrownStackTrace(parseThrowable(record));
+
+    return lr;
+  }
+
+
+  private String getAttribute(int index, String record) {
+    int start = record.lastIndexOf(ATTRIBUTE_DELIMITER, index - 1);
+
+    if (start == -1) {
+      return record.substring(0, index);
+    }
+
+    start = record.indexOf("]", start);
+
+    return record.substring(start + 1, index).trim();
+  }
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+
+}
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/util/LogMonitorAdapter.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/LogMonitorAdapter.java
new file mode 100644
index 0000000..6ba927c
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/LogMonitorAdapter.java
@@ -0,0 +1,289 @@
+/*
+ * 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.lf5.util;
+
+import java.awt.Toolkit;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+
+/**
+ * <p>LogMonitorAdapter facilitates the usage of the LogMonitor</p>
+ *
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogMonitorAdapter {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+  public static final int LOG4J_LOG_LEVELS = 0;
+  public static final int JDK14_LOG_LEVELS = 1;
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+  private LogBrokerMonitor _logMonitor;
+  private LogLevel _defaultLevel = null;
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  private LogMonitorAdapter(List userDefinedLevels) {
+    super();
+    // set the default level to be the first entry in the list
+    _defaultLevel = (LogLevel) userDefinedLevels.get(0);
+    _logMonitor = new LogBrokerMonitor(userDefinedLevels);
+
+    _logMonitor.setFrameSize(getDefaultMonitorWidth(),
+        getDefaultMonitorHeight());
+    _logMonitor.setFontSize(12);
+    _logMonitor.show();
+  }
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+  /**
+   * <p>Creates an instance of LogMonitorAdapter using the
+   * log levels inticated by the parameter. Log4J and JDK1.4 both have default
+   * LogLevels which are set but these levels can be overriden.<p>
+   *
+   * @param loglevels An integer representing either Log4J or JDK1.4 logging levels
+   * @return LogMonitorAdapter
+   */
+  public static LogMonitorAdapter newInstance(int loglevels) {
+    LogMonitorAdapter adapter;
+    if (loglevels == JDK14_LOG_LEVELS) {
+      adapter = newInstance(LogLevel.getJdk14Levels());
+      adapter.setDefaultLevel(LogLevel.FINEST);
+      adapter.setSevereLevel(LogLevel.SEVERE);
+    } else {
+      adapter = newInstance(LogLevel.getLog4JLevels());
+      adapter.setDefaultLevel(LogLevel.DEBUG);
+      adapter.setSevereLevel(LogLevel.FATAL);
+    }
+    return adapter;
+  }
+
+  /**
+   * <p>Creates an instance of LogMonitorAdapter using the specified LogLevels.
+   * The first LogLevel in the array is used as the default LogLevel unless
+   * changed using the setDefaultLevel method.<p>
+   *
+   * @param userDefined An array of user defined LogLevel objects.
+   * @return LogMonitorAdapter
+   */
+  public static LogMonitorAdapter newInstance(LogLevel[] userDefined) {
+    if (userDefined == null) {
+      return null;
+    }
+    return newInstance(Arrays.asList(userDefined));
+  }
+
+  /**
+   * <p>Creates an instance of LogMonitorAdapter using the specified LogLevels.
+   * The first LogLevel in the List is used as the default LogLevel unless
+   * changed using the setDefaultLevel method.<p>
+   *
+   * @param userDefinedLevels A list of user defined LogLevel objects.
+   * @return LogMonitorAdapter
+   */
+  public static LogMonitorAdapter newInstance(List userDefinedLevels) {
+    return new LogMonitorAdapter(userDefinedLevels);
+  }
+
+  /**
+   * <p>Adds a LogRecord to the LogMonitor.<p>
+   *
+   * @param record The LogRecord object to be logged in the logging monitor.
+   */
+  public void addMessage(LogRecord record) {
+    _logMonitor.addMessage(record);
+  }
+
+  /**
+   * <p>Set the maximum number of records to be displayed in the monitor<p>
+   *
+   * @param maxNumberOfRecords
+   */
+  public void setMaxNumberOfRecords(int maxNumberOfRecords) {
+    _logMonitor.setMaxNumberOfLogRecords(maxNumberOfRecords);
+  }
+
+  /**
+   * <p>Set the default log level to be used when logging messages without
+   * specifying a LogLevel.<p>
+   *
+   * @param level
+   */
+  public void setDefaultLevel(LogLevel level) {
+    _defaultLevel = level;
+  }
+
+  /**
+   * <p>Gets the default LogLevel for the Adapter.<p>
+   *
+   * @return LogLevel
+   */
+  public LogLevel getDefaultLevel() {
+    return _defaultLevel;
+  }
+
+  /**
+   * <p>Sets the Severe LogLevel.</p>
+   *
+   * @param level
+   */
+  public void setSevereLevel(LogLevel level) {
+    AdapterLogRecord.setSevereLevel(level);
+  }
+
+  /**
+   * <p>Gets the current Severe LogLevel <p>
+   *
+   * @return LogLevel
+   */
+  public LogLevel getSevereLevel() {
+    return AdapterLogRecord.getSevereLevel();
+  }
+
+  /**
+   * <p>Log a complete message to the Monitor.<p>
+   *
+   * @param category The category to be used
+   * @param level The log level to apply to the message
+   * @param message The message
+   * @param t The throwable content of the message
+   * @param NDC The NDC really only applies to Log4J and the parameter can
+   *            usually be ignored.
+   */
+  public void log(String category, LogLevel level, String message,
+      Throwable t, String NDC) {
+    AdapterLogRecord record = new AdapterLogRecord();
+    record.setCategory(category);
+    record.setMessage(message);
+    record.setNDC(NDC);
+    record.setThrown(t);
+
+    if (level == null) {
+      record.setLevel(getDefaultLevel());
+    } else {
+      record.setLevel(level);
+    }
+
+    addMessage(record);
+  }
+
+  /**
+   * <p>Log a message to the Monitor and use the default LogLevel.<p>
+   *
+   * @param category The category to be used
+   * @param message The message
+   */
+  public void log(String category, String message) {
+    log(category, null, message);
+  }
+
+  /**
+   * <p>Log a message to the Monitor.<p>
+   *
+   * @param category The category to be used
+   * @param level The log level to apply to the message
+   * @param message The message
+   * @param NDC
+   */
+  public void log(String category, LogLevel level, String message, String NDC) {
+    log(category, level, message, null, NDC);
+  }
+
+  /**
+   * <p>Log a message to the Monitor.<p>
+   *
+   * @param category The category to be used
+   * @param level The log level to apply to the message
+   * @param message The message
+   * @param t The throwable content of the message
+   */
+  public void log(String category, LogLevel level, String message,
+      Throwable t) {
+    log(category, level, message, t, null);
+  }
+
+  /**
+   * <p>Log a message to the Monitor.<p>
+   *
+   * @param category The category to be used
+   * @param level The log level to apply to the message
+   * @param message The message
+   */
+  public void log(String category, LogLevel level, String message) {
+    log(category, level, message, null, null);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+  /**
+   * @return the screen width from Toolkit.getScreenSize()
+   * if possible, otherwise returns 800
+   * @see java.awt.Toolkit
+   */
+  protected static int getScreenWidth() {
+    try {
+      return Toolkit.getDefaultToolkit().getScreenSize().width;
+    } catch (Throwable t) {
+      return 800;
+    }
+  }
+
+  /**
+   * @return the screen height from Toolkit.getScreenSize()
+   * if possible, otherwise returns 600
+   * @see java.awt.Toolkit
+   */
+  protected static int getScreenHeight() {
+    try {
+      return Toolkit.getDefaultToolkit().getScreenSize().height;
+    } catch (Throwable t) {
+      return 600;
+    }
+  }
+
+  protected static int getDefaultMonitorWidth() {
+    return (3 * getScreenWidth()) / 4;
+  }
+
+  protected static int getDefaultMonitorHeight() {
+    return (3 * getScreenHeight()) / 4;
+  }
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/util/Resource.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/Resource.java
new file mode 100644
index 0000000..66293cf
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/Resource.java
@@ -0,0 +1,156 @@
+/*
+ * 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.lf5.util;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+
+/**
+ * Resource encapsulates access to Resources via the Classloader.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class Resource {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected String _name;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Default, no argument constructor.
+   */
+  public Resource() {
+    super();
+  }
+
+  /**
+   * Construct a Resource given a name.
+   *
+   * @see #setName(String)
+   */
+  public Resource(String name) {
+    _name = name;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Set the name of the resource.
+   * <p>
+   * A resource is some data (images, audio, text, etc) that can be accessed
+   * by class code in a way that is independent of the location of the code.
+   * </p>
+   * <p>
+   * The name of a resource is a "/"-separated path name that identifies
+   * the resource.
+   * </p>
+   *
+   * @see #getName()
+   */
+  public void setName(String name) {
+    _name = name;
+  }
+
+  /**
+   * Get the name of the resource.  Set setName() for a description of
+   * a resource.
+   *
+   * @see #setName
+   */
+  public String getName() {
+    return (_name);
+  }
+
+  /**
+   * Get the InputStream for this Resource.  Uses the classloader
+   * from this Resource.
+   *
+   * @see #getInputStreamReader
+   * @see ResourceUtils
+   */
+  public InputStream getInputStream() {
+    InputStream in = ResourceUtils.getResourceAsStream(this, this);
+
+    return (in);
+  }
+
+  /**
+   * Get the InputStreamReader for this Resource. Uses the classloader from
+   * this Resource.
+   *
+   * @see #getInputStream
+   * @see ResourceUtils
+   */
+  public InputStreamReader getInputStreamReader() {
+    InputStream in = ResourceUtils.getResourceAsStream(this, this);
+
+    if (in == null) {
+      return null;
+    }
+
+    InputStreamReader reader = new InputStreamReader(in);
+
+    return reader;
+  }
+
+  /**
+   * Get the URL of the Resource.  Uses the classloader from this Resource.
+   *
+   * @see ResourceUtils
+   */
+  public URL getURL() {
+    return (ResourceUtils.getResourceAsURL(this, this));
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/util/ResourceUtils.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/ResourceUtils.java
new file mode 100644
index 0000000..5f022b6
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/ResourceUtils.java
@@ -0,0 +1,133 @@
+/*
+ * 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.lf5.util;
+
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * ResourceUtils.  Provide a set of convenience methods for working with
+ * Resources.
+ *
+ * @see org.apache.log4j.lf5.util.Resource
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class ResourceUtils {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Get the InputStream for this resource.  Note: to convert an InputStream
+   * into an InputReader, use: new InputStreamReader(InputStream).
+   *
+   * @param object   The object to grab the Classloader from.
+   *                 This parameter is quite important from a
+   *                 visibility of resources standpoint as the
+   *                 hierarchy of Classloaders plays a role.
+   *
+   * @param resource The resource to load.
+   *
+   * @return If the Resource was found, the InputStream, otherwise null.
+   *
+   * @see Resource
+   * @see #getResourceAsURL(Object,Resource)
+   * @see InputStream
+   */
+  public static InputStream getResourceAsStream(Object object, Resource resource) {
+    ClassLoader loader = object.getClass().getClassLoader();
+
+    InputStream in = null;
+
+    if (loader != null) {
+      in = loader.getResourceAsStream(resource.getName());
+    } else {
+      in = ClassLoader.getSystemResourceAsStream(resource.getName());
+    }
+
+    return in;
+  }
+
+  /**
+   * Get the URL for this resource.
+   *
+   * @param object   The object to grab the Classloader from.
+   *                 This parameter is quite important from a
+   *                 visibility of resources standpoint as the
+   *                 hierarchy of Classloaders plays a role.
+   *
+   * @param resource The resource to load.
+   *
+   * @return If the Resource was found, the URL, otherwise null.
+   *
+   * @see Resource
+   * @see #getResourceAsStream(Object,Resource)
+   */
+  public static URL getResourceAsURL(Object object, Resource resource) {
+    ClassLoader loader = object.getClass().getClassLoader();
+
+    URL url = null;
+
+    if (loader != null) {
+      url = loader.getResource(resource.getName());
+    } else {
+      url = ClassLoader.getSystemResource(resource.getName());
+    }
+
+    return (url);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/util/StreamUtils.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/StreamUtils.java
new file mode 100644
index 0000000..183f9aa
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/util/StreamUtils.java
@@ -0,0 +1,124 @@
+/*
+ * 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.lf5.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Provides utility methods for input and output streams.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public abstract class StreamUtils {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Default value is 2048.
+   */
+  public static final int DEFAULT_BUFFER_SIZE = 2048;
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Copies information from the input stream to the output stream using
+   * a default buffer size of 2048 bytes.
+   * @throws java.io.IOException
+   */
+  public static void copy(InputStream input, OutputStream output)
+      throws IOException {
+    copy(input, output, DEFAULT_BUFFER_SIZE);
+  }
+
+  /**
+   * Copies information from the input stream to the output stream using
+   * the specified buffer size
+   * @throws java.io.IOException
+   */
+  public static void copy(InputStream input,
+      OutputStream output,
+      int bufferSize)
+      throws IOException {
+    byte[] buf = new byte[bufferSize];
+    int bytesRead = input.read(buf);
+    while (bytesRead != -1) {
+      output.write(buf, 0, bytesRead);
+      bytesRead = input.read(buf);
+    }
+    output.flush();
+  }
+
+  /**
+   * Copies information between specified streams and then closes
+   * both of the streams.
+   * @throws java.io.IOException
+   */
+  public static void copyThenClose(InputStream input, OutputStream output)
+      throws IOException {
+    copy(input, output);
+    input.close();
+    output.close();
+  }
+
+  /**
+   * @return a byte[] containing the information contained in the
+   * specified InputStream.
+   * @throws java.io.IOException
+   */
+  public static byte[] getBytes(InputStream input)
+      throws IOException {
+    ByteArrayOutputStream result = new ByteArrayOutputStream();
+    copy(input, result);
+    result.close();
+    return result.toByteArray();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+
+}
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java
new file mode 100644
index 0000000..2c68180
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java
@@ -0,0 +1,263 @@
+/*
+ * 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.lf5.viewer;
+
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.LogRecordFilter;
+import org.apache.log4j.lf5.PassingLogRecordFilter;
+
+import javax.swing.table.AbstractTableModel;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * A TableModel for LogRecords which includes filtering support.
+ *
+ * @author Richard Wan
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class FilteredLogTableModel
+    extends AbstractTableModel {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  protected LogRecordFilter _filter = new PassingLogRecordFilter();
+  protected List _allRecords = new ArrayList();
+  protected List _filteredRecords;
+  protected int _maxNumberOfLogRecords = 5000;
+  protected String[] _colNames = {"Date",
+                                  "Thread",
+                                  "Message #",
+                                  "Level",
+                                  "NDC",
+                                  "Category",
+                                  "Message",
+                                  "Location",
+                                  "Thrown"};
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public FilteredLogTableModel() {
+    super();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public void setLogRecordFilter(LogRecordFilter filter) {
+    _filter = filter;
+  }
+
+  public LogRecordFilter getLogRecordFilter() {
+    return _filter;
+  }
+
+  public String getColumnName(int i) {
+    return _colNames[i];
+  }
+
+  public int getColumnCount() {
+    return _colNames.length;
+  }
+
+  public int getRowCount() {
+    return getFilteredRecords().size();
+  }
+
+  public int getTotalRowCount() {
+    return _allRecords.size();
+  }
+
+  public Object getValueAt(int row, int col) {
+    LogRecord record = getFilteredRecord(row);
+    return getColumn(col, record);
+  }
+
+  public void setMaxNumberOfLogRecords(int maxNumRecords) {
+    if (maxNumRecords > 0) {
+      _maxNumberOfLogRecords = maxNumRecords;
+    }
+
+  }
+
+  public synchronized boolean addLogRecord(LogRecord record) {
+
+    _allRecords.add(record);
+
+    if (_filter.passes(record) == false) {
+      return false;
+    }
+    getFilteredRecords().add(record);
+    fireTableRowsInserted(getRowCount(), getRowCount());
+    trimRecords();
+    return true;
+  }
+
+  /**
+   * Forces the LogTableModel to requery its filters to determine
+   * which records to display.
+   */
+  public synchronized void refresh() {
+    _filteredRecords = createFilteredRecordsList();
+    fireTableDataChanged();
+  }
+
+  public synchronized void fastRefresh() {
+    _filteredRecords.remove(0);
+    fireTableRowsDeleted(0, 0);
+  }
+
+
+  /**
+   * Clears all records from the LogTableModel
+   */
+  public synchronized void clear() {
+    _allRecords.clear();
+    _filteredRecords.clear();
+    fireTableDataChanged();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected List getFilteredRecords() {
+    if (_filteredRecords == null) {
+      refresh();
+    }
+    return _filteredRecords;
+  }
+
+  protected List createFilteredRecordsList() {
+    List result = new ArrayList();
+    Iterator records = _allRecords.iterator();
+    LogRecord current;
+    while (records.hasNext()) {
+      current = (LogRecord) records.next();
+      if (_filter.passes(current)) {
+        result.add(current);
+      }
+    }
+    return result;
+  }
+
+  protected LogRecord getFilteredRecord(int row) {
+    List records = getFilteredRecords();
+    int size = records.size();
+    if (row < size) {
+      return (LogRecord) records.get(row);
+    }
+    // a minor problem has happened. JTable has asked for
+    // a row outside the bounds, because the size of
+    // _filteredRecords has changed while it was looping.
+    // return the last row.
+    return (LogRecord) records.get(size - 1);
+
+  }
+
+  protected Object getColumn(int col, LogRecord lr) {
+    if (lr == null) {
+      return "NULL Column";
+    }
+    String date = new Date(lr.getMillis()).toString();
+    switch (col) {
+      case 0:
+        return date + " (" + lr.getMillis() + ")";
+      case 1:
+        return lr.getThreadDescription();
+      case 2:
+        return new Long(lr.getSequenceNumber());
+      case 3:
+        return lr.getLevel();
+      case 4:
+        return lr.getNDC();
+      case 5:
+        return lr.getCategory();
+      case 6:
+        return lr.getMessage();
+      case 7:
+        return lr.getLocation();
+      case 8:
+        return lr.getThrownStackTrace();
+      default:
+        String message = "The column number " + col + "must be between 0 and 8";
+        throw new IllegalArgumentException(message);
+    }
+  }
+
+  // We don't want the amount of rows to grow without bound,
+  // leading to a out-of-memory-exception.  Especially not good
+  // in a production environment :)
+
+  // This method & clearLogRecords() are synchronized so we don't
+  // delete rows that don't exist.
+  protected void trimRecords() {
+    if (needsTrimming()) {
+      trimOldestRecords();
+    }
+  }
+
+  protected boolean needsTrimming() {
+    return (_allRecords.size() > _maxNumberOfLogRecords);
+  }
+
+  protected void trimOldestRecords() {
+    synchronized (_allRecords) {
+      int trim = numberOfRecordsToTrim();
+      if (trim > 1) {
+        List oldRecords =
+            _allRecords.subList(0, trim);
+        oldRecords.clear();
+        refresh();
+      } else {
+        _allRecords.remove(0);
+        fastRefresh();
+      }
+    }
+
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+  private int numberOfRecordsToTrim() {
+    return _allRecords.size() - _maxNumberOfLogRecords;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LF5SwingUtils.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LF5SwingUtils.java
new file mode 100644
index 0000000..ef29447
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LF5SwingUtils.java
@@ -0,0 +1,153 @@
+/*
+ * 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.lf5.viewer;
+
+import java.awt.Adjustable;
+
+import javax.swing.JComponent;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.table.TableModel;
+
+/**
+ * Provides methods to accomplish common yet non-trivial tasks
+ * with Swing. Obvious implementations of these methods have been
+ * tried and failed.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LF5SwingUtils {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Selects a the specified row in the specified JTable and scrolls
+   * the specified JScrollpane to the newly selected row. More importantly,
+   * the call to repaint() delayed long enough to have the table
+   * properly paint the newly selected row which may be offscre
+   * @param table should belong to the specified JScrollPane
+   */
+  public static void selectRow(int row, JTable table, JScrollPane pane) {
+    if (table == null || pane == null) {
+      return;
+    }
+    if (contains(row, table.getModel()) == false) {
+      return;
+    }
+    moveAdjustable(row * table.getRowHeight(), pane.getVerticalScrollBar());
+    selectRow(row, table.getSelectionModel());
+    // repaint must be done later because moveAdjustable
+    // posts requests to the swing thread which must execute before
+    // the repaint logic gets executed.
+    repaintLater(table);
+  }
+
+  /**
+   * Makes the specified Adjustable track if the view area expands and
+   * the specified Adjustable is located near the of the view.
+   */
+  public static void makeScrollBarTrack(Adjustable scrollBar) {
+    if (scrollBar == null) {
+      return;
+    }
+    scrollBar.addAdjustmentListener(new TrackingAdjustmentListener());
+  }
+
+  /**
+   * Makes the vertical scroll bar of the specified JScrollPane
+   * track if the view expands (e.g. if rows are added to an underlying
+   * table).
+   */
+  public static void makeVerticalScrollBarTrack(JScrollPane pane) {
+    if (pane == null) {
+      return;
+    }
+    makeScrollBarTrack(pane.getVerticalScrollBar());
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+  protected static boolean contains(int row, TableModel model) {
+    if (model == null) {
+      return false;
+    }
+    if (row < 0) {
+      return false;
+    }
+    if (row >= model.getRowCount()) {
+      return false;
+    }
+    return true;
+  }
+
+  protected static void selectRow(int row, ListSelectionModel model) {
+    if (model == null) {
+      return;
+    }
+    model.setSelectionInterval(row, row);
+  }
+
+  protected static void moveAdjustable(int location, Adjustable scrollBar) {
+    if (scrollBar == null) {
+      return;
+    }
+    scrollBar.setValue(location);
+  }
+
+  /**
+   * Work around for JTable/viewport bug.
+   * @link http://developer.java.sun.com/developer/bugParade/bugs/4205145.html
+   */
+  protected static void repaintLater(final JComponent component) {
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run() {
+        component.repaint();
+      }
+    });
+  }
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java
new file mode 100644
index 0000000..934ba17
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java
@@ -0,0 +1,1612 @@
+/*
+ * 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.lf5.viewer;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GraphicsEnvironment;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JColorChooser;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTextArea;
+import javax.swing.JToolBar;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.LogRecordFilter;
+import org.apache.log4j.lf5.util.DateFormatManager;
+import org.apache.log4j.lf5.util.LogFileParser;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerTree;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryPath;
+import org.apache.log4j.lf5.viewer.configure.ConfigurationManager;
+import org.apache.log4j.lf5.viewer.configure.MRUFileManager;
+
+/**
+ * LogBrokerMonitor
+ *.
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brad Marlborough
+ * @author Richard Wan
+ * @author Brent Sprecher
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogBrokerMonitor {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  public static final String DETAILED_VIEW = "Detailed";
+//    public static final String STANDARD_VIEW = "Standard";
+//    public static final String COMPACT_VIEW = "Compact";
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected JFrame _logMonitorFrame;
+  protected int _logMonitorFrameWidth = 550;
+  protected int _logMonitorFrameHeight = 500;
+  protected LogTable _table;
+  protected CategoryExplorerTree _categoryExplorerTree;
+  protected String _searchText;
+  protected String _NDCTextFilter = "";
+  protected LogLevel _leastSevereDisplayedLogLevel = LogLevel.DEBUG;
+
+  protected JScrollPane _logTableScrollPane;
+  protected JLabel _statusLabel;
+  protected Object _lock = new Object();
+  protected JComboBox _fontSizeCombo;
+
+  protected int _fontSize = 10;
+  protected String _fontName = "Dialog";
+  protected String _currentView = DETAILED_VIEW;
+
+  protected boolean _loadSystemFonts = false;
+  protected boolean _trackTableScrollPane = true;
+  protected Dimension _lastTableViewportSize;
+  protected boolean _callSystemExitOnClose = false;
+  protected List _displayedLogBrokerProperties = new Vector();
+
+  protected Map _logLevelMenuItems = new HashMap();
+  protected Map _logTableColumnMenuItems = new HashMap();
+
+  protected List _levels = null;
+  protected List _columns = null;
+  protected boolean _isDisposed = false;
+
+  protected ConfigurationManager _configurationManager = null;
+  protected MRUFileManager _mruFileManager = null;
+  protected File _fileLocation = null;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Construct a LogBrokerMonitor.
+   */
+  public LogBrokerMonitor(List logLevels) {
+
+    _levels = logLevels;
+    _columns = LogTableColumn.getLogTableColumns();
+    // This allows us to use the LogBroker in command line tools and
+    // have the option for it to shutdown.
+
+    String callSystemExitOnClose =
+        System.getProperty("monitor.exit");
+    if (callSystemExitOnClose == null) {
+      callSystemExitOnClose = "false";
+    }
+    callSystemExitOnClose = callSystemExitOnClose.trim().toLowerCase();
+
+    if (callSystemExitOnClose.equals("true")) {
+      _callSystemExitOnClose = true;
+    }
+
+    initComponents();
+
+
+    _logMonitorFrame.addWindowListener(
+        new LogBrokerMonitorWindowAdaptor(this));
+
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Show the frame for the LogBrokerMonitor. Dispatched to the
+   * swing thread.
+   */
+  public void show(final int delay) {
+    if (_logMonitorFrame.isVisible()) {
+      return;
+    }
+    // This request is very low priority, let other threads execute first.
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run() {
+        Thread.yield();
+        pause(delay);
+        _logMonitorFrame.setVisible(true);
+      }
+    });
+  }
+
+  public void show() {
+    show(0);
+  }
+
+  /**
+   * Dispose of the frame for the LogBrokerMonitor.
+   */
+  public void dispose() {
+    _logMonitorFrame.dispose();
+    _isDisposed = true;
+
+    if (_callSystemExitOnClose == true) {
+      System.exit(0);
+    }
+  }
+
+  /**
+   * Hide the frame for the LogBrokerMonitor.
+   */
+  public void hide() {
+    _logMonitorFrame.setVisible(false);
+  }
+
+  /**
+   * Get the DateFormatManager for formatting dates.
+   */
+  public DateFormatManager getDateFormatManager() {
+    return _table.getDateFormatManager();
+  }
+
+  /**
+   * Set the date format manager for formatting dates.
+   */
+  public void setDateFormatManager(DateFormatManager dfm) {
+    _table.setDateFormatManager(dfm);
+  }
+
+  /**
+   * Get the value of whether or not System.exit() will be called
+   * when the LogBrokerMonitor is closed.
+   */
+  public boolean getCallSystemExitOnClose() {
+    return _callSystemExitOnClose;
+  }
+
+  /**
+   * Set the value of whether or not System.exit() will be called
+   * when the LogBrokerMonitor is closed.
+   */
+  public void setCallSystemExitOnClose(boolean callSystemExitOnClose) {
+    _callSystemExitOnClose = callSystemExitOnClose;
+  }
+
+  /**
+   * Add a log record message to be displayed in the LogTable.
+   * This method is thread-safe as it posts requests to the SwingThread
+   * rather than processing directly.
+   */
+  public void addMessage(final LogRecord lr) {
+    if (_isDisposed == true) {
+      // If the frame has been disposed of, do not log any more
+      // messages.
+      return;
+    }
+
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run() {
+        _categoryExplorerTree.getExplorerModel().addLogRecord(lr);
+        _table.getFilteredLogTableModel().addLogRecord(lr); // update table
+        updateStatusLabel(); // show updated counts
+      }
+    });
+  }
+
+  public void setMaxNumberOfLogRecords(int maxNumberOfLogRecords) {
+    _table.getFilteredLogTableModel().setMaxNumberOfLogRecords(maxNumberOfLogRecords);
+  }
+
+  public JFrame getBaseFrame() {
+    return _logMonitorFrame;
+  }
+
+  public void setTitle(String title) {
+    _logMonitorFrame.setTitle(title + " - LogFactor5");
+  }
+
+  public void setFrameSize(int width, int height) {
+    Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
+    if (0 < width && width < screen.width) {
+      _logMonitorFrameWidth = width;
+    }
+    if (0 < height && height < screen.height) {
+      _logMonitorFrameHeight = height;
+    }
+    updateFrameSize();
+  }
+
+  public void setFontSize(int fontSize) {
+    changeFontSizeCombo(_fontSizeCombo, fontSize);
+    // setFontSizeSilently(actualFontSize); - changeFontSizeCombo fires event
+    // refreshDetailTextArea();
+  }
+
+  public void addDisplayedProperty(Object messageLine) {
+    _displayedLogBrokerProperties.add(messageLine);
+  }
+
+  public Map getLogLevelMenuItems() {
+    return _logLevelMenuItems;
+  }
+
+  public Map getLogTableColumnMenuItems() {
+    return _logTableColumnMenuItems;
+  }
+
+  public JCheckBoxMenuItem getTableColumnMenuItem(LogTableColumn column) {
+    return getLogTableColumnMenuItem(column);
+  }
+
+  public CategoryExplorerTree getCategoryExplorerTree() {
+    return _categoryExplorerTree;
+  }
+
+  // Added in version 1.2 - gets the value of the NDC text filter
+  // This value is set back to null each time the Monitor is initialized.
+  public String getNDCTextFilter() {
+    return _NDCTextFilter;
+  }
+
+  // Added in version 1.2 - sets the NDC Filter based on
+  // a String passed in by the user.  This value is persisted
+  // in the XML Configuration file.
+  public void setNDCLogRecordFilter(String textFilter) {
+    _table.getFilteredLogTableModel().
+        setLogRecordFilter(createNDCLogRecordFilter(textFilter));
+  }
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected void setSearchText(String text) {
+    _searchText = text;
+  }
+
+  // Added in version 1.2 - Sets the text filter for the NDC
+  protected void setNDCTextFilter(String text) {
+    // if no value is set, set it to a blank string
+    // otherwise use the value provided
+    if (text == null) {
+      _NDCTextFilter = "";
+    } else {
+      _NDCTextFilter = text;
+    }
+  }
+
+  // Added in version 1.2 - Uses a different filter that sorts
+  // based on an NDC string passed in by the user.  If the string
+  // is null or is an empty string, we do nothing.
+  protected void sortByNDC() {
+    String text = _NDCTextFilter;
+    if (text == null || text.length() == 0) {
+      return;
+    }
+
+    // Use new NDC filter
+    _table.getFilteredLogTableModel().
+        setLogRecordFilter(createNDCLogRecordFilter(text));
+  }
+
+  protected void findSearchText() {
+    String text = _searchText;
+    if (text == null || text.length() == 0) {
+      return;
+    }
+    int startRow = getFirstSelectedRow();
+    int foundRow = findRecord(
+        startRow,
+        text,
+        _table.getFilteredLogTableModel().getFilteredRecords()
+    );
+    selectRow(foundRow);
+  }
+
+  protected int getFirstSelectedRow() {
+    return _table.getSelectionModel().getMinSelectionIndex();
+  }
+
+  protected void selectRow(int foundRow) {
+    if (foundRow == -1) {
+      String message = _searchText + " not found.";
+      JOptionPane.showMessageDialog(
+          _logMonitorFrame,
+          message,
+          "Text not found",
+          JOptionPane.INFORMATION_MESSAGE
+      );
+      return;
+    }
+    LF5SwingUtils.selectRow(foundRow, _table, _logTableScrollPane);
+  }
+
+  protected int findRecord(
+      int startRow,
+      String searchText,
+      List records
+      ) {
+    if (startRow < 0) {
+      startRow = 0; // start at first element if no rows are selected
+    } else {
+      startRow++; // start after the first selected row
+    }
+    int len = records.size();
+
+    for (int i = startRow; i < len; i++) {
+      if (matches((LogRecord) records.get(i), searchText)) {
+        return i; // found a record
+      }
+    }
+    // wrap around to beginning if when we reach the end with no match
+    len = startRow;
+    for (int i = 0; i < len; i++) {
+      if (matches((LogRecord) records.get(i), searchText)) {
+        return i; // found a record
+      }
+    }
+    // nothing found
+    return -1;
+  }
+
+  /**
+   * Check to see if the any records contain the search string.
+   * Searching now supports NDC messages and date.
+   */
+  protected boolean matches(LogRecord record, String text) {
+    String message = record.getMessage();
+    String NDC = record.getNDC();
+
+    if (message == null && NDC == null || text == null) {
+      return false;
+    }
+    if (message.toLowerCase().indexOf(text.toLowerCase()) == -1 &&
+        NDC.toLowerCase().indexOf(text.toLowerCase()) == -1) {
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * When the fontsize of a JTextArea is changed, the word-wrapped lines
+   * may become garbled.  This method clears and resets the text of the
+   * text area.
+   */
+  protected void refresh(JTextArea textArea) {
+    String text = textArea.getText();
+    textArea.setText("");
+    textArea.setText(text);
+  }
+
+  protected void refreshDetailTextArea() {
+    refresh(_table._detailTextArea);
+  }
+
+  protected void clearDetailTextArea() {
+    _table._detailTextArea.setText("");
+  }
+
+  /**
+   * Changes the font selection in the combo box and returns the
+   * size actually selected.
+   * @return -1 if unable to select an appropriate font
+   */
+  protected int changeFontSizeCombo(JComboBox box, int requestedSize) {
+    int len = box.getItemCount();
+    int currentValue;
+    Object currentObject;
+    Object selectedObject = box.getItemAt(0);
+    int selectedValue = Integer.parseInt(String.valueOf(selectedObject));
+    for (int i = 0; i < len; i++) {
+      currentObject = box.getItemAt(i);
+      currentValue = Integer.parseInt(String.valueOf(currentObject));
+      if (selectedValue < currentValue && currentValue <= requestedSize) {
+        selectedValue = currentValue;
+        selectedObject = currentObject;
+      }
+    }
+    box.setSelectedItem(selectedObject);
+    return selectedValue;
+  }
+
+  /**
+   * Does not update gui or cause any events to be fired.
+   */
+  protected void setFontSizeSilently(int fontSize) {
+    _fontSize = fontSize;
+    setFontSize(_table._detailTextArea, fontSize);
+    selectRow(0);
+    setFontSize(_table, fontSize);
+  }
+
+  protected void setFontSize(Component component, int fontSize) {
+    Font oldFont = component.getFont();
+    Font newFont =
+        new Font(oldFont.getFontName(), oldFont.getStyle(), fontSize);
+    component.setFont(newFont);
+  }
+
+  protected void updateFrameSize() {
+    _logMonitorFrame.setSize(_logMonitorFrameWidth, _logMonitorFrameHeight);
+    centerFrame(_logMonitorFrame);
+  }
+
+  protected void pause(int millis) {
+    try {
+      Thread.sleep(millis);
+    } catch (InterruptedException e) {
+
+    }
+  }
+
+  protected void initComponents() {
+    //
+    // Configure the Frame.
+    //
+    _logMonitorFrame = new JFrame("LogFactor5");
+
+    _logMonitorFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+
+    String resource =
+        "/org/apache/log4j/lf5/viewer/images/lf5_small_icon.gif";
+    URL lf5IconURL = getClass().getResource(resource);
+
+    if (lf5IconURL != null) {
+      _logMonitorFrame.setIconImage(new ImageIcon(lf5IconURL).getImage());
+    }
+    updateFrameSize();
+
+    //
+    // Configure the LogTable.
+    //
+    JTextArea detailTA = createDetailTextArea();
+    JScrollPane detailTAScrollPane = new JScrollPane(detailTA);
+    _table = new LogTable(detailTA);
+    setView(_currentView, _table);
+    _table.setFont(new Font(_fontName, Font.PLAIN, _fontSize));
+    _logTableScrollPane = new JScrollPane(_table);
+
+    if (_trackTableScrollPane) {
+      _logTableScrollPane.getVerticalScrollBar().addAdjustmentListener(
+          new TrackingAdjustmentListener()
+      );
+    }
+
+
+    // Configure the SplitPane between the LogTable & DetailTextArea
+    //
+
+    JSplitPane tableViewerSplitPane = new JSplitPane();
+    tableViewerSplitPane.setOneTouchExpandable(true);
+    tableViewerSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
+    tableViewerSplitPane.setLeftComponent(_logTableScrollPane);
+    tableViewerSplitPane.setRightComponent(detailTAScrollPane);
+    // Make sure to do this last..
+    //tableViewerSplitPane.setDividerLocation(1.0); Doesn't work
+    //the same under 1.2.x & 1.3
+    // "350" is a magic number that provides the correct default
+    // behaviour under 1.2.x & 1.3.  For example, bumping this
+    // number to 400, causes the pane to be completely open in 1.2.x
+    // and closed in 1.3
+    tableViewerSplitPane.setDividerLocation(350);
+
+    //
+    // Configure the CategoryExplorer
+    //
+
+    _categoryExplorerTree = new CategoryExplorerTree();
+
+    _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter());
+
+    JScrollPane categoryExplorerTreeScrollPane =
+        new JScrollPane(_categoryExplorerTree);
+    categoryExplorerTreeScrollPane.setPreferredSize(new Dimension(130, 400));
+
+    // Load most recently used file list
+    _mruFileManager = new MRUFileManager();
+
+    //
+    // Configure the SplitPane between the CategoryExplorer & (LogTable/Detail)
+    //
+
+    JSplitPane splitPane = new JSplitPane();
+    splitPane.setOneTouchExpandable(true);
+    splitPane.setRightComponent(tableViewerSplitPane);
+    splitPane.setLeftComponent(categoryExplorerTreeScrollPane);
+    // Do this last.
+    splitPane.setDividerLocation(130);
+    //
+    // Add the MenuBar, StatusArea, CategoryExplorer|LogTable to the
+    // LogMonitorFrame.
+    //
+    _logMonitorFrame.getRootPane().setJMenuBar(createMenuBar());
+    _logMonitorFrame.getContentPane().add(splitPane, BorderLayout.CENTER);
+    _logMonitorFrame.getContentPane().add(createToolBar(),
+        BorderLayout.NORTH);
+    _logMonitorFrame.getContentPane().add(createStatusArea(),
+        BorderLayout.SOUTH);
+
+    makeLogTableListenToCategoryExplorer();
+    addTableModelProperties();
+
+    //
+    // Configure ConfigurationManager
+    //
+    _configurationManager = new ConfigurationManager(this, _table);
+
+  }
+
+  protected LogRecordFilter createLogRecordFilter() {
+    LogRecordFilter result = new LogRecordFilter() {
+      public boolean passes(LogRecord record) {
+        CategoryPath path = new CategoryPath(record.getCategory());
+        return
+            getMenuItem(record.getLevel()).isSelected() &&
+            _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path);
+      }
+    };
+    return result;
+  }
+
+  // Added in version 1.2 - Creates a new filter that sorts records based on
+  // an NDC string passed in by the user.
+  protected LogRecordFilter createNDCLogRecordFilter(String text) {
+    _NDCTextFilter = text;
+    LogRecordFilter result = new LogRecordFilter() {
+      public boolean passes(LogRecord record) {
+        String NDC = record.getNDC();
+        CategoryPath path = new CategoryPath(record.getCategory());
+        if (NDC == null || _NDCTextFilter == null) {
+          return false;
+        } else if (NDC.toLowerCase().indexOf(_NDCTextFilter.toLowerCase()) == -1) {
+          return false;
+        } else {
+          return getMenuItem(record.getLevel()).isSelected() &&
+              _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path);
+        }
+      }
+    };
+
+    return result;
+  }
+
+
+  protected void updateStatusLabel() {
+    _statusLabel.setText(getRecordsDisplayedMessage());
+  }
+
+  protected String getRecordsDisplayedMessage() {
+    FilteredLogTableModel model = _table.getFilteredLogTableModel();
+    return getStatusText(model.getRowCount(), model.getTotalRowCount());
+  }
+
+  protected void addTableModelProperties() {
+    final FilteredLogTableModel model = _table.getFilteredLogTableModel();
+
+    addDisplayedProperty(new Object() {
+      public String toString() {
+        return getRecordsDisplayedMessage();
+      }
+    });
+    addDisplayedProperty(new Object() {
+      public String toString() {
+        return "Maximum number of displayed LogRecords: "
+            + model._maxNumberOfLogRecords;
+      }
+    });
+  }
+
+  protected String getStatusText(int displayedRows, int totalRows) {
+    StringBuffer result = new StringBuffer();
+    result.append("Displaying: ");
+    result.append(displayedRows);
+    result.append(" records out of a total of: ");
+    result.append(totalRows);
+    result.append(" records.");
+    return result.toString();
+  }
+
+  protected void makeLogTableListenToCategoryExplorer() {
+    ActionListener listener = new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        _table.getFilteredLogTableModel().refresh();
+        updateStatusLabel();
+      }
+    };
+    _categoryExplorerTree.getExplorerModel().addActionListener(listener);
+  }
+
+  protected JPanel createStatusArea() {
+    JPanel statusArea = new JPanel();
+    JLabel status =
+        new JLabel("No log records to display.");
+    _statusLabel = status;
+    status.setHorizontalAlignment(JLabel.LEFT);
+
+    statusArea.setBorder(BorderFactory.createEtchedBorder());
+    statusArea.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+    statusArea.add(status);
+
+    return (statusArea);
+  }
+
+  protected JTextArea createDetailTextArea() {
+    JTextArea detailTA = new JTextArea();
+    detailTA.setFont(new Font("Monospaced", Font.PLAIN, 14));
+    detailTA.setTabSize(3);
+    detailTA.setLineWrap(true);
+    detailTA.setWrapStyleWord(false);
+    return (detailTA);
+  }
+
+  protected JMenuBar createMenuBar() {
+    JMenuBar menuBar = new JMenuBar();
+    menuBar.add(createFileMenu());
+    menuBar.add(createEditMenu());
+    menuBar.add(createLogLevelMenu());
+    menuBar.add(createViewMenu());
+    menuBar.add(createConfigureMenu());
+    menuBar.add(createHelpMenu());
+
+    return (menuBar);
+  }
+
+  protected JMenu createLogLevelMenu() {
+    JMenu result = new JMenu("Log Level");
+    result.setMnemonic('l');
+    Iterator levels = getLogLevels();
+    while (levels.hasNext()) {
+      result.add(getMenuItem((LogLevel) levels.next()));
+    }
+
+    result.addSeparator();
+    result.add(createAllLogLevelsMenuItem());
+    result.add(createNoLogLevelsMenuItem());
+    result.addSeparator();
+    result.add(createLogLevelColorMenu());
+    result.add(createResetLogLevelColorMenuItem());
+
+    return result;
+  }
+
+  protected JMenuItem createAllLogLevelsMenuItem() {
+    JMenuItem result = new JMenuItem("Show all LogLevels");
+    result.setMnemonic('s');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        selectAllLogLevels(true);
+        _table.getFilteredLogTableModel().refresh();
+        updateStatusLabel();
+      }
+    });
+    return result;
+  }
+
+  protected JMenuItem createNoLogLevelsMenuItem() {
+    JMenuItem result = new JMenuItem("Hide all LogLevels");
+    result.setMnemonic('h');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        selectAllLogLevels(false);
+        _table.getFilteredLogTableModel().refresh();
+        updateStatusLabel();
+      }
+    });
+    return result;
+  }
+
+  protected JMenu createLogLevelColorMenu() {
+    JMenu colorMenu = new JMenu("Configure LogLevel Colors");
+    colorMenu.setMnemonic('c');
+    Iterator levels = getLogLevels();
+    while (levels.hasNext()) {
+      colorMenu.add(createSubMenuItem((LogLevel) levels.next()));
+    }
+
+    return colorMenu;
+  }
+
+  protected JMenuItem createResetLogLevelColorMenuItem() {
+    JMenuItem result = new JMenuItem("Reset LogLevel Colors");
+    result.setMnemonic('r');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        // reset the level colors in the map
+        LogLevel.resetLogLevelColorMap();
+
+        // refresh the table
+        _table.getFilteredLogTableModel().refresh();
+      }
+    });
+    return result;
+  }
+
+  protected void selectAllLogLevels(boolean selected) {
+    Iterator levels = getLogLevels();
+    while (levels.hasNext()) {
+      getMenuItem((LogLevel) levels.next()).setSelected(selected);
+    }
+  }
+
+  protected JCheckBoxMenuItem getMenuItem(LogLevel level) {
+    JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logLevelMenuItems.get(level));
+    if (result == null) {
+      result = createMenuItem(level);
+      _logLevelMenuItems.put(level, result);
+    }
+    return result;
+  }
+
+  protected JMenuItem createSubMenuItem(LogLevel level) {
+    final JMenuItem result = new JMenuItem(level.toString());
+    final LogLevel logLevel = level;
+    result.setMnemonic(level.toString().charAt(0));
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        showLogLevelColorChangeDialog(result, logLevel);
+      }
+    });
+
+    return result;
+
+  }
+
+  protected void showLogLevelColorChangeDialog(JMenuItem result, LogLevel level) {
+    JMenuItem menuItem = result;
+    Color newColor = JColorChooser.showDialog(
+        _logMonitorFrame,
+        "Choose LogLevel Color",
+        result.getForeground());
+
+    if (newColor != null) {
+      // set the color for the record
+      level.setLogLevelColorMap(level, newColor);
+      _table.getFilteredLogTableModel().refresh();
+    }
+
+  }
+
+  protected JCheckBoxMenuItem createMenuItem(LogLevel level) {
+    JCheckBoxMenuItem result = new JCheckBoxMenuItem(level.toString());
+    result.setSelected(true);
+    result.setMnemonic(level.toString().charAt(0));
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        _table.getFilteredLogTableModel().refresh();
+        updateStatusLabel();
+      }
+    });
+    return result;
+  }
+
+  // view menu
+  protected JMenu createViewMenu() {
+    JMenu result = new JMenu("View");
+    result.setMnemonic('v');
+    Iterator columns = getLogTableColumns();
+    while (columns.hasNext()) {
+      result.add(getLogTableColumnMenuItem((LogTableColumn) columns.next()));
+    }
+
+    result.addSeparator();
+    result.add(createAllLogTableColumnsMenuItem());
+    result.add(createNoLogTableColumnsMenuItem());
+    return result;
+  }
+
+  protected JCheckBoxMenuItem getLogTableColumnMenuItem(LogTableColumn column) {
+    JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logTableColumnMenuItems.get(column));
+    if (result == null) {
+      result = createLogTableColumnMenuItem(column);
+      _logTableColumnMenuItems.put(column, result);
+    }
+    return result;
+  }
+
+  protected JCheckBoxMenuItem createLogTableColumnMenuItem(LogTableColumn column) {
+    JCheckBoxMenuItem result = new JCheckBoxMenuItem(column.toString());
+
+    result.setSelected(true);
+    result.setMnemonic(column.toString().charAt(0));
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        // update list of columns and reset the view
+        List selectedColumns = updateView();
+        _table.setView(selectedColumns);
+      }
+    });
+    return result;
+  }
+
+  protected List updateView() {
+    ArrayList updatedList = new ArrayList();
+    Iterator columnIterator = _columns.iterator();
+    while (columnIterator.hasNext()) {
+      LogTableColumn column = (LogTableColumn) columnIterator.next();
+      JCheckBoxMenuItem result = getLogTableColumnMenuItem(column);
+      // check and see if the checkbox is checked
+      if (result.isSelected()) {
+        updatedList.add(column);
+      }
+    }
+
+    return updatedList;
+  }
+
+  protected JMenuItem createAllLogTableColumnsMenuItem() {
+    JMenuItem result = new JMenuItem("Show all Columns");
+    result.setMnemonic('s');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        selectAllLogTableColumns(true);
+        // update list of columns and reset the view
+        List selectedColumns = updateView();
+        _table.setView(selectedColumns);
+      }
+    });
+    return result;
+  }
+
+  protected JMenuItem createNoLogTableColumnsMenuItem() {
+    JMenuItem result = new JMenuItem("Hide all Columns");
+    result.setMnemonic('h');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        selectAllLogTableColumns(false);
+        // update list of columns and reset the view
+        List selectedColumns = updateView();
+        _table.setView(selectedColumns);
+      }
+    });
+    return result;
+  }
+
+  protected void selectAllLogTableColumns(boolean selected) {
+    Iterator columns = getLogTableColumns();
+    while (columns.hasNext()) {
+      getLogTableColumnMenuItem((LogTableColumn) columns.next()).setSelected(selected);
+    }
+  }
+
+  protected JMenu createFileMenu() {
+    JMenu fileMenu = new JMenu("File");
+    fileMenu.setMnemonic('f');
+    JMenuItem exitMI;
+    fileMenu.add(createOpenMI());
+    fileMenu.add(createOpenURLMI());
+    fileMenu.addSeparator();
+    fileMenu.add(createCloseMI());
+    createMRUFileListMI(fileMenu);
+    fileMenu.addSeparator();
+    fileMenu.add(createExitMI());
+    return fileMenu;
+  }
+
+  /**
+   * Menu item added to allow log files to be opened with
+   * the LF5 GUI.
+   */
+  protected JMenuItem createOpenMI() {
+    JMenuItem result = new JMenuItem("Open...");
+    result.setMnemonic('o');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        requestOpen();
+      }
+    });
+    return result;
+  }
+
+  /**
+   * Menu item added to allow log files loaded from a URL
+   * to be opened by the LF5 GUI.
+   */
+  protected JMenuItem createOpenURLMI() {
+    JMenuItem result = new JMenuItem("Open URL...");
+    result.setMnemonic('u');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        requestOpenURL();
+      }
+    });
+    return result;
+  }
+
+  protected JMenuItem createCloseMI() {
+    JMenuItem result = new JMenuItem("Close");
+    result.setMnemonic('c');
+    result.setAccelerator(KeyStroke.getKeyStroke("control Q"));
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        requestClose();
+      }
+    });
+    return result;
+  }
+
+  /**
+   * Creates a Most Recently Used file list to be
+   * displayed in the File menu
+   */
+  protected void createMRUFileListMI(JMenu menu) {
+
+    String[] files = _mruFileManager.getMRUFileList();
+
+    if (files != null) {
+      menu.addSeparator();
+      for (int i = 0; i < files.length; i++) {
+        JMenuItem result = new JMenuItem((i + 1) + " " + files[i]);
+        result.setMnemonic(i + 1);
+        result.addActionListener(new ActionListener() {
+          public void actionPerformed(ActionEvent e) {
+            requestOpenMRU(e);
+          }
+        });
+        menu.add(result);
+      }
+    }
+  }
+
+  protected JMenuItem createExitMI() {
+    JMenuItem result = new JMenuItem("Exit");
+    result.setMnemonic('x');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        requestExit();
+      }
+    });
+    return result;
+  }
+
+  protected JMenu createConfigureMenu() {
+    JMenu configureMenu = new JMenu("Configure");
+    configureMenu.setMnemonic('c');
+    configureMenu.add(createConfigureSave());
+    configureMenu.add(createConfigureReset());
+    configureMenu.add(createConfigureMaxRecords());
+
+    return configureMenu;
+  }
+
+  protected JMenuItem createConfigureSave() {
+    JMenuItem result = new JMenuItem("Save");
+    result.setMnemonic('s');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        saveConfiguration();
+      }
+    });
+
+    return result;
+  }
+
+  protected JMenuItem createConfigureReset() {
+    JMenuItem result = new JMenuItem("Reset");
+    result.setMnemonic('r');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        resetConfiguration();
+      }
+    });
+
+    return result;
+  }
+
+  protected JMenuItem createConfigureMaxRecords() {
+    JMenuItem result = new JMenuItem("Set Max Number of Records");
+    result.setMnemonic('m');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        setMaxRecordConfiguration();
+      }
+    });
+
+    return result;
+  }
+
+
+  protected void saveConfiguration() {
+    _configurationManager.save();
+  }
+
+  protected void resetConfiguration() {
+    _configurationManager.reset();
+  }
+
+  protected void setMaxRecordConfiguration() {
+    LogFactor5InputDialog inputDialog = new LogFactor5InputDialog(
+        getBaseFrame(), "Set Max Number of Records", "", 10);
+
+    String temp = inputDialog.getText();
+
+    if (temp != null) {
+      try {
+        setMaxNumberOfLogRecords(Integer.parseInt(temp));
+      } catch (NumberFormatException e) {
+        LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+            getBaseFrame(),
+            "'" + temp + "' is an invalid parameter.\nPlease try again.");
+        setMaxRecordConfiguration();
+      }
+    }
+  }
+
+
+  protected JMenu createHelpMenu() {
+    JMenu helpMenu = new JMenu("Help");
+    helpMenu.setMnemonic('h');
+    helpMenu.add(createHelpProperties());
+    return helpMenu;
+  }
+
+  protected JMenuItem createHelpProperties() {
+    final String title = "LogFactor5 Properties";
+    final JMenuItem result = new JMenuItem(title);
+    result.setMnemonic('l');
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        showPropertiesDialog(title);
+      }
+    });
+    return result;
+  }
+
+  protected void showPropertiesDialog(String title) {
+    JOptionPane.showMessageDialog(
+        _logMonitorFrame,
+        _displayedLogBrokerProperties.toArray(),
+        title,
+        JOptionPane.PLAIN_MESSAGE
+    );
+  }
+
+  protected JMenu createEditMenu() {
+    JMenu editMenu = new JMenu("Edit");
+    editMenu.setMnemonic('e');
+    editMenu.add(createEditFindMI());
+    editMenu.add(createEditFindNextMI());
+    editMenu.addSeparator();
+    editMenu.add(createEditSortNDCMI());
+    editMenu.add(createEditRestoreAllNDCMI());
+    return editMenu;
+  }
+
+  protected JMenuItem createEditFindNextMI() {
+    JMenuItem editFindNextMI = new JMenuItem("Find Next");
+    editFindNextMI.setMnemonic('n');
+    editFindNextMI.setAccelerator(KeyStroke.getKeyStroke("F3"));
+    editFindNextMI.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        findSearchText();
+      }
+    });
+    return editFindNextMI;
+  }
+
+  protected JMenuItem createEditFindMI() {
+    JMenuItem editFindMI = new JMenuItem("Find");
+    editFindMI.setMnemonic('f');
+    editFindMI.setAccelerator(KeyStroke.getKeyStroke("control F"));
+
+    editFindMI.addActionListener(
+        new ActionListener() {
+          public void actionPerformed(ActionEvent e) {
+            String inputValue =
+                JOptionPane.showInputDialog(
+                    _logMonitorFrame,
+                    "Find text: ",
+                    "Search Record Messages",
+                    JOptionPane.QUESTION_MESSAGE
+                );
+            setSearchText(inputValue);
+            findSearchText();
+          }
+        }
+
+    );
+    return editFindMI;
+  }
+
+  // Added version 1.2 - Allows users to Sort Log Records by an
+  // NDC text filter. A new LogRecordFilter was created to
+  // sort the records.
+  protected JMenuItem createEditSortNDCMI() {
+    JMenuItem editSortNDCMI = new JMenuItem("Sort by NDC");
+    editSortNDCMI.setMnemonic('s');
+    editSortNDCMI.addActionListener(
+        new ActionListener() {
+          public void actionPerformed(ActionEvent e) {
+            String inputValue =
+                JOptionPane.showInputDialog(
+                    _logMonitorFrame,
+                    "Sort by this NDC: ",
+                    "Sort Log Records by NDC",
+                    JOptionPane.QUESTION_MESSAGE
+                );
+            setNDCTextFilter(inputValue);
+            sortByNDC();
+            _table.getFilteredLogTableModel().refresh();
+            updateStatusLabel();
+          }
+        }
+
+    );
+    return editSortNDCMI;
+  }
+
+  // Added in version 1.2 - Resets the LogRecordFilter back to default
+  // filter.
+  protected JMenuItem createEditRestoreAllNDCMI() {
+    JMenuItem editRestoreAllNDCMI = new JMenuItem("Restore all NDCs");
+    editRestoreAllNDCMI.setMnemonic('r');
+    editRestoreAllNDCMI.addActionListener(
+        new ActionListener() {
+          public void actionPerformed(ActionEvent e) {
+            _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter());
+            // reset the text filter
+            setNDCTextFilter("");
+            _table.getFilteredLogTableModel().refresh();
+            updateStatusLabel();
+          }
+        }
+    );
+    return editRestoreAllNDCMI;
+  }
+
+  protected JToolBar createToolBar() {
+    JToolBar tb = new JToolBar();
+    tb.putClientProperty("JToolBar.isRollover", Boolean.TRUE);
+    JComboBox fontCombo = new JComboBox();
+    JComboBox fontSizeCombo = new JComboBox();
+    _fontSizeCombo = fontSizeCombo;
+
+    ClassLoader cl = this.getClass().getClassLoader();
+    if(cl == null) {
+        cl = ClassLoader.getSystemClassLoader();
+    }
+    URL newIconURL = cl.getResource("org/apache/log4j/lf5/viewer/" +
+        "images/channelexplorer_new.gif");
+
+    ImageIcon newIcon = null;
+
+    if (newIconURL != null) {
+      newIcon = new ImageIcon(newIconURL);
+    }
+
+    JButton newButton = new JButton("Clear Log Table");
+
+    if (newIcon != null) {
+      newButton.setIcon(newIcon);
+    }
+
+    newButton.setToolTipText("Clear Log Table.");
+    //newButton.setBorder(BorderFactory.createEtchedBorder());
+
+    newButton.addActionListener(
+        new ActionListener() {
+          public void actionPerformed(ActionEvent e) {
+            _table.clearLogRecords();
+            _categoryExplorerTree.getExplorerModel().resetAllNodeCounts();
+            updateStatusLabel();
+            clearDetailTextArea();
+            LogRecord.resetSequenceNumber();
+          }
+        }
+    );
+
+    Toolkit tk = Toolkit.getDefaultToolkit();
+    // This will actually grab all the fonts
+
+    String[] fonts;
+
+    if (_loadSystemFonts) {
+      fonts = GraphicsEnvironment.
+          getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
+    } else {
+      fonts = tk.getFontList();
+    }
+
+    for (int j = 0; j < fonts.length; j++) {
+      fontCombo.addItem(fonts[j]);
+    }
+
+    fontCombo.setSelectedItem(_fontName);
+
+    fontCombo.addActionListener(
+
+        new ActionListener() {
+          public void actionPerformed(ActionEvent e) {
+            JComboBox box = (JComboBox) e.getSource();
+            String font = (String) box.getSelectedItem();
+            _table.setFont(new Font(font, Font.PLAIN, _fontSize));
+            _fontName = font;
+          }
+        }
+    );
+
+    fontSizeCombo.addItem("8");
+    fontSizeCombo.addItem("9");
+    fontSizeCombo.addItem("10");
+    fontSizeCombo.addItem("12");
+    fontSizeCombo.addItem("14");
+    fontSizeCombo.addItem("16");
+    fontSizeCombo.addItem("18");
+    fontSizeCombo.addItem("24");
+
+    fontSizeCombo.setSelectedItem(String.valueOf(_fontSize));
+    fontSizeCombo.addActionListener(
+        new ActionListener() {
+          public void actionPerformed(ActionEvent e) {
+            JComboBox box = (JComboBox) e.getSource();
+            String size = (String) box.getSelectedItem();
+            int s = Integer.valueOf(size).intValue();
+
+            setFontSizeSilently(s);
+            refreshDetailTextArea();
+            _fontSize = s;
+          }
+        }
+    );
+
+    tb.add(new JLabel(" Font: "));
+    tb.add(fontCombo);
+    tb.add(fontSizeCombo);
+    tb.addSeparator();
+    tb.addSeparator();
+    tb.add(newButton);
+
+    newButton.setAlignmentY(0.5f);
+    newButton.setAlignmentX(0.5f);
+
+    fontCombo.setMaximumSize(fontCombo.getPreferredSize());
+    fontSizeCombo.setMaximumSize(
+        fontSizeCombo.getPreferredSize());
+
+    return (tb);
+  }
+
+//    protected void setView(String viewString, LogTable table) {
+//        if (STANDARD_VIEW.equals(viewString)) {
+//            table.setStandardView();
+//        } else if (COMPACT_VIEW.equals(viewString)) {
+//            table.setCompactView();
+//        } else if (DETAILED_VIEW.equals(viewString)) {
+//            table.setDetailedView();
+//        } else {
+//            String message = viewString + "does not match a supported view.";
+//            throw new IllegalArgumentException(message);
+//        }
+//        _currentView = viewString;
+//    }
+
+  protected void setView(String viewString, LogTable table) {
+    if (DETAILED_VIEW.equals(viewString)) {
+      table.setDetailedView();
+    } else {
+      String message = viewString + "does not match a supported view.";
+      throw new IllegalArgumentException(message);
+    }
+    _currentView = viewString;
+  }
+
+  protected JComboBox createLogLevelCombo() {
+    JComboBox result = new JComboBox();
+    Iterator levels = getLogLevels();
+    while (levels.hasNext()) {
+      result.addItem(levels.next());
+    }
+    result.setSelectedItem(_leastSevereDisplayedLogLevel);
+
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        JComboBox box = (JComboBox) e.getSource();
+        LogLevel level = (LogLevel) box.getSelectedItem();
+        setLeastSevereDisplayedLogLevel(level);
+      }
+    });
+    result.setMaximumSize(result.getPreferredSize());
+    return result;
+  }
+
+  protected void setLeastSevereDisplayedLogLevel(LogLevel level) {
+    if (level == null || _leastSevereDisplayedLogLevel == level) {
+      return; // nothing to do
+    }
+    _leastSevereDisplayedLogLevel = level;
+    _table.getFilteredLogTableModel().refresh();
+    updateStatusLabel();
+  }
+
+  /**
+   * Ensures that the Table's ScrollPane Viewport will "track" with updates
+   * to the Table.  When the vertical scroll bar is at its bottom anchor
+   * and tracking is enabled then viewport will stay at the bottom most
+   * point of the component.  The purpose of this feature is to allow
+   * a developer to watch the table as messages arrive and not have to
+   * scroll after each new message arrives.  When the vertical scroll bar
+   * is at any other location, then no tracking will happen.
+   * @deprecated tracking is now done automatically.
+   */
+  protected void trackTableScrollPane() {
+    // do nothing
+  }
+
+  protected void centerFrame(JFrame frame) {
+    Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
+    Dimension comp = frame.getSize();
+
+    frame.setLocation(((screen.width - comp.width) / 2),
+        ((screen.height - comp.height) / 2));
+
+  }
+
+  /**
+   * Uses a JFileChooser to select a file to opened with the
+   * LF5 GUI.
+   */
+  protected void requestOpen() {
+    JFileChooser chooser;
+
+    if (_fileLocation == null) {
+      chooser = new JFileChooser();
+    } else {
+      chooser = new JFileChooser(_fileLocation);
+    }
+
+    int returnVal = chooser.showOpenDialog(_logMonitorFrame);
+    if (returnVal == JFileChooser.APPROVE_OPTION) {
+      File f = chooser.getSelectedFile();
+      if (loadLogFile(f)) {
+        _fileLocation = chooser.getSelectedFile();
+        _mruFileManager.set(f);
+        updateMRUList();
+      }
+    }
+  }
+
+  /**
+   * Uses a Dialog box to accept a URL to a file to be opened
+   * with the LF5 GUI.
+   */
+  protected void requestOpenURL() {
+    LogFactor5InputDialog inputDialog = new LogFactor5InputDialog(
+        getBaseFrame(), "Open URL", "URL:");
+    String temp = inputDialog.getText();
+
+    if (temp != null) {
+      if (temp.indexOf("://") == -1) {
+        temp = "http://" + temp;
+      }
+
+      try {
+        URL url = new URL(temp);
+        if (loadLogFile(url)) {
+          _mruFileManager.set(url);
+          updateMRUList();
+        }
+      } catch (MalformedURLException e) {
+        LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+            getBaseFrame(), "Error reading URL.");
+      }
+    }
+  }
+
+  /**
+   * Removes old file list and creates a new file list
+   * with the updated MRU list.
+   */
+  protected void updateMRUList() {
+    JMenu menu = _logMonitorFrame.getJMenuBar().getMenu(0);
+    menu.removeAll();
+    menu.add(createOpenMI());
+    menu.add(createOpenURLMI());
+    menu.addSeparator();
+    menu.add(createCloseMI());
+    createMRUFileListMI(menu);
+    menu.addSeparator();
+    menu.add(createExitMI());
+  }
+
+  protected void requestClose() {
+    setCallSystemExitOnClose(false);
+    closeAfterConfirm();
+  }
+
+  /**
+   * Opens a file in the MRU list.
+   */
+  protected void requestOpenMRU(ActionEvent e) {
+    String file = e.getActionCommand();
+    StringTokenizer st = new StringTokenizer(file);
+    String num = st.nextToken().trim();
+    file = st.nextToken("\n");
+
+    try {
+      int index = Integer.parseInt(num) - 1;
+
+      InputStream in = _mruFileManager.getInputStream(index);
+      LogFileParser lfp = new LogFileParser(in);
+      lfp.parse(this);
+
+      _mruFileManager.moveToTop(index);
+      updateMRUList();
+
+    } catch (Exception me) {
+      LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+          getBaseFrame(), "Unable to load file " + file);
+    }
+
+  }
+
+  protected void requestExit() {
+    _mruFileManager.save();
+    setCallSystemExitOnClose(true);
+    closeAfterConfirm();
+  }
+
+  protected void closeAfterConfirm() {
+    StringBuffer message = new StringBuffer();
+
+    if (_callSystemExitOnClose == false) {
+      message.append("Are you sure you want to close the logging ");
+      message.append("console?\n");
+      message.append("(Note: This will not shut down the Virtual Machine,\n");
+      message.append("or the Swing event thread.)");
+    } else {
+      message.append("Are you sure you want to exit?\n");
+      message.append("This will shut down the Virtual Machine.\n");
+    }
+
+    String title =
+        "Are you sure you want to dispose of the Logging Console?";
+
+    if (_callSystemExitOnClose == true) {
+      title = "Are you sure you want to exit?";
+    }
+    int value = JOptionPane.showConfirmDialog(
+        _logMonitorFrame,
+        message.toString(),
+        title,
+        JOptionPane.OK_CANCEL_OPTION,
+        JOptionPane.QUESTION_MESSAGE,
+        null
+    );
+
+    if (value == JOptionPane.OK_OPTION) {
+      dispose();
+    }
+  }
+
+  protected Iterator getLogLevels() {
+    return _levels.iterator();
+  }
+
+  protected Iterator getLogTableColumns() {
+    return _columns.iterator();
+  }
+
+  /**
+   * Loads and parses a log file.
+   */
+  protected boolean loadLogFile(File file) {
+    boolean ok = false;
+    try {
+      LogFileParser lfp = new LogFileParser(file);
+      lfp.parse(this);
+      ok = true;
+    } catch (IOException e) {
+      LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+          getBaseFrame(), "Error reading " + file.getName());
+    }
+
+    return ok;
+  }
+
+  /**
+   * Loads a parses a log file running on a server.
+   */
+  protected boolean loadLogFile(URL url) {
+    boolean ok = false;
+    try {
+      LogFileParser lfp = new LogFileParser(url.openStream());
+      lfp.parse(this);
+      ok = true;
+    } catch (IOException e) {
+      LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+          getBaseFrame(), "Error reading URL:" + url.getFile());
+    }
+    return ok;
+  }
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+  class LogBrokerMonitorWindowAdaptor extends WindowAdapter {
+    protected LogBrokerMonitor _monitor;
+
+    public LogBrokerMonitorWindowAdaptor(LogBrokerMonitor monitor) {
+      _monitor = monitor;
+    }
+
+    public void windowClosing(WindowEvent ev) {
+      _monitor.requestClose();
+    }
+  }
+}
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java
new file mode 100644
index 0000000..763d870
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java
@@ -0,0 +1,151 @@
+/*
+ * 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.lf5.viewer;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Label;
+import java.awt.Toolkit;
+import java.awt.Window;
+
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+
+/**
+ * LogFactor5Dialog
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public abstract class LogFactor5Dialog extends JDialog {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+  protected static final Font DISPLAY_FONT = new Font("Arial", Font.BOLD, 12);
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  protected LogFactor5Dialog(JFrame jframe, String message, boolean modal) {
+    super(jframe, message, modal);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+  public void show() {
+    pack();
+    minimumSizeDialog(this, 200, 100);
+    centerWindow(this);
+    super.show();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+  protected void centerWindow(Window win) {
+    Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
+
+    // If larger than screen, reduce window width or height
+    if (screenDim.width < win.getSize().width) {
+      win.setSize(screenDim.width, win.getSize().height);
+    }
+
+    if (screenDim.height < win.getSize().height) {
+      win.setSize(win.getSize().width, screenDim.height);
+    }
+
+    // Center Frame, Dialogue or Window on screen
+    int x = (screenDim.width - win.getSize().width) / 2;
+    int y = (screenDim.height - win.getSize().height) / 2;
+    win.setLocation(x, y);
+  }
+
+  protected void wrapStringOnPanel(String message,
+      Container container) {
+    GridBagConstraints c = getDefaultConstraints();
+    c.gridwidth = GridBagConstraints.REMAINDER;
+    // Insets() args are top, left, bottom, right
+    c.insets = new Insets(0, 0, 0, 0);
+    GridBagLayout gbLayout = (GridBagLayout) container.getLayout();
+
+
+    while (message.length() > 0) {
+      int newLineIndex = message.indexOf('\n');
+      String line;
+      if (newLineIndex >= 0) {
+        line = message.substring(0, newLineIndex);
+        message = message.substring(newLineIndex + 1);
+      } else {
+        line = message;
+        message = "";
+      }
+      Label label = new Label(line);
+      label.setFont(DISPLAY_FONT);
+      gbLayout.setConstraints(label, c);
+      container.add(label);
+    }
+  }
+
+  protected GridBagConstraints getDefaultConstraints() {
+    GridBagConstraints constraints = new GridBagConstraints();
+    constraints.weightx = 1.0;
+    constraints.weighty = 1.0;
+    constraints.gridheight = 1; // One row high
+    // Insets() args are top, left, bottom, right
+    constraints.insets = new Insets(4, 4, 4, 4);
+    // fill of NONE means do not change size
+    constraints.fill = GridBagConstraints.NONE;
+    // WEST means align left
+    constraints.anchor = GridBagConstraints.WEST;
+
+    return constraints;
+  }
+
+  protected void minimumSizeDialog(Component component,
+      int minWidth,
+      int minHeight) {
+    // set the min width
+    if (component.getSize().width < minWidth)
+      component.setSize(minWidth, component.getSize().height);
+    // set the min height
+    if (component.getSize().height < minHeight)
+      component.setSize(component.getSize().width, minHeight);
+  }
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java
new file mode 100644
index 0000000..64b2e21
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java
@@ -0,0 +1,92 @@
+/*
+ * 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.lf5.viewer;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+/**
+ * LogFactor5ErrorDialog
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogFactor5ErrorDialog extends LogFactor5Dialog {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  public LogFactor5ErrorDialog(JFrame jframe, String message) {
+    super(jframe, "Error", true);
+
+    JButton ok = new JButton("Ok");
+    ok.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        hide();
+      }
+    });
+
+    JPanel bottom = new JPanel();
+    bottom.setLayout(new FlowLayout());
+    bottom.add(ok);
+
+    JPanel main = new JPanel();
+    main.setLayout(new GridBagLayout());
+    wrapStringOnPanel(message, main);
+
+    getContentPane().add(main, BorderLayout.CENTER);
+    getContentPane().add(bottom, BorderLayout.SOUTH);
+    show();
+
+  }
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java
new file mode 100644
index 0000000..890e6db
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java
@@ -0,0 +1,145 @@
+/*
+ * 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.lf5.viewer;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+/**
+ * LogFactor5InputDialog
+ *
+ * Creates a popup input dialog box so that users can enter
+ * a URL to open a log file from.
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogFactor5InputDialog extends LogFactor5Dialog {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+  public static final int SIZE = 30;
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+  private JTextField _textField;
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Configures an input dialog box using a defualt size for the text field.
+   * param jframe the frame where the dialog will be loaded from.
+   * param title the title of the dialog box.
+   * param label the label to be put in the dialog box.
+   */
+  public LogFactor5InputDialog(JFrame jframe, String title, String label) {
+    this(jframe, title, label, SIZE);
+  }
+
+  /**
+   * Configures an input dialog box.
+   * param jframe the frame where the dialog will be loaded from.
+   * param title the title of the dialog box.
+   * param label the label to be put in the dialog box.
+   * param size the size of the text field.
+   */
+  public LogFactor5InputDialog(JFrame jframe, String title, String label,
+      int size) {
+    super(jframe, title, true);
+
+    JPanel bottom = new JPanel();
+    bottom.setLayout(new FlowLayout());
+
+    JPanel main = new JPanel();
+    main.setLayout(new FlowLayout());
+    main.add(new JLabel(label));
+    _textField = new JTextField(size);
+    main.add(_textField);
+
+    addKeyListener(new KeyAdapter() {
+      public void keyPressed(KeyEvent e) {
+        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+          hide();
+        }
+      }
+    });
+
+    JButton ok = new JButton("Ok");
+    ok.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        hide();
+      }
+    });
+
+    JButton cancel = new JButton("Cancel");
+    cancel.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        hide();
+        // set the text field to blank just in case
+        // a file was selected before the Cancel
+        // button was pressed.
+        _textField.setText("");
+      }
+    });
+
+    bottom.add(ok);
+    bottom.add(cancel);
+    getContentPane().add(main, BorderLayout.CENTER);
+    getContentPane().add(bottom, BorderLayout.SOUTH);
+    pack();
+    centerWindow(this);
+    show();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+  public String getText() {
+    String s = _textField.getText();
+
+    if (s != null && s.trim().length() == 0) {
+      return null;
+    }
+
+    return s;
+
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java
new file mode 100644
index 0000000..3e5a62b
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java
@@ -0,0 +1,82 @@
+/*
+ * 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.lf5.viewer;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.GridBagLayout;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+/**
+ * LogFactor5LoadingDialog
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogFactor5LoadingDialog extends LogFactor5Dialog {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public LogFactor5LoadingDialog(JFrame jframe, String message) {
+    super(jframe, "LogFactor5", false);
+
+    JPanel bottom = new JPanel();
+    bottom.setLayout(new FlowLayout());
+
+    JPanel main = new JPanel();
+    main.setLayout(new GridBagLayout());
+    wrapStringOnPanel(message, main);
+
+    getContentPane().add(main, BorderLayout.CENTER);
+    getContentPane().add(bottom, BorderLayout.SOUTH);
+    show();
+
+  }
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTable.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTable.java
new file mode 100644
index 0000000..6bea10d
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTable.java
@@ -0,0 +1,277 @@
+/*
+ * 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.lf5.viewer;
+
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+import org.apache.log4j.lf5.util.DateFormatManager;
+
+/**
+ * LogTable.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brad Marlborough
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTable extends JTable {
+  private static final long serialVersionUID = 4867085140195148458L;
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected int _rowHeight = 30;
+  protected JTextArea _detailTextArea;
+
+  // For the columns:
+  protected int _numCols = 9;
+  protected TableColumn[] _tableColumns = new TableColumn[_numCols];
+  protected int[] _colWidths = {40, 40, 40, 70, 70, 360, 440, 200, 60};
+  protected LogTableColumn[] _colNames = LogTableColumn.getLogTableColumnArray();
+  protected int _colDate = 0;
+  protected int _colThread = 1;
+  protected int _colMessageNum = 2;
+  protected int _colLevel = 3;
+  protected int _colNDC = 4;
+  protected int _colCategory = 5;
+  protected int _colMessage = 6;
+  protected int _colLocation = 7;
+  protected int _colThrown = 8;
+
+  protected DateFormatManager _dateFormatManager = null;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public LogTable(JTextArea detailTextArea) {
+    super();
+
+    init();
+
+    _detailTextArea = detailTextArea;
+
+    setModel(new FilteredLogTableModel());
+
+    Enumeration columns = getColumnModel().getColumns();
+    int i = 0;
+    while (columns.hasMoreElements()) {
+      TableColumn col = (TableColumn) columns.nextElement();
+      col.setCellRenderer(new LogTableRowRenderer());
+      col.setPreferredWidth(_colWidths[i]);
+
+      _tableColumns[i] = col;
+      i++;
+    }
+
+    ListSelectionModel rowSM = getSelectionModel();
+    rowSM.addListSelectionListener(new LogTableListSelectionListener(this));
+
+    //setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Get the DateFormatManager for formatting dates.
+   */
+  public DateFormatManager getDateFormatManager() {
+    return _dateFormatManager;
+  }
+
+  /**
+   * Set the date format manager for formatting dates.
+   */
+  public void setDateFormatManager(DateFormatManager dfm) {
+    _dateFormatManager = dfm;
+  }
+
+  public synchronized void clearLogRecords() {
+    //For JDK1.3
+    //((DefaultTableModel)getModel()).setRowCount(0);
+
+    // For JDK1.2.x
+    getFilteredLogTableModel().clear();
+  }
+
+  public FilteredLogTableModel getFilteredLogTableModel() {
+    return (FilteredLogTableModel) getModel();
+  }
+
+  // default view if a view is not set and saved
+  public void setDetailedView() {
+    //TODO: Defineable Views.
+    TableColumnModel model = getColumnModel();
+    // Remove all the columns:
+    for (int f = 0; f < _numCols; f++) {
+      model.removeColumn(_tableColumns[f]);
+    }
+    // Add them back in the correct order:
+    for (int i = 0; i < _numCols; i++) {
+      model.addColumn(_tableColumns[i]);
+    }
+    //SWING BUG:
+    sizeColumnsToFit(-1);
+  }
+
+  public void setView(List columns) {
+    TableColumnModel model = getColumnModel();
+
+    // Remove all the columns:
+    for (int f = 0; f < _numCols; f++) {
+      model.removeColumn(_tableColumns[f]);
+    }
+    Iterator selectedColumns = columns.iterator();
+    Vector columnNameAndNumber = getColumnNameAndNumber();
+    while (selectedColumns.hasNext()) {
+      // add the column to the view
+      model.addColumn(_tableColumns[columnNameAndNumber.indexOf(selectedColumns.next())]);
+    }
+
+    //SWING BUG:
+    sizeColumnsToFit(-1);
+  }
+
+  public void setFont(Font font) {
+    super.setFont(font);
+    Graphics g = this.getGraphics();
+    if (g != null) {
+      FontMetrics fm = g.getFontMetrics(font);
+      int height = fm.getHeight();
+      _rowHeight = height + height / 3;
+      setRowHeight(_rowHeight);
+    }
+
+
+  }
+
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected void init() {
+    setRowHeight(_rowHeight);
+    setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+  }
+
+  // assign a column number to a column name
+  protected Vector getColumnNameAndNumber() {
+    Vector columnNameAndNumber = new Vector();
+    for (int i = 0; i < _colNames.length; i++) {
+      columnNameAndNumber.add(i, _colNames[i]);
+    }
+    return columnNameAndNumber;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+  class LogTableListSelectionListener implements ListSelectionListener {
+    protected JTable _table;
+
+    public LogTableListSelectionListener(JTable table) {
+      _table = table;
+    }
+
+    public void valueChanged(ListSelectionEvent e) {
+      //Ignore extra messages.
+      if (e.getValueIsAdjusting()) {
+        return;
+      }
+
+      ListSelectionModel lsm = (ListSelectionModel) e.getSource();
+      if (lsm.isSelectionEmpty()) {
+        //no rows are selected
+      } else {
+        StringBuffer buf = new StringBuffer();
+        int selectedRow = lsm.getMinSelectionIndex();
+
+        for (int i = 0; i < _numCols - 1; i++) {
+          String value = "";
+          Object obj = _table.getModel().getValueAt(selectedRow, i);
+          if (obj != null) {
+            value = obj.toString();
+          }
+
+          buf.append(_colNames[i] + ":");
+          buf.append("\t");
+
+          if (i == _colThread || i == _colMessage || i == _colLevel) {
+            buf.append("\t"); // pad out the date.
+          }
+
+          if (i == _colDate || i == _colNDC) {
+            buf.append("\t\t"); // pad out the date.
+          }
+
+//               if( i == _colSequence)
+//               {
+//                  buf.append("\t\t\t"); // pad out the Sequnce.
+//               }
+
+          buf.append(value);
+          buf.append("\n");
+        }
+        buf.append(_colNames[_numCols - 1] + ":\n");
+        Object obj = _table.getModel().getValueAt(selectedRow, _numCols - 1);
+        if (obj != null) {
+          buf.append(obj.toString());
+        }
+
+        _detailTextArea.setText(buf.toString());
+      }
+    }
+  }
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableColumn.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableColumn.java
new file mode 100644
index 0000000..c86c6bb
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableColumn.java
@@ -0,0 +1,166 @@
+/*
+ * 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.lf5.viewer;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * LogTableColumn
+ *
+ * @author Michael J. Sikorsky
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTableColumn implements java.io.Serializable {
+  private static final long serialVersionUID = -4275827753626456547L;
+
+  // log4j table columns.
+  public final static LogTableColumn DATE = new LogTableColumn("Date");
+  public final static LogTableColumn THREAD = new LogTableColumn("Thread");
+  public final static LogTableColumn MESSAGE_NUM = new LogTableColumn("Message #");
+  public final static LogTableColumn LEVEL = new LogTableColumn("Level");
+  public final static LogTableColumn NDC = new LogTableColumn("NDC");
+  public final static LogTableColumn CATEGORY = new LogTableColumn("Category");
+  public final static LogTableColumn MESSAGE = new LogTableColumn("Message");
+  public final static LogTableColumn LOCATION = new LogTableColumn("Location");
+  public final static LogTableColumn THROWN = new LogTableColumn("Thrown");
+
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected String _label;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+  private static LogTableColumn[] _log4JColumns;
+  private static Map _logTableColumnMap;
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  static {
+    _log4JColumns = new LogTableColumn[]{DATE, THREAD, MESSAGE_NUM, LEVEL, NDC, CATEGORY,
+                                         MESSAGE, LOCATION, THROWN};
+
+    _logTableColumnMap = new HashMap();
+
+    for (int i = 0; i < _log4JColumns.length; i++) {
+      _logTableColumnMap.put(_log4JColumns[i].getLabel(), _log4JColumns[i]);
+    }
+  }
+
+
+  public LogTableColumn(String label) {
+    _label = label;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Return the Label of the LogLevel.
+   */
+  public String getLabel() {
+    return _label;
+  }
+
+  /**
+   * Convert a column label into a LogTableColumn object.
+   *
+   * @param column The label of a level to be converted into a LogTableColumn.
+   * @return LogTableColumn The LogTableColumn with a label equal to column.
+   * @throws LogTableColumnFormatException Is thrown when the column can not be
+   *         converted into a LogTableColumn.
+   */
+  public static LogTableColumn valueOf(String column)
+      throws LogTableColumnFormatException {
+    LogTableColumn tableColumn = null;
+    if (column != null) {
+      column = column.trim();
+      tableColumn = (LogTableColumn) _logTableColumnMap.get(column);
+    }
+
+    if (tableColumn == null) {
+      StringBuffer buf = new StringBuffer();
+      buf.append("Error while trying to parse (" + column + ") into");
+      buf.append(" a LogTableColumn.");
+      throw new LogTableColumnFormatException(buf.toString());
+    }
+    return tableColumn;
+  }
+
+
+  public boolean equals(Object o) {
+    boolean equals = false;
+
+    if (o instanceof LogTableColumn) {
+      if (this.getLabel() ==
+          ((LogTableColumn) o).getLabel()) {
+        equals = true;
+      }
+    }
+
+    return equals;
+  }
+
+  public int hashCode() {
+    return _label.hashCode();
+  }
+
+  public String toString() {
+    return _label;
+  }
+
+  /**
+   * @return A <code>List</code> of <code>LogTableColumn/code> objects that map
+   * to log4j <code>Column</code> objects.
+   */
+  public static List getLogTableColumns() {
+    return Arrays.asList(_log4JColumns);
+  }
+
+  public static LogTableColumn[] getLogTableColumnArray() {
+    return _log4JColumns;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java
new file mode 100644
index 0000000..b161fe7
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java
@@ -0,0 +1,74 @@
+/*
+ * 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.lf5.viewer;
+
+/**
+ * Thrown to indicate that the client has attempted to convert a string
+ * to one the LogLevel types, but the string does not have the appropriate
+ * format.
+ *
+ * @author Michael J. Sikorsky
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTableColumnFormatException extends Exception {
+  private static final long serialVersionUID = 6529165785030431653L;
+
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public LogTableColumnFormatException(String message) {
+    super(message);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableModel.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableModel.java
new file mode 100644
index 0000000..3b60000
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableModel.java
@@ -0,0 +1,77 @@
+/*
+ * 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.lf5.viewer;
+
+import javax.swing.table.DefaultTableModel;
+
+/**
+ * LogTableModel
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTableModel extends DefaultTableModel {
+  private static final long serialVersionUID = 3593300685868700894L;
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public LogTableModel(Object[] colNames, int numRows) {
+    super(colNames, numRows);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public boolean isCellEditable(int row, int column) {
+    return (false);
+  }
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java
new file mode 100644
index 0000000..3d1e397
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java
@@ -0,0 +1,109 @@
+/*
+ * 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.lf5.viewer;
+
+import java.awt.Color;
+import java.awt.Component;
+
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableCellRenderer;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogRecord;
+
+/**
+ * LogTableRowRenderer
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTableRowRenderer extends DefaultTableCellRenderer {
+  private static final long serialVersionUID = -3951639953706443213L;
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected boolean _highlightFatal = true;
+  protected Color _color = new Color(230, 230, 230);
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public Component getTableCellRendererComponent(JTable table,
+      Object value,
+      boolean isSelected,
+      boolean hasFocus,
+      int row,
+      int col) {
+
+    if ((row % 2) == 0) {
+      setBackground(_color);
+    } else {
+      setBackground(Color.white);
+    }
+
+    FilteredLogTableModel model = (FilteredLogTableModel) table.getModel();
+    LogRecord record = model.getFilteredRecord(row);
+
+    setForeground(getLogLevelColor(record.getLevel()));
+
+    return (super.getTableCellRendererComponent(table,
+        value,
+        isSelected,
+        hasFocus,
+        row, col));
+  }
+
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+  protected Color getLogLevelColor(LogLevel level) {
+    return (Color) LogLevel.getLogLevelColorMap().get(level);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java
new file mode 100644
index 0000000..4aa959c
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java
@@ -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.lf5.viewer;
+
+import java.awt.Adjustable;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+
+/**
+ * An AdjustmentListener which ensures that an Adjustable (e.g. a Scrollbar)
+ * will "track" when the Adjustable expands.
+ * For example, when a vertical scroll bar is at its bottom anchor,
+ * the scrollbar will remain at the bottom.  When the vertical scroll bar
+ * is at any other location, then no tracking will happen.
+ * An instance of this class should only listen to one Adjustable as
+ * it retains state information about the Adjustable it listens to.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class TrackingAdjustmentListener implements AdjustmentListener {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  protected int _lastMaximum = -1;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public void adjustmentValueChanged(AdjustmentEvent e) {
+    Adjustable bar = e.getAdjustable();
+    int currentMaximum = bar.getMaximum();
+    if (bar.getMaximum() == _lastMaximum) {
+      return; // nothing to do, the adjustable has not expanded
+    }
+    int bottom = bar.getValue() + bar.getVisibleAmount();
+
+    if (bottom + bar.getUnitIncrement() >= _lastMaximum) {
+      bar.setValue(bar.getMaximum()); // use the most recent maximum
+    }
+    _lastMaximum = currentMaximum;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java
new file mode 100644
index 0000000..e5bbaa6
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java
@@ -0,0 +1,172 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import java.awt.Component;
+import java.awt.event.MouseEvent;
+import java.util.EventObject;
+
+import javax.swing.JTable;
+import javax.swing.JTree;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.EventListenerList;
+import javax.swing.table.TableCellEditor;
+import javax.swing.tree.TreeCellEditor;
+
+/**
+ * CategoryAbstractCellEditor.  Base class to handle the some common
+ * details of cell editing.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryAbstractCellEditor implements TableCellEditor, TreeCellEditor {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected EventListenerList _listenerList = new EventListenerList();
+  protected Object _value;
+  protected ChangeEvent _changeEvent = null;
+  protected int _clickCountToStart = 1;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public Object getCellEditorValue() {
+    return _value;
+  }
+
+  public void setCellEditorValue(Object value) {
+    _value = value;
+  }
+
+  public void setClickCountToStart(int count) {
+    _clickCountToStart = count;
+  }
+
+  public int getClickCountToStart() {
+    return _clickCountToStart;
+  }
+
+  public boolean isCellEditable(EventObject anEvent) {
+    if (anEvent instanceof MouseEvent) {
+      if (((MouseEvent) anEvent).getClickCount() < _clickCountToStart) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public boolean shouldSelectCell(EventObject anEvent) {
+    if (this.isCellEditable(anEvent)) {
+      if (anEvent == null ||
+          ((MouseEvent) anEvent).getClickCount() >= _clickCountToStart) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public boolean stopCellEditing() {
+    fireEditingStopped();
+    return true;
+  }
+
+  public void cancelCellEditing() {
+    fireEditingCanceled();
+  }
+
+  public void addCellEditorListener(CellEditorListener l) {
+    _listenerList.add(CellEditorListener.class, l);
+  }
+
+  public void removeCellEditorListener(CellEditorListener l) {
+    _listenerList.remove(CellEditorListener.class, l);
+  }
+
+  public Component getTreeCellEditorComponent(
+      JTree tree, Object value,
+      boolean isSelected,
+      boolean expanded,
+      boolean leaf, int row) {
+    return null;
+  }
+
+  public Component getTableCellEditorComponent(
+      JTable table, Object value,
+      boolean isSelected,
+      int row, int column) {
+    return null;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+  protected void fireEditingStopped() {
+    Object[] listeners = _listenerList.getListenerList();
+
+    for (int i = listeners.length - 2; i >= 0; i -= 2) {
+      if (listeners[i] == CellEditorListener.class) {
+        if (_changeEvent == null) {
+          _changeEvent = new ChangeEvent(this);
+        }
+
+        ((CellEditorListener) listeners[i + 1]).editingStopped(_changeEvent);
+      }
+    }
+  }
+
+  protected void fireEditingCanceled() {
+    Object[] listeners = _listenerList.getListenerList();
+
+    for (int i = listeners.length - 2; i >= 0; i -= 2) {
+      if (listeners[i] == CellEditorListener.class) {
+        if (_changeEvent == null) {
+          _changeEvent = new ChangeEvent(this);
+        }
+
+        ((CellEditorListener) listeners[i + 1]).editingCanceled(_changeEvent);
+      }
+    }
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java
new file mode 100644
index 0000000..1391fd5
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java
@@ -0,0 +1,84 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+/**
+ * CategoryElement represents a single element or part of a Category.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryElement {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected String _categoryTitle;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public CategoryElement() {
+    super();
+  }
+
+  public CategoryElement(String title) {
+    _categoryTitle = title;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public String getTitle() {
+    return (_categoryTitle);
+  }
+
+  public void setTitle(String title) {
+    _categoryTitle = title;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java
new file mode 100644
index 0000000..63f91ae
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.LogRecordFilter;
+
+import java.util.Enumeration;
+
+/**
+ * An implementation of LogRecordFilter based on a CategoryExplorerModel
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryExplorerLogRecordFilter implements LogRecordFilter {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  protected CategoryExplorerModel _model;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public CategoryExplorerLogRecordFilter(CategoryExplorerModel model) {
+    _model = model;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * @return true if the CategoryExplorer model specified at construction
+   * is accepting the category of the specified LogRecord.  Has a side-effect
+   * of adding the CategoryPath of the LogRecord to the explorer model
+   * if the CategoryPath is new.
+   */
+  public boolean passes(LogRecord record) {
+    CategoryPath path = new CategoryPath(record.getCategory());
+    return _model.isCategoryPathActive(path);
+  }
+
+  /**
+   * Resets the counters for the contained CategoryNodes to zero.
+   */
+  public void reset() {
+    resetAllNodes();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected void resetAllNodes() {
+    Enumeration nodes = _model.getRootCategoryNode().depthFirstEnumeration();
+    CategoryNode current;
+    while (nodes.hasMoreElements()) {
+      current = (CategoryNode) nodes.nextElement();
+      current.resetNumberOfContainedRecords();
+      _model.nodeChanged(current);
+    }
+  }
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java
new file mode 100644
index 0000000..c70b55c
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java
@@ -0,0 +1,347 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import java.awt.AWTEventMulticaster;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Enumeration;
+
+import javax.swing.SwingUtilities;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import org.apache.log4j.lf5.LogRecord;
+
+/**
+ * CategoryExplorerModel
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brent Sprecher
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryExplorerModel extends DefaultTreeModel {
+  private static final long serialVersionUID = -3413887384316015901L;
+
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  protected boolean _renderFatal = true;
+  protected ActionListener _listener = null;
+  protected ActionEvent _event = new ActionEvent(this,
+      ActionEvent.ACTION_PERFORMED,
+      "Nodes Selection changed");
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public CategoryExplorerModel(CategoryNode node) {
+    super(node);
+  }
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public void addLogRecord(LogRecord lr) {
+    CategoryPath path = new CategoryPath(lr.getCategory());
+    addCategory(path); // create category path if it is new
+    CategoryNode node = getCategoryNode(path);
+    node.addRecord(); // update category node
+    if (_renderFatal && lr.isFatal()) {
+      TreeNode[] nodes = getPathToRoot(node);
+      int len = nodes.length;
+      CategoryNode parent;
+
+      // i = 0 gives root node
+      // skip node and root, loop through "parents" in between
+      for (int i = 1; i < len - 1; i++) {
+        parent = (CategoryNode) nodes[i];
+        parent.setHasFatalChildren(true);
+        nodeChanged(parent);
+      }
+      node.setHasFatalRecords(true);
+      nodeChanged(node);
+    }
+  }
+
+  public CategoryNode getRootCategoryNode() {
+    return (CategoryNode) getRoot();
+  }
+
+  public CategoryNode getCategoryNode(String category) {
+    CategoryPath path = new CategoryPath(category);
+    return (getCategoryNode(path));
+  }
+
+  /**
+   * returns null if no CategoryNode exists.
+   */
+  public CategoryNode getCategoryNode(CategoryPath path) {
+    CategoryNode root = (CategoryNode) getRoot();
+    CategoryNode parent = root; // Start condition.
+
+    for (int i = 0; i < path.size(); i++) {
+      CategoryElement element = path.categoryElementAt(i);
+
+      // If the two nodes have matching titles they are considered equal.
+      Enumeration children = parent.children();
+
+      boolean categoryAlreadyExists = false;
+      while (children.hasMoreElements()) {
+        CategoryNode node = (CategoryNode) children.nextElement();
+        String title = node.getTitle().toLowerCase();
+
+        String pathLC = element.getTitle().toLowerCase();
+        if (title.equals(pathLC)) {
+          categoryAlreadyExists = true;
+          // This is now the new parent node.
+          parent = node;
+          break; // out of the while, and back to the for().
+        }
+      }
+
+      if (categoryAlreadyExists == false) {
+        return null; // Didn't find the Node.
+      }
+    }
+
+    return (parent);
+  }
+
+  /**
+   * @return true if all the nodes in the specified CategoryPath are
+   * selected.
+   */
+  public boolean isCategoryPathActive(CategoryPath path) {
+    CategoryNode root = (CategoryNode) getRoot();
+    CategoryNode parent = root; // Start condition.
+    boolean active = false;
+
+    for (int i = 0; i < path.size(); i++) {
+      CategoryElement element = path.categoryElementAt(i);
+
+      // If the two nodes have matching titles they are considered equal.
+      Enumeration children = parent.children();
+
+      boolean categoryAlreadyExists = false;
+      active = false;
+
+      while (children.hasMoreElements()) {
+        CategoryNode node = (CategoryNode) children.nextElement();
+        String title = node.getTitle().toLowerCase();
+
+        String pathLC = element.getTitle().toLowerCase();
+        if (title.equals(pathLC)) {
+          categoryAlreadyExists = true;
+          // This is now the new parent node.
+          parent = node;
+
+          if (parent.isSelected()) {
+            active = true;
+          }
+
+          break; // out of the while, and back to the for().
+        }
+      }
+
+      if (active == false || categoryAlreadyExists == false) {
+        return false;
+      }
+    }
+
+    return (active);
+  }
+
+
+  /**
+   * <p>Method altered by Richard Hurst such that it returns the CategoryNode
+   * corresponding to the CategoryPath</p>
+   *
+   * @param path category path.
+   * @return CategoryNode
+   */
+  public CategoryNode addCategory(CategoryPath path) {
+    CategoryNode root = (CategoryNode) getRoot();
+    CategoryNode parent = root; // Start condition.
+
+    for (int i = 0; i < path.size(); i++) {
+      CategoryElement element = path.categoryElementAt(i);
+
+      // If the two nodes have matching titles they are considered equal.
+      Enumeration children = parent.children();
+
+      boolean categoryAlreadyExists = false;
+      while (children.hasMoreElements()) {
+        CategoryNode node = (CategoryNode) children.nextElement();
+        String title = node.getTitle().toLowerCase();
+
+        String pathLC = element.getTitle().toLowerCase();
+        if (title.equals(pathLC)) {
+          categoryAlreadyExists = true;
+          // This is now the new parent node.
+          parent = node;
+          break;
+        }
+      }
+
+      if (categoryAlreadyExists == false) {
+        // We need to add the node.
+        CategoryNode newNode = new CategoryNode(element.getTitle());
+
+        //This method of adding a new node cause parent roots to be
+        // collapsed.
+        //parent.add( newNode );
+        //reload(parent);
+
+        // This doesn't force the nodes to collapse.
+        insertNodeInto(newNode, parent, parent.getChildCount());
+        refresh(newNode);
+
+        // The newly added node is now the parent.
+        parent = newNode;
+
+      }
+    }
+
+    return parent;
+  }
+
+  public void update(CategoryNode node, boolean selected) {
+    if (node.isSelected() == selected) {
+      return; // nothing was changed, nothing to do
+    }
+    // select parents or deselect children
+    if (selected) {
+      setParentSelection(node, true);
+    } else {
+      setDescendantSelection(node, false);
+    }
+  }
+
+  public void setDescendantSelection(CategoryNode node, boolean selected) {
+    Enumeration descendants = node.depthFirstEnumeration();
+    CategoryNode current;
+    while (descendants.hasMoreElements()) {
+      current = (CategoryNode) descendants.nextElement();
+      // does the current node need to be changed?
+      if (current.isSelected() != selected) {
+        current.setSelected(selected);
+        nodeChanged(current);
+      }
+    }
+    notifyActionListeners();
+  }
+
+  public void setParentSelection(CategoryNode node, boolean selected) {
+    TreeNode[] nodes = getPathToRoot(node);
+    int len = nodes.length;
+    CategoryNode parent;
+
+    // i = 0 gives root node, i=len-1 gives this node
+    // skip the root node
+    for (int i = 1; i < len; i++) {
+      parent = (CategoryNode) nodes[i];
+      if (parent.isSelected() != selected) {
+        parent.setSelected(selected);
+        nodeChanged(parent);
+      }
+    }
+    notifyActionListeners();
+  }
+
+
+  public synchronized void addActionListener(ActionListener l) {
+    _listener = AWTEventMulticaster.add(_listener, l);
+  }
+
+  public synchronized void removeActionListener(ActionListener l) {
+    _listener = AWTEventMulticaster.remove(_listener, l);
+  }
+
+  public void resetAllNodeCounts() {
+    Enumeration nodes = getRootCategoryNode().depthFirstEnumeration();
+    CategoryNode current;
+    while (nodes.hasMoreElements()) {
+      current = (CategoryNode) nodes.nextElement();
+      current.resetNumberOfContainedRecords();
+      nodeChanged(current);
+    }
+  }
+
+  /**
+   * <p>Returns the CategoryPath to the specified CategoryNode</p>
+   *
+   * @param node The target CategoryNode
+   * @return CategoryPath
+   */
+  public TreePath getTreePathToRoot(CategoryNode node) {
+    if (node == null) {
+      return null;
+    }
+    return (new TreePath(getPathToRoot(node)));
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+  protected void notifyActionListeners() {
+    if (_listener != null) {
+      _listener.actionPerformed(_event);
+    }
+  }
+
+  /**
+   * Fires a nodechanged event on the SwingThread.
+   */
+  protected void refresh(final CategoryNode node) {
+    SwingUtilities.invokeLater(new Runnable() {
+      public void run() {
+        nodeChanged(node); // remind the tree to render the new node
+      }
+    });
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java
new file mode 100644
index 0000000..a8e97f5
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java
@@ -0,0 +1,157 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import java.awt.event.MouseEvent;
+
+import javax.swing.JTree;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.tree.TreePath;
+
+/**
+ * CategoryExplorerTree
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brent Sprecher
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryExplorerTree extends JTree {
+  private static final long serialVersionUID = 8066257446951323576L;
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected CategoryExplorerModel _model;
+  protected boolean _rootAlreadyExpanded = false;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Construct a CategoryExplorerTree with a specific model.
+   */
+  public CategoryExplorerTree(CategoryExplorerModel model) {
+    super(model);
+
+    _model = model;
+    init();
+  }
+
+  /**
+   * Construct a CategoryExplorerTree and create a default CategoryExplorerModel.
+   */
+  public CategoryExplorerTree() {
+    super();
+
+    CategoryNode rootNode = new CategoryNode("Categories");
+
+    _model = new CategoryExplorerModel(rootNode);
+
+    setModel(_model);
+
+    init();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public CategoryExplorerModel getExplorerModel() {
+    return (_model);
+  }
+
+  public String getToolTipText(MouseEvent e) {
+
+    try {
+      return super.getToolTipText(e);
+    } catch (Exception ex) {
+      return "";
+    }
+
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected void init() {
+    // Put visible lines on the JTree.
+    putClientProperty("JTree.lineStyle", "Angled");
+
+    // Configure the Tree with the appropriate Renderers and Editors.
+
+    CategoryNodeRenderer renderer = new CategoryNodeRenderer();
+    setEditable(true);
+    setCellRenderer(renderer);
+
+    CategoryNodeEditor editor = new CategoryNodeEditor(_model);
+
+    setCellEditor(new CategoryImmediateEditor(this,
+        new CategoryNodeRenderer(),
+        editor));
+    setShowsRootHandles(true);
+
+    setToolTipText("");
+
+    ensureRootExpansion();
+
+  }
+
+  protected void expandRootNode() {
+    if (_rootAlreadyExpanded) {
+      return;
+    }
+    _rootAlreadyExpanded = true;
+    TreePath path = new TreePath(_model.getRootCategoryNode().getPath());
+    expandPath(path);
+  }
+
+  protected void ensureRootExpansion() {
+    _model.addTreeModelListener(new TreeModelAdapter() {
+      public void treeNodesInserted(TreeModelEvent e) {
+        expandRootNode();
+      }
+    });
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java
new file mode 100644
index 0000000..4c7374b
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java
@@ -0,0 +1,148 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+import java.util.EventObject;
+
+import javax.swing.Icon;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellEditor;
+import javax.swing.tree.TreePath;
+
+/**
+ * CategoryImmediateEditor
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryImmediateEditor extends DefaultTreeCellEditor {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  private CategoryNodeRenderer renderer;
+  protected Icon editingIcon = null;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  public CategoryImmediateEditor(JTree tree,
+      CategoryNodeRenderer renderer,
+      CategoryNodeEditor editor) {
+    super(tree, renderer, editor);
+    this.renderer = renderer;
+    renderer.setIcon(null);
+    renderer.setLeafIcon(null);
+    renderer.setOpenIcon(null);
+    renderer.setClosedIcon(null);
+
+    super.editingIcon = null;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+  public boolean shouldSelectCell(EventObject e) {
+    boolean rv = false;  // only mouse events
+
+    if (e instanceof MouseEvent) {
+      MouseEvent me = (MouseEvent) e;
+      TreePath path = tree.getPathForLocation(me.getX(),
+          me.getY());
+      CategoryNode node = (CategoryNode)
+          path.getLastPathComponent();
+
+      rv = node.isLeaf() /*|| !inCheckBoxHitRegion(me)*/;
+    }
+    return rv;
+  }
+
+  public boolean inCheckBoxHitRegion(MouseEvent e) {
+    TreePath path = tree.getPathForLocation(e.getX(),
+        e.getY());
+    if (path == null) {
+      return false;
+    }
+    CategoryNode node = (CategoryNode) path.getLastPathComponent();
+    boolean rv = false;
+
+    if (true) {
+      // offset and lastRow DefaultTreeCellEditor
+      // protected members
+
+      Rectangle bounds = tree.getRowBounds(lastRow);
+      Dimension checkBoxOffset =
+          renderer.getCheckBoxOffset();
+
+      bounds.translate(offset + checkBoxOffset.width,
+          checkBoxOffset.height);
+
+      rv = bounds.contains(e.getPoint());
+    }
+    return true;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected boolean canEditImmediately(EventObject e) {
+    boolean rv = false;
+
+    if (e instanceof MouseEvent) {
+      MouseEvent me = (MouseEvent) e;
+      rv = inCheckBoxHitRegion(me);
+    }
+
+    return rv;
+  }
+
+  protected void determineOffset(JTree tree, Object value,
+      boolean isSelected, boolean expanded,
+      boolean leaf, int row) {
+    // Very important: means that the tree won't jump around.
+    offset = 0;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java
new file mode 100644
index 0000000..95bdc42
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java
@@ -0,0 +1,197 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+import java.util.Enumeration;
+
+/**
+ * CategoryNode
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryNode extends DefaultMutableTreeNode {
+  private static final long serialVersionUID = 5958994817693177319L;
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected boolean _selected = true;
+  protected int _numberOfContainedRecords = 0;
+  protected int _numberOfRecordsFromChildren = 0;
+  protected boolean _hasFatalChildren = false;
+  protected boolean _hasFatalRecords = false;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  /**
+   *
+   */
+  public CategoryNode(String title) {
+    setUserObject(title);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+  public String getTitle() {
+    return (String) getUserObject();
+  }
+
+  public void setSelected(boolean s) {
+    if (s != _selected) {
+      _selected = s;
+    }
+  }
+
+  public boolean isSelected() {
+    return _selected;
+  }
+
+  /**
+   * @deprecated
+   */
+  public void setAllDescendantsSelected() {
+    Enumeration children = children();
+    while (children.hasMoreElements()) {
+      CategoryNode node = (CategoryNode) children.nextElement();
+      node.setSelected(true);
+      node.setAllDescendantsSelected();
+    }
+  }
+
+  /**
+   * @deprecated
+   */
+  public void setAllDescendantsDeSelected() {
+    Enumeration children = children();
+    while (children.hasMoreElements()) {
+      CategoryNode node = (CategoryNode) children.nextElement();
+      node.setSelected(false);
+      node.setAllDescendantsDeSelected();
+    }
+  }
+
+  public String toString() {
+    return (getTitle());
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof CategoryNode) {
+      CategoryNode node = (CategoryNode) obj;
+      String tit1 = getTitle().toLowerCase();
+      String tit2 = node.getTitle().toLowerCase();
+
+      if (tit1.equals(tit2)) {
+        return (true);
+      }
+    }
+    return (false);
+  }
+
+  public int hashCode() {
+    return (getTitle().hashCode());
+  }
+
+  public void addRecord() {
+    _numberOfContainedRecords++;
+    addRecordToParent();
+  }
+
+  public int getNumberOfContainedRecords() {
+    return _numberOfContainedRecords;
+  }
+
+  public void resetNumberOfContainedRecords() {
+    _numberOfContainedRecords = 0;
+    _numberOfRecordsFromChildren = 0;
+    _hasFatalRecords = false;
+    _hasFatalChildren = false;
+  }
+
+  public boolean hasFatalRecords() {
+    return _hasFatalRecords;
+  }
+
+  public boolean hasFatalChildren() {
+    return _hasFatalChildren;
+  }
+
+  public void setHasFatalRecords(boolean flag) {
+    _hasFatalRecords = flag;
+  }
+
+  public void setHasFatalChildren(boolean flag) {
+    _hasFatalChildren = flag;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected int getTotalNumberOfRecords() {
+    return getNumberOfRecordsFromChildren() + getNumberOfContainedRecords();
+  }
+
+  /**
+   * Passes up the addition from child to parent
+   */
+  protected void addRecordFromChild() {
+    _numberOfRecordsFromChildren++;
+    addRecordToParent();
+  }
+
+  protected int getNumberOfRecordsFromChildren() {
+    return _numberOfRecordsFromChildren;
+  }
+
+  protected void addRecordToParent() {
+    TreeNode parent = getParent();
+    if (parent == null) {
+      return;
+    }
+    ((CategoryNode) parent).addRecordFromChild();
+  }
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java
new file mode 100644
index 0000000..b653cd7
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java
@@ -0,0 +1,291 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+import javax.swing.JCheckBox;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
+import javax.swing.JTree;
+import javax.swing.tree.TreePath;
+
+/**
+ * CategoryNodeEditor
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryNodeEditor extends CategoryAbstractCellEditor {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected CategoryNodeEditorRenderer _renderer;
+  protected CategoryNode _lastEditedNode;
+  protected JCheckBox _checkBox;
+  protected CategoryExplorerModel _categoryModel;
+  protected JTree _tree;
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public CategoryNodeEditor(CategoryExplorerModel model) {
+    _renderer = new CategoryNodeEditorRenderer();
+    _checkBox = _renderer.getCheckBox();
+    _categoryModel = model;
+
+    _checkBox.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        _categoryModel.update(_lastEditedNode, _checkBox.isSelected());
+        stopCellEditing();
+      }
+    });
+
+    _renderer.addMouseListener(new MouseAdapter() {
+      public void mousePressed(MouseEvent e) {
+        if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) {
+          showPopup(_lastEditedNode, e.getX(), e.getY());
+        }
+        stopCellEditing();
+      }
+    });
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public Component getTreeCellEditorComponent(JTree tree, Object value,
+      boolean selected, boolean expanded,
+      boolean leaf, int row) {
+    _lastEditedNode = (CategoryNode) value;
+    _tree = tree;
+
+    return _renderer.getTreeCellRendererComponent(tree,
+        value, selected, expanded,
+        leaf, row, true);
+    // hasFocus ignored
+  }
+
+  public Object getCellEditorValue() {
+    return _lastEditedNode.getUserObject();
+  }
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected JMenuItem createPropertiesMenuItem(final CategoryNode node) {
+    JMenuItem result = new JMenuItem("Properties");
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        showPropertiesDialog(node);
+      }
+    });
+    return result;
+  }
+
+  protected void showPropertiesDialog(CategoryNode node) {
+    JOptionPane.showMessageDialog(
+        _tree,
+        getDisplayedProperties(node),
+        "Category Properties: " + node.getTitle(),
+        JOptionPane.PLAIN_MESSAGE
+    );
+  }
+
+  protected Object getDisplayedProperties(CategoryNode node) {
+    ArrayList result = new ArrayList();
+    result.add("Category: " + node.getTitle());
+    if (node.hasFatalRecords()) {
+      result.add("Contains at least one fatal LogRecord.");
+    }
+    if (node.hasFatalChildren()) {
+      result.add("Contains descendants with a fatal LogRecord.");
+    }
+    result.add("LogRecords in this category alone: " +
+        node.getNumberOfContainedRecords());
+    result.add("LogRecords in descendant categories: " +
+        node.getNumberOfRecordsFromChildren());
+    result.add("LogRecords in this category including descendants: " +
+        node.getTotalNumberOfRecords());
+    return result.toArray();
+  }
+
+  protected void showPopup(CategoryNode node, int x, int y) {
+    JPopupMenu popup = new JPopupMenu();
+    popup.setSize(150, 400);
+    //
+    // Configure the Popup
+    //
+    if (node.getParent() == null) {
+      popup.add(createRemoveMenuItem());
+      popup.addSeparator();
+    }
+    popup.add(createSelectDescendantsMenuItem(node));
+    popup.add(createUnselectDescendantsMenuItem(node));
+    popup.addSeparator();
+    popup.add(createExpandMenuItem(node));
+    popup.add(createCollapseMenuItem(node));
+    popup.addSeparator();
+    popup.add(createPropertiesMenuItem(node));
+    popup.show(_renderer, x, y);
+  }
+
+  protected JMenuItem createSelectDescendantsMenuItem(final CategoryNode node) {
+    JMenuItem selectDescendants =
+        new JMenuItem("Select All Descendant Categories");
+    selectDescendants.addActionListener(
+        new ActionListener() {
+          public void actionPerformed(ActionEvent e) {
+            _categoryModel.setDescendantSelection(node, true);
+          }
+        }
+    );
+    return selectDescendants;
+  }
+
+  protected JMenuItem createUnselectDescendantsMenuItem(final CategoryNode node) {
+    JMenuItem unselectDescendants =
+        new JMenuItem("Deselect All Descendant Categories");
+    unselectDescendants.addActionListener(
+
+        new ActionListener() {
+          public void actionPerformed(ActionEvent e) {
+            _categoryModel.setDescendantSelection(node, false);
+          }
+        }
+
+    );
+    return unselectDescendants;
+  }
+
+  protected JMenuItem createExpandMenuItem(final CategoryNode node) {
+    JMenuItem result = new JMenuItem("Expand All Descendant Categories");
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        expandDescendants(node);
+      }
+    });
+    return result;
+  }
+
+  protected JMenuItem createCollapseMenuItem(final CategoryNode node) {
+    JMenuItem result = new JMenuItem("Collapse All Descendant Categories");
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        collapseDescendants(node);
+      }
+    });
+    return result;
+  }
+
+  /**
+   * This featured was moved from the LogBrokerMonitor class
+   * to the CategoryNodeExplorer so that the Category tree
+   * could be pruned from the Category Explorer popup menu.
+   * This menu option only appears when a user right clicks on
+   * the Category parent node.
+   *
+   * See removeUnusedNodes()
+   */
+  protected JMenuItem createRemoveMenuItem() {
+    JMenuItem result = new JMenuItem("Remove All Empty Categories");
+    result.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        while (removeUnusedNodes() > 0) ;
+      }
+    });
+    return result;
+  }
+
+  protected void expandDescendants(CategoryNode node) {
+    Enumeration descendants = node.depthFirstEnumeration();
+    CategoryNode current;
+    while (descendants.hasMoreElements()) {
+      current = (CategoryNode) descendants.nextElement();
+      expand(current);
+    }
+  }
+
+  protected void collapseDescendants(CategoryNode node) {
+    Enumeration descendants = node.depthFirstEnumeration();
+    CategoryNode current;
+    while (descendants.hasMoreElements()) {
+      current = (CategoryNode) descendants.nextElement();
+      collapse(current);
+    }
+  }
+
+  /**
+   * Removes any inactive nodes from the Category tree.
+   */
+  protected int removeUnusedNodes() {
+    int count = 0;
+    CategoryNode root = _categoryModel.getRootCategoryNode();
+    Enumeration enumeration = root.depthFirstEnumeration();
+    while (enumeration.hasMoreElements()) {
+      CategoryNode node = (CategoryNode) enumeration.nextElement();
+      if (node.isLeaf() && node.getNumberOfContainedRecords() == 0
+          && node.getParent() != null) {
+        _categoryModel.removeNodeFromParent(node);
+        count++;
+      }
+    }
+
+    return count;
+  }
+
+  protected void expand(CategoryNode node) {
+    _tree.expandPath(getTreePath(node));
+  }
+
+  protected TreePath getTreePath(CategoryNode node) {
+    return new TreePath(node.getPath());
+  }
+
+  protected void collapse(CategoryNode node) {
+    _tree.collapsePath(getTreePath(node));
+  }
+
+  //-----------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java
new file mode 100644
index 0000000..57be9fc
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java
@@ -0,0 +1,84 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import java.awt.Component;
+
+import javax.swing.JCheckBox;
+import javax.swing.JTree;
+
+/**
+ * CategoryNodeEditorRenderer
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryNodeEditorRenderer extends CategoryNodeRenderer {
+  private static final long serialVersionUID = -6094804684259929574L;
+
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public Component getTreeCellRendererComponent(
+      JTree tree, Object value,
+      boolean selected, boolean expanded,
+      boolean leaf, int row,
+      boolean hasFocus) {
+    Component c = super.getTreeCellRendererComponent(tree,
+        value, selected, expanded,
+        leaf, row, hasFocus);
+
+    return c;
+  }
+
+  public JCheckBox getCheckBox() {
+    return _checkBox;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java
new file mode 100644
index 0000000..4eb461d
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java
@@ -0,0 +1,151 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import java.awt.*;
+import java.net.URL;
+
+/**
+ * CategoryNodeRenderer
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryNodeRenderer extends DefaultTreeCellRenderer {
+  private static final long serialVersionUID = -6046702673278595048L;
+
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  public static final Color FATAL_CHILDREN = new Color(189, 113, 0);
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected JCheckBox _checkBox = new JCheckBox();
+  protected JPanel _panel = new JPanel();
+  protected static ImageIcon _sat = null;
+//   protected JLabel              _label  = new JLabel();
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  public CategoryNodeRenderer() {
+    _panel.setBackground(UIManager.getColor("Tree.textBackground"));
+
+    if (_sat == null) {
+      // Load the satellite image.
+      String resource =
+          "/org/apache/log4j/lf5/viewer/images/channelexplorer_satellite.gif";
+      URL satURL = getClass().getResource(resource);
+
+      _sat = new ImageIcon(satURL);
+    }
+
+    setOpaque(false);
+    _checkBox.setOpaque(false);
+    _panel.setOpaque(false);
+
+    // The flowlayout set to LEFT is very important so that the editor
+    // doesn't jump around.
+    _panel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+    _panel.add(_checkBox);
+    _panel.add(this);
+
+    setOpenIcon(_sat);
+    setClosedIcon(_sat);
+    setLeafIcon(_sat);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+  public Component getTreeCellRendererComponent(
+      JTree tree, Object value,
+      boolean selected, boolean expanded,
+      boolean leaf, int row,
+      boolean hasFocus) {
+
+    CategoryNode node = (CategoryNode) value;
+    //FileNode node = (FileNode)value;
+    //String s = tree.convertValueToText(value, selected,
+    //						   expanded, leaf, row, hasFocus);
+
+    super.getTreeCellRendererComponent(
+        tree, value, selected, expanded,
+        leaf, row, hasFocus);
+
+    if (row == 0) {
+      // Root row -- no check box
+      _checkBox.setVisible(false);
+    } else {
+      _checkBox.setVisible(true);
+      _checkBox.setSelected(node.isSelected());
+    }
+    String toolTip = buildToolTip(node);
+    _panel.setToolTipText(toolTip);
+    if (node.hasFatalChildren()) {
+      this.setForeground(FATAL_CHILDREN);
+    }
+    if (node.hasFatalRecords()) {
+      this.setForeground(Color.red);
+    }
+
+    return _panel;
+  }
+
+  public Dimension getCheckBoxOffset() {
+    return new Dimension(0, 0);
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  protected String buildToolTip(CategoryNode node) {
+    StringBuffer result = new StringBuffer();
+    result.append(node.getTitle()).append(" contains a total of ");
+    result.append(node.getTotalNumberOfRecords());
+    result.append(" LogRecords.");
+    result.append(" Right-click for more info.");
+    return result.toString();
+  }
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java
new file mode 100644
index 0000000..8cc3da1
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java
@@ -0,0 +1,157 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import java.util.LinkedList;
+import java.util.StringTokenizer;
+
+/**
+ * CategoryPath is a collection of CategoryItems which represent a
+ * path of categories.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryPath {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+  protected LinkedList _categoryElements = new LinkedList();
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  public CategoryPath() {
+    super();
+  }
+
+  /**
+   * Construct a CategoryPath.  If the category is null, it defaults to "Debug".
+   */
+  public CategoryPath(String category) {
+    String processedCategory = category;
+
+    if (processedCategory == null) {
+      processedCategory = "Debug";
+    }
+
+    processedCategory = processedCategory.replace('/', '.');
+    processedCategory = processedCategory.replace('\\', '.');
+
+    StringTokenizer st = new StringTokenizer(processedCategory, ".");
+    while (st.hasMoreTokens()) {
+      String element = st.nextToken();
+      addCategoryElement(new CategoryElement(element));
+    }
+  }
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * returns the number of CategoryElements.
+   */
+  public int size() {
+    int count = _categoryElements.size();
+
+    return (count);
+  }
+
+  public boolean isEmpty() {
+    boolean empty = false;
+
+    if (_categoryElements.size() == 0) {
+      empty = true;
+    }
+
+    return (empty);
+  }
+
+
+  /**
+   * Removes all categoryElements.
+   */
+  public void removeAllCategoryElements() {
+    _categoryElements.clear();
+  }
+
+  /**
+   * Adds the specified categoryElement to the end of the categoryElement set.
+   */
+  public void addCategoryElement(CategoryElement categoryElement) {
+    _categoryElements.addLast(categoryElement);
+  }
+
+  /**
+   * Returns the CategoryElement at the specified index.
+   */
+  public CategoryElement categoryElementAt(int index) {
+    return ((CategoryElement) _categoryElements.get(index));
+  }
+
+
+  public String toString() {
+    StringBuffer out = new StringBuffer(100);
+
+    out.append("\n");
+    out.append("===========================\n");
+    out.append("CategoryPath:                   \n");
+    out.append("---------------------------\n");
+
+    out.append("\nCategoryPath:\n\t");
+
+    if (this.size() > 0) {
+      for (int i = 0; i < this.size(); i++) {
+        out.append(this.categoryElementAt(i).toString());
+        out.append("\n\t");
+      }
+    } else {
+      out.append("<<NONE>>");
+    }
+
+    out.append("\n");
+    out.append("===========================\n");
+
+    return (out.toString());
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java
new file mode 100644
index 0000000..7323dcc
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java
@@ -0,0 +1,75 @@
+/*
+ * 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.lf5.viewer.categoryexplorer;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+
+/**
+ * Default implementation of TreeModelListener which does nothing.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class TreeModelAdapter implements TreeModelListener {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+  public void treeNodesChanged(TreeModelEvent e) {
+  }
+
+  public void treeNodesInserted(TreeModelEvent e) {
+  }
+
+  public void treeNodesRemoved(TreeModelEvent e) {
+  }
+
+  public void treeStructureChanged(TreeModelEvent e) {
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java
new file mode 100644
index 0000000..a94ffab
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java
@@ -0,0 +1,466 @@
+/*
+ * 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.lf5.viewer.configure;
+
+import java.awt.Color;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.tree.TreePath;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogLevelFormatException;
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+import org.apache.log4j.lf5.viewer.LogTable;
+import org.apache.log4j.lf5.viewer.LogTableColumn;
+import org.apache.log4j.lf5.viewer.LogTableColumnFormatException;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerModel;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerTree;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryNode;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryPath;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * <p>ConfigurationManager handles the storage and retrival of the state of
+ * the CategoryExplorer
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class ConfigurationManager extends Object {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+  private static final String CONFIG_FILE_NAME = "lf5_configuration.xml";
+  private static final String NAME = "name";
+  private static final String PATH = "path";
+  private static final String SELECTED = "selected";
+  private static final String EXPANDED = "expanded";
+  private static final String CATEGORY = "category";
+  private static final String FIRST_CATEGORY_NAME = "Categories";
+  private static final String LEVEL = "level";
+  private static final String COLORLEVEL = "colorlevel";
+  private static final String RED = "red";
+  private static final String GREEN = "green";
+  private static final String BLUE = "blue";
+  private static final String COLUMN = "column";
+  private static final String NDCTEXTFILTER = "searchtext";
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+  private LogBrokerMonitor _monitor = null;
+  private LogTable _table = null;
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  public ConfigurationManager(LogBrokerMonitor monitor, LogTable table) {
+    super();
+    _monitor = monitor;
+    _table = table;
+    load();
+  }
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  public void save() {
+    CategoryExplorerModel model = _monitor.getCategoryExplorerTree().getExplorerModel();
+    CategoryNode root = model.getRootCategoryNode();
+
+    StringBuffer xml = new StringBuffer(2048);
+    openXMLDocument(xml);
+    openConfigurationXML(xml);
+    processLogRecordFilter(_monitor.getNDCTextFilter(), xml);
+    processLogLevels(_monitor.getLogLevelMenuItems(), xml);
+    processLogLevelColors(_monitor.getLogLevelMenuItems(),
+        LogLevel.getLogLevelColorMap(), xml);
+    processLogTableColumns(LogTableColumn.getLogTableColumns(), xml);
+    processConfigurationNode(root, xml);
+    closeConfigurationXML(xml);
+    store(xml.toString());
+  }
+
+  public void reset() {
+    deleteConfigurationFile();
+    collapseTree();
+    selectAllNodes();
+  }
+
+  public static String treePathToString(TreePath path) {
+    // count begins at one so as to not include the 'Categories' - root category
+    StringBuffer sb = new StringBuffer();
+    CategoryNode n = null;
+    Object[] objects = path.getPath();
+    for (int i = 1; i < objects.length; i++) {
+      n = (CategoryNode) objects[i];
+      if (i > 1) {
+        sb.append(".");
+      }
+      sb.append(n.getTitle());
+    }
+    return sb.toString();
+  }
+
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+  protected void load() {
+    File file = new File(getFilename());
+    if (file.exists()) {
+      try {
+        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.
+            newInstance();
+        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+        Document doc = docBuilder.parse(file);
+        processRecordFilter(doc);
+        processCategories(doc);
+        processLogLevels(doc);
+        processLogLevelColors(doc);
+        processLogTableColumns(doc);
+      } catch (Exception e) {
+        // ignore all error and just continue as if there was no
+        // configuration xml file but do report a message
+        System.err.println("Unable process configuration file at " +
+            getFilename() + ". Error Message=" + e.getMessage());
+      }
+    }
+
+  }
+
+  // Added in version 1.2 - reads in the NDC text filter from the
+  // xml configuration file.  If the value of the filter is not null
+  // or an empty string ("") then the manager will set the LogBrokerMonitor's
+  // LogRecordFilter to use the NDC LogRecordFilter.  Otherwise, the
+  // LogBrokerMonitor will use the default LogRecordFilter.
+  protected void processRecordFilter(Document doc) {
+    NodeList nodeList = doc.getElementsByTagName(NDCTEXTFILTER);
+
+    // there is only one value stored
+    Node n = nodeList.item(0);
+    // add check for backwards compatibility  as this feature was added in
+    // version 1.2
+    if (n == null) {
+      return;
+    }
+
+    NamedNodeMap map = n.getAttributes();
+    String text = getValue(map, NAME);
+
+    if (text == null || text.equals("")) {
+      return;
+    }
+    _monitor.setNDCLogRecordFilter(text);
+  }
+
+  protected void processCategories(Document doc) {
+    CategoryExplorerTree tree = _monitor.getCategoryExplorerTree();
+    CategoryExplorerModel model = tree.getExplorerModel();
+    NodeList nodeList = doc.getElementsByTagName(CATEGORY);
+
+    // determine where the starting node is
+    NamedNodeMap map = nodeList.item(0).getAttributes();
+    int j = (getValue(map, NAME).equalsIgnoreCase(FIRST_CATEGORY_NAME)) ? 1 : 0;
+    // iterate backwards throught the nodeList so that expansion of the
+    // list can occur
+    for (int i = nodeList.getLength() - 1; i >= j; i--) {
+      Node n = nodeList.item(i);
+      map = n.getAttributes();
+      CategoryNode chnode = model.addCategory(new CategoryPath(getValue(map, PATH)));
+      chnode.setSelected((getValue(map, SELECTED).equalsIgnoreCase("true")) ? true : false);
+      if (getValue(map, EXPANDED).equalsIgnoreCase("true")) ;
+      tree.expandPath(model.getTreePathToRoot(chnode));
+    }
+
+  }
+
+  protected void processLogLevels(Document doc) {
+    NodeList nodeList = doc.getElementsByTagName(LEVEL);
+    Map menuItems = _monitor.getLogLevelMenuItems();
+
+    for (int i = 0; i < nodeList.getLength(); i++) {
+      Node n = nodeList.item(i);
+      NamedNodeMap map = n.getAttributes();
+      String name = getValue(map, NAME);
+      try {
+        JCheckBoxMenuItem item =
+            (JCheckBoxMenuItem) menuItems.get(LogLevel.valueOf(name));
+        item.setSelected(getValue(map, SELECTED).equalsIgnoreCase("true"));
+      } catch (LogLevelFormatException e) {
+        // ignore it will be on by default.
+      }
+    }
+  }
+
+  protected void processLogLevelColors(Document doc) {
+    NodeList nodeList = doc.getElementsByTagName(COLORLEVEL);
+    LogLevel.getLogLevelColorMap();
+
+    for (int i = 0; i < nodeList.getLength(); i++) {
+      Node n = nodeList.item(i);
+      // check for backwards compatibility since this feature was added
+      // in version 1.3
+      if (n == null) {
+        return;
+      }
+
+      NamedNodeMap map = n.getAttributes();
+      String name = getValue(map, NAME);
+      try {
+        LogLevel level = LogLevel.valueOf(name);
+        int red = Integer.parseInt(getValue(map, RED));
+        int green = Integer.parseInt(getValue(map, GREEN));
+        int blue = Integer.parseInt(getValue(map, BLUE));
+        Color c = new Color(red, green, blue);
+        if (level != null) {
+          level.setLogLevelColorMap(level, c);
+        }
+
+      } catch (LogLevelFormatException e) {
+        // ignore it will be on by default.
+      }
+    }
+  }
+
+  protected void processLogTableColumns(Document doc) {
+    NodeList nodeList = doc.getElementsByTagName(COLUMN);
+    Map menuItems = _monitor.getLogTableColumnMenuItems();
+    List selectedColumns = new ArrayList();
+    for (int i = 0; i < nodeList.getLength(); i++) {
+      Node n = nodeList.item(i);
+      // check for backwards compatibility since this feature was added
+      // in version 1.3
+      if (n == null) {
+        return;
+      }
+      NamedNodeMap map = n.getAttributes();
+      String name = getValue(map, NAME);
+      try {
+        LogTableColumn column = LogTableColumn.valueOf(name);
+        JCheckBoxMenuItem item =
+            (JCheckBoxMenuItem) menuItems.get(column);
+        item.setSelected(getValue(map, SELECTED).equalsIgnoreCase("true"));
+
+        if (item.isSelected()) {
+          selectedColumns.add(column);
+        }
+      } catch (LogTableColumnFormatException e) {
+        // ignore it will be on by default.
+      }
+
+      if (selectedColumns.isEmpty()) {
+        _table.setDetailedView();
+      } else {
+        _table.setView(selectedColumns);
+      }
+
+    }
+  }
+
+  protected String getValue(NamedNodeMap map, String attr) {
+    Node n = map.getNamedItem(attr);
+    return n.getNodeValue();
+  }
+
+  protected void collapseTree() {
+    // collapse everything except the first category
+    CategoryExplorerTree tree = _monitor.getCategoryExplorerTree();
+    for (int i = tree.getRowCount() - 1; i > 0; i--) {
+      tree.collapseRow(i);
+    }
+  }
+
+  protected void selectAllNodes() {
+    CategoryExplorerModel model = _monitor.getCategoryExplorerTree().getExplorerModel();
+    CategoryNode root = model.getRootCategoryNode();
+    Enumeration all = root.breadthFirstEnumeration();
+    CategoryNode n = null;
+    while (all.hasMoreElements()) {
+      n = (CategoryNode) all.nextElement();
+      n.setSelected(true);
+    }
+  }
+
+  protected void store(String s) {
+
+    try {
+      PrintWriter writer = new PrintWriter(new FileWriter(getFilename()));
+      writer.print(s);
+      writer.close();
+    } catch (IOException e) {
+      // do something with this error.
+      e.printStackTrace();
+    }
+
+  }
+
+  protected void deleteConfigurationFile() {
+    try {
+      File f = new File(getFilename());
+      if (f.exists()) {
+        f.delete();
+      }
+    } catch (SecurityException e) {
+      System.err.println("Cannot delete " + getFilename() +
+          " because a security violation occured.");
+    }
+  }
+
+  protected String getFilename() {
+    String home = System.getProperty("user.home");
+    String sep = System.getProperty("file.separator");
+
+    return home + sep + "lf5" + sep + CONFIG_FILE_NAME;
+  }
+
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+  private void processConfigurationNode(CategoryNode node, StringBuffer xml) {
+    CategoryExplorerModel model = _monitor.getCategoryExplorerTree().getExplorerModel();
+
+    Enumeration all = node.breadthFirstEnumeration();
+    CategoryNode n = null;
+    while (all.hasMoreElements()) {
+      n = (CategoryNode) all.nextElement();
+      exportXMLElement(n, model.getTreePathToRoot(n), xml);
+    }
+
+  }
+
+  private void processLogLevels(Map logLevelMenuItems, StringBuffer xml) {
+    xml.append("\t<loglevels>\r\n");
+    Iterator it = logLevelMenuItems.keySet().iterator();
+    while (it.hasNext()) {
+      LogLevel level = (LogLevel) it.next();
+      JCheckBoxMenuItem item = (JCheckBoxMenuItem) logLevelMenuItems.get(level);
+      exportLogLevelXMLElement(level.getLabel(), item.isSelected(), xml);
+    }
+
+    xml.append("\t</loglevels>\r\n");
+  }
+
+  private void processLogLevelColors(Map logLevelMenuItems, Map logLevelColors, StringBuffer xml) {
+    xml.append("\t<loglevelcolors>\r\n");
+    // iterate through the list of log levels being used (log4j, jdk1.4, custom levels)
+    Iterator it = logLevelMenuItems.keySet().iterator();
+    while (it.hasNext()) {
+      LogLevel level = (LogLevel) it.next();
+      // for each level, get the associated color from the log level color map
+      Color color = (Color) logLevelColors.get(level);
+      exportLogLevelColorXMLElement(level.getLabel(), color, xml);
+    }
+
+    xml.append("\t</loglevelcolors>\r\n");
+  }
+
+
+  private void processLogTableColumns(List logTableColumnMenuItems, StringBuffer xml) {
+    xml.append("\t<logtablecolumns>\r\n");
+    Iterator it = logTableColumnMenuItems.iterator();
+    while (it.hasNext()) {
+      LogTableColumn column = (LogTableColumn) it.next();
+      JCheckBoxMenuItem item = _monitor.getTableColumnMenuItem(column);
+      exportLogTableColumnXMLElement(column.getLabel(), item.isSelected(), xml);
+    }
+
+    xml.append("\t</logtablecolumns>\r\n");
+  }
+
+  // Added in version 1.2 - stores the NDC text filter in the xml file
+  // for future use.
+  private void processLogRecordFilter(String text, StringBuffer xml) {
+    xml.append("\t<").append(NDCTEXTFILTER).append(" ");
+    xml.append(NAME).append("=\"").append(text).append("\"");
+    xml.append("/>\r\n");
+  }
+
+  private void openXMLDocument(StringBuffer xml) {
+    xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n");
+  }
+
+  private void openConfigurationXML(StringBuffer xml) {
+    xml.append("<configuration>\r\n");
+  }
+
+  private void closeConfigurationXML(StringBuffer xml) {
+    xml.append("</configuration>\r\n");
+  }
+
+  private void exportXMLElement(CategoryNode node, TreePath path, StringBuffer xml) {
+    CategoryExplorerTree tree = _monitor.getCategoryExplorerTree();
+
+    xml.append("\t<").append(CATEGORY).append(" ");
+    xml.append(NAME).append("=\"").append(node.getTitle()).append("\" ");
+    xml.append(PATH).append("=\"").append(treePathToString(path)).append("\" ");
+    xml.append(EXPANDED).append("=\"").append(tree.isExpanded(path)).append("\" ");
+    xml.append(SELECTED).append("=\"").append(node.isSelected()).append("\"/>\r\n");
+  }
+
+  private void exportLogLevelXMLElement(String label, boolean selected, StringBuffer xml) {
+    xml.append("\t\t<").append(LEVEL).append(" ").append(NAME);
+    xml.append("=\"").append(label).append("\" ");
+    xml.append(SELECTED).append("=\"").append(selected);
+    xml.append("\"/>\r\n");
+  }
+
+  private void exportLogLevelColorXMLElement(String label, Color color, StringBuffer xml) {
+    xml.append("\t\t<").append(COLORLEVEL).append(" ").append(NAME);
+    xml.append("=\"").append(label).append("\" ");
+    xml.append(RED).append("=\"").append(color.getRed()).append("\" ");
+    xml.append(GREEN).append("=\"").append(color.getGreen()).append("\" ");
+    xml.append(BLUE).append("=\"").append(color.getBlue());
+    xml.append("\"/>\r\n");
+  }
+
+  private void exportLogTableColumnXMLElement(String label, boolean selected, StringBuffer xml) {
+    xml.append("\t\t<").append(COLUMN).append(" ").append(NAME);
+    xml.append("=\"").append(label).append("\" ");
+    xml.append(SELECTED).append("=\"").append(selected);
+    xml.append("\"/>\r\n");
+  }
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces:
+  //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java
new file mode 100644
index 0000000..6ff275d
--- /dev/null
+++ b/modules/lf5/src/main/java/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java
@@ -0,0 +1,293 @@
+/*
+ * 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.lf5.viewer.configure;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+
+/**
+ * <p>MRUFileManager handles the storage and retrival the most
+ * recently opened log files.
+ *
+ * @author Brad Marlborough
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class MRUFileManager {
+  //--------------------------------------------------------------------------
+  //   Constants:
+  //--------------------------------------------------------------------------
+  private static final String CONFIG_FILE_NAME = "mru_file_manager";
+  private static final int DEFAULT_MAX_SIZE = 3;
+
+  //--------------------------------------------------------------------------
+  //   Protected Variables:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Private Variables:
+  //--------------------------------------------------------------------------
+  private int _maxSize = 0;
+  private LinkedList _mruFileList;
+
+  //--------------------------------------------------------------------------
+  //   Constructors:
+  //--------------------------------------------------------------------------
+  public MRUFileManager() {
+    load();
+    setMaxSize(DEFAULT_MAX_SIZE);
+  }
+
+  public MRUFileManager(int maxSize) {
+    load();
+    setMaxSize(maxSize);
+  }
+  //--------------------------------------------------------------------------
+  //   Public Methods:
+  //--------------------------------------------------------------------------
+
+  /**
+   * Saves a list of MRU files out to a file.
+   */
+  public void save() {
+    File file = new File(getFilename());
+
+    try {
+      ObjectOutputStream oos = new ObjectOutputStream(new
+          FileOutputStream(file));
+      oos.writeObject(_mruFileList);
+      oos.flush();
+      oos.close();
+    } catch (Exception e) {
+      // do nothing
+      e.printStackTrace();
+    }
+  }
+
+  /**
+   * Gets the size of the MRU file list.
+   */
+  public int size() {
+    return _mruFileList.size();
+  }
+
+  /**
+   * Returns a particular file name stored in a MRU file
+   * list based on an index value.
+   */
+  public Object getFile(int index) {
+    if (index < size()) {
+      return _mruFileList.get(index);
+    }
+
+    return null;
+  }
+
+  /**
+   * Returns a input stream to the resource at the specified index
+   */
+  public InputStream getInputStream(int index) throws IOException,
+      FileNotFoundException {
+    if (index < size()) {
+      Object o = getFile(index);
+      if (o instanceof File) {
+        return getInputStream((File) o);
+      } else {
+        return getInputStream((URL) o);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Adds a file name to the MRU file list.
+   */
+  public void set(File file) {
+    setMRU(file);
+  }
+
+  /**
+   * Adds a url to the MRU file list.
+   */
+  public void set(URL url) {
+    setMRU(url);
+  }
+
+  /**
+   * Gets the list of files stored in the MRU file list.
+   */
+  public String[] getMRUFileList() {
+    if (size() == 0) {
+      return null;
+    }
+
+    String[] ss = new String[size()];
+
+    for (int i = 0; i < size(); i++) {
+      Object o = getFile(i);
+      if (o instanceof File) {
+        ss[i] = ((File) o).getAbsolutePath();
+      } else // must be a url
+      {
+        ss[i] = o.toString();
+      }
+
+    }
+
+    return ss;
+  }
+
+  /**
+   * Moves the the index to the top of the MRU List
+   *
+   * @param index The index to be first in the mru list
+   */
+  public void moveToTop(int index) {
+    _mruFileList.add(0, _mruFileList.remove(index));
+  }
+
+  /**
+   * Creates the directory where the MRU file list will be written.
+   * The "lf5" directory is created in the Documents and Settings
+   * directory on Windows 2000 machines and where ever the user.home
+   * variable points on all other platforms.
+   */
+  public static void createConfigurationDirectory() {
+    String home = System.getProperty("user.home");
+    String sep = System.getProperty("file.separator");
+    File f = new File(home + sep + "lf5");
+    if (!f.exists()) {
+      try {
+        f.mkdir();
+      } catch (SecurityException e) {
+        e.printStackTrace();
+      }
+    }
+
+  }
+  //--------------------------------------------------------------------------
+  //   Protected Methods:
+  //--------------------------------------------------------------------------
+  /**
+   * Gets an input stream for the corresponding file.
+   *
+   * @param file The file to create the input stream from.
+   * @return InputStream
+   */
+  protected InputStream getInputStream(File file) throws IOException,
+      FileNotFoundException {
+    BufferedInputStream reader =
+        new BufferedInputStream(new FileInputStream(file));
+
+    return reader;
+  }
+
+  /**
+   * Gets an input stream for the corresponding URL.
+   *
+   * @param url The url to create the input stream from.
+   * @return InputStream
+   */
+  protected InputStream getInputStream(URL url) throws IOException {
+    return url.openStream();
+  }
+
+  /**
+   * Adds an object to the mru.
+   */
+  protected void setMRU(Object o) {
+    int index = _mruFileList.indexOf(o);
+
+    if (index == -1) {
+      _mruFileList.add(0, o);
+      setMaxSize(_maxSize);
+    } else {
+      moveToTop(index);
+    }
+  }
+
+  /**
+   * Loads the MRU file list in from a file and stores it in a LinkedList.
+   * If no file exists, a new LinkedList is created.
+   */
+  protected void load() {
+    createConfigurationDirectory();
+    File file = new File(getFilename());
+    if (file.exists()) {
+      try {
+        ObjectInputStream ois = new ObjectInputStream(
+            new FileInputStream(file));
+        _mruFileList = (LinkedList) ois.readObject();
+        ois.close();
+
+        // check that only files and url are in linked list
+        Iterator it = _mruFileList.iterator();
+        while (it.hasNext()) {
+          Object o = it.next();
+          if (!(o instanceof File) && !(o instanceof URL)) {
+            it.remove();
+          }
+        }
+      } catch (Exception e) {
+        _mruFileList = new LinkedList();
+      }
+    } else {
+      _mruFileList = new LinkedList();
+    }
+
+  }
+
+  protected String getFilename() {
+    String home = System.getProperty("user.home");
+    String sep = System.getProperty("file.separator");
+
+    return home + sep + "lf5" + sep + CONFIG_FILE_NAME;
+  }
+
+  /**
+   * Ensures that the MRU list will have a MaxSize.
+   */
+  protected void setMaxSize(int maxSize) {
+    if (maxSize < _mruFileList.size()) {
+      for (int i = 0; i < _mruFileList.size() - maxSize; i++) {
+        _mruFileList.removeLast();
+      }
+    }
+
+    _maxSize = maxSize;
+  }
+  //--------------------------------------------------------------------------
+  //   Private Methods:
+  //--------------------------------------------------------------------------
+
+  //--------------------------------------------------------------------------
+  //   Nested Top-Level Classes or Interfaces
+  //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/modules/lf5/src/main/resources/org/apache/log4j/lf5/config/defaultconfig.properties b/modules/lf5/src/main/resources/org/apache/log4j/lf5/config/defaultconfig.properties
new file mode 100644
index 0000000..5c066d9
--- /dev/null
+++ b/modules/lf5/src/main/resources/org/apache/log4j/lf5/config/defaultconfig.properties
@@ -0,0 +1,31 @@
+# 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.
+
+# For the general syntax of property based configuration files see the
+# documenation of org.apache.log4j.PropertyConfigurator.
+
+# The root category uses the appender called A1. Since no priority is
+# specified, the root category assumes the default priority for root
+# which is DEBUG in log4j. The root category is the only category that
+# has a default priority. All other categories need not be assigned a
+# priority in which case they inherit their priority from the
+# hierarchy.
+
+log4j.rootCategory=, A1
+
+# A1 is set to be a LogMonitorAppender which outputs to a swing
+# logging console. 
+  
+log4j.appender.A1=org.apache.log4j.lf5.LF5Appender
diff --git a/modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/channelexplorer_new.gif b/modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/channelexplorer_new.gif
new file mode 100644
index 0000000..1cc488d
--- /dev/null
+++ b/modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/channelexplorer_new.gif
Binary files differ
diff --git a/modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/channelexplorer_satellite.gif b/modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/channelexplorer_satellite.gif
new file mode 100644
index 0000000..f706cd9
--- /dev/null
+++ b/modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/channelexplorer_satellite.gif
Binary files differ
diff --git a/modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/lf5_small_icon.gif b/modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/lf5_small_icon.gif
new file mode 100644
index 0000000..c17300c
--- /dev/null
+++ b/modules/lf5/src/main/resources/org/apache/log4j/lf5/viewer/images/lf5_small_icon.gif
Binary files differ
diff --git a/modules/net/pom.xml b/modules/net/pom.xml
new file mode 100644
index 0000000..0d13fae
--- /dev/null
+++ b/modules/net/pom.xml
@@ -0,0 +1,65 @@
+<!--
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-modules</artifactId>
+      <version>1.4.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.log4j</groupId>
+  <artifactId>log4j-net</artifactId>
+  <name>Apache Log4j-Net</name>
+  <description>Apache Log4j Network Module</description>
+  <packaging>bundle</packaging>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.0.1</version>
+        <extensions>true</extensions>
+        <configuration>
+            <instructions>
+                <Export-Package>org.apache.log4j.net.*</Export-Package>
+                <Import-Package>javax.mail.*;resolution:=optional,
+                                *</Import-Package>
+                <Bundle-DocURL>http://logging.apache.org/log4j</Bundle-DocURL>
+            </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <version>${project.version}</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.mail</groupId>
+      <artifactId>mail</artifactId>
+      <version>1.4.1</version>
+      <optional>true</optional>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/src/main/java/org/apache/log4j/net/JMSAppender.java b/modules/net/src/main/java/org/apache/log4j/net/JMSAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/JMSAppender.java
rename to modules/net/src/main/java/org/apache/log4j/net/JMSAppender.java
diff --git a/src/main/java/org/apache/log4j/net/JMSSink.java b/modules/net/src/main/java/org/apache/log4j/net/JMSSink.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/JMSSink.java
rename to modules/net/src/main/java/org/apache/log4j/net/JMSSink.java
diff --git a/src/main/java/org/apache/log4j/net/SMTPAppender.java b/modules/net/src/main/java/org/apache/log4j/net/SMTPAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/SMTPAppender.java
rename to modules/net/src/main/java/org/apache/log4j/net/SMTPAppender.java
diff --git a/src/main/java/org/apache/log4j/net/SimpleSocketServer.java b/modules/net/src/main/java/org/apache/log4j/net/SimpleSocketServer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/SimpleSocketServer.java
rename to modules/net/src/main/java/org/apache/log4j/net/SimpleSocketServer.java
diff --git a/src/main/java/org/apache/log4j/net/SocketAppender.java b/modules/net/src/main/java/org/apache/log4j/net/SocketAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/SocketAppender.java
rename to modules/net/src/main/java/org/apache/log4j/net/SocketAppender.java
diff --git a/src/main/java/org/apache/log4j/net/SocketHubAppender.java b/modules/net/src/main/java/org/apache/log4j/net/SocketHubAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/SocketHubAppender.java
rename to modules/net/src/main/java/org/apache/log4j/net/SocketHubAppender.java
diff --git a/src/main/java/org/apache/log4j/net/SocketNode.java b/modules/net/src/main/java/org/apache/log4j/net/SocketNode.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/SocketNode.java
rename to modules/net/src/main/java/org/apache/log4j/net/SocketNode.java
diff --git a/src/main/java/org/apache/log4j/net/SocketServer.java b/modules/net/src/main/java/org/apache/log4j/net/SocketServer.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/SocketServer.java
rename to modules/net/src/main/java/org/apache/log4j/net/SocketServer.java
diff --git a/src/main/java/org/apache/log4j/net/SyslogAppender.java b/modules/net/src/main/java/org/apache/log4j/net/SyslogAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/SyslogAppender.java
rename to modules/net/src/main/java/org/apache/log4j/net/SyslogAppender.java
diff --git a/src/main/java/org/apache/log4j/net/TelnetAppender.java b/modules/net/src/main/java/org/apache/log4j/net/TelnetAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/TelnetAppender.java
rename to modules/net/src/main/java/org/apache/log4j/net/TelnetAppender.java
diff --git a/src/main/java/org/apache/log4j/net/ZeroConfSupport.java b/modules/net/src/main/java/org/apache/log4j/net/ZeroConfSupport.java
similarity index 100%
rename from src/main/java/org/apache/log4j/net/ZeroConfSupport.java
rename to modules/net/src/main/java/org/apache/log4j/net/ZeroConfSupport.java
diff --git a/src/main/java/org/apache/log4j/net/package.html b/modules/net/src/main/java/org/apache/log4j/net/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/net/package.html
rename to modules/net/src/main/java/org/apache/log4j/net/package.html
diff --git a/tests/src/java/org/apache/log4j/net/SMTPAppenderTest.java b/modules/net/src/test/java/org/apache/log4j/net/SMTPAppenderTest.java
similarity index 95%
rename from tests/src/java/org/apache/log4j/net/SMTPAppenderTest.java
rename to modules/net/src/test/java/org/apache/log4j/net/SMTPAppenderTest.java
index 583a5bd..b6ecc22 100644
--- a/tests/src/java/org/apache/log4j/net/SMTPAppenderTest.java
+++ b/modules/net/src/test/java/org/apache/log4j/net/SMTPAppenderTest.java
@@ -54,7 +54,7 @@
      * Tests that triggeringPolicy element will set evaluator.
      */
   public void testTrigger() {
-      DOMConfigurator.configure("input/xml/smtpAppender1.xml");
+      DOMConfigurator.configure("target/test-classes/input/xml/smtpAppender1.xml");
       SMTPAppender appender = (SMTPAppender) Logger.getRootLogger().getAppender("A1");
       TriggeringEventEvaluator evaluator = appender.getEvaluator();
       assertTrue(evaluator instanceof MockTriggeringEventEvaluator);
diff --git a/tests/src/java/org/apache/log4j/net/ShortSocketServer.java b/modules/net/src/test/java/org/apache/log4j/net/ShortSocketServer.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/net/ShortSocketServer.java
rename to modules/net/src/test/java/org/apache/log4j/net/ShortSocketServer.java
diff --git a/tests/src/java/org/apache/log4j/net/SocketAppenderTest.java b/modules/net/src/test/java/org/apache/log4j/net/SocketAppenderTest.java
similarity index 96%
rename from tests/src/java/org/apache/log4j/net/SocketAppenderTest.java
rename to modules/net/src/test/java/org/apache/log4j/net/SocketAppenderTest.java
index 8a18d0a..29b4ce2 100644
--- a/tests/src/java/org/apache/log4j/net/SocketAppenderTest.java
+++ b/modules/net/src/test/java/org/apache/log4j/net/SocketAppenderTest.java
@@ -34,7 +34,7 @@
     /* JUnit's setUp and tearDown */
 
     protected void setUp() {
-        DOMConfigurator.configure("input/xml/SocketAppenderTestConfig.xml");
+        DOMConfigurator.configure("target/test-classes/input/xml/SocketAppenderTestConfig.xml");
 
         logger = Logger.getLogger(SocketAppenderTest.class);
         secondary = (LastOnlyAppender) Logger.getLogger(
diff --git a/tests/src/java/org/apache/log4j/net/SocketServerTestCase.java b/modules/net/src/test/java/org/apache/log4j/net/SocketServerTestCase.java
similarity index 95%
rename from tests/src/java/org/apache/log4j/net/SocketServerTestCase.java
rename to modules/net/src/test/java/org/apache/log4j/net/SocketServerTestCase.java
index 5919407..d72f6b2 100644
--- a/tests/src/java/org/apache/log4j/net/SocketServerTestCase.java
+++ b/modules/net/src/test/java/org/apache/log4j/net/SocketServerTestCase.java
@@ -142,7 +142,7 @@
           new JunitTestRunnerFilter(),
           new SunReflectFilter() });
 
-    assertTrue(Compare.compare(FILTERED, "witness/socketServer.2"));
+    assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/socketServer.2"));
   }
 
   /**
@@ -165,7 +165,7 @@
           new JunitTestRunnerFilter(),
           new SunReflectFilter() });
 
-    assertTrue(Compare.compare(FILTERED, "witness/socketServer.3"));
+    assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/socketServer.3"));
   }
 
   /**
@@ -195,7 +195,7 @@
               new JunitTestRunnerFilter(),
               new SunReflectFilter() });
 
-        assertTrue(Compare.compare(FILTERED, "witness/socketServer.4"));
+        assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/socketServer.4"));
     }
   }
 
@@ -238,7 +238,7 @@
               new JunitTestRunnerFilter(),
               new SunReflectFilter() });
 
-        assertTrue(Compare.compare(FILTERED, "witness/socketServer.5"));
+        assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/socketServer.5"));
     }
   }
 
@@ -275,7 +275,7 @@
               new JunitTestRunnerFilter(),
               new SunReflectFilter() });
 
-        assertTrue(Compare.compare(FILTERED, "witness/socketServer.6"));
+        assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/socketServer.6"));
     }
   }
 
@@ -307,7 +307,7 @@
           new Filter[] { cf, new LineNumberFilter(), 
               new JunitTestRunnerFilter(),
               new SunReflectFilter() });
-        assertTrue(Compare.compare(FILTERED, "witness/socketServer.7"));
+        assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/socketServer.7"));
     }
   }
 
@@ -349,7 +349,7 @@
           new Filter[] { cf, new LineNumberFilter(), 
               new JunitTestRunnerFilter(),
               new SunReflectFilter() });
-        assertTrue(Compare.compare(FILTERED, "witness/socketServer.8"));
+        assertTrue(Compare.compare(FILTERED, "target/test-classes/witness/socketServer.8"));
     }
   }
 
diff --git a/tests/src/java/org/apache/log4j/net/SyslogAppenderTest.java b/modules/net/src/test/java/org/apache/log4j/net/SyslogAppenderTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/net/SyslogAppenderTest.java
rename to modules/net/src/test/java/org/apache/log4j/net/SyslogAppenderTest.java
diff --git a/tests/src/java/org/apache/log4j/net/TelnetAppenderTest.java b/modules/net/src/test/java/org/apache/log4j/net/TelnetAppenderTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/net/TelnetAppenderTest.java
rename to modules/net/src/test/java/org/apache/log4j/net/TelnetAppenderTest.java
diff --git a/tests/input/socketServer1.properties b/modules/net/src/test/resources/input/socketServer1.properties
similarity index 100%
rename from tests/input/socketServer1.properties
rename to modules/net/src/test/resources/input/socketServer1.properties
diff --git a/tests/input/socketServer2.properties b/modules/net/src/test/resources/input/socketServer2.properties
similarity index 100%
rename from tests/input/socketServer2.properties
rename to modules/net/src/test/resources/input/socketServer2.properties
diff --git a/tests/input/socketServer3.properties b/modules/net/src/test/resources/input/socketServer3.properties
similarity index 100%
rename from tests/input/socketServer3.properties
rename to modules/net/src/test/resources/input/socketServer3.properties
diff --git a/tests/input/socketServer4.properties b/modules/net/src/test/resources/input/socketServer4.properties
similarity index 100%
rename from tests/input/socketServer4.properties
rename to modules/net/src/test/resources/input/socketServer4.properties
diff --git a/tests/input/socketServer5.properties b/modules/net/src/test/resources/input/socketServer5.properties
similarity index 100%
rename from tests/input/socketServer5.properties
rename to modules/net/src/test/resources/input/socketServer5.properties
diff --git a/tests/input/socketServer6.properties b/modules/net/src/test/resources/input/socketServer6.properties
similarity index 100%
rename from tests/input/socketServer6.properties
rename to modules/net/src/test/resources/input/socketServer6.properties
diff --git a/tests/input/socketServer7.properties b/modules/net/src/test/resources/input/socketServer7.properties
similarity index 100%
rename from tests/input/socketServer7.properties
rename to modules/net/src/test/resources/input/socketServer7.properties
diff --git a/tests/input/socketServer8.properties b/modules/net/src/test/resources/input/socketServer8.properties
similarity index 100%
rename from tests/input/socketServer8.properties
rename to modules/net/src/test/resources/input/socketServer8.properties
diff --git a/tests/input/xml/SocketAppenderTestConfig.xml b/modules/net/src/test/resources/input/xml/SocketAppenderTestConfig.xml
similarity index 100%
rename from tests/input/xml/SocketAppenderTestConfig.xml
rename to modules/net/src/test/resources/input/xml/SocketAppenderTestConfig.xml
diff --git a/tests/input/xml/smtpAppender1.xml b/modules/net/src/test/resources/input/xml/smtpAppender1.xml
similarity index 100%
rename from tests/input/xml/smtpAppender1.xml
rename to modules/net/src/test/resources/input/xml/smtpAppender1.xml
diff --git a/modules/nt/pom.xml b/modules/nt/pom.xml
new file mode 100644
index 0000000..47fcea8
--- /dev/null
+++ b/modules/nt/pom.xml
@@ -0,0 +1,190 @@
+<!--
+ 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.
+
+-->
+<!--
+Usage notes:
+
+To build NTEventLogAppender.dll using MinGW, place mingw\bin on path for Windows or install mingw
+package on other platforms.  Build will execute gcc and windres on Windows and
+i586-mingw32msvc-gcc and i586-mingw32msvc-windres on other platforms.
+
+To build NTEventLogAppender.dll using Microsoft tools, run the appropriate vcvars for the
+target platform and specify -Dntdll_target=msbuild on the mvn command line.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-modules</artifactId>
+      <version>1.4.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.log4j</groupId>
+  <artifactId>log4j-nt</artifactId>
+  <name>Apache Log4j-Windows NT Event Log Appender</name>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <tools.jar>${java.home}/../Classes/classes.jar</tools.jar>
+    <m2_repo>${user.home}/.m2/repository</m2_repo>
+    <ntdll_target>build</ntdll_target>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <version>1.2</version>
+        <executions>
+          <!--   generate NTEventLogAppender.dll    -->
+          <execution>
+            <phase>process-classes</phase>
+            <id>ntdll</id>
+            <configuration>
+              <tasks>
+                <ant antfile="src/ntdll/build.xml" target="${ntdll_target}">
+                  <property name="target.dir" location="target" />
+                  <property name="classes.dir" location="target/classes" />
+                  <property name="src.dir" location="src/ntdll" />
+                  <property name="jni.include.dir" location="${java.home}/../include" />
+                </ant>
+              </tasks>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+          <!--   create tests/output prior to test run    -->
+          <execution>
+            <phase>test-compile</phase>
+            <id>mkdir_tests_output</id>
+            <configuration>
+              <tasks>
+                <mkdir dir="tests/output" />
+              </tasks>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+          <execution>
+            <phase>clean</phase>
+            <id>rmdir_tests_output</id>
+            <configuration>
+              <tasks>
+                <delete dir="tests/output" />
+                <delete dir="tests/classes" />
+              </tasks>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+          <execution>
+            <phase>test</phase>
+            <id>runAll</id>
+            <configuration>
+              <tasks>
+                 <ant dir="tests" target="runAll">
+                  <property name="m2_repo" location="${m2_repo}" />
+                  <property name="log4j.jar" location="target/classes" />
+                  <property name="project.lib.home" location="target" />
+                </ant>
+              </tasks>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+          <!--   release builds will put SVN tags into the SCM page, this changes it back to trunk  -->
+          <execution>
+            <phase>site</phase>
+            <id>untag-site</id>
+            <configuration>
+              <tasks>
+                <taskdef name="replaceregexp" classname="org.apache.tools.ant.taskdefs.optional.ReplaceRegExp" />
+                <replaceregexp file="target/site/source-repository.html" match="/tags/[^ &quot;'&lt;]*" replace="/trunk" flags="g" />
+                <replaceregexp match="-- Generated by (.*) on .*--" replace="-- Generated by \1 --" flags="g">
+                  <fileset dir="target/site/apidocs" includes="**/*.html" />
+                </replaceregexp>
+              </tasks>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+          <execution>
+            <phase>post-site</phase>
+            <id>post-site</id>
+            <configuration>
+              <tasks>
+                <ant target="post-site" />
+              </tasks>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+          <execution>
+            <phase>site-deploy</phase>
+            <id>site-deploy</id>
+            <configuration>
+              <tasks>
+                <ant target="site-deploy" />
+              </tasks>
+            </configuration>
+            <goals>
+              <goal>run</goal>
+            </goals>
+          </execution>
+        </executions>
+        <dependencies>
+          <dependency>
+            <groupId>ant</groupId>
+            <artifactId>ant-nodeps</artifactId>
+            <version>1.6.5</version>
+          </dependency>
+          <dependency>
+            <groupId>ant-contrib</groupId>
+            <artifactId>ant-contrib</artifactId>
+            <version>1.0b2</version>
+          </dependency>
+          <dependency>
+            <groupId>ant</groupId>
+            <artifactId>ant-junit</artifactId>
+            <version>1.6.5</version>
+          </dependency>
+          <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+          </dependency>
+
+        </dependencies>
+      </plugin>
+    </plugins>
+  </build>
+  
+</project>
\ No newline at end of file
diff --git a/src/main/java/org/apache/log4j/nt/NTEventLogAppender.java b/modules/nt/src/main/java/org/apache/log4j/nt/NTEventLogAppender.java
similarity index 100%
rename from src/main/java/org/apache/log4j/nt/NTEventLogAppender.java
rename to modules/nt/src/main/java/org/apache/log4j/nt/NTEventLogAppender.java
diff --git a/src/main/java/org/apache/log4j/nt/package.html b/modules/nt/src/main/java/org/apache/log4j/nt/package.html
similarity index 100%
rename from src/main/java/org/apache/log4j/nt/package.html
rename to modules/nt/src/main/java/org/apache/log4j/nt/package.html
diff --git a/src/ntdll/EventLogCategories.mc b/modules/nt/src/ntdll/EventLogCategories.mc
similarity index 100%
rename from src/ntdll/EventLogCategories.mc
rename to modules/nt/src/ntdll/EventLogCategories.mc
diff --git a/src/ntdll/MSG00001.bin b/modules/nt/src/ntdll/MSG00001.bin
similarity index 100%
rename from src/ntdll/MSG00001.bin
rename to modules/nt/src/ntdll/MSG00001.bin
Binary files differ
diff --git a/NTEventLogAppender.amd64.dll b/modules/nt/src/ntdll/NTEventLogAppender.amd64.dll
similarity index 100%
rename from NTEventLogAppender.amd64.dll
rename to modules/nt/src/ntdll/NTEventLogAppender.amd64.dll
Binary files differ
diff --git a/src/ntdll/NTEventLogAppender.def b/modules/nt/src/ntdll/NTEventLogAppender.def
similarity index 100%
rename from src/ntdll/NTEventLogAppender.def
rename to modules/nt/src/ntdll/NTEventLogAppender.def
diff --git a/src/ntdll/NTEventLogAppender.rc b/modules/nt/src/ntdll/NTEventLogAppender.rc
similarity index 100%
rename from src/ntdll/NTEventLogAppender.rc
rename to modules/nt/src/ntdll/NTEventLogAppender.rc
diff --git a/src/ntdll/build.xml b/modules/nt/src/ntdll/build.xml
similarity index 100%
rename from src/ntdll/build.xml
rename to modules/nt/src/ntdll/build.xml
diff --git a/src/ntdll/nteventlog.cpp b/modules/nt/src/ntdll/nteventlog.cpp
similarity index 100%
rename from src/ntdll/nteventlog.cpp
rename to modules/nt/src/ntdll/nteventlog.cpp
diff --git a/tests/src/java/org/apache/log4j/nt/NTEventLogAppenderTest.java b/modules/nt/src/tests/java/org/apache/log4j/nt/NTEventLogAppenderTest.java
similarity index 100%
rename from tests/src/java/org/apache/log4j/nt/NTEventLogAppenderTest.java
rename to modules/nt/src/tests/java/org/apache/log4j/nt/NTEventLogAppenderTest.java
diff --git a/tests/build.xml b/modules/nt/tests/build.xml
similarity index 100%
rename from tests/build.xml
rename to modules/nt/tests/build.xml
diff --git a/modules/performance/pom.xml b/modules/performance/pom.xml
new file mode 100644
index 0000000..c9531df
--- /dev/null
+++ b/modules/performance/pom.xml
@@ -0,0 +1,54 @@
+<!--
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-modules</artifactId>
+      <version>1.4.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.log4j</groupId>
+  <artifactId>log4j-performance</artifactId>
+  <name>Apache Log4j-Performance Code</name>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.0.1</version>
+        <extensions>true</extensions>
+        <configuration>
+            <instructions>
+                <Export-Package>org.apache.log4j.performance.*</Export-Package>
+            </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
\ No newline at end of file
diff --git a/src/performance/java/org/apache/log4j/performance/ListVsVector.java b/modules/performance/src/main/java/org/apache/log4j/performance/ListVsVector.java
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/ListVsVector.java
rename to modules/performance/src/main/java/org/apache/log4j/performance/ListVsVector.java
diff --git a/src/performance/java/org/apache/log4j/performance/NOPWriter.java b/modules/performance/src/main/java/org/apache/log4j/performance/NOPWriter.java
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/NOPWriter.java
rename to modules/performance/src/main/java/org/apache/log4j/performance/NOPWriter.java
diff --git a/src/performance/java/org/apache/log4j/performance/NewVsSetLen.java b/modules/performance/src/main/java/org/apache/log4j/performance/NewVsSetLen.java
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/NewVsSetLen.java
rename to modules/performance/src/main/java/org/apache/log4j/performance/NewVsSetLen.java
diff --git a/src/performance/java/org/apache/log4j/performance/NullAppender.java b/modules/performance/src/main/java/org/apache/log4j/performance/NullAppender.java
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/NullAppender.java
rename to modules/performance/src/main/java/org/apache/log4j/performance/NullAppender.java
diff --git a/src/performance/java/org/apache/log4j/performance/SystemTime.java b/modules/performance/src/main/java/org/apache/log4j/performance/SystemTime.java
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/SystemTime.java
rename to modules/performance/src/main/java/org/apache/log4j/performance/SystemTime.java
diff --git a/src/performance/java/org/apache/log4j/performance/history/FALKNIS.logging b/modules/performance/src/main/java/org/apache/log4j/performance/history/FALKNIS.logging
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/history/FALKNIS.logging
rename to modules/performance/src/main/java/org/apache/log4j/performance/history/FALKNIS.logging
diff --git a/src/performance/java/org/apache/log4j/performance/history/GIL.logging b/modules/performance/src/main/java/org/apache/log4j/performance/history/GIL.logging
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/history/GIL.logging
rename to modules/performance/src/main/java/org/apache/log4j/performance/history/GIL.logging
diff --git a/src/performance/java/org/apache/log4j/performance/history/NAPOLI.logging b/modules/performance/src/main/java/org/apache/log4j/performance/history/NAPOLI.logging
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/history/NAPOLI.logging
rename to modules/performance/src/main/java/org/apache/log4j/performance/history/NAPOLI.logging
diff --git a/src/performance/java/org/apache/log4j/performance/history/TORINO.logging b/modules/performance/src/main/java/org/apache/log4j/performance/history/TORINO.logging
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/history/TORINO.logging
rename to modules/performance/src/main/java/org/apache/log4j/performance/history/TORINO.logging
diff --git a/src/performance/java/org/apache/log4j/performance/logging b/modules/performance/src/main/java/org/apache/log4j/performance/logging
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/logging
rename to modules/performance/src/main/java/org/apache/log4j/performance/logging
diff --git a/src/performance/java/org/apache/log4j/performance/package.html b/modules/performance/src/main/java/org/apache/log4j/performance/package.html
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/package.html
rename to modules/performance/src/main/java/org/apache/log4j/performance/package.html
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging1.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging1.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging1.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging1.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging10.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging10.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging10.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging10.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging100.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging100.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging100.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging100.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging1000.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging1000.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging1000.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging1000.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging101.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging101.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging101.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging101.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging102.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging102.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging102.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging102.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging103.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging103.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging103.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging103.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging104.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging104.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging104.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging104.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging105.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging105.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging105.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging105.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging106.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging106.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging106.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging106.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging107.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging107.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging107.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging107.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging11.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging11.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging11.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging11.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging12.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging12.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging12.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging12.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging13.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging13.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging13.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging13.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging2.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging2.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging2.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging2.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging200.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging200.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging200.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging200.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging201.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging201.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging201.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging201.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging202.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging202.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging202.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging202.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging203.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging203.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging203.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging203.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging204.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging204.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging204.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging204.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging205.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging205.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging205.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging205.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging206.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging206.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging206.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging206.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging207.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging207.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging207.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging207.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging220.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging220.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging220.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging220.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging221.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging221.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging221.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging221.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging222.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging222.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging222.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging222.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging223.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging223.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging223.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging223.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging224.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging224.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging224.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging224.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging225.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging225.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging225.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging225.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging226.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging226.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging226.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging226.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging227.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging227.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging227.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging227.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging3.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging3.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging3.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging3.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging300.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging300.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging300.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging300.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging301.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging301.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging301.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging301.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging302.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging302.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging302.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging302.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging303.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging303.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging303.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging303.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging304.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging304.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging304.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging304.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging305.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging305.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging305.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging305.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging306.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging306.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging306.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging306.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging307.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging307.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging307.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging307.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging4.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging4.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging4.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging4.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging5.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging5.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging5.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging5.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging6.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging6.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging6.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging6.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging7.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging7.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging7.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging7.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging8.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging8.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging8.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging8.xml
diff --git a/src/performance/java/org/apache/log4j/performance/xml/logging9.xml b/modules/performance/src/main/java/org/apache/log4j/performance/xml/logging9.xml
similarity index 100%
rename from src/performance/java/org/apache/log4j/performance/xml/logging9.xml
rename to modules/performance/src/main/java/org/apache/log4j/performance/xml/logging9.xml
diff --git a/modules/pom.xml b/modules/pom.xml
new file mode 100644
index 0000000..2c0821d
--- /dev/null
+++ b/modules/pom.xml
@@ -0,0 +1,40 @@
+<!--
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+      <groupId>org.apache.log4j</groupId>
+      <artifactId>log4j-parent</artifactId>
+      <version>1.4.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.log4j</groupId>
+  <artifactId>log4j-modules</artifactId>
+  <name>Apache Log4j-Modules POM</name>
+  <description>Apache Log4j Modules Container</description>
+  <packaging>pom</packaging>
+  
+  <modules>
+    <module>chainsaw</module>
+    <module>contribs</module>
+    <module>lf5</module>
+    <module>net</module>
+    <!-- build not implemented <module>nt</module> -->
+    <module>performance</module>
+  </modules>
+  
+</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index cfacd83..582bc3e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -15,27 +15,20 @@
  limitations under the License.
 
 -->
-<!--
-Usage notes:
-
-To build NTEventLogAppender.dll using MinGW, place mingw\bin on path for Windows or install mingw
-package on other platforms.  Build will execute gcc and windres on Windows and
-i586-mingw32msvc-gcc and i586-mingw32msvc-windres on other platforms.
-
-To build NTEventLogAppender.dll using Microsoft tools, run the appropriate vcvars for the
-target platform and specify -Dntdll_target=msbuild on the mvn command line.
-
--->        
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
-  <groupId>log4j</groupId>
-  <artifactId>log4j</artifactId>
-  <packaging>bundle</packaging>
-  <name>Apache Log4j</name>
-  <version>1.2.18-SNAPSHOT</version>
-  <description>Apache Log4j 1.2</description>
-  <url>http://logging.apache.org/log4j/1.2/</url>
+  <groupId>org.apache.log4j</groupId>
+  <artifactId>log4j-parent</artifactId>
+  <packaging>pom</packaging>
+  <name>Apache Log4j-Parent POM</name>
+  <version>1.4.0-SNAPSHOT</version>
+  <description>Apache Log4j parent module container.</description>
+  <url>http://logging.apache.org/log4j/</url>
+  <modules>
+    <module>core</module>
+    <module>modules</module>
+    <!--Build not implemented <module>examples</module>-->
+  </modules>
   <issueManagement>
     <system>Bugzilla</system>
     <url>https://issues.apache.org/bugzilla/describecomponents.cgi?product=Log4j</url>
@@ -98,52 +91,7 @@
         <artifactId>maven-surefire-plugin</artifactId>
         <version>2.5</version>
         <configuration>
-          <workingDirectory>tests</workingDirectory>
-          <reportFormat>plain</reportFormat>
           <forkMode>pertest</forkMode>
-          <skip>false</skip>
-          <includes>
-            <include>org/apache/log4j/LevelTest.java</include>
-            <include>org/apache/log4j/PriorityTest.java</include>
-            <include>org/apache/log4j/CategoryTest.java</include>
-            <include>org/apache/log4j/FileAppenderTest.java</include>
-            <include>org/apache/log4j/LogManagerTest.java</include>
-            <include>org/apache/log4j/helpers.LogLogTest.java</include>
-            <include>org/apache/log4j/LayoutTest.java</include>
-            <include>org/apache/log4j/helpers.DateLayoutTest.java</include>
-            <include>org/apache/log4j/TTCCLayoutTest.java</include>
-            <include>org/apache/log4j/xml.XMLLayoutTest.java</include>
-            <include>org/apache/log4j/HTMLLayoutTest.java</include>
-            <include>org/apache/log4j/PatternLayoutTest.java</include>
-            <include>org/apache/log4j/spi.LoggingEventTest.java</include>
-            <include>org/apache/log4j/spi.ThrowableInformationTest.java</include>
-            <include>org/apache/log4j/spi.LocationInfoTest.java</include>
-            <include>org/apache/log4j/PropertyConfiguratorTest.java</include>
-            <include>org/apache/log4j/MinimumTestCase.java</include>
-            <include>org/apache/log4j/LoggerTestCase.java</include>
-            <include>org/apache/log4j/PatternLayoutTestCase.java</include>
-            <include>org/apache/log4j/HierarchyThresholdTestCase.java</include>
-            <include>org/apache/log4j/xml/DOMTestCase.java</include>
-            <include>org/apache/log4j/xml/CustomLevelTestCase.java</include>
-            <include>org/apache/log4j/customLogger/XLoggerTestCase.java</include>
-            <!-- DefaultInit -->
-            <!-- SocketServer -->
-            <include>org/apache/log4j/xml/XMLLayoutTestCase.java</include>
-            <include>org/apache/log4j/xml/AsyncAppenderTestCase.java</include>
-            <include>org/apache/log4j/varia/LevelMatchFilterTestCase.java</include>
-            <!-- ErrorHandlerTestCase is not run in Ant build either <include>org/apache/log4j/varia/ErrorHandlerTestCase.java</include> -->
-            <!-- include>org/apache/log4j/helpers/OptionConverterTestCase.java</include -->
-            <include>org/apache/log4j/helpers/BoundedFIFOTestCase.java</include>
-            <include>org/apache/log4j/helpers/CyclicBufferTestCase.java</include>
-            <include>org/apache/log4j/helpers/PatternParserTestCase.java</include>
-            <include>org/apache/log4j/or/ORTestCase.java</include>
-            <include>org/apache/log4j/DRFATestCase.java</include>
-            <include>org/apache/log4j/RFATestCase.java</include>
-            <include>org/apache/log4j/varia/ERFATestCase.java</include>
-            <include>org/apache/log4j/net/SyslogAppenderTest</include>
-            <include>org/apache/log4j/nt/NTEventLogAppenderTest</include>
-            <include>org/apache/log4j/net/SocketAppenderTest</include>
-          </includes>
         </configuration>
       </plugin>
       <plugin>
@@ -156,204 +104,6 @@
         </configuration>
       </plugin>
       <plugin>
-        <artifactId>maven-jar-plugin</artifactId>
-        <version>2.3</version>
-        <configuration>
-          <archive>
-            <manifestSections>
-              <manifestSection>
-                <name>org/apache/log4j/</name>
-                <manifestEntries>
-                  <DynamicImport-Package>*</DynamicImport-Package>
-                  <Implementation-Title>log4j</Implementation-Title>
-                  <Implementation-Version>${project.version}</Implementation-Version>
-                  <Implementation-Vendor>"Apache Software Foundation"</Implementation-Vendor>
-                </manifestEntries>
-              </manifestSection>
-            </manifestSections>
-          </archive>
-        </configuration>
-      </plugin>
-      <plugin>
-        <artifactId>maven-antrun-plugin</artifactId>
-        <version>1.2</version>
-        <executions>
-          <!-- generate NTEventLogAppender.dll -->
-          <execution>
-            <phase>process-classes</phase>
-            <id>ntdll</id>
-            <configuration>
-              <tasks>
-                <ant antfile="src/ntdll/build.xml" target="${ntdll_target}">
-                  <property name="target.dir" location="target" />
-                  <property name="classes.dir" location="target/classes" />
-                  <property name="src.dir" location="src/ntdll" />
-                  <property name="jni.include.dir" location="${java.home}/../include" />
-                </ant>
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <!-- create tests/output prior to test run -->
-            <!--
-
-            TODO: several tests are run from tests/build.xml,
-            for example longUnit
-
-            Refactor these tests to run with Surefire
-
-
-          <execution>
-            <phase>test-compile</phase>
-            <id>mkdir_tests_output</id>
-            <configuration>
-              <tasks>
-                <mkdir dir="tests/output" />
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <execution>
-            <phase>clean</phase>
-            <id>rmdir_tests_output</id>
-            <configuration>
-              <tasks>
-                <delete dir="tests/output" />
-                <delete dir="tests/classes" />
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <execution>
-            <phase>test</phase>
-            <id>runAll</id>
-            <configuration>
-              <tasks>
-                <ant dir="tests" target="runAll">
-                  <property name="m2_repo" location="${m2_repo}" />
-                  <property name="log4j.jar" location="target/classes" />
-                  <property name="project.lib.home" location="target" />
-                </ant>
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          -->
-          <!-- release builds will put SVN tags into the SCM page, this changes it back to trunk -->
-          <execution>
-            <phase>site</phase>
-            <id>untag-site</id>
-            <configuration>
-              <tasks>
-                <taskdef name="replaceregexp" classname="org.apache.tools.ant.taskdefs.optional.ReplaceRegExp" />
-                <replaceregexp file="target/site/source-repository.html" match="/tags/[^ &quot;'&lt;]*"
-                  replace="/trunk" flags="g" />
-                <replaceregexp match="-- Generated by (.*) on .*--" replace="-- Generated by \1 --" flags="g">
-                  <fileset dir="target/site/apidocs" includes="**/*.html" />
-                </replaceregexp>
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-
-            <!--
-
-            TODO: checks out a site from svn. Is that necessary?
-
-          <execution>
-            <phase>post-site</phase>
-            <id>post-site</id>
-            <configuration>
-              <tasks>
-                <ant target="post-site" />
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-
-          -->
-
-
-            <!--
-
-            TODO: this is doable from maven
-
-          <execution>
-            <phase>site-deploy</phase>
-            <id>site-deploy</id>
-            <configuration>
-              <tasks>
-                <ant target="site-deploy" />
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          -->
-
-          <execution>
-            <id>javadoc.resources</id>
-            <phase>generate-sources</phase>
-            <goals>
-              <goal>run</goal>
-            </goals>
-            <configuration>
-              <tasks>
-                <copy todir="${project.build.directory}/apidocs/META-INF">
-                  <fileset dir="${basedir}">
-                    <include name="LICENSE" />
-                    <include name="NOTICE" />
-                  </fileset>
-                </copy>
-              </tasks>
-            </configuration>
-          </execution>
-        </executions>
-        <dependencies>
-          <dependency>
-            <groupId>ant</groupId>
-            <artifactId>ant-nodeps</artifactId>
-            <version>1.6.5</version>
-          </dependency>
-          <dependency>
-            <groupId>ant-contrib</groupId>
-            <artifactId>ant-contrib</artifactId>
-            <version>1.0b2</version>
-          </dependency>
-          <dependency>
-            <groupId>ant</groupId>
-            <artifactId>ant-junit</artifactId>
-            <version>1.6.5</version>
-          </dependency>
-          <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>3.8.1</version>
-            <scope>compile</scope>
-          </dependency>
-          <dependency>
-            <groupId>sun.jdk</groupId>
-            <artifactId>tools</artifactId>
-            <version>1.4.2</version>
-            <scope>system</scope>
-            <systemPath>${tools.jar}</systemPath>
-          </dependency>
-        </dependencies>
-      </plugin>
-      <plugin>
         <artifactId>maven-assembly-plugin</artifactId>
         <version>2.2-beta-5</version>
         <configuration>
@@ -430,11 +180,9 @@
         <artifactId>rat-maven-plugin</artifactId>
         <version>1.0-alpha-3</version>
         <configuration>
-          <excludes>
-            <exclude>tests/witness/**</exclude>
-            <exclude>tests/output/**</exclude>
-            <exclude>tests/classes/**</exclude>
-          </excludes>
+            <excludes>
+                <exclude>src/tests/witness/**</exclude>
+            </excludes>
         </configuration>
       </plugin>
         <plugin>
@@ -471,99 +219,9 @@
         </executions>
       </plugin>
     </plugins>
-    <testSourceDirectory>tests/src/java</testSourceDirectory>
-    <testResources>
-      <testResource>
-        <directory>tests/resources</directory>
-      </testResource>
-    </testResources>
   </build>
-  <reporting>
-    <plugins>
-      <plugin>
-        <artifactId>maven-project-info-reports-plugin</artifactId>
-        <version>2.4</version>
-        <reportSets>
-          <reportSet>
-            <reports>
-              <report>scm</report>
-              <report>dependencies</report>
-              <report>cim</report>
-              <report>issue-tracking</report>
-              <report>mailing-list</report>
-              <report>license</report>
-            </reports>
-          </reportSet>
-        </reportSets>
-      </plugin>
-      <plugin>
-        <artifactId>maven-jxr-plugin</artifactId>
-        <version>2.1</version>
-      </plugin>
-      <plugin>
-        <artifactId>maven-changes-plugin</artifactId>
-        <version>2.7</version>
-        <reportSets>
-          <reportSet>
-            <reports>
-              <report>changes-report</report>
-            </reports>
-          </reportSet>
-        </reportSets>
-      </plugin>
-    </plugins>
-  </reporting>
-  <profiles>
-    <profile>
-      <id>mac</id>
-      <activation>
-        <os>
-          <family>mac</family>
-        </os>
-      </activation>
-      <properties>
-        <tools.jar>${java.home}/../Classes/classes.jar</tools.jar>
-        <m2_repo>${user.home}/.m2/repository</m2_repo>
-        <ntdll_target>build</ntdll_target>
-      </properties>
-    </profile>
-    <profile>
-      <id>default</id>
-      <activation>
-        <activeByDefault>true</activeByDefault>
-      </activation>
-      <properties>
-        <tools.jar>${java.home}/../lib/tools.jar</tools.jar>
-        <m2_repo>${user.home}/.m2/repository</m2_repo>
-        <ntdll_target>build</ntdll_target>
-      </properties>
-    </profile>
-  </profiles>
-  <repositories>
-    <repository>
-      <id>maven2-repository.dev.java.net</id>
-      <name>Java.net Repository for Maven</name>
-      <url>http://download.java.net/maven/2/</url>
-      <layout>default</layout>
-    </repository>
-  </repositories>
   <dependencies>
     <dependency>
-      <groupId>javax.mail</groupId>
-      <artifactId>mail</artifactId>
-      <version>1.4.3</version>
-      <optional>true</optional>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.openejb</groupId>
-      <artifactId>javaee-api</artifactId>
-      <version>5.0-2</version>
-      <type>jar</type>
-      <scope>provided</scope>
-    </dependency>
-    <!-- the following dependency is not needed on JDK 1.5 and higher <dependency> <groupId>com.sun.jmx</groupId> <artifactId>jmxri</artifactId> 
-      <version>1.2.1</version> <optional>true</optional> </dependency> -->
-    <dependency>
       <groupId>oro</groupId>
       <artifactId>oro</artifactId>
       <version>2.0.8</version>
diff --git a/tests/src/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java b/tests/src/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java
deleted file mode 100644
index 58105ee..0000000
--- a/tests/src/java/org/apache/log4j/helpers/UtilLoggingLevelTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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"));
-    }
-
-}
-