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