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; 023import java.util.concurrent.TimeUnit; 024 025import javax.naming.Context; 026import javax.naming.InitialContext; 027import javax.naming.NamingException; 028 029import org.apache.logging.log4j.core.appender.AbstractManager; 030import org.apache.logging.log4j.core.appender.ManagerFactory; 031import org.apache.logging.log4j.core.util.JndiCloser; 032import org.apache.logging.log4j.util.PropertiesUtil; 033 034/** 035 * Manages a JNDI {@link javax.naming.Context}. 036 * 037 * @since 2.1 038 */ 039public class JndiManager extends AbstractManager { 040 041 private static final JndiManagerFactory FACTORY = new JndiManagerFactory(); 042 private static final String PREFIX = "log4j2.enableJndi"; 043 private static final String JAVA_SCHEME = "java"; 044 045 private static final boolean JNDI_CONTEXT_SELECTOR_ENABLED = isJndiEnabled("ContextSelector"); 046 private static final boolean JNDI_JDBC_ENABLED = isJndiEnabled("Jdbc"); 047 private static final boolean JNDI_JMS_ENABLED = isJndiEnabled("Jms"); 048 private static final boolean JNDI_LOOKUP_ENABLED = isJndiEnabled("Lookup"); 049 050 private final InitialContext context; 051 052 private static boolean isJndiEnabled(final String subKey) { 053 return PropertiesUtil.getProperties().getBooleanProperty(PREFIX + subKey, false); 054 } 055 056 public static boolean isJndiEnabled() { 057 return isJndiContextSelectorEnabled() || isJndiJdbcEnabled() || isJndiJmsEnabled() || isJndiLookupEnabled(); 058 } 059 060 public static boolean isJndiContextSelectorEnabled() { 061 return JNDI_CONTEXT_SELECTOR_ENABLED; 062 } 063 064 public static boolean isJndiJdbcEnabled() { 065 return JNDI_JDBC_ENABLED; 066 } 067 068 public static boolean isJndiJmsEnabled() { 069 return JNDI_JMS_ENABLED; 070 } 071 072 public static boolean isJndiLookupEnabled() { 073 return JNDI_LOOKUP_ENABLED; 074 } 075 076 private JndiManager(final String name, final InitialContext context) { 077 super(null, name); 078 this.context = context; 079 } 080 081 /** 082 * Gets the default JndiManager using the default {@link javax.naming.InitialContext}. 083 * 084 * @return the default JndiManager 085 */ 086 public static JndiManager getDefaultManager() { 087 return getManager(JndiManager.class.getName(), FACTORY, null); 088 } 089 090 /** 091 * Gets a named JndiManager using the default {@link javax.naming.InitialContext}. 092 * 093 * @param name the name of the JndiManager instance to create or use if available 094 * @return a default JndiManager 095 */ 096 public static JndiManager getDefaultManager(final String name) { 097 return getManager(name, FACTORY, null); 098 } 099 100 /** 101 * Gets a JndiManager with the provided configuration information. 102 * 103 * @param initialContextFactoryName Fully qualified class name of an implementation of 104 * {@link javax.naming.spi.InitialContextFactory}. 105 * @param providerURL The provider URL to use for the JNDI connection (specific to the above factory). 106 * @param urlPkgPrefixes A colon-separated list of package prefixes for the class name of the factory 107 * class that will create a URL context factory 108 * @param securityPrincipal The name of the identity of the Principal. 109 * @param securityCredentials The security credentials of the Principal. 110 * @param additionalProperties Any additional JNDI environment properties to set or {@code null} for none. 111 * @return the JndiManager for the provided parameters. 112 */ 113 public static JndiManager getJndiManager(final String initialContextFactoryName, 114 final String providerURL, 115 final String urlPkgPrefixes, 116 final String securityPrincipal, 117 final String securityCredentials, 118 final Properties additionalProperties) { 119 final Properties properties = createProperties(initialContextFactoryName, providerURL, urlPkgPrefixes, 120 securityPrincipal, securityCredentials, additionalProperties); 121 return getManager(createManagerName(), FACTORY, properties); 122 } 123 124 /** 125 * Gets a JndiManager with the provided configuration information. 126 * 127 * @param properties JNDI properties, usually created by calling {@link #createProperties(String, String, String, String, String, Properties)}. 128 * @return the JndiManager for the provided parameters. 129 * @see #createProperties(String, String, String, String, String, Properties) 130 * @since 2.9 131 */ 132 public static JndiManager getJndiManager(final Properties properties) { 133 return getManager(createManagerName(), FACTORY, properties); 134 } 135 136 private static String createManagerName() { 137 return JndiManager.class.getName() + '@' + JndiManager.class.hashCode(); 138 } 139 140 /** 141 * Creates JNDI Properties with the provided configuration information. 142 * 143 * @param initialContextFactoryName 144 * Fully qualified class name of an implementation of {@link javax.naming.spi.InitialContextFactory}. 145 * @param providerURL 146 * The provider URL to use for the JNDI connection (specific to the above factory). 147 * @param urlPkgPrefixes 148 * A colon-separated list of package prefixes for the class name of the factory class that will create a 149 * URL context factory 150 * @param securityPrincipal 151 * The name of the identity of the Principal. 152 * @param securityCredentials 153 * The security credentials of the Principal. 154 * @param additionalProperties 155 * Any additional JNDI environment properties to set or {@code null} for none. 156 * @return the Properties for the provided parameters. 157 * @since 2.9 158 */ 159 public static Properties createProperties(final String initialContextFactoryName, final String providerURL, 160 final String urlPkgPrefixes, final String securityPrincipal, final String securityCredentials, 161 final Properties additionalProperties) { 162 if (initialContextFactoryName == null) { 163 return null; 164 } 165 final Properties properties = new Properties(); 166 properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName); 167 if (providerURL != null) { 168 properties.setProperty(Context.PROVIDER_URL, providerURL); 169 } else { 170 LOGGER.warn("The JNDI InitialContextFactory class name [{}] was provided, but there was no associated " 171 + "provider URL. This is likely to cause problems.", initialContextFactoryName); 172 } 173 if (urlPkgPrefixes != null) { 174 properties.setProperty(Context.URL_PKG_PREFIXES, urlPkgPrefixes); 175 } 176 if (securityPrincipal != null) { 177 properties.setProperty(Context.SECURITY_PRINCIPAL, securityPrincipal); 178 if (securityCredentials != null) { 179 properties.setProperty(Context.SECURITY_CREDENTIALS, securityCredentials); 180 } else { 181 LOGGER.warn("A security principal [{}] was provided, but with no corresponding security credentials.", 182 securityPrincipal); 183 } 184 } 185 if (additionalProperties != null) { 186 properties.putAll(additionalProperties); 187 } 188 return properties; 189 } 190 191 @Override 192 protected boolean releaseSub(final long timeout, final TimeUnit timeUnit) { 193 return JndiCloser.closeSilently(this.context); 194 } 195 196 /** 197 * Looks up a named object through this JNDI context. 198 * 199 * @param name name of the object to look up. 200 * @param <T> the type of the object. 201 * @return the named object if it could be located. 202 * @throws NamingException if a naming exception is encountered 203 */ 204 @SuppressWarnings("unchecked") 205 public <T> T lookup(final String name) throws NamingException { 206 if (context == null) { 207 return null; 208 } 209 try { 210 URI uri = new URI(name); 211 if (uri.getScheme() == null || uri.getScheme().equals(JAVA_SCHEME)) { 212 return (T) this.context.lookup(name); 213 } 214 LOGGER.warn("Unsupported JNDI URI - {}", name); 215 } catch (URISyntaxException ex) { 216 LOGGER.warn("Invalid JNDI URI - {}", name); 217 } 218 return null; 219 } 220 221 private static class JndiManagerFactory implements ManagerFactory<JndiManager, Properties> { 222 223 @Override 224 public JndiManager createManager(final String name, final Properties data) { 225 if (!isJndiEnabled()) { 226 throw new IllegalStateException(String.format("JNDI must be enabled by setting one of the %s* properties to true", PREFIX)); 227 } 228 try { 229 return new JndiManager(name, new InitialContext(data)); 230 } catch (final NamingException e) { 231 LOGGER.error("Error creating JNDI InitialContext for '{}'.", name, e); 232 return null; 233 } 234 } 235 236 } 237 238 @Override 239 public String toString() { 240 return "JndiManager [context=" + context + ", count=" + count + "]"; 241 } 242 243}