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 */
017package org.apache.logging.log4j.spi;
018
019import java.lang.ref.WeakReference;
020import java.net.URL;
021import java.util.Properties;
022
023import org.apache.logging.log4j.Logger;
024import org.apache.logging.log4j.status.StatusLogger;
025
026/**
027 * Model class for a Log4j 2 provider. The properties in this class correspond to the properties used in a
028 * {@code META-INF/log4j-provider.properties} file. Note that this class is automatically created by Log4j and should
029 * not be used by providers.
030 */
031public class Provider {
032    /**
033     * Property name to set for a Log4j 2 provider to specify the priority of this implementation.
034     */
035    public static final String FACTORY_PRIORITY = "FactoryPriority";
036    /**
037     * Property name to set to the implementation of {@link org.apache.logging.log4j.spi.ThreadContextMap}.
038     */
039    public static final String THREAD_CONTEXT_MAP = "ThreadContextMap";
040    /**
041     * Property name to set to the implementation of {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
042     */
043    public static final String LOGGER_CONTEXT_FACTORY = "LoggerContextFactory";
044
045    private static final Integer DEFAULT_PRIORITY = Integer.valueOf(-1);
046    private static final Logger LOGGER = StatusLogger.getLogger();
047
048    private final Integer priority;
049    private final String className;
050    private final String threadContextMap;
051    private final URL url;
052    private final WeakReference<ClassLoader> classLoader;
053
054    public Provider(final Properties props, final URL url, final ClassLoader classLoader) {
055        this.url = url;
056        this.classLoader = new WeakReference<>(classLoader);
057        final String weight = props.getProperty(FACTORY_PRIORITY);
058        priority = weight == null ? DEFAULT_PRIORITY : Integer.valueOf(weight);
059        className = props.getProperty(LOGGER_CONTEXT_FACTORY);
060        threadContextMap = props.getProperty(THREAD_CONTEXT_MAP);
061    }
062
063    /**
064     * Gets the priority (natural ordering) of this Provider.
065     *
066     * @return the priority of this Provider
067     */
068    public Integer getPriority() {
069        return priority;
070    }
071
072    /**
073     * Gets the class name of the {@link org.apache.logging.log4j.spi.LoggerContextFactory} implementation of this
074     * Provider.
075     *
076     * @return the class name of a LoggerContextFactory implementation
077     */
078    public String getClassName() {
079        return className;
080    }
081
082    /**
083     * Loads the {@link org.apache.logging.log4j.spi.LoggerContextFactory} class specified by this Provider.
084     *
085     * @return the LoggerContextFactory implementation class or {@code null} if there was an error loading it
086     */
087    public Class<? extends LoggerContextFactory> loadLoggerContextFactory() {
088        if (className == null) {
089            return null;
090        }
091        final ClassLoader loader = classLoader.get();
092        if (loader == null) {
093            return null;
094        }
095        try {
096            final Class<?> clazz = loader.loadClass(className);
097            if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
098                return clazz.asSubclass(LoggerContextFactory.class);
099            }
100        } catch (final Exception e) {
101            LOGGER.error("Unable to create class {} specified in {}", className, url.toString(), e);
102        }
103        return null;
104    }
105
106    /**
107     * Gets the class name of the {@link org.apache.logging.log4j.spi.ThreadContextMap} implementation of this Provider.
108     *
109     * @return the class name of a ThreadContextMap implementation
110     */
111    public String getThreadContextMap() {
112        return threadContextMap;
113    }
114
115    /**
116     * Loads the {@link org.apache.logging.log4j.spi.ThreadContextMap} class specified by this Provider.
117     *
118     * @return the ThreadContextMap implementation class or {@code null} if there was an error loading it
119     */
120    public Class<? extends ThreadContextMap> loadThreadContextMap() {
121        if (threadContextMap == null) {
122            return null;
123        }
124        final ClassLoader loader = classLoader.get();
125        if (loader == null) {
126            return null;
127        }
128        try {
129            final Class<?> clazz = loader.loadClass(threadContextMap);
130            if (ThreadContextMap.class.isAssignableFrom(clazz)) {
131                return clazz.asSubclass(ThreadContextMap.class);
132            }
133        } catch (final Exception e) {
134            LOGGER.error("Unable to create class {} specified in {}", threadContextMap, url.toString(), e);
135        }
136        return null;
137    }
138
139    /**
140     * Gets the URL containing this Provider's Log4j details.
141     *
142     * @return the URL corresponding to the Provider {@code META-INF/log4j-provider.properties} file
143     */
144    public URL getUrl() {
145        return url;
146    }
147    
148    @Override
149    public String toString() {
150        String result = "Provider[";
151        if (priority != DEFAULT_PRIORITY) {
152            result += "priority=" + priority + ", ";
153        }
154        if (threadContextMap != null) {
155            result += "threadContextMap=" + threadContextMap + ", ";
156        }
157        if (className != null) {
158            result += "className=" + className + ", ";
159        }
160        result += "url=" + url;
161        final ClassLoader loader = classLoader.get();
162        if (loader == null) {
163            result += ", classLoader=null(not reachable)";
164        } else {
165            result += ", classLoader=" + loader;
166        }
167        result += "]";
168        return result;
169    }
170}