/*
 * $Header: /home/cvs/commons/logging-1.0.3/ja/src/org/apache/commons/logging/impl/LogFactoryImpl.java,v 1.4 2004/04/20 14:55:40 hioki Exp $
 * $Revision: 1.4 $
 * $Date: 2004/04/20 14:55:40 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.commons.logging.impl;


import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogConfigurationException;
import org.apache.commons.logging.LogFactory;


/**
 * <p>ログ実装のラッパを生成するため、次のアルゴリズムで動的にログ実装を選択する、
 * {@link LogFactory} の具象サブクラスです。
 * {@primary Concrete subclass of {@link LogFactory} that implements the
 * following algorithm to dynamically select a logging implementation
 * class to instantiate a wrapper for.}</p>
 * <ul>
 * <li>ファクトリの設定属性 <code>org.apache.commons.logging.Log</code>
 *     を使用して要求された実装クラスを判別します。</li>
 * <li>システムプロパティ <code>org.apache.commons.logging.Log</code>
 *     を使用して要求された実装クラスを判別します。</li>
 * <li>もし <em>Log4J</em> を利用できる場合には、
 *     <code>org.apache.commons.logging.impl.Log4JLogger</code> インスタンスを返します。</li>
 * <li>もし <em>JDK 1.4 以降</em> を利用できる場合には、
 *     <code>org.apache.commons.logging.impl.Jdk14Logger</code> インスタンスを返します。</li>
 * <li>それら以外の場合には、
 *     <code>org.apache.commons.logging.impl.SimpleLog</code> インスタンスを返します。</li>
 * </ul>
 * {@primary <ul>
 * <li>Use a factory configuration attribute named
 *     <code>org.apache.commons.logging.Log</code> to identify the
 *     requested implementation class.</li>
 * <li>Use the <code>org.apache.commons.logging.Log</code> system property
 *     to identify the requested implementation class.</li>
 * <li>If <em>Log4J</em> is available, return an instance of
 *     <code>org.apache.commons.logging.impl.Log4JLogger</code>.</li>
 * <li>If <em>JDK 1.4 or later</em> is available, return an instance of
 *     <code>org.apache.commons.logging.impl.Jdk14Logger</code>.</li>
 * <li>Otherwise, return an instance of
 *     <code>org.apache.commons.logging.impl.SimpleLog</code>.</li>
 * </ul>}
 *
 * <p>もし選択した {@link Log} 実装クラスが {@link LogFactory} を引数に取る
 * <code>setLogFactory()</code> メソッドを持つ場合は、ファクトリと関連付けるために
 * 新たに生成したインスタンスのこのメソッドを実行します。
 * これにより、 Log インスタンスはファクトリに設定された属性を利用可能になります。
 * {@primary If the selected {@link Log} implementation class has a
 * <code>setLogFactory()</code> method that accepts a {@link LogFactory}
 * parameter, this method will be called on each newly created instance
 * to identify the associated factory.  This makes factory configuration
 * attributes available to the Log instance, if it so desires.}</p>
 *
 * <p>このファクトリは前に作成した <code>Log</code> インスタンスを記憶し、同じ名前で
 * 再度 <code>getInstance()</code> メソッドが呼ばれた場合に(同じインスタンスを)返します。
 * この実装はいかなる設定された属性も無視します。
 * {@primary This factory will remember previously created <code>Log</code> instances
 * for the same name, and will return them on repeated requests to the
 * <code>getInstance()</code> method.  This implementation ignores any
 * configured attributes.}</p>
 *
 * @author Rod Waldhoff
 * @author Craig R. McClanahan
 * @author Richard A. Sitze
 * @translator 日置 聡
 * @editor 本間 宏崇
 * @status completion
 * @update 2003/04/20
 * @version $Revision: 1.4 $ $Date: 2004/04/20 14:55:40 $
 */

public class LogFactoryImpl extends LogFactory {

    // ----------------------------------------------------------- Constructors


    /**
     * ファクトリの検索機構が必要とする、引数なしの public コンストラクタ。
     * {@primary Public no-arguments constructor required by the lookup mechanism.}
     */
    public LogFactoryImpl() {
        super();
    }


    // ----------------------------------------------------- Manifest Constants


    /**
     * 使用する {@link Log} 実装クラスを特定するシステムプロパティ名。
     * {@primary The name of the system property identifying our {@link Log}
     * implementation class.}
     */
    public static final String LOG_PROPERTY =
        "org.apache.commons.logging.Log";


    /**
     * 古い {@link org.apache.commons.logging.LogSource} クラスとの
     * 下位互換のための、推奨されないシステムプロパティ名。
     * {@primary The deprecated system property used for backwards compatibility with
     * the old {@link org.apache.commons.logging.LogSource} class.}
     */
    protected static final String LOG_PROPERTY_OLD =
        "org.apache.commons.logging.log";


    // ----------------------------------------------------- Instance Variables


    /**
     * 設定のための属性。
     * {@primary Configuration attributes}
     */
    protected Hashtable attributes = new Hashtable();


    /**
     * ロガーの名称をキーにして保持される、既に生成された
     * {@link org.apache.commons.logging.Log} インスタンス。
     * {@primary The {@link org.apache.commons.logging.Log} instances that have
     * already been created, keyed by logger name.}
     */
    protected Hashtable instances = new Hashtable();


    /**
     * Log インターフェイスを実装するクラスの名称。
     * {@primary Name of the class implementing the Log interface.}
     */
    private String logClassName;


    /**
     * 新規インスタンスの生成に使用される、引数が 1 つの
     * {@link org.apache.commons.logging.Log} 実装クラスのコンストラクタ。
     * この値は <code>getLogConstructor()</code> で初期化され、繰り返し使用されます。
     * {@primary The one-argument constructor of the
     * {@link org.apache.commons.logging.Log}
     * implementation class that will be used to create new instances.
     * This value is initialized by <code>getLogConstructor()</code>,
     * and then returned repeatedly.}
     */
    protected Constructor logConstructor = null;


    /**
     * コンストラクタで使用される引数。
     * {@primary The signature of the Constructor to be used.}
     */
    protected Class logConstructorSignature[] =
    { java.lang.String.class };


    /**
     * 選択された {@link org.apache.commons.logging.Log}
     * クラスの引数が 1 つの <code>setLogFactory</code> メソッド(もしあれば)。
     * {@primary The one-argument <code>setLogFactory</code> method of the selected
     * {@link org.apache.commons.logging.Log} method, if it exists.}
     */
    protected Method logMethod = null;


    /**
     * <code>setLogFactory</code> メソッドで使用される引数。
     * {@primary The signature of the <code>setLogFactory</code> method to be used.}
     */
    protected Class logMethodSignature[] =
    { LogFactory.class };


    // --------------------------------------------------------- Public Methods


    /**
     * 指定された名称の設定属性を返します(もしあれば)。
     * 指定された属性がない場合には <code>null</code> を返します。
     * {@primary Return the configuration attribute with the specified name (if any),
     * or <code>null</code> if there is no such attribute.}
     *
     * @param name 取得する属性の名称
     * {@primary Name of the attribute to return}
     */
    public Object getAttribute(String name) {

        return (attributes.get(name));

    }


    /**
     * 現在設定されている全属性の名称を配列で返します。
     * 属性が 1 つも設定されていない場合は、サイズ 0 の配列を返します。
     * {@primary Return an array containing the names of all currently defined
     * configuration attributes.  If there are no such attributes, a zero
     * length array is returned.}
     */
    public String[] getAttributeNames() {

        Vector names = new Vector();
        Enumeration keys = attributes.keys();
        while (keys.hasMoreElements()) {
            names.addElement((String) keys.nextElement());
        }
        String results[] = new String[names.size()];
        for (int i = 0; i < results.length; i++) {
            results[i] = (String) names.elementAt(i);
        }
        return (results);

    }


    /**
     * 指定クラスから取得した名称を引数に <code>getInstance(String)</code>
     * を呼ぶ、手順簡略化のためのメソッドです。
     * {@primary Convenience method to derive a name from the specified class and
     * call <code>getInstance(String)</code> with it.}
     *
     * @param clazz 適切な Log 名称の取得元クラス
     * {@primary Class for which a suitable Log name will be derived}
     *
     * @exception LogConfigurationException 適切な <code>Log</code>
     *  インスタンスを返せなかった場合
     * {@primary if a suitable <code>Log</code> instance cannot be returned}
     */
    public Log getInstance(Class clazz) throws LogConfigurationException {

        return (getInstance(clazz.getName()));

    }


    /**
     * <p>現在ファクトリに設定されている属性を使用して
     * <code>Log</code> インスタンスを(必要であれば)生成し返します。
     * {@primary Construct (if necessary) and return a <code>Log</code> instance,
     * using the factory's current set of configuration attributes.}</p>
     *
     * <p><strong>注</strong> - 返される <code>Log</code> 
     * インスタンスが現在のアプリケーション固有のものであるかどうか、
     * また複数回同じ名称でこのメソッドを呼んだ場合に同じインスタンスが返されるかどうかは、
     * 使用している <code>LogFactory</code> の実装に依存します。
     * {@primary <strong>NOTE</strong> - Depending upon the implementation of
     * the <code>LogFactory</code> you are using, the <code>Log</code>
     * instance you are returned may or may not be local to the current
     * application, and may or may not be returned again on a subsequent
     * call with the same name argument.}</p>
     *
     * @param name 返される <code>Log</code> インスタンスの論理名
     *  (名称の意味はラップされ内部で動作するログ処理の実装のみが知っています)
     * {@primary Logical name of the <code>Log</code> instance to be
     *  returned (the meaning of this name is only known to the underlying
     *  logging implementation that is being wrapped)}
     *
     * @exception LogConfigurationException 適切な <code>Log</code>
     *  インスタンスを返せなかった場合
     * {@primary if a suitable <code>Log</code> instance cannot be returned}
     */
    public Log getInstance(String name) throws LogConfigurationException {

        Log instance = (Log) instances.get(name);
        if (instance == null) {
            instance = newInstance(name);
            instances.put(name, instance);
        }
        return (instance);

    }


    /**
     * このファクトリがこれまで生成・返却した全ての
     * {@link org.apache.commons.logging.Log} インスタンスへの内部参照を解放します。
     * これはサーブレットコンテナのような環境で、実装されたアプリケーションに対して
     * クラスローダからリロードが指示された場合に有用です。
     * そのクラスローダの配下で参照され続けるオブジェクトはガベージコレクションを妨げます。
     * {@primary Release any internal references to previously created
     * {@link org.apache.commons.logging.Log}
     * instances returned by this factory.  This is useful environments
     * like servlet containers, which implement application reloading by
     * throwing away a ClassLoader.  Dangling references to objects in that
     * class loader would prevent garbage collection.}
     */
    public void release() {

        instances.clear();
    }


    /**
     * 指定された名称の設定属性を削除します。
     * 該当する属性がない場合には何も行いません。
     * {@primary Remove any configuration attribute associated with the specified name.
     * If there is no such attribute, no action is taken.}
     *
     * @param name 削除する属性の名称
     * {@primary Name of the attribute to remove}
     */
    public void removeAttribute(String name) {

        attributes.remove(name);

    }


    /**
     * 指定された名称の設定属性を設定します。
     * 引数 value への <code>null</code> 指定は
     * <code>removeAttribute(name)</code> と同等です。
     * {@primary Set the configuration attribute with the specified name.  Calling
     * this with a <code>null</code> value is equivalent to calling
     * <code>removeAttribute(name)</code>.}
     *
     * @param name 設定する属性の名称
     * {@primary Name of the attribute to set}
     * @param value 設定する属性の値。 <code>null</code> の場合にはこの属性を削除
     * {@primary Value of the attribute to set, or <code>null</code>
     *  to remove any setting for this attribute}
     */
    public void setAttribute(String name, Object value) {

        if (value == null) {
            attributes.remove(name);
        } else {
            attributes.put(name, value);
        }

    }


    // ------------------------------------------------------ Protected Methods



    /**
     * 使用する {@link Log} 実装クラスの完全修飾クラス名を返します。
     * {@primary Return the fully qualified Java classname of the {@link Log}
     * implementation we will be using.}
     */
    protected String getLogClassName() {

        // Return the previously identified class name (if any)
        if (logClassName != null) {
            return logClassName;
        }

        logClassName = (String) getAttribute(LOG_PROPERTY);

        if (logClassName == null) { // @deprecated
            logClassName = (String) getAttribute(LOG_PROPERTY_OLD);
        }

        if (logClassName == null) {
            try {
                logClassName = System.getProperty(LOG_PROPERTY);
            } catch (SecurityException e) {
                ;
            }
        }

        if (logClassName == null) { // @deprecated
            try {
                logClassName = System.getProperty(LOG_PROPERTY_OLD);
            } catch (SecurityException e) {
                ;
            }
        }

        if ((logClassName == null) && isLog4JAvailable()) {
            logClassName = "org.apache.commons.logging.impl.Log4JLogger";
        }

        if ((logClassName == null) && isJdk14Available()) {
            logClassName = "org.apache.commons.logging.impl.Jdk14Logger";
        }

        if (logClassName == null) {
            logClassName = "org.apache.commons.logging.impl.SimpleLog";
        }

        return (logClassName);

    }


    /**
     * <p>新規 {@link org.apache.commons.logging.Log}
     * インスタンスの生成に使用するコンストラクタを返します。
     * {@primary <p>Return the <code>Constructor</code> that can be called to instantiate
     * new {@link org.apache.commons.logging.Log} instances.}</p>
     *
     * <p><strong>実装の注</strong> - 結局いかなる状況でも同じ
     * <code>Constructor</code> インスタンスが取得されるため、
     * 複数のスレッドがこのメソッドを呼ぶことにより起こる競合状況は無視されます。
     * {@primary <strong>IMPLEMENTATION NOTE</strong> - Race conditions caused by
     * calling this method from more than one thread are ignored, because
     * the same <code>Constructor</code> instance will ultimately be derived
     * in all circumstances.}</p>
     *
     * @exception LogConfigurationException 適切なコンストラクタを返せない場合
     * {@primary if a suitable constructor cannot be returned}
     */
    protected Constructor getLogConstructor()
        throws LogConfigurationException {

        // Return the previously identified Constructor (if any)
        if (logConstructor != null) {
            return logConstructor;
        }

        String logClassName = getLogClassName();

        // Attempt to load the Log implementation class
        Class logClass = null;
        try {
            logClass = loadClass(logClassName);
            if (logClass == null) {
                throw new LogConfigurationException
                    ("No suitable Log implementation for " + logClassName);
            }
            if (!Log.class.isAssignableFrom(logClass)) {
                throw new LogConfigurationException
                    ("Class " + logClassName + " does not implement Log");
            }
        } catch (Throwable t) {
            throw new LogConfigurationException(t);
        }

        // Identify the <code>setLogFactory</code> method (if there is one)
        try {
            logMethod = logClass.getMethod("setLogFactory",
                                           logMethodSignature);
        } catch (Throwable t) {
            logMethod = null;
        }

        // Identify the corresponding constructor to be used
        try {
            logConstructor = logClass.getConstructor(logConstructorSignature);
            return (logConstructor);
        } catch (Throwable t) {
            throw new LogConfigurationException
                ("No suitable Log constructor " +
                 logConstructorSignature+ " for " + logClassName, t);
        }
    }


    /**
     * <b>このメソッドは PRIVATE のままでなくてはなりません。</b>
     * {@primary MUST KEEP THIS METHOD PRIVATE.}
     *
     * <p>このメソッドは <code>AccessController.doPrivileged()</code> を使用しているため、
     * <code>org.apache.commons.logging.LogFactoryImpl</code> の外部へ公開すると
     * セキュリティ侵害につながります。
     * {@primary Exposing this method outside of
     * <code>org.apache.commons.logging.LogFactoryImpl</code>
     * will create a security violation:
     * This method uses <code>AccessController.doPrivileged()</code>.
     * }</p>
     *
     * クラスをロードします。まずスレッドクラスローダからロードを試み、
     * ロードできなかった場合にはこのクラスをロードしたクラスローダを使用します。
     * {@primary Load a class, try first the thread class loader, and
     * if it fails use the loader that loaded this class.}
     */
    private static Class loadClass( final String name )
        throws ClassNotFoundException
    {
        Object result = AccessController.doPrivileged(
            new PrivilegedAction() {
                public Object run() {
                    ClassLoader threadCL = getContextClassLoader();
                    if (threadCL != null) {
                        try {
                            return threadCL.loadClass(name);
                        } catch( ClassNotFoundException ex ) {
                            // ignore
                        }
                    }
                    try {
                        return Class.forName( name );
                    } catch (ClassNotFoundException e) {
                        return e;
                    }
                }
            });

        if (result instanceof Class)
            return (Class)result;

        throw (ClassNotFoundException)result;
    }


    /**
     * <em>JDK 1.4 以降</em>のログ機能を利用可能か?
     * {@primary Is <em>JDK 1.4 or later</em> logging available?}
     */
    protected boolean isJdk14Available() {

        try {
            loadClass("java.util.logging.Logger");
            loadClass("org.apache.commons.logging.impl.Jdk14Logger");
            return (true);
        } catch (Throwable t) {
            return (false);
        }
    }


    /**
     * <em>Log4J</em> 実装を利用可能か?
     * {@primary Is a <em>Log4J</em> implementation available?}
     */
    protected boolean isLog4JAvailable() {

        try {
            loadClass("org.apache.log4j.Logger");
            loadClass("org.apache.commons.logging.impl.Log4JLogger");
            return (true);
        } catch (Throwable t) {
            return (false);
        }
    }


    /**
     * 指定された名称の新規 {@link org.apache.commons.logging.Log}
     * インスタンスを生成し、返します。
     * {@primary Create and return a new {@link org.apache.commons.logging.Log}
     * instance for the specified name.}
     *
     * @param name 新規ロガーの名称
     * {@primary Name of the new logger}
     *
     * @exception LogConfigurationException 新規インスタンスを生成できなかった場合
     * {@primary if a new instance cannot be created}
     */
    protected Log newInstance(String name) throws LogConfigurationException {

        Log instance = null;
        try {
            Object params[] = new Object[1];
            params[0] = name;
            instance = (Log) getLogConstructor().newInstance(params);
            if (logMethod != null) {
                params[0] = this;
                logMethod.invoke(instance, params);
            }
            return (instance);
        } catch (Throwable t) {
            throw new LogConfigurationException(t);
        }

    }


}

