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 2016/11/03 16:57:20 UTC

logging-log4j2 git commit: [LOG4J2-1637] OSGi support is broken in Log4j2 2.7.

Repository: logging-log4j2
Updated Branches:
  refs/heads/master 5968074f2 -> 4a76057d2


[LOG4J2-1637] OSGi support is broken in Log4j2 2.7. 

Related to LOG4J2-920. 

Project: http://git-wip-us.apache.org/repos/asf/logging-log4j2/repo
Commit: http://git-wip-us.apache.org/repos/asf/logging-log4j2/commit/4a76057d
Tree: http://git-wip-us.apache.org/repos/asf/logging-log4j2/tree/4a76057d
Diff: http://git-wip-us.apache.org/repos/asf/logging-log4j2/diff/4a76057d

Branch: refs/heads/master
Commit: 4a76057d22e29f479cd2512084e4e7146fb2913d
Parents: 5968074
Author: Gary Gregory <gg...@apache.org>
Authored: Thu Nov 3 09:57:14 2016 -0700
Committer: Gary Gregory <gg...@apache.org>
Committed: Thu Nov 3 09:57:14 2016 -0700

----------------------------------------------------------------------
 log4j-osgi/pom.xml                              | 239 ++++++++++++++++++
 .../osgi/tests/AbstractLoadBundleTest.java      | 243 +++++++++++++++++++
 .../tests/equinox/EquinoxLoadApiBundleTest.java |  33 +++
 .../tests/felix/FelixLoadApiBundleTest.java     |  32 +++
 .../log4j/osgi/tests/junit/BundleTestInfo.java  |  71 ++++++
 .../log4j/osgi/tests/junit/OsgiRule.java        |  75 ++++++
 pom.xml                                         |   1 +
 7 files changed, 694 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4a76057d/log4j-osgi/pom.xml
----------------------------------------------------------------------
diff --git a/log4j-osgi/pom.xml b/log4j-osgi/pom.xml
new file mode 100644
index 0000000..e14ce13
--- /dev/null
+++ b/log4j-osgi/pom.xml
@@ -0,0 +1,239 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<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.logging.log4j</groupId>
+    <artifactId>log4j</artifactId>
+    <version>2.7.1-SNAPSHOT</version>
+    <relativePath>../</relativePath>
+  </parent>
+  <artifactId>log4j-osgi</artifactId>
+  <packaging>jar</packaging>
+  <name>Apache Log4j OSGi</name>
+  <description>The Apache Log4j OSGi tests</description>
+  <properties>
+    <log4jParentDir>${basedir}/..</log4jParentDir>
+    <docLabel>OSGi Documentation</docLabel>
+    <projectDir>/osgi</projectDir>
+  </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jboss.spec.javax.jms</groupId>
+      <artifactId>jboss-jms-api_1.1_spec</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.logging.log4j.samples</groupId>
+      <artifactId>log4j-samples-configuration</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <!-- Place Felix before Equinox because Felix is signed. / also place it before org.osgi.core so that its versions of the OSGi classes are used -->
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.framework</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.eclipse.osgi</groupId>
+      <artifactId>org.eclipse.osgi</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <forkCount>2C</forkCount>
+          <reuseForks>true</reuseForks>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- Include the standard NOTICE and LICENSE -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-remote-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>process</goal>
+            </goals>
+            <configuration>
+              <skip>false</skip>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <configuration>
+          <instructions>
+            <Bundle-SymbolicName>org.apache.logging.log4j.osgi</Bundle-SymbolicName>
+            <Import-Package>
+              *
+            </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <version>${clirr.plugin.version}</version>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-changes-plugin</artifactId>
+        <version>${changes.plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>changes-report</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+        <configuration>
+          <issueLinkTemplate>%URL%/show_bug.cgi?id=%ISSUE%</issueLinkTemplate>
+          <useJql>true</useJql>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>${checkstyle.plugin.version}</version>
+        <configuration>
+          <!--<propertiesLocation>${vfs.parent.dir}/checkstyle.properties</propertiesLocation> -->
+          <configLocation>${log4jParentDir}/checkstyle.xml</configLocation>
+          <suppressionsLocation>${log4jParentDir}/checkstyle-suppressions.xml</suppressionsLocation>
+          <enableRulesSummary>false</enableRulesSummary>
+          <propertyExpansion>basedir=${basedir}</propertyExpansion>
+          <propertyExpansion>licensedir=${log4jParentDir}/checkstyle-header.txt</propertyExpansion>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>${javadoc.plugin.version}</version>
+        <configuration>
+          <bottom><![CDATA[<p align="center">Copyright &#169; {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br />
+            Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo,
+            and the Apache Log4j logo are trademarks of The Apache Software Foundation.</p>]]></bottom>
+          <!-- module link generation is completely broken in the javadoc plugin for a multi-module non-aggregating
+               project -->
+          <detectOfflineLinks>false</detectOfflineLinks>
+          <linksource>true</linksource>
+          <links>
+            <link>http://www.osgi.org/javadoc/r4v43/core/</link>
+          </links>
+        </configuration>
+        <reportSets>
+          <reportSet>
+            <id>non-aggregate</id>
+            <reports>
+              <report>javadoc</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <version>${findbugs.plugin.version}</version>
+        <configuration>
+          <fork>true</fork>
+          <jvmArgs>-Duser.language=en</jvmArgs>
+          <threshold>Normal</threshold>
+          <effort>Default</effort>
+          <excludeFilterFile>${log4jParentDir}/findbugs-exclude-filter.xml</excludeFilterFile>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jxr-plugin</artifactId>
+        <version>${jxr.plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <id>non-aggregate</id>
+            <reports>
+              <report>jxr</report>
+            </reports>
+          </reportSet>
+          <reportSet>
+            <id>aggregate</id>
+            <reports>
+              <report>aggregate</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <version>${pmd.plugin.version}</version>
+        <configuration>
+          <targetJdk>${maven.compile.target}</targetJdk>
+        </configuration>
+      </plugin>
+    </plugins>
+  </reporting>
+</project>
+

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4a76057d/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java
----------------------------------------------------------------------
diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java
new file mode 100644
index 0000000..2f3e3ae
--- /dev/null
+++ b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.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.logging.log4j.osgi.tests;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.logging.log4j.osgi.tests.junit.BundleTestInfo;
+import org.apache.logging.log4j.osgi.tests.junit.OsgiRule;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.FrameworkFactory;
+
+/**
+ * Tests a basic Log4J 'setup' in an OSGi container.
+ */
+public abstract class AbstractLoadBundleTest {
+
+    private BundleContext bundleContext;
+
+    private final BundleTestInfo bundleTestInfo;
+
+    private Path here;
+    
+    @Rule
+    public OsgiRule osgi = new OsgiRule(getFactory());
+    /**
+     * Constructs a test for a given bundle.
+     */
+    public AbstractLoadBundleTest() {
+        super();
+        this.bundleTestInfo = new BundleTestInfo();
+    }
+
+    /**
+     * Called before each @Test.
+     */
+    @Before
+    public void before() throws BundleException {
+        bundleContext = osgi.getFramework().getBundleContext();
+        
+        here = Paths.get(".").toAbsolutePath().normalize();
+    }
+
+    private Bundle getApiBundle() throws BundleException {
+        final Path apiPath = here.resolveSibling("log4j-api").resolve("target").resolve("log4j-api-" + bundleTestInfo.getVersion() + ".jar");
+        return bundleContext.installBundle(apiPath.toUri().toString());
+    }
+
+
+    private Bundle getCoreBundle() throws BundleException {
+        final Path corePath = here.resolveSibling("log4j-core").resolve("target").resolve("log4j-core-" + bundleTestInfo.getVersion() + ".jar");
+        return bundleContext.installBundle(corePath.toUri().toString());
+    }
+    
+    private Bundle getDummyBundle() throws BundleException {
+        final Path dumyPath = here.resolveSibling("log4j-samples").resolve("configuration").resolve("target").resolve("log4j-samples-configuration-" + bundleTestInfo.getVersion() + ".jar");
+        return bundleContext.installBundle(dumyPath.toUri().toString());
+    }
+
+    protected abstract FrameworkFactory getFactory();
+    
+    private void log(final Bundle dummy) throws ReflectiveOperationException {
+        // use reflection to log in the context of the dummy bundle
+
+        final Class<?> logManagerClass = dummy.loadClass("org.apache.logging.log4j.LogManager");
+        final Method getLoggerMethod = logManagerClass.getMethod("getLogger", Class.class);
+
+        final Class<?> loggerClass = dummy.loadClass("org.apache.logging.log4j.configuration.CustomConfiguration");
+
+        final Object logger = getLoggerMethod.invoke(null, loggerClass);
+        final Method infoMethod = logger.getClass().getMethod("error", Object.class);
+
+        infoMethod.invoke(logger, "Test OK");
+    }
+
+    private PrintStream setupStream(final Bundle api, final PrintStream newStream) throws ReflectiveOperationException {
+        // use reflection to access the classes internals and in the context of the api bundle
+
+        final Class<?> statusLoggerClass = api.loadClass("org.apache.logging.log4j.status.StatusLogger");
+
+        final Field statusLoggerField = statusLoggerClass.getDeclaredField("STATUS_LOGGER");
+        statusLoggerField.setAccessible(true);
+        final Object statusLoggerFieldValue = statusLoggerField.get(null);
+
+        final Field loggerField = statusLoggerClass.getDeclaredField("logger");
+        loggerField.setAccessible(true);
+        final Object loggerFieldValue = loggerField.get(statusLoggerFieldValue);
+
+        final Class<?> simpleLoggerClass = api.loadClass("org.apache.logging.log4j.simple.SimpleLogger");
+
+        final Field streamField = simpleLoggerClass.getDeclaredField("stream");
+        streamField.setAccessible(true);
+
+        final PrintStream oldStream = (PrintStream) streamField.get(loggerFieldValue);
+
+        streamField.set(loggerFieldValue, newStream);
+
+        return oldStream;
+    }
+
+    private void start(final Bundle api, final Bundle core, final Bundle dummy) throws BundleException {
+        api.start();
+        core.start();
+        dummy.start();        
+    }
+
+    private void stop(final Bundle api, final Bundle core, final Bundle dummy) throws BundleException {
+        dummy.stop();
+        core.stop();
+        api.stop();
+    }
+    /**
+     * Tests LOG4J2-1637.
+     */
+    @Test
+    public void testClassNotFoundErrorLogger() throws BundleException {
+
+        final Bundle api = getApiBundle();
+        final Bundle core = getCoreBundle();
+
+        api.start();
+        // fails if LOG4J2-1637 is not fixed
+        try {
+            core.start();
+        }
+        catch (BundleException ex) {
+            final Throwable t = ex.getCause();
+            if (t != null)
+            {
+                final Throwable t2 = t.getCause();
+                if (t2 != null)
+                {
+                    final String cause = t2.toString();
+                    boolean result = cause.equals("java.lang.ClassNotFoundException: org.apache.logging.log4j.Logger") // Equinox
+                                  || cause.equals("java.lang.ClassNotFoundException: org.apache.logging.log4j.Logger not found by org.apache.logging.log4j.core [2]"); // Felix
+                    Assert.assertFalse("org.apache.logging.log4j package is not properly imported in org.apache.logging.log4j.core bundle, check that the package is exported from api and is not split between api and core", result);
+                }
+                throw ex; // rethrow if the cause of the exception is something else
+            }
+        }
+
+        core.stop();
+        api.stop();
+        
+        core.uninstall();
+        api.uninstall();
+    }
+
+    /**
+     * Tests LOG4J2-920.
+     */
+    @Test
+    @Ignore("org.osgi.framework.BundleException: The bundle \"org.apache.logging.log4j.core_2.7.1.SNAPSHOT [2]\" could not be resolved. Reason: Missing Constraint: Import-Package: javax.jms; version=\"[1.1.0,2.0.0)\"")
+    public void testMissingImportOfCoreOsgiPackage() throws BundleException, ReflectiveOperationException {
+
+        final Bundle api = getApiBundle();
+        final Bundle core = getCoreBundle();
+        final Bundle dummy = getDummyBundle();
+
+        start(api, core, dummy);
+
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        final PrintStream logStream = new PrintStream(baos);
+
+        final PrintStream bakStream = setupStream(api, logStream);
+
+        log(dummy);
+
+        setupStream(api, bakStream);
+
+        final boolean result = baos.toString().contains(
+            "ERROR StatusLogger Unable to create context org.apache.logging.log4j.core.osgi.BundleContextSelector");
+        Assert.assertFalse(
+            "org.apache.logging.log4j.core.osgi;resolution:=optional is missing in Import-Package in the POM", result);
+
+        stop(api, core, dummy);
+        uninstall(api, core, dummy);
+    }
+
+    /**
+     * Tests the log of a simple message in an OSGi container
+     */
+    @Test
+    @Ignore("org.osgi.framework.BundleException: The bundle \"org.apache.logging.log4j.core_2.7.1.SNAPSHOT [2]\" could not be resolved. Reason: Missing Constraint: Import-Package: javax.jms; version=\"[1.1.0,2.0.0)\"")
+    public void testSimpleLogInAnOsgiContext() throws BundleException, ReflectiveOperationException {
+
+        final Bundle api = getApiBundle();
+        final Bundle core = getCoreBundle();
+        final Bundle dummy = getDummyBundle();
+
+        start(api, core, dummy);
+
+        final PrintStream bakStream = System.out;
+        try {
+            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            final PrintStream logStream = new PrintStream(baos);
+            System.setOut(logStream);
+
+            log(dummy);
+
+            final String result = baos.toString().substring(
+                12).trim(); // remove the instant then the spaces at start and end, that are non constant
+            Assert.assertEquals("[main] ERROR org.apache.logging.log4j.configuration.CustomConfiguration - Test OK",
+                result);
+        } finally {
+            System.setOut(bakStream);
+        }
+
+        stop(api, core, dummy);
+        uninstall(api, core, dummy);
+    }
+
+    private void uninstall(final Bundle api, final Bundle core, final Bundle dummy) throws BundleException {
+        dummy.uninstall();
+        core.uninstall();
+        api.uninstall();
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4a76057d/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/equinox/EquinoxLoadApiBundleTest.java
----------------------------------------------------------------------
diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/equinox/EquinoxLoadApiBundleTest.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/equinox/EquinoxLoadApiBundleTest.java
new file mode 100644
index 0000000..10e7a06
--- /dev/null
+++ b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/equinox/EquinoxLoadApiBundleTest.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.logging.log4j.osgi.tests.equinox;
+
+import org.apache.logging.log4j.osgi.tests.AbstractLoadBundleTest;
+import org.eclipse.osgi.launch.EquinoxFactory;
+import org.osgi.framework.launch.FrameworkFactory;
+
+/**
+ * Tests loading the Core bundle into an Eclipse Equinox OSGi container.
+ */
+public class EquinoxLoadApiBundleTest extends AbstractLoadBundleTest {
+
+    @Override
+    protected FrameworkFactory getFactory() {
+        return new EquinoxFactory();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4a76057d/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/felix/FelixLoadApiBundleTest.java
----------------------------------------------------------------------
diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/felix/FelixLoadApiBundleTest.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/felix/FelixLoadApiBundleTest.java
new file mode 100644
index 0000000..0072486
--- /dev/null
+++ b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/felix/FelixLoadApiBundleTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.logging.log4j.osgi.tests.felix;
+
+import org.apache.logging.log4j.osgi.tests.AbstractLoadBundleTest;
+import org.osgi.framework.launch.FrameworkFactory;
+
+/**
+ * Tests loading the Core bundle into an Apache Felix OSGi container.
+ */
+public class FelixLoadApiBundleTest extends AbstractLoadBundleTest {
+
+    @Override
+    protected FrameworkFactory getFactory() {
+        return new org.apache.felix.framework.FrameworkFactory();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4a76057d/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/BundleTestInfo.java
----------------------------------------------------------------------
diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/BundleTestInfo.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/BundleTestInfo.java
new file mode 100644
index 0000000..9a4f8a8
--- /dev/null
+++ b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/BundleTestInfo.java
@@ -0,0 +1,71 @@
+/*
+ * 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.logging.log4j.osgi.tests.junit;
+
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.maven.model.Model;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Provides tests with bundle information. Reads the {@code pom.xml} in the current directory to get project settings.
+ */
+public class BundleTestInfo {
+
+    private final MavenProject project;
+
+    /**
+     * Constructs a new helper objects and initializes itself.
+     */
+    public BundleTestInfo() {
+        try (final FileReader reader = new FileReader("pom.xml")) {
+            // get a raw POM view, not a fully realized POM object.
+            final Model model = new MavenXpp3Reader().read(reader);
+            this.project = new MavenProject(model);
+        } catch (final IOException | XmlPullParserException e) {
+            throw new IllegalStateException("Could not read pom.xml", e);
+        }
+    }
+
+    /**
+     * Gets the Maven artifact ID.
+     *
+     * @return the Maven artifact ID.
+     */
+    public String getArtifactId() {
+        return project.getArtifactId();
+    }
+
+    /**
+     * Gets the Maven version String.
+     *
+     * @return the Maven version String.
+     */
+    public String getVersion() {
+        return project.getVersion();
+    }
+
+    @Override
+    public String toString() {
+        return "BundleTestInfo [project=" + project + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4a76057d/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/OsgiRule.java
----------------------------------------------------------------------
diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/OsgiRule.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/OsgiRule.java
new file mode 100644
index 0000000..9821c3b
--- /dev/null
+++ b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/OsgiRule.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.logging.log4j.osgi.tests.junit;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.rules.ExternalResource;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+
+/**
+ * JUnit rule to initialize and shutdown an OSGi framework.
+ */
+public class OsgiRule extends ExternalResource {
+
+    private final FrameworkFactory factory;
+    private Framework framework;
+
+    public OsgiRule(final FrameworkFactory factory) {
+        this.factory = factory;
+    }
+
+    @Override
+    protected void after() {
+        if (framework != null) {
+            try {
+                framework.stop();
+            } catch (final BundleException e) {
+                throw new RuntimeException(e);
+            } finally {
+                framework = null;
+            }
+        }
+    }
+
+    @Override
+    protected void before() throws Throwable {
+        final Map<String, String> configMap = new HashMap<>(2);
+        // Cleans framework before first init. Subsequent init invocations do not clean framework.
+        configMap.put("org.osgi.framework.storage.clean", "onFirstInit");
+        configMap.put("felix.log.level", "4");
+        configMap.put("eclipse.log.level", "ALL");        
+        // Delegates loading of endorsed libraries to JVM classloader
+        // config.put("org.osgi.framework.bootdelegation", "javax.*,org.w3c.*,org.xml.*");
+        framework = factory.newFramework(configMap);
+        framework.init();
+        framework.start();
+    }
+
+    public Framework getFramework() {
+        return framework;
+    }
+
+    @Override
+    public String toString() {
+        return "OsgiRule [factory=" + factory + ", framework=" + framework + "]";
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j2/blob/4a76057d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 385f8ad..82a001d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1250,6 +1250,7 @@
     <module>log4j-taglib</module>
     <module>log4j-jmx-gui</module>
     <module>log4j-samples</module>
+    <module>log4j-osgi</module>
     <module>log4j-bom</module>
     <module>log4j-nosql</module>
     <module>log4j-web</module>