    001/*
    002 * Copyright 2001-2004 The Apache Software Foundation.
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *      http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    013 * See the License for the specific language governing permissions and
    014 * limitations under the License.
    015 */
    016
    017package org.apache.commons.logging.impl;
    018
    019import org.apache.commons.logging.Log;
    020import org.apache.commons.logging.LogConfigurationException;
    021import org.apache.commons.logging.LogFactory;
    022import org.slf4j.Logger;
    023import org.slf4j.LoggerFactory;
    024import org.slf4j.spi.LocationAwareLogger;
    025
    026import java.util.ArrayList;
    027import java.util.Enumeration;
    028import java.util.Hashtable;
    029import java.util.List;
    030import java.util.concurrent.ConcurrentHashMap;
    031import java.util.concurrent.ConcurrentMap;
    032
    033/**
    034 * <p>
    035 * Concrete subclass of {@link LogFactory} which always delegates to the
    036 * {@link LoggerFactory org.slf4j.LoggerFactory} class.
    037 *
    038 * <p>
    039 * This factory generates instances of {@link SLF4JLog}. It will remember
    040 * previously created instances for the same name, and will return them on
    041 * repeated requests to the <code>getInstance()</code> method.
    042 *
    043 * <p>
    044 * This implementation ignores any configured attributes.
    045 * </p>
    046 *
    047 * @author Rod Waldhoff
    048 * @author Craig R. McClanahan
    049 * @author Richard A. Sitze
    050 * @author Ceki G&uuml;lc&uuml;
    051 */
    052@SuppressWarnings("rawtypes")
    053public class SLF4JLogFactory extends LogFactory {
    054
    055    // ----------------------------------------------------------- Constructors
    056
    057    /**
    058     * The {@link org.apache.commons.logging.Log}instances that have already been
    059     * created, keyed by logger name.
    060     */
    061    ConcurrentMap<String, Log> loggerMap;
    062
    063    /**
    064     * Public no-arguments constructor required by the lookup mechanism.
    065     */
    066    public SLF4JLogFactory() {
    067        loggerMap = new ConcurrentHashMap<String, Log>();
    068    }
    069
    070    // ----------------------------------------------------- Manifest Constants
    071
    072    /**
    073     * The name of the system property identifying our {@link Log}implementation
    074     * class.
    075     */
    076    public static final String LOG_PROPERTY = "org.apache.commons.logging.Log";
    077
    078    // ----------------------------------------------------- Instance Variables
    079
    080    /**
    081     * Configuration attributes.
    082     */
    083    protected Hashtable attributes = new Hashtable();
    084
    085    // --------------------------------------------------------- Public Methods
    086
    087    /**
    088     * Return the configuration attribute with the specified name (if any), or
    089     * <code>null</code> if there is no such attribute.
    090     *
    091     * @param name
    092     *          Name of the attribute to return
    093     */
    094    public Object getAttribute(String name) {
    095
    096        return (attributes.get(name));
    097
    098    }
    099
    100    /**
    101     * Return an array containing the names of all currently defined configuration
    102     * attributes. If there are no such attributes, a zero length array is
    103     * returned.
    104     */
    105    @SuppressWarnings("unchecked")
    106    public String[] getAttributeNames() {
    107
    108        List<String> names = new ArrayList<String>();
    109        Enumeration<String> keys = attributes.keys();
    110        while (keys.hasMoreElements()) {
    111            names.add((String) keys.nextElement());
    112        }
    113        String results[] = new String[names.size()];
    114        for (int i = 0; i < results.length; i++) {
    115            results[i] = (String) names.get(i);
    116        }
    117        return (results);
    118
    119    }
    120
    121    /**
    122     * Convenience method to derive a name from the specified class and call
    123     * <code>getInstance(String)</code> with it.
    124     *
    125     * @param clazz
    126     *          Class for which a suitable Log name will be derived
    127     *
    128     * @exception LogConfigurationException
    129     *              if a suitable <code>Log</code> instance cannot be returned
    130     */
    131    public Log getInstance(Class clazz) throws LogConfigurationException {
    132        return (getInstance(clazz.getName()));
    133    }
    134
    135    /**
    136     * <p>
    137     * Construct (if necessary) and return a <code>Log</code> instance, using
    138     * the factory's current set of configuration attributes.
    139     * </p>
    140     *
    141     * @param name
    142     *          Logical name of the <code>Log</code> instance to be returned
    143     *          (the meaning of this name is only known to the underlying logging
    144     *          implementation that is being wrapped)
    145     *
    146     * @exception LogConfigurationException
    147     *              if a suitable <code>Log</code> instance cannot be returned
    148     */
    149    public Log getInstance(String name) throws LogConfigurationException {
    150        Log instance = loggerMap.get(name);
    151        if (instance != null) {
    152            return instance;
    153        } else {
    154            Log newInstance;
    155            Logger slf4jLogger = LoggerFactory.getLogger(name);
    156            if (slf4jLogger instanceof LocationAwareLogger) {
    157                newInstance = new SLF4JLocationAwareLog((LocationAwareLogger) slf4jLogger);
    158            } else {
    159                newInstance = new SLF4JLog(slf4jLogger);
    160            }
    161            Log oldInstance = loggerMap.putIfAbsent(name, newInstance);
    162            return oldInstance == null ? newInstance : oldInstance;
    163        }
    164    }
    165
    166    /**
    167     * Release any internal references to previously created
    168     * {@link org.apache.commons.logging.Log}instances returned by this factory.
    169     * This is useful in environments like servlet containers, which implement
    170     * application reloading by throwing away a ClassLoader. Dangling references
    171     * to objects in that class loader would prevent garbage collection.
    172     */
    173    public void release() {
    174        // This method is never called by jcl-over-slf4j classes. However,
    175        // in certain deployment scenarios, in particular if jcl-over-slf4j.jar
    176        // is
    177        // in the the web-app class loader and the official commons-logging.jar is
    178        // deployed in some parent class loader (e.g. commons/lib), then it is
    179        // possible
    180        // for the parent class loader to mask the classes shipping in
    181        // jcl-over-slf4j.jar.
    182        System.out.println("WARN: The method " + SLF4JLogFactory.class + "#release() was invoked.");
    183        System.out.println("WARN: Please see http://www.slf4j.org/codes.html#release for an explanation.");
    184        System.out.flush();
    185    }
    186
    187    /**
    188     * Remove any configuration attribute associated with the specified name. If
    189     * there is no such attribute, no action is taken.
    190     *
    191     * @param name
    192     *          Name of the attribute to remove
    193     */
    194    public void removeAttribute(String name) {
    195        attributes.remove(name);
    196    }
    197
    198    /**
    199     * Set the configuration attribute with the specified name. Calling this with
    200     * a <code>null</code> value is equivalent to calling
    201     * <code>removeAttribute(name)</code>.
    202     *
    203     * @param name
    204     *          Name of the attribute to set
    205     * @param value
    206     *          Value of the attribute to set, or <code>null</code> to remove
    207     *          any setting for this attribute
    208     */
    209    @SuppressWarnings("unchecked")
    210    public void setAttribute(String name, Object value) {
    211
    212        if (value == null) {
    213            attributes.remove(name);
    214        } else {
    215            attributes.put(name, value);
    216        }
    217
    218    }
    219}