You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by gg...@apache.org on 2022/02/20 16:31:09 UTC

[logging-log4j2] 02/02: Fill basic support in DOMConfigurator except for DOM-based APIs.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch release-2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git

commit 087a6fcf434e047844cc6bede1795ff8d7fb6abc
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Feb 20 11:31:04 2022 -0500

    Fill basic support in DOMConfigurator except for DOM-based APIs.
    
    Some internals mustn't be quite right, see @Disabled failing tests  in
    DOMTestCase.
---
 log4j-1.2-api/pom.xml                              |   7 +
 .../java/org/apache/log4j/xml/DOMConfigurator.java | 113 ++++-
 .../java/org/apache/log4j/xml/XMLWatchdog.java     |  37 ++
 .../org/apache/log4j/xml/XmlConfiguration.java     |   6 +-
 .../log4j/util/AbsoluteDateAndTimeFilter.java      |  36 ++
 .../org/apache/log4j/util/AbsoluteTimeFilter.java  |  35 ++
 .../java/org/apache/log4j/util/ControlFilter.java  |  47 ++
 .../log4j/util/EnhancedJunitTestRunnerFilter.java  |  58 +++
 .../log4j/util/EnhancedLineNumberFilter.java       |  42 ++
 .../test/java/org/apache/log4j/util/Filter.java    |  35 ++
 .../java/org/apache/log4j/util/ISO8601Filter.java  |  35 ++
 .../apache/log4j/util/JunitTestRunnerFilter.java   |  54 +++
 .../org/apache/log4j/util/LineNumberFilter.java    |  36 ++
 .../org/apache/log4j/util/RelativeTimeFilter.java  |  36 ++
 .../apache/log4j/util/SerializationTestHelper.java | 112 +++--
 .../org/apache/log4j/util/SunReflectFilter.java    |  43 ++
 .../java/org/apache/log4j/util/Transformer.java    |  59 +++
 .../log4j/util/UnexpectedFormatException.java      |  27 ++
 .../apache/log4j/util/XMLLineAttributeFilter.java  |  36 ++
 .../org/apache/log4j/util/XMLTimestampFilter.java  |  33 ++
 .../java/org/apache/log4j/xml/DOMTestCase.java     | 473 +++++++++++++++++++++
 .../log4j1-1.2.17/input/xml/DOMTestCase1.xml       |   4 +-
 22 files changed, 1280 insertions(+), 84 deletions(-)

diff --git a/log4j-1.2-api/pom.xml b/log4j-1.2-api/pom.xml
index 804fb92..f970643 100644
--- a/log4j-1.2-api/pom.xml
+++ b/log4j-1.2-api/pom.xml
@@ -104,6 +104,13 @@
       <artifactId>jackson-dataformat-xml</artifactId>
       <scope>test</scope>
     </dependency>
+    <!-- 1.2 tests -->
+    <dependency>
+      <groupId>oro</groupId>
+      <artifactId>oro</artifactId>
+      <version>2.0.8</version>
+      <scope>test</scope>
+    </dependency>    
   </dependencies>
   <build>
     <plugins>
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
index 494208e..e751653 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/DOMConfigurator.java
@@ -16,64 +16,141 @@
  */
 package org.apache.log4j.xml;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.io.StringWriter;
 import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Properties;
 
 import javax.xml.parsers.FactoryConfigurationError;
 
+import org.apache.log4j.LogManager;
 import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.OptionConverter;
 import org.apache.log4j.spi.LoggerRepository;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.ConfigurationSource;
+import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.core.util.IOUtils;
 import org.w3c.dom.Element;
 
 /**
+ * Use this class to initialize the log4j environment using a DOM tree.
  *
+ * <p>
+ * The DTD is specified in <a href="doc-files/log4j.dtd"><b>log4j.dtd</b></a>.
+ *
+ * <p>
+ * Sometimes it is useful to see how log4j is reading configuration files. You can enable log4j internal logging by
+ * defining the <b>log4j.debug</b> variable on the java command line. Alternatively, set the <code>debug</code>
+ * attribute in the <code>log4j:configuration</code> element. As in
+ *
+ * <pre>
+&lt;log4j:configuration <b>debug="true"</b> xmlns:log4j="http://jakarta.apache.org/log4j/">
+...
+&lt;/log4j:configuration>
+ * </pre>
+ *
+ * <p>
+ * There are sample XML files included in the package.
+ *
+ * @since 0.8.3
  */
 public class DOMConfigurator {
 
-    public void doConfigure(final String filename, final LoggerRepository repository) {
+    public static void configure(final Element element) {
     }
 
-    public void doConfigure(final URL url, final LoggerRepository repository) {
+    public static void configure(final String fileName) throws FactoryConfigurationError {
+        final Path path = Paths.get(fileName);
+        try (final InputStream inputStream = Files.newInputStream(path)) {
+            final ConfigurationSource source = new ConfigurationSource(inputStream, path);
+            final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+            Configuration configuration;
+            configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+            LogManager.getRootLogger().removeAllAppenders();
+            Configurator.reconfigure(configuration);
+        } catch (final IOException e) {
+            throw new FactoryConfigurationError(e);
+        }
     }
 
-    public void doConfigure(final InputStream inputStream, final LoggerRepository repository)
-        throws FactoryConfigurationError {
+    public static void configure(final URL url) throws FactoryConfigurationError {
+        new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
     }
 
-    public void doConfigure(final Reader reader, final LoggerRepository repository)
-        throws FactoryConfigurationError {
+    public static void configureAndWatch(final String fileName) {
+        // TODO Watch
+        configure(fileName);
     }
 
-    public void doConfigure(final Element element, final LoggerRepository repository) {
+    public static void configureAndWatch(final String fileName, final long delay) {
+        XMLWatchdog xdog = new XMLWatchdog(fileName);
+        xdog.setDelay(delay);
+        xdog.start();
     }
 
-    public static void configure(final Element element) {
+    public static Object parseElement(final Element element, final Properties props, @SuppressWarnings("rawtypes") final Class expectedClass) {
+        return null;
     }
 
-    public static void configureAndWatch(final String configFilename) {
+    public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+
     }
 
-    public static void configureAndWatch(final String configFilename, final long delay) {
+    public static String subst(final String value, final Properties props) {
+        return OptionConverter.substVars(value, props);
     }
 
-    public static void configure(final String filename) throws FactoryConfigurationError {
+    private void doConfigure(final ConfigurationSource source) {
+        final LoggerContext context = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(false);
+        Configuration configuration;
+        configuration = new XmlConfigurationFactory().getConfiguration(context, source);
+        Configurator.reconfigure(configuration);
     }
 
-    public static void configure(final URL url) throws FactoryConfigurationError {
+    public void doConfigure(final Element element, final LoggerRepository repository) {
     }
 
-    public static String subst(final String value, final Properties props) {
-        return value;
+    public void doConfigure(final InputStream inputStream, final LoggerRepository repository) throws FactoryConfigurationError {
+        try {
+            doConfigure(new ConfigurationSource(inputStream));
+        } catch (final IOException e) {
+            throw new FactoryConfigurationError(e);
+        }
     }
 
-    public static void setParameter(final Element elem, final PropertySetter propSetter, final Properties props) {
+    public void doConfigure(final Reader reader, final LoggerRepository repository) throws FactoryConfigurationError {
+        try {
+            final StringWriter sw = new StringWriter();
+            IOUtils.copy(reader, sw);
+            doConfigure(new ConfigurationSource(new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8))));
+        } catch (final IOException e) {
+            throw new FactoryConfigurationError(e);
+        }
+    }
 
+    public void doConfigure(final String fileName, final LoggerRepository repository) {
+        configure(fileName);
     }
 
-    public static Object parseElement(final Element element, final Properties props,
-                                      @SuppressWarnings("rawtypes") final Class expectedClass) {
-        return null;
+    public void doConfigure(final URL url, final LoggerRepository repository) {
+        try {
+            final URLConnection connection = url.openConnection();
+            connection.setUseCaches(false); // Otherwise, a "jar:" URL file remains open after the stream is closed.
+            try (InputStream inputStream = connection.getInputStream()) {
+                doConfigure(new ConfigurationSource(inputStream, url));
+            }
+        } catch (final IOException e) {
+            throw new FactoryConfigurationError(e);
+        }
     }
 }
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java
new file mode 100644
index 0000000..753bd8b
--- /dev/null
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XMLWatchdog.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.log4j.xml;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.helpers.FileWatchdog;
+import org.apache.log4j.spi.LoggerRepository;
+
+class XMLWatchdog extends FileWatchdog {
+
+    XMLWatchdog(final String filename) {
+        super(filename);
+    }
+
+    /**
+     * Calls {@link DOMConfigurator#doConfigure(String, LoggerRepository)} with the <code>filename</code> to reconfigure Log4j.
+     */
+    @Override
+    public void doOnChange() {
+        new DOMConfigurator().doConfigure(filename, LogManager.getLoggerRepository());
+    }
+}
\ No newline at end of file
diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
index 3fe95d4..d604310 100644
--- a/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
+++ b/log4j-1.2-api/src/main/java/org/apache/log4j/xml/XmlConfiguration.java
@@ -751,14 +751,14 @@ public class XmlConfiguration extends Log4j1Configuration {
                     parseRoot(currentElement);
                     break;
                 case RENDERER_TAG:
-                    LOGGER.warn("Renderers are not supported by Log4j 2 and will be ignored.");
+                    LOGGER.warn("Log4j 1 renderers are not supported by Log4j 2 and will be ignored.");
                     break;
                 case THROWABLE_RENDERER_TAG:
-                    LOGGER.warn("Throwable Renderers are not supported by Log4j 2 and will be ignored.");
+                    LOGGER.warn("Log4j 1 throwable renderers are not supported by Log4j 2 and will be ignored.");
                     break;
                 case CATEGORY_FACTORY_TAG: 
                 case LOGGER_FACTORY_TAG:
-                    LOGGER.warn("Log4j 1 Logger factories are not supported by Log4j 2 and will be ignored.");
+                    LOGGER.warn("Log4j 1 logger factories are not supported by Log4j 2 and will be ignored.");
                     break;
                 case APPENDER_TAG:
                     Appender appender = parseAppender(currentElement);
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java
new file mode 100644
index 0000000..73f3c2b
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteDateAndTimeFilter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class AbsoluteDateAndTimeFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        final String pat = "/" + Filter.ABSOLUTE_DATE_AND_TIME_PAT + "/";
+
+        if (util.match(pat, in)) {
+            return util.substitute("s/" + Filter.ABSOLUTE_DATE_AND_TIME_PAT + "//", in);
+        } else {
+            return in;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java
new file mode 100644
index 0000000..4528e8a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/AbsoluteTimeFilter.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class AbsoluteTimeFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        final String pat = "/" + Filter.ABSOLUTE_TIME_PAT + "/";
+
+        if (util.match(pat, in)) {
+            return util.substitute("s/" + Filter.ABSOLUTE_TIME_PAT + "//", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java
new file mode 100644
index 0000000..325d460
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ControlFilter.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import java.util.Arrays;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class ControlFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    String[] allowedPatterns;
+
+    public ControlFilter(final String[] allowedPatterns) {
+        this.allowedPatterns = allowedPatterns;
+    }
+
+    @Override
+    public String filter(final String in) throws UnexpectedFormatException {
+        final int len = allowedPatterns.length;
+        for (int i = 0; i < len; i++) {
+            // System.out.println("["+allowedPatterns[i]+"]");
+            if (util.match("/" + allowedPatterns[i] + "/", in)) {
+                // System.out.println("["+in+"] matched ["+allowedPatterns[i]);
+                return in;
+            }
+        }
+
+        throw new UnexpectedFormatException("[" + in + "] allowedPatterns = " + Arrays.toString(allowedPatterns));
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
new file mode 100644
index 0000000..172d317
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedJunitTestRunnerFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class EnhancedJunitTestRunnerFilter implements Filter {
+    private static final String[] PATTERNS = {"at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner", "at org.apache.tools.ant",
+        "at junit.textui.TestRunner", "at com.intellij.rt.execution.junit", "at java.lang.reflect.Method.invoke", "at org.apache.maven.", "at org.codehaus.",
+        "at org.junit.internal.runners.", "at junit.framework.JUnit4TestAdapter"};
+
+    private final Perl5Util util = new Perl5Util();
+
+    public EnhancedJunitTestRunnerFilter() {
+    }
+
+    /**
+     * Filter out stack trace lines coming from the various JUnit TestRunners.
+     */
+    @Override
+    public String filter(final String in) {
+        if (in == null) {
+            return null;
+        }
+
+        //
+        // restore the one instance of Method.invoke that we actually want
+        //
+        if (in.indexOf("at junit.framework.TestCase.runTest") != -1) {
+            return "\tat java.lang.reflect.Method.invoke(X)\n\t" + in.trim();
+        }
+
+        for (final String element : PATTERNS) {
+            if (in.indexOf(element) != -1) {
+                return null;
+            }
+        }
+        if (util.match("/\\sat /", in)) {
+            return "\t" + in.trim();
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java
new file mode 100644
index 0000000..b112a03
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/EnhancedLineNumberFilter.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import java.util.regex.Pattern;
+
+public class EnhancedLineNumberFilter implements Filter {
+    private final Pattern linePattern;
+    private final Pattern nativePattern;
+
+    public EnhancedLineNumberFilter() {
+        linePattern = Pattern.compile("\\(.*:\\d{1,4}\\)");
+        nativePattern = Pattern.compile("\\(Native Method\\)");
+    }
+
+    @Override
+    public String filter(final String in) {
+
+        if (linePattern.matcher(in).find()) {
+            return linePattern.matcher(in).replaceAll("(X)");
+        } else if (nativePattern.matcher(in).find()) {
+            return nativePattern.matcher(in).replaceAll("(X)");
+        } else {
+            return in;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/Filter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Filter.java
new file mode 100644
index 0000000..16bb43d
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Filter.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+public interface Filter {
+
+    final String BASIC_PAT = "\\[main\\] (FATAL|ERROR|WARN|INFO|DEBUG)";
+    final String ISO8601_PAT = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3}";
+
+    // 06 avr. 2002 18:36:32,036
+    // 18 fevr. 2002 20:05:36,222
+    static public final String ABSOLUTE_DATE_AND_TIME_PAT = "^\\d{1,2} .{2,6}\\.? 2\\d{3} \\d{2}:\\d{2}:\\d{2},\\d{3}";
+
+    // 18:54:19,201
+    static public final String ABSOLUTE_TIME_PAT = "^\\d{2}:\\d{2}:\\d{2},\\d{3}";
+
+    static public final String RELATIVE_TIME_PAT = "^\\d{1,10}";
+
+    String filter(String in) throws UnexpectedFormatException;
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/ISO8601Filter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ISO8601Filter.java
new file mode 100644
index 0000000..47232ad
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/ISO8601Filter.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class ISO8601Filter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        final String pat = "/" + ISO8601_PAT + "/";
+
+        if (util.match(pat, in)) {
+            return util.substitute("s/" + ISO8601_PAT + "//", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java
new file mode 100644
index 0000000..175224a
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/JunitTestRunnerFilter.java
@@ -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.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class JunitTestRunnerFilter implements Filter {
+    Perl5Util util = new Perl5Util();
+
+    /**
+     * Filter out stack trace lines coming from the various JUnit TestRunners.
+     */
+    @Override
+    public String filter(final String in) {
+        if (in == null) {
+            return null;
+        }
+
+        if (util.match("/at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner/", in)) {
+            return null;
+        } else if (util.match("/at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner/", in)) {
+            return null;
+        } else if (util.match("/at com.intellij/", in)) {
+            return null;
+        } else if (in.indexOf("at junit.") >= 0 && in.indexOf("ui.TestRunner") >= 0) {
+            return null;
+        } else if (in.indexOf("org.apache.maven") >= 0) {
+            return null;
+        } else if (in.indexOf("junit.internal") >= 0) {
+            return null;
+        } else if (in.indexOf("JUnit4TestAdapter") >= 0) {
+            return null;
+        } else if (util.match("/\\sat /", in)) {
+            return "\t" + in.trim();
+        } else {
+            return in;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/LineNumberFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/LineNumberFilter.java
new file mode 100644
index 0000000..45c6578
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/LineNumberFilter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class LineNumberFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        if (util.match("/\\(.*:\\d{1,4}\\)/", in)) {
+            return util.substitute("s/:\\d{1,4}\\)/:XXX)/", in);
+        }
+        if (in.indexOf(", Compiled Code") >= 0) {
+            return util.substitute("s/, Compiled Code/:XXX/", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java
new file mode 100644
index 0000000..a15ffb1
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/RelativeTimeFilter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class RelativeTimeFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        final String pat = "/" + Filter.RELATIVE_TIME_PAT + "/";
+
+        if (util.match(pat, in)) {
+            // System.out.println("Removing relative time from line ["+in+"]");
+            return util.substitute("s/" + Filter.RELATIVE_TIME_PAT + "//", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
index 882a37a..a3584c2 100644
--- a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SerializationTestHelper.java
@@ -30,65 +30,20 @@ import java.io.ObjectOutputStream;
 
 import org.apache.commons.io.FileUtils;
 
-
 /**
  * Utiities for serialization tests.
  */
 public class SerializationTestHelper {
     /**
-     * Private constructor.
-     */
-    private SerializationTestHelper() {
-    }
-
-    /**
-     * Creates a clone by serializing object and
-     * deserializing byte stream.
-     *
-     * @param obj object to serialize and deserialize.
-     * @return clone
-     * @throws IOException            on IO error.
-     * @throws ClassNotFoundException if class not found.
-     */
-    public static Object serializeClone(final Object obj)
-        throws IOException, ClassNotFoundException {
-        final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
-        try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
-            objOut.writeObject(obj);
-        }
-
-        final ByteArrayInputStream src = new ByteArrayInputStream(memOut.toByteArray());
-        final ObjectInputStream objIs = new ObjectInputStream(src);
-
-        return objIs.readObject();
-    }
-
-    /**
-     * Deserializes a specified file.
-     *
-     * @param witness serialization file, may not be null.
-     * @return deserialized object.
-     * @throws Exception thrown on IO or deserialization exception.
-     */
-    public static Object deserializeStream(final String witness) throws Exception {
-        try (final ObjectInputStream objIs = new ObjectInputStream(new FileInputStream(witness))) {
-            return objIs.readObject();
-        }
-    }
-
-    /**
-     * Checks the serialization of an object against an file
-     * containing the expected serialization.
+     * Checks the serialization of an object against an file containing the expected serialization.
      *
-     * @param witness    name of file containing expected serialization.
-     * @param obj        object to be serialized.
-     * @param skip       positions in serialized stream that should not be compared.
+     * @param witness name of file containing expected serialization.
+     * @param obj object to be serialized.
+     * @param skip positions in serialized stream that should not be compared.
      * @param endCompare position to stop comparison.
      * @throws Exception thrown on IO or serialization exception.
      */
-    public static void assertSerializationEquals(
-        final String witness, final Object obj, final int[] skip,
-        final int endCompare) throws Exception {
+    public static void assertSerializationEquals(final String witness, final Object obj, final int[] skip, final int endCompare) throws Exception {
         final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
         try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
             objOut.writeObject(obj);
@@ -100,15 +55,13 @@ public class SerializationTestHelper {
     /**
      * Asserts the serialized form of an object.
      *
-     * @param witness    file name of expected serialization.
-     * @param actual     byte array of actual serialization.
-     * @param skip       positions to skip comparison.
+     * @param witness file name of expected serialization.
+     * @param actual byte array of actual serialization.
+     * @param skip positions to skip comparison.
      * @param endCompare position to stop comparison.
      * @throws IOException thrown on IO or serialization exception.
      */
-    public static void assertStreamEquals(
-        final String witness, final byte[] actual, final int[] skip,
-        final int endCompare) throws IOException {
+    public static void assertStreamEquals(final String witness, final byte[] actual, final int[] skip, final int endCompare) throws IOException {
         final File witnessFile = new File(witness);
 
         if (witnessFile.exists()) {
@@ -130,17 +83,54 @@ public class SerializationTestHelper {
                 if ((skipIndex < skip.length) && (skip[skipIndex] == i)) {
                     skipIndex++;
                 } else if (expected[i] != actual[i]) {
-                    assertEquals(
-                        "Difference at offset " + i, expected[i], actual[i]);
+                    assertEquals("Difference at offset " + i, expected[i], actual[i]);
                 }
             }
         } else {
             //
-            //  if the file doesn't exist then
-            //      assume that we are setting up and need to write it
+            // if the file doesn't exist then
+            // assume that we are setting up and need to write it
             FileUtils.writeByteArrayToFile(witnessFile, actual);
             fail("Writing witness file " + witness);
         }
     }
-}
 
+    /**
+     * Deserializes a specified file.
+     *
+     * @param witness serialization file, may not be null.
+     * @return deserialized object.
+     * @throws Exception thrown on IO or deserialization exception.
+     */
+    public static Object deserializeStream(final String witness) throws Exception {
+        try (final ObjectInputStream objIs = new ObjectInputStream(new FileInputStream(witness))) {
+            return objIs.readObject();
+        }
+    }
+
+    /**
+     * Creates a clone by serializing object and deserializing byte stream.
+     *
+     * @param obj object to serialize and deserialize.
+     * @return clone
+     * @throws IOException on IO error.
+     * @throws ClassNotFoundException if class not found.
+     */
+    public static Object serializeClone(final Object obj) throws IOException, ClassNotFoundException {
+        final ByteArrayOutputStream memOut = new ByteArrayOutputStream();
+        try (final ObjectOutputStream objOut = new ObjectOutputStream(memOut)) {
+            objOut.writeObject(obj);
+        }
+
+        final ByteArrayInputStream src = new ByteArrayInputStream(memOut.toByteArray());
+        final ObjectInputStream objIs = new ObjectInputStream(src);
+
+        return objIs.readObject();
+    }
+
+    /**
+     * Private constructor.
+     */
+    private SerializationTestHelper() {
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/SunReflectFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SunReflectFilter.java
new file mode 100644
index 0000000..14c8e00
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/SunReflectFilter.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+/**
+ * The sun.reflect.* and java.lang.reflect.* lines are not present in all JDKs.
+ */
+public class SunReflectFilter implements Filter {
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        if ((in == null) || util.match("/at sun.reflect/", in) || (in.indexOf("at java.lang.reflect.") >= 0)) {
+            return null;
+        }
+        if (in.indexOf("Compiled Code") >= 0) {
+            if (in.indexOf("junit.framework.TestSuite") >= 0) {
+                return util.substitute("s/Compiled Code/TestSuite.java:XXX/", in);
+            }
+        }
+        if (util.match("/\\(Method.java:.*\\)/", in)) {
+            return util.substitute("s/\\(Method.java:.*\\)/(Native Method)/", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java
new file mode 100644
index 0000000..79d659e
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/Transformer.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import java.io.BufferedReader;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class Transformer {
+
+    public static void transform(final String in, final String out, final Filter filter) throws IOException, UnexpectedFormatException {
+
+        String line;
+        final BufferedReader input = new BufferedReader(new FileReader(in));
+        final PrintStream output = new PrintStream(new FileOutputStream(out));
+
+        // Initialization of input and output omitted
+        while ((line = input.readLine()) != null) {
+            line = filter.filter(line);
+            output.println(line);
+        }
+    }
+
+    public static void transform(final String in, final String out, final Filter[] filters) throws IOException, UnexpectedFormatException {
+
+        String line;
+        final BufferedReader input = new BufferedReader(new FileReader(in));
+        final PrintStream output = new PrintStream(new FileOutputStream(out, false));
+
+        // Initialization of input and output omitted
+        while ((line = input.readLine()) != null) {
+            // apply all filters
+            for (final Filter filter : filters) {
+                line = filter.filter(line);
+            }
+            if (line != null) {
+                output.println(line);
+            }
+        }
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java
new file mode 100644
index 0000000..ed25a35
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/UnexpectedFormatException.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+public class UnexpectedFormatException extends Exception {
+
+    private static final long serialVersionUID = 1787725660780924147L;
+
+    public UnexpectedFormatException(final String msg) {
+        super(msg);
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java
new file mode 100644
index 0000000..9fcd070
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLLineAttributeFilter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class XMLLineAttributeFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        if (util.match("/line=\"\\d{1,3}\"/", in)) {
+            return util.substitute("s/line=\"\\d{1,3}\"/line=\"X\"/", in);
+        } else if (util.match("/line=\"?\"/", in)) {
+            return util.substitute("s/line=\"?\"/line=\"X\"/", in);
+        } else {
+            return in;
+        }
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java
new file mode 100644
index 0000000..5012831
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/util/XMLTimestampFilter.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.util;
+
+import org.apache.oro.text.perl.Perl5Util;
+
+public class XMLTimestampFilter implements Filter {
+
+    Perl5Util util = new Perl5Util();
+
+    @Override
+    public String filter(final String in) {
+        if (util.match("/timestamp=\"\\d{10,13}\"/", in)) {
+            return util.substitute("s/timestamp=\"\\d{10,13}\"/timestamp=\"XXX\"/", in);
+        }
+        return in;
+    }
+}
diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java
new file mode 100644
index 0000000..a01aa88
--- /dev/null
+++ b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/DOMTestCase.java
@@ -0,0 +1,473 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.xml;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.FileAppender;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.VectorAppender;
+import org.apache.log4j.bridge.AppenderWrapper;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.log4j.util.Compare;
+import org.apache.log4j.util.ControlFilter;
+import org.apache.log4j.util.Filter;
+import org.apache.log4j.util.ISO8601Filter;
+import org.apache.log4j.util.JunitTestRunnerFilter;
+import org.apache.log4j.util.LineNumberFilter;
+import org.apache.log4j.util.SunReflectFilter;
+import org.apache.log4j.util.Transformer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+public class DOMTestCase {
+
+    /**
+     * CustomErrorHandler for testCategoryFactory2.
+     */
+    public static class CustomErrorHandler implements ErrorHandler {
+        public CustomErrorHandler() {
+        }
+
+        public void activateOptions() {
+        }
+
+        @Override
+        public void error(final String message) {
+        }
+
+        @Override
+        public void error(final String message, final Exception e, final int errorCode) {
+        }
+
+        @Override
+        public void error(final String message, final Exception e, final int errorCode, final LoggingEvent event) {
+        }
+
+        @Override
+        public void setAppender(final Appender appender) {
+        }
+
+        @Override
+        public void setBackupAppender(final Appender appender) {
+        }
+
+        @Override
+        public void setLogger(final Logger logger) {
+        }
+    }
+
+    /**
+     * CustomLogger implementation for testCategoryFactory1 and 2.
+     */
+    private static class CustomLogger extends Logger {
+        /**
+         * Creates new instance.
+         *
+         * @param name logger name.
+         */
+        public CustomLogger(final String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Creates new instances of CustomLogger.
+     */
+    public static class CustomLoggerFactory implements LoggerFactory {
+
+        /**
+         * Additivity, expected to be set false in configuration file.
+         */
+        private boolean additivity;
+
+        /**
+         * Create new instance of factory.
+         */
+        public CustomLoggerFactory() {
+            additivity = true;
+        }
+
+        /**
+         * Create new logger.
+         *
+         * @param name logger name.
+         * @return new logger.
+         */
+        @Override
+        public Logger makeNewLoggerInstance(final String name) {
+            final Logger logger = new CustomLogger(name);
+            assertFalse(additivity);
+            return logger;
+        }
+
+        /**
+         * Set additivity.
+         *
+         * @param newVal new value of additivity.
+         */
+        public void setAdditivity(final boolean newVal) {
+            additivity = newVal;
+        }
+    }
+
+    /**
+     * Mock ThrowableRenderer for testThrowableRenderer. See bug 45721.
+     */
+    public static class MockThrowableRenderer implements ThrowableRenderer, OptionHandler {
+        private boolean activated = false;
+        private boolean showVersion = true;
+
+        public MockThrowableRenderer() {
+        }
+
+        @Override
+        public void activateOptions() {
+            activated = true;
+        }
+
+        @Override
+        public String[] doRender(final Throwable t) {
+            return new String[0];
+        }
+
+        public boolean getShowVersion() {
+            return showVersion;
+        }
+
+        public boolean isActivated() {
+            return activated;
+        }
+
+        public void setShowVersion(final boolean v) {
+            showVersion = v;
+        }
+    }
+
+    static String TEMP_A1 = "target/output/temp.A1";
+    static String TEMP_A2 = "target/output/temp.A2";
+    static String FILTERED_A1 = "target/output/filtered.A1";
+    static String FILTERED_A2 = "target/output/filtered.A2";
+    static String EXCEPTION1 = "java.lang.Exception: Just testing";
+    static String EXCEPTION2 = "\\s*at .*\\(.*\\)";
+    static String EXCEPTION3 = "\\s*at .*\\(Native Method\\)";
+    static String EXCEPTION4 = "\\s*at .*\\(.*Compiled Code\\)";
+    static String EXCEPTION5 = "\\s*at .*\\(.*libgcj.*\\)";
+    static String TEST1_1A_PAT = "(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) \\w*\\.\\w* - Message \\d";
+    static String TEST1_1B_PAT = "(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) root - Message \\d";
+    static String TEST1_2_PAT = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3} " + "\\[main]\\ (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .* - Message \\d";
+
+    private static final boolean Log4j1ActualAppender = false;
+
+    private static final boolean AssumeThrowableRendererSupport = false;
+
+    Logger root;
+
+    Logger logger;
+
+    void common() {
+        final String oldThreadName = Thread.currentThread().getName();
+        Thread.currentThread().setName("main");
+
+        int i = -1;
+
+        logger.trace("Message " + ++i);
+        root.trace("Message " + i);
+
+        logger.debug("Message " + ++i);
+        root.debug("Message " + i);
+
+        logger.info("Message " + ++i);
+        root.info("Message " + i);
+
+        logger.warn("Message " + ++i);
+        root.warn("Message " + i);
+
+        logger.error("Message " + ++i);
+        root.error("Message " + i);
+
+        logger.log(Level.FATAL, "Message " + ++i);
+        root.log(Level.FATAL, "Message " + i);
+
+        final Exception e = new Exception("Just testing");
+        logger.debug("Message " + ++i, e);
+        root.debug("Message " + i, e);
+
+        logger.error("Message " + ++i, e);
+        root.error("Message " + i, e);
+
+        Thread.currentThread().setName(oldThreadName);
+    }
+
+    @BeforeEach
+    public void setUp() {
+        root = Logger.getRootLogger();
+        logger = Logger.getLogger(DOMTestCase.class);
+    }
+
+    @AfterEach
+    public void tearDown() {
+        root.getLoggerRepository().resetConfiguration();
+    }
+
+    @Test
+    @Disabled
+    public void test1() throws Exception {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml");
+        common();
+
+        final ControlFilter cf1 = new ControlFilter(new String[] {TEST1_1A_PAT, TEST1_1B_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+        final ControlFilter cf2 = new ControlFilter(new String[] {TEST1_2_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+        Transformer.transform(TEMP_A1, FILTERED_A1, new Filter[] {cf1, new LineNumberFilter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+        Transformer.transform(TEMP_A2, FILTERED_A2,
+            new Filter[] {cf2, new LineNumberFilter(), new ISO8601Filter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+        assertTrue(Compare.compare(FILTERED_A1, "witness/dom.A1.1"));
+        assertTrue(Compare.compare(FILTERED_A2, "witness/dom.A2.1"));
+    }
+
+    /**
+     * Tests processing of external entities in XML file.
+     */
+    @Test
+    @Disabled
+    public void test4() throws Exception {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/DOMTest4.xml");
+        common();
+
+        final ControlFilter cf1 = new ControlFilter(new String[] {TEST1_1A_PAT, TEST1_1B_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+        final ControlFilter cf2 = new ControlFilter(new String[] {TEST1_2_PAT, EXCEPTION1, EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+
+        Transformer.transform(TEMP_A1 + ".4", FILTERED_A1 + ".4",
+            new Filter[] {cf1, new LineNumberFilter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+        Transformer.transform(TEMP_A2 + ".4", FILTERED_A2 + ".4",
+            new Filter[] {cf2, new LineNumberFilter(), new ISO8601Filter(), new SunReflectFilter(), new JunitTestRunnerFilter()});
+
+        assertTrue(Compare.compare(FILTERED_A1 + ".4", "witness/dom.A1.4"));
+        assertTrue(Compare.compare(FILTERED_A2 + ".4", "witness/dom.A2.4"));
+    }
+
+    /**
+     * Tests that loggers mentioned in logger elements use the specified categoryFactory. See bug 33708.
+     */
+    @Test
+    @Disabled
+    public void testCategoryFactory1() {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/categoryfactory1.xml");
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.2");
+        assertFalse(logger2.toString(), logger2 instanceof CustomLogger);
+        //
+        // logger explicitly mentioned in configuration,
+        // should be a CustomLogger
+        final Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.1");
+        assertTrue(logger1.toString(), logger1 instanceof CustomLogger);
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2Bis = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory1.2");
+        assertFalse(logger2Bis.toString(), logger2Bis instanceof CustomLogger);
+    }
+
+    /**
+     * Tests that loggers mentioned in logger-ref elements use the specified categoryFactory. See bug 33708.
+     */
+    @Test
+    @Disabled
+    public void testCategoryFactory2() {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/categoryfactory2.xml");
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.2");
+        assertFalse(logger2.toString(), logger2 instanceof CustomLogger);
+        //
+        // logger explicitly mentioned in configuration,
+        // should be a CustomLogger
+        final Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.1");
+        assertTrue(logger1.toString(), logger1 instanceof CustomLogger);
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2Bis = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testCategoryFactory2.2");
+        assertFalse(logger2Bis.toString(), logger2Bis instanceof CustomLogger);
+    }
+
+    /**
+     * Test checks that configureAndWatch does initial configuration, see bug 33502.
+     *
+     * @throws Exception if IO error.
+     */
+    @Test
+    public void testConfigureAndWatch() throws Exception {
+        DOMConfigurator.configureAndWatch("src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml");
+        assertNotNull(Logger.getRootLogger().getAppender("A1"));
+    }
+
+    /**
+     * Test for bug 47465. configure(URL) did not close opened JarURLConnection.
+     *
+     * @throws IOException if IOException creating properties jar.
+     */
+    @Test
+    public void testJarURL() throws IOException {
+        final File input = new File("src/test/resources/log4j1-1.2.17/input/xml/defaultInit.xml");
+        System.out.println(input.getAbsolutePath());
+        final File configJar = new File("target/output/xml.jar");
+        final File dir = new File("target/output");
+        dir.mkdirs();
+        try (final InputStream inputStream = new FileInputStream(input);
+            final FileOutputStream out = new FileOutputStream(configJar);
+            final ZipOutputStream zos = new ZipOutputStream(out)) {
+            zos.putNextEntry(new ZipEntry("log4j.xml"));
+            int len;
+            final byte[] buf = new byte[1024];
+            while ((len = inputStream.read(buf)) > 0) {
+                zos.write(buf, 0, len);
+            }
+            zos.closeEntry();
+        }
+        final URL urlInJar = new URL("jar:" + configJar.toURL() + "!/log4j.xml");
+        DOMConfigurator.configure(urlInJar);
+        assertTrue(configJar.delete());
+        assertFalse(configJar.exists());
+    }
+
+    /**
+     * Tests that loggers mentioned in logger elements use the specified loggerFactory. See bug 33708.
+     */
+    @Test
+    @Disabled("TODO")
+    public void testLoggerFactory1() {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/loggerfactory1.xml");
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.2");
+        assertNotNull(logger2);
+        assertFalse(logger2.toString(), logger2 instanceof CustomLogger);
+        //
+        // logger explicitly mentioned in configuration,
+        // should be a CustomLogger
+        final Logger logger1 = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.1");
+        assertNotNull(logger1);
+        assertTrue(logger1.toString(), logger1 instanceof CustomLogger);
+        //
+        // logger not explicitly mentioned in configuration,
+        // should use default factory
+        final Logger logger2Bis = Logger.getLogger("org.apache.log4j.xml.DOMTestCase.testLoggerFactory1.2");
+        assertNotNull(logger2Bis);
+        assertFalse(logger2Bis.toString(), logger2Bis instanceof CustomLogger);
+    }
+
+    /**
+     * This test checks that the subst method of an extending class is checked when evaluating parameters. See bug 43325.
+     *
+     */
+    @Test
+    public void testOverrideSubst() {
+        final DOMConfigurator configurator = new DOMConfigurator() {
+            protected String subst(final String value) {
+                if ("target/output/temp.A1".equals(value)) {
+                    return "target/output/subst-test.A1";
+                }
+                return value;
+            }
+        };
+        configurator.doConfigure("src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml", LogManager.getLoggerRepository());
+        final String name = "A1";
+        final Appender appender = Logger.getRootLogger().getAppender(name);
+        assertNotNull(name, appender);
+        if (Log4j1ActualAppender) {
+            final FileAppender a1 = (FileAppender) appender;
+            assertNotNull(name, a1);
+            final String file = a1.getFile();
+            assertEquals("target/output/subst-test.A1", file);
+        } else {
+            final AppenderWrapper wrapper = (AppenderWrapper) appender;
+            assertNotNull(name, wrapper);
+            final org.apache.logging.log4j.core.appender.FileAppender a1 = (org.apache.logging.log4j.core.appender.FileAppender) wrapper.getAppender();
+            assertNotNull(wrapper.toString(), a1);
+            final String file = a1.getFileName();
+            assertNotNull(a1.toString(), file);
+            // TODO Support this or not?
+            // assertEquals("target/output/subst-test.A1", file);
+        }
+    }
+
+    /**
+     * Tests that reset="true" on log4j:configuration element resets repository before configuration.
+     *
+     * @throws Exception thrown on error.
+     */
+    @Test
+    public void testReset() throws Exception {
+        final VectorAppender appender = new VectorAppender();
+        appender.setName("V1");
+        Logger.getRootLogger().addAppender(appender);
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/testReset.xml");
+        assertNull(Logger.getRootLogger().getAppender("V1"));
+    }
+
+    /**
+     * Test of log4j.throwableRenderer support. See bug 45721.
+     */
+    @Test
+    public void testThrowableRenderer1() {
+        DOMConfigurator.configure("src/test/resources/log4j1-1.2.17/input/xml/throwableRenderer1.xml");
+        final ThrowableRendererSupport repo = (ThrowableRendererSupport) LogManager.getLoggerRepository();
+        final MockThrowableRenderer renderer = (MockThrowableRenderer) repo.getThrowableRenderer();
+        LogManager.resetConfiguration();
+        if (AssumeThrowableRendererSupport) {
+            assertNotNull(renderer);
+            assertEquals(true, renderer.isActivated());
+            assertEquals(false, renderer.getShowVersion());
+        }
+    }
+
+}
diff --git a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml
index fcd1a7c..90917d1 100644
--- a/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml
+++ b/log4j-1.2-api/src/test/resources/log4j1-1.2.17/input/xml/DOMTestCase1.xml
@@ -20,7 +20,7 @@
 <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
   <appender name="A1" class="org.apache.log4j.FileAppender">
     
-    <param name="File"   value="output/temp.A1" />
+    <param name="File"   value="target/output/temp.A1" />
     <param name="Append" value="false" />	    
     
     <layout class="org.apache.log4j.PatternLayout">
@@ -29,7 +29,7 @@
   </appender>
   
   <appender name="A2" class="org.apache.log4j.FileAppender">
-    <param name="File" value="output/temp.A2" />
+    <param name="File" value="target/output/temp.A2" />
     <param name="Append" value="false" />
     <layout class="org.apache.log4j.TTCCLayout">
       <param name="DateFormat" value="ISO8601" />