Index: core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java =================================================================== --- core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java (revision 1504271) +++ core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java (working copy) @@ -63,6 +63,7 @@ this.defaultLookup = new MapLookup(new HashMap()); lookups.put("sys", new SystemPropertiesLookup()); lookups.put("env", new EnvironmentLookup()); + lookups.put("jndi", new JndiLookup()); } /** Index: core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java =================================================================== --- core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java (revision 0) +++ core/src/main/java/org/apache/logging/log4j/core/lookup/JndiLookup.java (revision 0) @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.lookup; + +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.config.plugins.Plugin; + +/** + * Looks up keys from JNDI resources. + */ +@Plugin(name = "jndi", category = "Lookup") +public class JndiLookup implements StrLookup { + + /** JNDI resourcce path prefix used in a J2EE container */ + static final String CONTAINER_JNDI_RESOURCE_PATH_PREFIX = "java:comp/env/"; + + /** + * Get the value of the JNDI resource. + * @param key the JNDI resource name to be looked up, may be null + * @return The value of the JNDI resource. + */ + @Override + public String lookup(final String key) { + return lookup(null, key); + } + + /** + * Get 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 + * @return The value of the JNDI resource. + */ + @Override + public String lookup(final LogEvent event, final String key) { + if (key == null) { + return null; + } + + try { + InitialContext ctx = new InitialContext(); + + if (ctx != null) { + return (String) ctx.lookup(convertJndiName(key)); + } + } catch (NamingException 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 + * @return + */ + private String convertJndiName(String jndiName) { + if (!jndiName.startsWith(CONTAINER_JNDI_RESOURCE_PATH_PREFIX) && jndiName.indexOf(':') == -1) { + jndiName = CONTAINER_JNDI_RESOURCE_PATH_PREFIX + jndiName; + } + + return jndiName; + } +} Index: core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java =================================================================== --- core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java (revision 0) +++ core/src/test/java/org/apache/logging/log4j/core/appender/routing/RoutingAppenderWithJndiTest.java (revision 0) @@ -0,0 +1,122 @@ +/* + * 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 static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +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.LogManager; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.XMLConfigurationFactory; +import org.apache.logging.log4j.message.StructuredDataMessage; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockejb.jndi.MockContextFactory; + +/** + * RoutingAppenderWithJndiTest + */ +public class RoutingAppenderWithJndiTest { + + private static final String CONFIG = "log4j-routing-by-jndi.xml"; + private static Configuration config; + private static ListAppender listAppender1; + private static ListAppender listAppender2; + private static LoggerContext ctx; + + @BeforeClass + @SuppressWarnings("unchecked") + public static void setupClass() { + System.setProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY, CONFIG); + ctx = (LoggerContext) LogManager.getContext(false); + config = ctx.getConfiguration(); + for (final Map.Entry> entry : config.getAppenders().entrySet()) { + if (entry.getKey().equals("List1")) { + listAppender1 = (ListAppender) entry.getValue(); + } + if (entry.getKey().equals("List2")) { + listAppender2 = (ListAppender) entry.getValue(); + } + } + } + + @Before + public void before() throws NamingException { + MockContextFactory.setAsInitial(); + } + + @After + public void after() { + MockContextFactory.revertSetAsInitial(); + } + + @AfterClass + public static void cleanupClass() { + System.clearProperty(XMLConfigurationFactory.CONFIGURATION_FILE_PROPERTY); + ctx.reconfigure(); + StatusLogger.getLogger().reset(); + } + + @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); + File defaultLogFile = new File("target/routingbyjndi/routingbyjnditest-default.log"); + assertTrue("The default log file was not created", defaultLogFile.exists()); + + // now set jndi resource to Application1 + Context context = new InitialContext(); + context.bind("java:comp/env/logging/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("java:comp/env/logging/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); + } +} Index: core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java =================================================================== --- core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java (revision 1504271) +++ core/src/test/java/org/apache/logging/log4j/core/lookup/InterpolatorTest.java (working copy) @@ -16,16 +16,22 @@ */ package org.apache.logging.log4j.core.lookup; -import org.apache.logging.log4j.ThreadContext; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.apache.logging.log4j.ThreadContext; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockejb.jndi.MockContextFactory; /** * @@ -35,18 +41,25 @@ private static final String TESTKEY = "TestKey"; private static final String TESTVAL = "TestValue"; + private static final String TEST_CONTEXT_RESOURCE_NAME = "logging/context-name"; + private static final String TEST_CONTEXT_NAME = "app-1"; @BeforeClass - public static void before() { + public static void before() throws NamingException { System.setProperty(TESTKEY, TESTVAL); + + MockContextFactory.setAsInitial(); + Context context = new InitialContext(); + context.bind(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME); } @AfterClass public static void after() { + MockContextFactory.revertSetAsInitial(); + System.clearProperty(TESTKEY); } - @Test public void testLookup() { final Map map = new HashMap(); @@ -64,5 +77,18 @@ ThreadContext.clear(); value = lookup.lookup("ctx:" + TESTKEY); assertEquals(TESTVAL, value); + value = lookup.lookup("jndi:" + TEST_CONTEXT_RESOURCE_NAME); + assertEquals(TEST_CONTEXT_NAME, value); + } + + @Test + public void testLookupWithDefaultInterpolator() { + final StrLookup lookup = new Interpolator(); + String value = lookup.lookup("sys:" + TESTKEY); + assertEquals(TESTVAL, value); + value = lookup.lookup("env:PATH"); + assertNotNull(value); + value = lookup.lookup("jndi:" + TEST_CONTEXT_RESOURCE_NAME); + assertEquals(TEST_CONTEXT_NAME, value); } } Index: core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java =================================================================== --- core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java (revision 0) +++ core/src/test/java/org/apache/logging/log4j/core/lookup/JndiLookupTest.java (revision 0) @@ -0,0 +1,64 @@ +/* + * 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockejb.jndi.MockContextFactory; + +/** + * JndiLookupTest + */ +public class JndiLookupTest { + + private static final String TEST_CONTEXT_RESOURCE_NAME = "logging/context-name"; + private static final String TEST_CONTEXT_NAME = "app-1"; + + @Before + public void before() throws NamingException { + MockContextFactory.setAsInitial(); + Context context = new InitialContext(); + context.bind(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME, TEST_CONTEXT_NAME); + } + + @After + public void after() { + MockContextFactory.revertSetAsInitial(); + } + + @Test + public void testLookup() { + final StrLookup lookup = new JndiLookup(); + + String contextName = lookup.lookup(TEST_CONTEXT_RESOURCE_NAME); + assertEquals(TEST_CONTEXT_NAME, contextName); + + contextName = lookup.lookup(JndiLookup.CONTAINER_JNDI_RESOURCE_PATH_PREFIX + TEST_CONTEXT_RESOURCE_NAME); + assertEquals(TEST_CONTEXT_NAME, contextName); + + String nonExistingResource = lookup.lookup("logging/non-existing-resource"); + assertNull(nonExistingResource); + } +} Index: core/src/test/resources/log4j-routing-by-jndi.xml =================================================================== --- core/src/test/resources/log4j-routing-by-jndi.xml (revision 0) +++ core/src/test/resources/log4j-routing-by-jndi.xml (revision 0) @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file