View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.spi;
18  
19  import java.lang.ref.WeakReference;
20  import java.net.URL;
21  import java.util.Properties;
22  
23  import org.apache.logging.log4j.Logger;
24  import org.apache.logging.log4j.status.StatusLogger;
25  
26  /**
27   * Model class for a Log4j 2 provider. The properties in this class correspond to the properties used in a
28   * {@code META-INF/log4j-provider.properties} file. Note that this class is automatically created by Log4j and should
29   * not be used by providers.
30   */
31  public class Provider {
32      /**
33       * Property name to set for a Log4j 2 provider to specify the priority of this implementation.
34       */
35      public static final String FACTORY_PRIORITY = "FactoryPriority";
36      /**
37       * Property name to set to the implementation of {@link org.apache.logging.log4j.spi.ThreadContextMap}.
38       */
39      public static final String THREAD_CONTEXT_MAP = "ThreadContextMap";
40      /**
41       * Property name to set to the implementation of {@link org.apache.logging.log4j.spi.LoggerContextFactory}.
42       */
43      public static final String LOGGER_CONTEXT_FACTORY = "LoggerContextFactory";
44  
45      private static final Integer DEFAULT_PRIORITY = Integer.valueOf(-1);
46      private static final Logger LOGGER = StatusLogger.getLogger();
47  
48      private final Integer priority;
49      private final String className;
50      private final Class<? extends LoggerContextFactory> loggerContextFactoryClass;
51      private final String threadContextMap;
52      private final Class<? extends ThreadContextMap> threadContextMapClass;
53      private final String versions;
54      private final URL url;
55      private final WeakReference<ClassLoader> classLoader;
56  
57      public Provider(final Properties props, final URL url, final ClassLoader classLoader) {
58          this.url = url;
59          this.classLoader = new WeakReference<>(classLoader);
60          final String weight = props.getProperty(FACTORY_PRIORITY);
61          priority = weight == null ? DEFAULT_PRIORITY : Integer.valueOf(weight);
62          className = props.getProperty(LOGGER_CONTEXT_FACTORY);
63          threadContextMap = props.getProperty(THREAD_CONTEXT_MAP);
64          loggerContextFactoryClass = null;
65          threadContextMapClass = null;
66          versions = null;
67      }
68  
69      public Provider(final Integer priority, final String versions,
70                      final Class<? extends LoggerContextFactory> loggerContextFactoryClass) {
71          this(priority, versions, loggerContextFactoryClass, null);
72      }
73  
74  
75      public Provider(final Integer priority, final String versions,
76                      final Class<? extends LoggerContextFactory> loggerContextFactoryClass,
77                      final Class<? extends ThreadContextMap> threadContextMapClass) {
78          this.url = null;
79          this.classLoader = null;
80          this.priority = priority;
81          this.loggerContextFactoryClass = loggerContextFactoryClass;
82          this.threadContextMapClass = threadContextMapClass;
83          this.className = null;
84          this.threadContextMap = null;
85          this.versions = versions;
86      }
87  
88      /**
89       * Returns the Log4j API versions supported by the implementation.
90       * @return A String containing the Log4j versions supported.
91       */
92      public String getVersions() {
93          return versions;
94      }
95  
96      /**
97       * Gets the priority (natural ordering) of this Provider.
98       *
99       * @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 }