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.util;
    18
    19  import java.io.IOException;
    20  import java.net.URL;
    21  import java.util.Collection;
    22  import java.util.Enumeration;
    23  import java.util.HashSet;
    24  import java.util.Properties;
    25  import java.util.concurrent.locks.Lock;
    26  import java.util.concurrent.locks.ReentrantLock;
    27
    28  import org.apache.logging.log4j.Logger;
    29  import org.apache.logging.log4j.spi.Provider;
    30  import org.apache.logging.log4j.status.StatusLogger;
    31
    32  /**
    33   * <em>Consider this class private.</em>
    34   * Utility class for Log4j {@link Provider}s. When integrating with an application container framework, any Log4j
    35   * Providers not accessible through standard classpath scanning should {@link #loadProvider(java.net.URL, ClassLoader)}
    36   * a classpath accordingly.
    37   */
    38  public final class ProviderUtil {
    39
    40      /**
    41       * Resource name for a Log4j 2 provider properties file.
    42       */
    43      protected static final String PROVIDER_RESOURCE = "META-INF/log4j-provider.properties";
    44      private static final String API_VERSION = "Log4jAPIVersion";
    45
    46      private static final String[] COMPATIBLE_API_VERSIONS = {
    47          "2.0.0", "2.1.0"
    48      };
    49
    50      private static final Logger LOGGER = StatusLogger.getLogger();
    51
    52      protected static final Collection<Provider> PROVIDERS = new HashSet<Provider>();
    53
    54      /**
    55       * Guards the ProviderUtil singleton instance from lazy initialization. This is primarily used for OSGi support.
    56       *
    57       * @since 2.1
    58       */
    59      protected static final Lock STARTUP_LOCK = new ReentrantLock();
    60      // STARTUP_LOCK guards INSTANCE for lazy initialization; this allows the OSGi Activator to pause the startup and
    61      // wait for a Provider to be installed. See LOG4J2-373
    62      private static volatile ProviderUtil INSTANCE;
    63
    64      private ProviderUtil() {
    65          for (final LoaderUtil.UrlResource resource : LoaderUtil.findUrlResources(PROVIDER_RESOURCE)) {
    66              loadProvider(resource.getUrl(), resource.getClassLoader());
    67          }
    68      }
    69
    70      /**
    71       * Loads an individual Provider implementation. This method is really only useful for the OSGi bundle activator
    72       * and this class itself.
    73       *
    74       * @param url the URL to the provider properties file
    75       * @param cl the ClassLoader to load the provider classes with
    76       */
    77      protected static void loadProvider(final URL url, final ClassLoader cl) {
    78          try {
    79              final Properties props = PropertiesUtil.loadClose(url.openStream(), url);
    80              if (validVersion(props.getProperty(API_VERSION))) {
    81                  PROVIDERS.add(new Provider(props, url, cl));
    82              }
    83          } catch (final IOException e) {
    84              LOGGER.error("Unable to open {}", url, e);
    85          }
    86      }
    87
    88      /**
    89       * @deprecated Use {@link #loadProvider(java.net.URL, ClassLoader)} instead. Will be removed in 3.0.
    90       */
    91      @Deprecated
    92      protected static void loadProviders(final Enumeration<URL> urls, final ClassLoader cl) {
    93          if (urls != null) {
    94              while (urls.hasMoreElements()) {
    95                  loadProvider(urls.nextElement(), cl);
    96              }
    97          }
    98      }
    99
    100     public static Iterable<Provider> getProviders() {
    101         lazyInit();
    102         return PROVIDERS;
    103     }
    104
    105     public static boolean hasProviders() {
    106         lazyInit();
    107         return !PROVIDERS.isEmpty();
    108     }
    109
    110     /**
    111      * Lazily initializes the ProviderUtil singleton.
    112      *
    113      * @since 2.1
    114      */
    115     protected static void lazyInit() {
    116         //noinspection DoubleCheckedLocking
    117         if (INSTANCE == null) {
    118             try {
    119                 STARTUP_LOCK.lockInterruptibly();
    120                 try {
    121                     if (INSTANCE == null) {
    122                         INSTANCE = new ProviderUtil();
    123                     }
    124                 } finally {
    125                     STARTUP_LOCK.unlock();
    126                 }
    127             } catch (final InterruptedException e) {
    128                 LOGGER.fatal("Interrupted before Log4j Providers could be loaded.", e);
    129                 Thread.currentThread().interrupt();
    130             }
    131         }
    132     }
    133
    134     public static ClassLoader findClassLoader() {
    135         return LoaderUtil.getThreadContextClassLoader();
    136     }
    137
    138     private static boolean validVersion(final String version) {
    139         for (final String v : COMPATIBLE_API_VERSIONS) {
    140             if (version.startsWith(v)) {
    141                 return true;
    142             }
    143         }
    144         return false;
    145     }
    146 }