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     */
017    package org.apache.logging.log4j.util;
018    
019    import java.io.IOException;
020    import java.net.URL;
021    import java.util.Collection;
022    import java.util.Enumeration;
023    import java.util.Properties;
024    import java.util.concurrent.CopyOnWriteArraySet;
025    
026    import org.apache.logging.log4j.Logger;
027    import org.apache.logging.log4j.spi.Provider;
028    import org.apache.logging.log4j.status.StatusLogger;
029    
030    /**
031     * <em>Consider this class private.</em>
032     */
033    public final class ProviderUtil {
034    
035        /**
036         * Resource name for a Log4j 2 provider properties file.
037         */
038        protected static final String PROVIDER_RESOURCE = "META-INF/log4j-provider.properties";
039        private static final String API_VERSION = "Log4jAPIVersion";
040    
041        private static final String[] COMPATIBLE_API_VERSIONS = {
042            "2.0.0"
043        };
044    
045        private static final Logger LOGGER = StatusLogger.getLogger();
046    
047        private static final Collection<Provider> PROVIDERS = new CopyOnWriteArraySet<Provider>();
048    
049        private ProviderUtil() {
050        }
051    
052        static {
053            final ClassLoader cl = findClassLoader();
054            Enumeration<URL> enumResources = null;
055            try {
056                enumResources = cl.getResources(PROVIDER_RESOURCE);
057            } catch (final IOException e) {
058                LOGGER.fatal("Unable to locate {}", PROVIDER_RESOURCE, e);
059            }
060            loadProviders(enumResources, cl);
061        }
062    
063        protected static void loadProviders(final Enumeration<URL> enumResources, ClassLoader cl) {
064            if (enumResources != null) {
065                while (enumResources.hasMoreElements()) {
066                    final URL url = enumResources.nextElement();
067                    loadProvider(url, cl);
068                }
069            }
070        }
071    
072        /**
073         * Loads an individual Provider implementation. This method is really only useful for the OSGi bundle activator
074         * and this class itself.
075         *
076         * @param url the URL to the provider properties file
077         * @param cl the ClassLoader to load the provider classes with
078         */
079        protected static void loadProvider(final URL url, final ClassLoader cl) {
080            try {
081                final Properties props = PropertiesUtil.loadClose(url.openStream(), url);
082                if (validVersion(props.getProperty(API_VERSION))) {
083                    PROVIDERS.add(new Provider(props, url, cl));
084                }
085            } catch (final IOException e) {
086                LOGGER.error("Unable to open {}", url, e);
087            }
088        }
089    
090        public static Iterable<Provider> getProviders() {
091            return PROVIDERS;
092        }
093    
094        public static boolean hasProviders() {
095            return !PROVIDERS.isEmpty();
096        }
097    
098        public static ClassLoader findClassLoader() {
099            return LoaderUtil.getThreadContextClassLoader();
100        }
101    
102        private static boolean validVersion(final String version) {
103            for (final String v : COMPATIBLE_API_VERSIONS) {
104                if (version.startsWith(v)) {
105                    return true;
106                }
107            }
108            return false;
109        }
110    }