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 Class<? extends LoggerContextFactory> loggerContextFactoryClass;
051    private final String threadContextMap;
052    private final Class<? extends ThreadContextMap> threadContextMapClass;
053    private final String versions;
054    private final URL url;
055    private final WeakReference<ClassLoader> classLoader;
056
057    public Provider(final Properties props, final URL url, final ClassLoader classLoader) {
058        this.url = url;
059        this.classLoader = new WeakReference<>(classLoader);
060        final String weight = props.getProperty(FACTORY_PRIORITY);
061        priority = weight == null ? DEFAULT_PRIORITY : Integer.valueOf(weight);
062        className = props.getProperty(LOGGER_CONTEXT_FACTORY);
063        threadContextMap = props.getProperty(THREAD_CONTEXT_MAP);
064        loggerContextFactoryClass = null;
065        threadContextMapClass = null;
066        versions = null;
067    }
068
069    public Provider(final Integer priority, final String versions,
070                    final Class<? extends LoggerContextFactory> loggerContextFactoryClass) {
071        this(priority, versions, loggerContextFactoryClass, null);
072    }
073
074
075    public Provider(final Integer priority, final String versions,
076                    final Class<? extends LoggerContextFactory> loggerContextFactoryClass,
077                    final Class<? extends ThreadContextMap> threadContextMapClass) {
078        this.url = null;
079        this.classLoader = null;
080        this.priority = priority;
081        this.loggerContextFactoryClass = loggerContextFactoryClass;
082        this.threadContextMapClass = threadContextMapClass;
083        this.className = null;
084        this.threadContextMap = null;
085        this.versions = versions;
086    }
087
088    /**
089     * Returns the Log4j API versions supported by the implementation.
090     * @return A String containing the Log4j versions supported.
091     */
092    public String getVersions() {
093        return versions;
094    }
095
096    /**
097     * Gets the priority (natural ordering) of this Provider.
098     *
099     * @return the priority of this Provider
100     */
101    public Integer getPriority() {
102        return priority;
103    }
104
105    /**
106     * Gets the class name of the {@link org.apache.logging.log4j.spi.LoggerContextFactory} implementation of this
107     * Provider.
108     *
109     * @return the class name of a LoggerContextFactory implementation
110     */
111    public String getClassName() {
112        if (loggerContextFactoryClass != null) {
113            return loggerContextFactoryClass.getName();
114        }
115        return className;
116    }
117
118    /**
119     * Loads the {@link org.apache.logging.log4j.spi.LoggerContextFactory} class specified by this Provider.
120     *
121     * @return the LoggerContextFactory implementation class or {@code null} if there was an error loading it
122     */
123    public Class<? extends LoggerContextFactory> loadLoggerContextFactory() {
124        if (loggerContextFactoryClass != null) {
125            return loggerContextFactoryClass;
126        }
127        if (className == null) {
128            return null;
129        }
130        final ClassLoader loader = classLoader.get();
131        if (loader == null) {
132            return null;
133        }
134        try {
135            final Class<?> clazz = loader.loadClass(className);
136            if (LoggerContextFactory.class.isAssignableFrom(clazz)) {
137                return clazz.asSubclass(LoggerContextFactory.class);
138            }
139        } catch (final Exception e) {
140            LOGGER.error("Unable to create class {} specified in {}", className, url.toString(), e);
141        }
142        return null;
143    }
144
145    /**
146     * Gets the class name of the {@link org.apache.logging.log4j.spi.ThreadContextMap} implementation of this Provider.
147     *
148     * @return the class name of a ThreadContextMap implementation
149     */
150    public String getThreadContextMap() {
151        if (threadContextMapClass != null) {
152            return threadContextMapClass.getName();
153        }
154        return threadContextMap;
155    }
156
157    /**
158     * Loads the {@link org.apache.logging.log4j.spi.ThreadContextMap} class specified by this Provider.
159     *
160     * @return the ThreadContextMap implementation class or {@code null} if there was an error loading it
161     */
162    public Class<? extends ThreadContextMap> loadThreadContextMap() {
163        if (threadContextMapClass != null) {
164            return threadContextMapClass;
165        }
166        if (threadContextMap == null) {
167            return null;
168        }
169        final ClassLoader loader = classLoader.get();
170        if (loader == null) {
171            return null;
172        }
173        try {
174            final Class<?> clazz = loader.loadClass(threadContextMap);
175            if (ThreadContextMap.class.isAssignableFrom(clazz)) {
176                return clazz.asSubclass(ThreadContextMap.class);
177            }
178        } catch (final Exception e) {
179            LOGGER.error("Unable to create class {} specified in {}", threadContextMap, url.toString(), e);
180        }
181        return null;
182    }
183
184    /**
185     * Gets the URL containing this Provider's Log4j details.
186     *
187     * @return the URL corresponding to the Provider {@code META-INF/log4j-provider.properties} file
188     */
189    public URL getUrl() {
190        return url;
191    }
192    
193    @Override
194    public String toString() {
195        final StringBuilder result = new StringBuilder("Provider[");
196        if (!DEFAULT_PRIORITY.equals(priority)) {
197            result.append("priority=").append(priority).append(", ");
198        }
199        if (threadContextMap != null) {
200            result.append("threadContextMap=").append(threadContextMap).append(", ");
201        } else if (threadContextMapClass != null) {
202            result.append("threadContextMapClass=").append(threadContextMapClass.getName());
203        }
204        if (className != null) {
205            result.append("className=").append(className).append(", ");
206        } else if (loggerContextFactoryClass != null) {
207            result.append("class=").append(loggerContextFactoryClass.getName());
208        }
209        if (url != null) {
210            result.append("url=").append(url);
211        }
212        final ClassLoader loader;
213        if (classLoader == null || (loader = classLoader.get()) == null) {
214            result.append(", classLoader=null(not reachable)");
215        } else {
216            result.append(", classLoader=").append(loader);
217        }
218        result.append("]");
219        return result.toString();
220    }
221
222    @Override
223    public boolean equals(final Object o) {
224        if (this == o) {
225            return true;
226        }
227        if (o == null || getClass() != o.getClass()) {
228            return false;
229        }
230
231        final Provider provider = (Provider) o;
232
233        if (priority != null ? !priority.equals(provider.priority) : provider.priority != null) {
234            return false;
235        }
236        if (className != null ? !className.equals(provider.className) : provider.className != null) {
237            return false;
238        }
239        if (loggerContextFactoryClass != null ? !loggerContextFactoryClass.equals(provider.loggerContextFactoryClass) : provider.loggerContextFactoryClass != null) {
240            return false;
241        }
242        return versions != null ? versions.equals(provider.versions) : provider.versions == null;
243    }
244
245    @Override
246    public int hashCode() {
247        int result = priority != null ? priority.hashCode() : 0;
248        result = 31 * result + (className != null ? className.hashCode() : 0);
249        result = 31 * result + (loggerContextFactoryClass != null ? loggerContextFactoryClass.hashCode() : 0);
250        result = 31 * result + (versions != null ? versions.hashCode() : 0);
251        return result;
252    }
253}