001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017 018package org.apache.logging.log4j.core.net; 019 020import java.net.URI; 021import java.net.URISyntaxException; 022import java.util.Properties; 023 024import javax.naming.Context; 025import javax.naming.InitialContext; 026import javax.naming.NamingException; 027 028import org.apache.logging.log4j.core.appender.AbstractManager; 029import org.apache.logging.log4j.core.appender.ManagerFactory; 030import org.apache.logging.log4j.core.util.JndiCloser; 031import org.apache.logging.log4j.util.PropertiesUtil; 032 033/** 034 * JNDI {@link javax.naming.Context} manager. 035 * 036 * @since 2.1 037 */ 038public class JndiManager extends AbstractManager { 039 040 private static final JndiManagerFactory FACTORY = new JndiManagerFactory(); 041 private static final String PREFIX = "log4j2.enableJndi"; 042 private static final String JAVA_SCHEME = "java"; 043 044 private static final boolean JNDI_CONTEXT_SELECTOR_ENABLED = isJndiEnabled("ContextSelector"); 045 private static final boolean JNDI_JDBC_ENABLED = isJndiEnabled("Jdbc"); 046 private static final boolean JNDI_JMS_ENABLED = isJndiEnabled("Jms"); 047 private static final boolean JNDI_LOOKUP_ENABLED = isJndiEnabled("Lookup"); 048 049 private final InitialContext context; 050 051 private static String createManagerName() { 052 return JndiManager.class.getName() + '@' + JndiManager.class.hashCode(); 053 } 054 055 private static boolean isJndiEnabled(final String subKey) { 056 return PropertiesUtil.getProperties().getBooleanProperty(PREFIX + subKey, false); 057 } 058 059 public static boolean isJndiEnabled() { 060 return isJndiContextSelectorEnabled() || isJndiJdbcEnabled() || isJndiJmsEnabled() || isJndiLookupEnabled(); 061 } 062 063 public static boolean isJndiContextSelectorEnabled() { 064 return JNDI_CONTEXT_SELECTOR_ENABLED; 065 } 066 067 public static boolean isJndiJdbcEnabled() { 068 return JNDI_JDBC_ENABLED; 069 } 070 071 public static boolean isJndiJmsEnabled() { 072 return JNDI_JMS_ENABLED; 073 } 074 075 public static boolean isJndiLookupEnabled() { 076 return JNDI_LOOKUP_ENABLED; 077 } 078 079 private JndiManager(final String name, final InitialContext context) { 080 super(name); 081 this.context = context; 082 } 083 084 /** 085 * Gets the default JndiManager using the default {@link javax.naming.InitialContext}. 086 * 087 * @return the default JndiManager 088 */ 089 public static JndiManager getDefaultManager() { 090 return getManager(JndiManager.class.getName(), FACTORY, null); 091 } 092 093 /** 094 * Gets a named JndiManager using the default {@link javax.naming.InitialContext}. 095 * @param name the name of the JndiManager instance to create or use if available 096 * @return a default JndiManager 097 */ 098 public static JndiManager getDefaultManager(final String name) { 099 return getManager(name, FACTORY, null); 100 } 101 102 /** 103 * Gets a JndiManager with the provided configuration information. 104 * 105 * @param properties JNDI properties, usually created by calling {@link #createProperties(String, String, String, String, String, Properties)}. 106 * @return the JndiManager for the provided parameters. 107 * @see #createProperties(String, String, String, String, String, Properties) 108 * @since 2.9 109 */ 110 public static JndiManager getJndiManager(final Properties properties) { 111 return getManager(createManagerName(), FACTORY, properties); 112 } 113 114 /** 115 * Gets a JndiManager with the provided configuration information. 116 * 117 * @param initialContextFactoryName Fully qualified class name of an implementation of 118 * {@link javax.naming.spi.InitialContextFactory}. 119 * @param providerURL The provider URL to use for the JNDI connection (specific to the above factory). 120 * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory 121 * class that will create a URL context factory 122 * @param securityPrincipal The name of the identity of the Principal. 123 * @param securityCredentials The security credentials of the Principal. 124 * @param additionalProperties Any additional JNDI environment properties to set or {@code null} for none. 125 * @return the JndiManager for the provided parameters. 126 */ 127 public static JndiManager getJndiManager(final String initialContextFactoryName, 128 final String providerURL, 129 final String urlPkgPrefixes, 130 final String securityPrincipal, 131 final String securityCredentials, 132 final Properties additionalProperties) { 133 final String name = JndiManager.class.getName() + '@' + JndiManager.class.hashCode(); 134 if (initialContextFactoryName == null) { 135 return getManager(name, FACTORY, null); 136 } 137 final Properties properties = new Properties(); 138 properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName); 139 if (providerURL != null) { 140 properties.setProperty(Context.PROVIDER_URL, providerURL); 141 } else { 142 LOGGER.warn("The JNDI InitialContextFactory class name [{}] was provided, but there was no associated " + 143 "provider URL. This is likely to cause problems.", initialContextFactoryName); 144 } 145 if (urlPkgPrefixes != null) { 146 properties.setProperty(Context.URL_PKG_PREFIXES, urlPkgPrefixes); 147 } 148 if (securityPrincipal != null) { 149 properties.setProperty(Context.SECURITY_PRINCIPAL, securityPrincipal); 150 if (securityCredentials != null) { 151 properties.setProperty(Context.SECURITY_CREDENTIALS, securityCredentials); 152 } else { 153 LOGGER.warn("A security principal [{}] was provided, but with no corresponding security credentials.", 154 securityPrincipal); 155 } 156 } 157 if (additionalProperties != null) { 158 properties.putAll(additionalProperties); 159 } 160 return getManager(name, FACTORY, properties); 161 } 162 163 @Override 164 protected void releaseSub() { 165 JndiCloser.closeSilently(this.context); 166 } 167 168 /** 169 * Looks up a named object through this JNDI context. 170 * 171 * @param name name of the object to look up. 172 * @param <T> the type of the object. 173 * @return the named object if it could be located. 174 * @throws NamingException 175 */ 176 @SuppressWarnings("unchecked") 177 public <T> T lookup(final String name) throws NamingException { 178 if (context == null) { 179 return null; 180 } 181 try { 182 URI uri = new URI(name); 183 if (uri.getScheme() == null || uri.getScheme().equals(JAVA_SCHEME)) { 184 return (T) this.context.lookup(name); 185 } 186 LOGGER.warn("Unsupported JNDI URI - {}", name); 187 } catch (URISyntaxException ex) { 188 LOGGER.warn("Invalid JNDI URI - {}", name); 189 } 190 return null; 191 } 192 193 private static class JndiManagerFactory implements ManagerFactory<JndiManager, Properties> { 194 195 @Override 196 public JndiManager createManager(final String name, final Properties data) { 197 if (!isJndiEnabled()) { 198 throw new IllegalStateException(String.format("JNDI must be enabled by setting one of the %s* properties to true", PREFIX)); 199 } 200 try { 201 return new JndiManager(name, new InitialContext(data)); 202 } catch (final NamingException e) { 203 LOGGER.error("Error creating JNDI InitialContext.", e); 204 return null; 205 } 206 } 207 } 208}