You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2021/12/20 20:47:26 UTC

[logging-log4j2] branch log4j-2.12 updated: [LOG4J2-3242] Limit JNDI to the java protocol only. (#645)

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

rgoers pushed a commit to branch log4j-2.12
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/log4j-2.12 by this push:
     new bf8ba18  [LOG4J2-3242] Limit JNDI to the java protocol only. (#645)
bf8ba18 is described below

commit bf8ba18f63ab9f9ffd54387c5c527ecc7a681037
Author: Gary Gregory <ga...@users.noreply.github.com>
AuthorDate: Mon Dec 20 15:47:21 2021 -0500

    [LOG4J2-3242] Limit JNDI to the java protocol only. (#645)
    
    * [LOG4J2-3242] Limit JNDI to the java protocol only. JNDI will remain
    disabled by default. The enablement property has been renamed to
    'log4j2.enableJndiJava'.
    
    * Do not declare log4j-api-java9 and log4j-core-java9 as depdendencies as
    it causes problems with the Maven enforcer plugin.
    
    I'm not updating changes.xml to avoid git conflicts.
    
    * [LOG4J2-3242] Limit JNDI to the java protocol only. JNDI will remain
    disabled by default. The enablement property has been renamed to
    'log4j2.enableJndiJava'.
    
    Oops, add missing test fixture for RoutingAppenderWithJndiTest.
---
 log4j-api/pom.xml                                  |   6 -
 log4j-core/pom.xml                                 |   7 --
 .../log4j/core/appender/AbstractManager.java       |   3 +-
 .../log4j/core/appender/mom/JmsManager.java        |  13 +--
 .../logging/log4j/core/lookup/Interpolator.java    |  13 ++-
 .../logging/log4j/core/lookup/JndiLookup.java      |  52 ++++++++-
 .../apache/logging/log4j/core/net/JndiManager.java |  39 +++++--
 .../log4j/core/selector/JndiContextSelector.java   |   4 +-
 .../log4j/core/appender/mom/JmsAppenderTest.java   |   7 +-
 .../routing/RoutingAppenderWithJndiTest.java       | 125 +++++++++++++++++++++
 .../log4j/core/lookup/InterpolatorTest.java        |   2 +
 .../log4j/core/lookup/JndiDisabledLookupTest.java  |  38 +++++++
 .../logging/log4j/core/lookup/JndiLookupTest.java  |  42 ++++++-
 .../core/lookup/JndiRestrictedLookupTest.java      |  43 +++++++
 .../logging/log4j/core/net/JndiManagerTest.java    |  98 ++++++++++++++++
 .../apache/logging/log4j/test/JUnit5Bridge.java    |  35 ++++++
 .../src/test/resources/log4j-routing-by-jndi.xml   |  59 ++++++++++
 17 files changed, 542 insertions(+), 44 deletions(-)

diff --git a/log4j-api/pom.xml b/log4j-api/pom.xml
index 059e23f..755d0a9 100644
--- a/log4j-api/pom.xml
+++ b/log4j-api/pom.xml
@@ -33,12 +33,6 @@
     <projectDir>/api</projectDir>
   </properties>
   <dependencies>
-    <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-api-java9</artifactId>
-      <scope>provided</scope>
-      <type>zip</type>
-    </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>
diff --git a/log4j-core/pom.xml b/log4j-core/pom.xml
index 7e86130..f1bdc22 100644
--- a/log4j-core/pom.xml
+++ b/log4j-core/pom.xml
@@ -39,13 +39,6 @@
       <groupId>org.apache.logging.log4j</groupId>
       <artifactId>log4j-api</artifactId>
     </dependency>
-    <!-- Classes and resources to be shaded into the core jar -->
-    <dependency>
-      <groupId>org.apache.logging.log4j</groupId>
-      <artifactId>log4j-core-java9</artifactId>
-      <scope>provided</scope>
-      <type>zip</type>
-    </dependency>
     <!-- Used for OSGi bundle support -->
     <dependency>
       <groupId>org.osgi</groupId>
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
index e85871f..bf2c53a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AbstractManager.java
@@ -18,6 +18,7 @@ package org.apache.logging.log4j.core.appender;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -110,7 +111,7 @@ public abstract class AbstractManager implements AutoCloseable {
             @SuppressWarnings("unchecked")
             M manager = (M) MAP.get(name);
             if (manager == null) {
-                manager = factory.createManager(name, data);
+                manager = Objects.requireNonNull(factory, "factory").createManager(name, data);
                 if (manager == null) {
                     throw new IllegalStateException("ManagerFactory [" + factory + "] unable to create manager for ["
                             + name + "] with data [" + data + "]");
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
index c9f105b..d2d397d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/mom/JmsManager.java
@@ -125,17 +125,16 @@ public class JmsManager extends AbstractManager {
 
         @Override
         public JmsManager createManager(final String name, final JmsManagerConfiguration data) {
-            if (JndiManager.isJndiEnabled()) {
+            if (JndiManager.isJndiJmsEnabled()) {
                 try {
                     return new JmsManager(name, data);
                 } catch (final Exception e) {
                     logger().error("Error creating JmsManager using JmsManagerConfiguration [{}]", data, e);
                     return null;
                 }
-            } else {
-                logger().error("Jndi has not been enabled. The log4j2.enableJndi property must be set to true");
-                return null;
             }
+            logger().error("JNDI must be enabled by setting log4j2.enableJndiJms=true");
+            return null;
         }
     }
 
@@ -354,7 +353,7 @@ public class JmsManager extends AbstractManager {
      * @param object
      *            The LogEvent or String message to wrap.
      * @return A new JMS message containing the provided object.
-     * @throws JMSException
+     * @throws JMSException if the JMS provider fails to create a message due to some internal error.
      */
     public Message createMessage(final Serializable object) throws JMSException {
         if (object instanceof String) {
@@ -375,7 +374,7 @@ public class JmsManager extends AbstractManager {
      * Creates a MessageConsumer on this Destination using the current Session.
      *
      * @return A MessageConsumer on this Destination.
-     * @throws JMSException
+     * @throws JMSException if the session fails to create a consumer due to some internal error.
      */
     public MessageConsumer createMessageConsumer() throws JMSException {
         return this.session.createConsumer(this.destination);
@@ -389,7 +388,7 @@ public class JmsManager extends AbstractManager {
      * @param destination
      *            The JMS Destination for the MessageProducer
      * @return A MessageProducer on this Destination.
-     * @throws JMSException
+     * @throws JMSException if the session fails to create a MessageProducer due to some internal error.
      */
     public MessageProducer createMessageProducer(final Session session, final Destination destination)
             throws JMSException {
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
index c8409ab..3d8c07d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java
@@ -26,6 +26,7 @@ import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.ConfigurationAware;
 import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
 import org.apache.logging.log4j.core.config.plugins.util.PluginType;
+import org.apache.logging.log4j.core.net.JndiManager;
 import org.apache.logging.log4j.core.util.Loader;
 import org.apache.logging.log4j.core.util.ReflectionUtil;
 import org.apache.logging.log4j.status.StatusLogger;
@@ -73,7 +74,7 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
         for (final Map.Entry<String, PluginType<?>> entry : plugins.entrySet()) {
             try {
                 final Class<? extends StrLookup> clazz = entry.getValue().getPluginClass().asSubclass(StrLookup.class);
-                if (!clazz.getName().equals("org.apache.logging.log4j.core.lookup.JndiLookup")) {
+                if (!clazz.getName().equals("org.apache.logging.log4j.core.lookup.JndiLookup") || JndiManager.isJndiLookupEnabled()) {
                     strLookupMap.put(entry.getKey().toLowerCase(), ReflectionUtil.instantiate(clazz));
                 }
             } catch (final Throwable t) {
@@ -101,6 +102,16 @@ public class Interpolator extends AbstractConfigurationAwareLookup {
         strLookupMap.put("main", MainMapLookup.MAIN_SINGLETON);
         strLookupMap.put("marker", new MarkerLookup());
         strLookupMap.put("java", new JavaLookup());
+        // JNDI
+        if (JndiManager.isJndiLookupEnabled()) {
+            try {
+                // [LOG4J2-703] We might be on Android
+                strLookupMap.put(LOOKUP_KEY_JNDI,
+                        Loader.newCheckedInstanceOf("org.apache.logging.log4j.core.lookup.JndiLookup", StrLookup.class));
+            } catch (final LinkageError | Exception e) {
+                handleError(LOOKUP_KEY_JNDI, e);
+            }
+        }
         // JMX input args
         try {
             // We might be on Android
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java
index bd19e54..a783ea4 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java
@@ -16,9 +16,16 @@
  */
 package org.apache.logging.log4j.core.lookup;
 
+import java.util.Objects;
+
+import javax.naming.NamingException;
+
 import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.Marker;
+import org.apache.logging.log4j.MarkerManager;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.net.JndiManager;
 import org.apache.logging.log4j.status.StatusLogger;
 
 /**
@@ -26,17 +33,54 @@ import org.apache.logging.log4j.status.StatusLogger;
  */
 @Plugin(name = "jndi", category = StrLookup.CATEGORY)
 public class JndiLookup extends AbstractLookup {
+
     private static final Logger LOGGER = StatusLogger.getLogger();
-    private static final String RESULT = "JNDI is not supported";
+    private static final Marker LOOKUP = MarkerManager.getMarker("LOOKUP");
+
+    /** JNDI resource path prefix used in a J2EE container */
+    static final String CONTAINER_JNDI_RESOURCE_PATH_PREFIX = "java:comp/env/";
+
+    /**
+     * Constructs a new instance or throw IllegalStateException if this feature is disabled.
+     */
+    public JndiLookup() {
+        if (!JndiManager.isJndiLookupEnabled()) {
+            throw new IllegalStateException("JNDI must be enabled by setting log4j2.enableJndiLookup=true");
+        }
+    }
+
     /**
      * Looks up the value of the JNDI resource.
+     * 
      * @param event The current LogEvent (is ignored by this StrLookup).
-     * @param key  the JNDI resource name to be looked up, may be null
+     * @param key the JNDI resource name to be looked up, may be null
      * @return The String value of the JNDI resource.
      */
     @Override
     public String lookup(final LogEvent event, final String key) {
-        LOGGER.warn("Attempt to use JNDI Lookup");
-        return RESULT;
+        if (key == null) {
+            return null;
+        }
+        final String jndiName = convertJndiName(key);
+        try (final JndiManager jndiManager = JndiManager.getDefaultManager()) {
+            return Objects.toString(jndiManager.lookup(jndiName), null);
+        } catch (final NamingException e) {
+            LOGGER.warn(LOOKUP, "Error looking up JNDI resource [{}].", jndiName, e);
+            return null;
+        }
+    }
+
+    /**
+     * Convert the given JNDI name to the actual JNDI name to use. Default implementation applies the "java:comp/env/"
+     * prefix unless other scheme like "java:" is given.
+     * 
+     * @param jndiName The name of the resource.
+     * @return The fully qualified name to look up.
+     */
+    private String convertJndiName(final String jndiName) {
+        if (!jndiName.startsWith(CONTAINER_JNDI_RESOURCE_PATH_PREFIX) && jndiName.indexOf(':') == -1) {
+            return CONTAINER_JNDI_RESOURCE_PATH_PREFIX + jndiName;
+        }
+        return jndiName;
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
index 06e2793..e005c62 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/JndiManager.java
@@ -39,11 +39,29 @@ import org.apache.logging.log4j.util.PropertiesUtil;
 public class JndiManager extends AbstractManager {
 
     private static final JndiManagerFactory FACTORY = new JndiManagerFactory();
+    private static final String PREFIX = "log4j2.enableJndi";
+    private static final String JAVA_SCHEME = "java";
 
     private final Context context;
 
+    private static boolean isJndiEnabled(final String subKey) {
+        return PropertiesUtil.getProperties().getBooleanProperty(PREFIX + subKey, false);
+    }
+
     public static boolean isJndiEnabled() {
-        return PropertiesUtil.getProperties().getBooleanProperty("log4j2.enableJndi", false);
+        return isJndiContextSelectorEnabled() || isJndiJmsEnabled() || isJndiLookupEnabled();
+    }
+
+    public static boolean isJndiContextSelectorEnabled() {
+        return isJndiEnabled("ContextSelector");
+    }
+
+    public static boolean isJndiJmsEnabled() {
+        return isJndiEnabled("Jms");
+    }
+
+    public static boolean isJndiLookupEnabled() {
+        return isJndiEnabled("Lookup");
     }
 
     private JndiManager(final String name, final Context context) {
@@ -181,7 +199,7 @@ public class JndiManager extends AbstractManager {
         }
         try {
             URI uri = new URI(name);
-            if (uri.getScheme() == null || uri.getScheme().equals("java")) {
+            if (uri.getScheme() == null || uri.getScheme().equals(JAVA_SCHEME)) {
                 return (T) this.context.lookup(name);
             }
             LOGGER.warn("Unsupported JNDI URI - {}", name);
@@ -195,15 +213,14 @@ public class JndiManager extends AbstractManager {
 
         @Override
         public JndiManager createManager(final String name, final Properties data) {
-            if (isJndiEnabled()) {
-                try {
-                    return new JndiManager(name, new InitialContext(data));
-                } catch (final NamingException e) {
-                    LOGGER.error("Error creating JNDI InitialContext.", e);
-                    return null;
-                }
-            } else {
-                return new JndiManager(name, null);
+            if (!isJndiEnabled()) {
+                throw new IllegalStateException(String.format("JNDI must be enabled by setting one of the %s* properties to true", PREFIX));
+            }
+            try {
+                return new JndiManager(name, new InitialContext(data));
+            } catch (final NamingException e) {
+                LOGGER.error("Error creating JNDI InitialContext for '{}'.", name, e);
+                return null;
             }
         }
 
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
index 81ec3f3..7da852d 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/selector/JndiContextSelector.java
@@ -93,8 +93,8 @@ public class JndiContextSelector implements NamedContextSelector {
     private static final StatusLogger LOGGER = StatusLogger.getLogger();
 
     public JndiContextSelector() {
-        if (!JndiManager.isJndiEnabled()) {
-            throw new IllegalStateException("JNDI must be enabled by setting log4j2.enableJndi=true");
+        if (!JndiManager.isJndiContextSelectorEnabled()) {
+            throw new IllegalStateException("JNDI must be enabled by setting log4j2.enableJndiContextSelector=true");
         }
     }
 
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/mom/JmsAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/mom/JmsAppenderTest.java
index daf2d0e..4c5b1a9 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/mom/JmsAppenderTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/mom/JmsAppenderTest.java
@@ -85,8 +85,13 @@ public class JmsAppenderTest {
     public RuleChain rules = RuleChain.outerRule(jndiRule).around(ctx);
 
     @BeforeClass
+    public static void afterClass() throws Exception {
+        System.clearProperty("log4j2.enableJndiJms");
+    }
+
+    @BeforeClass
     public static void beforeClass() throws Exception {
-        System.setProperty("log4j2.enableJndi", "true");
+        System.setProperty("log4j2.enableJndiJms", "true");
     }
 
     public JmsAppenderTest() throws Exception {
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
new file mode 100644
index 0000000..899fd5e
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.core.appender.routing;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Map;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import org.apache.logging.log4j.EventLogger;
+import org.apache.logging.log4j.junit.JndiRule;
+import org.apache.logging.log4j.junit.LoggerContextRule;
+import org.apache.logging.log4j.message.StructuredDataMessage;
+import org.apache.logging.log4j.test.appender.ListAppender;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+
+import static org.junit.Assert.*;
+
+/**
+ * RoutingAppenderWithJndiTest
+ */
+public class RoutingAppenderWithJndiTest {
+
+    public static final String JNDI_CONTEXT_NAME = "java:comp/env/logging/context-name";
+    private ListAppender listAppender1;
+    private ListAppender listAppender2;
+
+    public static LoggerContextRule loggerContextRule = new LoggerContextRule("log4j-routing-by-jndi.xml");
+
+    @ClassRule
+    public static RuleChain rules = RuleChain.outerRule(new JndiRule(initBindings())).around(loggerContextRule);
+
+    private static Map<String, Object> initBindings() {
+        System.setProperty("log4j2.enableJndiLookup", "true");
+        //System.setProperty("log4j2.enableJndiJms", "true");
+        //System.setProperty("log4j2.enableJndiContextSelector", "true");
+        return Collections.emptyMap();
+    }
+
+    @Before
+    public void before() {
+        listAppender1 = RoutingAppenderWithJndiTest.loggerContextRule.getListAppender("List1");
+        listAppender2 = RoutingAppenderWithJndiTest.loggerContextRule.getListAppender("List2");
+    }
+
+    @After
+    public void after() {
+        if (listAppender1 != null) {
+            listAppender1.clear();
+        }
+        if (listAppender2 != null) {
+            listAppender2.clear();
+        }
+    }
+
+    @Test
+    public void routingTest() throws NamingException {
+        // default route when there's no jndi resource
+        StructuredDataMessage msg = new StructuredDataMessage("Test", "This is a message from unknown context", "Context");
+        EventLogger.logEvent(msg);
+        final File defaultLogFile = new File("target/routingbyjndi/routingbyjnditest-unknown.log");
+        assertTrue("The default log file was not created", defaultLogFile.exists());
+
+        // now set jndi resource to Application1
+        final Context context = new InitialContext();
+        context.bind(JNDI_CONTEXT_NAME, "Application1");
+
+        msg = new StructuredDataMessage("Test", "This is a message from Application1", "Context");
+        EventLogger.logEvent(msg);
+        assertNotNull("No events generated", listAppender1.getEvents());
+        assertTrue("Incorrect number of events. Expected 1, got " + listAppender1.getEvents().size(), listAppender1.getEvents().size() == 1);
+
+        // now set jndi resource to Application2
+        context.rebind(JNDI_CONTEXT_NAME, "Application2");
+
+        msg = new StructuredDataMessage("Test", "This is a message from Application2", "Context");
+        EventLogger.logEvent(msg);
+        assertNotNull("No events generated", listAppender2.getEvents());
+        assertTrue("Incorrect number of events. Expected 1, got " + listAppender2.getEvents().size(), listAppender2.getEvents().size() == 1);
+        assertTrue("Incorrect number of events. Expected 1, got " + listAppender1.getEvents().size(), listAppender1.getEvents().size() == 1);
+
+        msg = new StructuredDataMessage("Test", "This is another message from Application2", "Context");
+        EventLogger.logEvent(msg);
+        assertNotNull("No events generated", listAppender2.getEvents());
+        assertTrue("Incorrect number of events. Expected 2, got " + listAppender2.getEvents().size(), listAppender2.getEvents().size() == 2);
+        assertTrue("Incorrect number of events. Expected 1, got " + listAppender1.getEvents().size(), listAppender1.getEvents().size() == 1);
+
+        // now set jndi resource to Application3.
+        // The context name, 'Application3', will be used as log file name by the default route.
+        context.rebind("java:comp/env/logging/context-name", "Application3");
+        msg = new StructuredDataMessage("Test", "This is a message from Application3", "Context");
+        EventLogger.logEvent(msg);
+        final File application3LogFile = new File("target/routingbyjndi/routingbyjnditest-Application3.log");
+        assertTrue("The Application3 log file was not created", application3LogFile.exists());
+
+        // now set jndi resource to Application4
+        // The context name, 'Application4', will be used as log file name by the default route.
+        context.rebind("java:comp/env/logging/context-name", "Application4");
+        msg = new StructuredDataMessage("Test", "This is a message from Application4", "Context");
+        EventLogger.logEvent(msg);
+        final File application4LogFile = new File("target/routingbyjndi/routingbyjnditest-Application4.log");
+        assertTrue("The Application3 log file was not created", application4LogFile.exists());
+    }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java
index 0af36fd..fc6f3d8 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java
@@ -44,12 +44,14 @@ public class InterpolatorTest {
         protected void before() throws Throwable {
             System.setProperty(TESTKEY, TESTVAL);
             System.setProperty(TESTKEY2, TESTVAL);
+            System.setProperty("log4j2.enableJndiLookup", "true");
         }
 
         @Override
         protected void after() {
             System.clearProperty(TESTKEY);
             System.clearProperty(TESTKEY2);
+            System.clearProperty("log4j2.enableJndiLookup");
         }
     });
 
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiDisabledLookupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiDisabledLookupTest.java
new file mode 100644
index 0000000..362a67d
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiDisabledLookupTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.core.lookup;
+
+import org.apache.logging.log4j.test.JUnit5Bridge;
+import org.junit.Test;
+
+/**
+ * JndiDisabledLookupTest
+ *
+ * Verifies the Lookups are disabled without the log4j2.enableJndiLookup property set to true.
+ */
+public class JndiDisabledLookupTest {
+
+    @Test
+    public void testLookup() {
+        JUnit5Bridge.assertThrows(IllegalStateException.class, new Runnable() {
+            @Override
+            public void run() {
+                new JndiLookup();
+            }
+        });
+    }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java
index ba45808..0675aa1 100644
--- a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java
@@ -16,17 +16,20 @@
  */
 package org.apache.logging.log4j.core.lookup;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.logging.log4j.junit.JndiRule;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 
-import static org.junit.Assert.*;
-
 /**
  * JndiLookupTest
  */
@@ -42,8 +45,22 @@ public class JndiLookupTest {
     @Rule
     public JndiRule jndiRule = new JndiRule(createBindings());
 
+    @AfterClass
+    public static void afterClass() {
+        System.clearProperty("log4j2.enableJndiLookup");
+    }
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty("log4j2.enableJndiLookup", "true");
+    }
+
     private Map<String, Object> createBindings() {
-        return new HashMap<>();
+        final Map<String, Object> map = new HashMap<>();
+        map.put(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME);
+        map.put(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_INTEGRAL_NAME, TEST_INTEGRAL_VALUE);
+        map.put(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_STRINGS_NAME, TEST_STRINGS_COLLECTION);
+        return map;
     }
 
     @Test
@@ -51,6 +68,23 @@ public class JndiLookupTest {
         final StrLookup lookup = new JndiLookup();
 
         String contextName = lookup.lookup(TEST_CONTEXT_RESOURCE_NAME);
-        assertEquals("JNDI is not supported", contextName);
+        assertEquals(TEST_CONTEXT_NAME, contextName);
+
+        contextName = lookup.lookup(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME);
+        assertEquals(TEST_CONTEXT_NAME, contextName);
+
+        final String nonExistingResource = lookup.lookup("logging/non-existing-resource");
+        assertNull(nonExistingResource);
+    }
+
+    @Test
+    public void testNonStringLookup() throws Exception {
+        // LOG4J2-1310
+        final StrLookup lookup = new JndiLookup();
+        final String integralValue = lookup.lookup(TEST_INTEGRAL_NAME);
+        assertEquals(String.valueOf(TEST_INTEGRAL_VALUE), integralValue);
+        final String collectionValue = lookup.lookup(TEST_STRINGS_NAME);
+        assertEquals(String.valueOf(TEST_STRINGS_COLLECTION), collectionValue);
     }
+
 }
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.java
new file mode 100644
index 0000000..e40cdb2
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/lookup/JndiRestrictedLookupTest.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.logging.log4j.core.lookup;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNull;
+
+/**
+ * JndiLookupTest
+ */
+public class JndiRestrictedLookupTest {
+
+    private static final String DOMAIN = "apache.org";
+
+    @BeforeClass
+    public static void beforeClass() {
+        System.setProperty("log4j2.enableJndiLookup", "true");
+    }
+
+    @Test
+    public void testDnsLookup() throws Exception {
+        final StrLookup lookup = new JndiLookup();
+        String result = lookup.lookup("dns:/" + DOMAIN);
+        assertNull("DNS data returend", result);
+
+    }
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/JndiManagerTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/JndiManagerTest.java
new file mode 100644
index 0000000..e2fd8f7
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/JndiManagerTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+
+package org.apache.logging.log4j.core.net;
+
+import static org.junit.Assert.assertFalse;
+
+import java.util.Properties;
+
+import org.apache.logging.log4j.test.JUnit5Bridge;
+import org.junit.Test;
+
+/**
+ * Tests {@link JndiManager}.
+ */
+public class JndiManagerTest {
+
+    @Test
+    public void testIsJndiEnabled() {
+        assertFalse(JndiManager.isJndiEnabled());
+    }
+
+    @Test
+    public void testIsJndiContextSelectorEnabled() {
+        assertFalse(JndiManager.isJndiContextSelectorEnabled());
+    }
+
+    @Test
+    public void testIsJndiJmsEnabled() {
+        assertFalse(JndiManager.isJndiJmsEnabled());
+    }
+
+    @Test
+    public void testIsJndiLookupEnabled() {
+        assertFalse(JndiManager.isJndiLookupEnabled());
+    }
+
+    @Test
+    public void testNoInstanceByDefault() {
+        JUnit5Bridge.assertThrows(IllegalStateException.class, new Runnable() {
+            @Override
+            public void run() {
+                JndiManager.getDefaultManager();
+            }
+        });
+        JUnit5Bridge.assertThrows(IllegalStateException.class, new Runnable() {
+            @Override
+            public void run() {
+                JndiManager.getDefaultManager(null);
+            }
+        });
+        JUnit5Bridge.assertThrows(IllegalStateException.class, new Runnable() {
+            @Override
+            public void run() {
+                JndiManager.getDefaultManager("A");
+            }
+        });
+        JUnit5Bridge.assertThrows(IllegalStateException.class, new Runnable() {
+            @Override
+            public void run() {
+                JndiManager.getJndiManager(null);
+            }
+        });
+        JUnit5Bridge.assertThrows(IllegalStateException.class, new Runnable() {
+            @Override
+            public void run() {
+                JndiManager.getJndiManager(new Properties());
+            }
+        });
+        JUnit5Bridge.assertThrows(IllegalStateException.class, new Runnable() {
+            @Override
+            public void run() {
+                JndiManager.getJndiManager(null, null, null, null, null, null);
+            }
+        });
+        JUnit5Bridge.assertThrows(IllegalStateException.class, new Runnable() {
+            @Override
+            public void run() {
+                JndiManager.getJndiManager("A", "A", "A", "A", "A", new Properties());
+            }
+        });
+    }
+
+}
diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/test/JUnit5Bridge.java b/log4j-core/src/test/java/org/apache/logging/log4j/test/JUnit5Bridge.java
new file mode 100644
index 0000000..c331433
--- /dev/null
+++ b/log4j-core/src/test/java/org/apache/logging/log4j/test/JUnit5Bridge.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.logging.log4j.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class JUnit5Bridge {
+
+    /** This branch is on Java 7 and uses JUnit 4, hence this hack. */
+    public static <T> void assertThrows(Class<T> expectedType, Runnable runnable) {
+        try {
+            runnable.run();
+            fail("Expected " + expectedType.getCanonicalName());
+        } catch (Throwable t) {
+            assertEquals(expectedType, t.getClass());
+        }
+    
+    }
+
+}
diff --git a/log4j-core/src/test/resources/log4j-routing-by-jndi.xml b/log4j-core/src/test/resources/log4j-routing-by-jndi.xml
new file mode 100644
index 0000000..2967eaa
--- /dev/null
+++ b/log4j-core/src/test/resources/log4j-routing-by-jndi.xml
@@ -0,0 +1,59 @@
+<?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.
+
+-->
+<Configuration status="OFF" name="RoutingByJndiTest">
+
+  <ThresholdFilter level="debug"/>
+
+  <Appenders>
+    <Console name="STDOUT">
+      <PatternLayout pattern="%m%n"/>
+    </Console>
+    <List name="List1">
+      <ThresholdFilter level="debug"/>
+    </List>
+    <List name="List2">
+      <ThresholdFilter level="debug"/>
+    </List>
+    <Routing name="Routing">
+      <Routes pattern="$${jndi:logging/context-name}">
+        <Route>
+          <File name="DefaultApplicationLogFile" fileName="target/routingbyjndi/routingbyjnditest-${jndi:logging/context-name:-unknown}.log" append="false">
+            <PatternLayout>
+              <Pattern>%d %p %C{1.} [%t] %m%n</Pattern>
+            </PatternLayout>
+          </File>
+        </Route>
+        <Route ref="List1" key="Application1"/>
+        <Route ref="List2" key="Application2"/>
+      </Routes>
+    </Routing>
+  </Appenders>
+
+  <Loggers>
+    <Logger name="EventLogger" level="info" additivity="false">
+      <AppenderRef ref="Routing"/>
+      <AppenderRef ref="STDOUT"/>
+    </Logger>
+
+    <Root level="error">
+      <AppenderRef ref="STDOUT"/>
+    </Root>
+  </Loggers>
+
+</Configuration>
\ No newline at end of file