    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
    018    package org.apache.logging.log4j.core.config.plugins.util;
    019
    020    import org.apache.logging.log4j.Logger;
    021    import org.apache.logging.log4j.status.StatusLogger;
    022    import org.apache.logging.log4j.util.Strings;
    023
    024    import java.util.Collection;
    025    import java.util.HashMap;
    026    import java.util.LinkedHashMap;
    027    import java.util.List;
    028    import java.util.Map;
    029    import java.util.concurrent.CopyOnWriteArrayList;
    030
    031    /**
    032     * Loads and manages all the plugins.
    033     */
    034    public class PluginManager {
    035
    036        private static final CopyOnWriteArrayList<String> PACKAGES = new CopyOnWriteArrayList<String>();
    037        private static final String LOG4J_PACKAGES = "org.apache.logging.log4j.core";
    038
    039        private static final Logger LOGGER = StatusLogger.getLogger();
    040
    041        private Map<String, PluginType<?>> plugins = new HashMap<String, PluginType<?>>();
    042        private final String category;
    043
    044        /**
    045         * Constructs a PluginManager for the plugin category name given.
    046         *
    047         * @param category The plugin category name.
    048         */
    049        public PluginManager(final String category) {
    050            this.category = category;
    051        }
    052
    053        /**
    054         * Process annotated plugins.
    055         *
    056         * @deprecated Use {@link org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor} instead. To do so,
    057         *             simply include {@code log4j-core} in your dependencies and make sure annotation processing is not
    058         *             disabled. By default, supported Java compilers will automatically use that plugin processor provided
    059         *             {@code log4j-core} is on the classpath.
    060         */
    061        @Deprecated
    062        // use PluginProcessor instead
    063        public static void main(final String[] args) {
    064            System.err.println("ERROR: this tool is superseded by the annotation processor included in log4j-core.");
    065            System.err.println("If the annotation processor does not work for you, please see the manual page:");
    066            System.err.println("http://logging.apache.org/log4j/2.x/manual/configuration.html#ConfigurationSyntax");
    067            System.exit(-1);
    068        }
    069
    070        /**
    071         * Adds a package name to be scanned for plugins. Must be invoked prior to plugins being collected.
    072         *
    073         * @param p The package name. Ignored if {@code null} or empty.
    074         */
    075        public static void addPackage(final String p) {
    076            if (Strings.isBlank(p)) {
    077                return;
    078            }
    079            PACKAGES.addIfAbsent(p);
    080        }
    081
    082        /**
    083         * Adds a list of package names to be scanned for plugins. Convenience method for {@link #addPackage(String)}.
    084         *
    085         * @param packages collection of package names to add. Empty and null package names are ignored.
    086         */
    087        public static void addPackages(final Collection<String> packages) {
    088            for (final String pkg : packages) {
    089                if (Strings.isNotBlank(pkg)) {
    090                    PACKAGES.addIfAbsent(pkg);
    091                }
    092            }
    093        }
    094
    095        /**
    096         * Returns the type of a specified plugin.
    097         *
    098         * @param name The name of the plugin.
    099         * @return The plugin's type.
    100         */
    101        public PluginType<?> getPluginType(final String name) {
    102            return plugins.get(name.toLowerCase());
    103        }
    104
    105        /**
    106         * Returns all the matching plugins.
    107         *
    108         * @return A Map containing the name of the plugin and its type.
    109         */
    110        public Map<String, PluginType<?>> getPlugins() {
    111            return plugins;
    112        }
    113
    114        /**
    115         * Locates all the plugins.
    116         */
    117        public void collectPlugins() {
    118            collectPlugins(null);
    119        }
    120
    121        /**
    122         * Locates all the plugins including search of specific packages. Warns about name collisions.
    123         *
    124         * @param packages the list of packages to scan for plugins
    125         * @since 2.1
    126         */
    127        public void collectPlugins(final List<String> packages) {
    128            final String categoryLowerCase = category.toLowerCase();
    129            final Map<String, PluginType<?>> newPlugins = new LinkedHashMap<String, PluginType<?>>();
    130
    131            // First, iterate the Log4j2Plugin.dat files found in the main CLASSPATH
    132            Map<String, List<PluginType<?>>> builtInPlugins = PluginRegistry.getInstance().loadFromMainClassLoader();
    133            if (builtInPlugins.isEmpty()) {
    134                // If we didn't find any plugins above, someone must have messed with the log4j-core.jar.
    135                // Search the standard package in the hopes we can find our core plugins.
    136                builtInPlugins = PluginRegistry.getInstance().loadFromPackage(LOG4J_PACKAGES);
    137            }
    138            mergeByName(newPlugins, builtInPlugins.get(categoryLowerCase));
    139
    140            // Next, iterate any Log4j2Plugin.dat files from OSGi Bundles
    141            for (final Map<String, List<PluginType<?>>> pluginsByCategory : PluginRegistry.getInstance().getPluginsByCategoryByBundleId().values()) {
    142                mergeByName(newPlugins, pluginsByCategory.get(categoryLowerCase));
    143            }
    144
    145            // Next iterate any packages passed to the static addPackage method.
    146            for (final String pkg : PACKAGES) {
    147                mergeByName(newPlugins, PluginRegistry.getInstance().loadFromPackage(pkg).get(categoryLowerCase));
    148            }
    149            // Finally iterate any packages provided in the configuration (note these can be changed at runtime).
    150            if (packages != null) {
    151                for (final String pkg : packages) {
    152                    mergeByName(newPlugins, PluginRegistry.getInstance().loadFromPackage(pkg).get(categoryLowerCase));
    153                }
    154            }
    155
    156            LOGGER.debug("PluginManager '{}' found {} plugins", category, newPlugins.size());
    157
    158            plugins = newPlugins;
    159        }
    160
    161        private static void mergeByName(final Map<String, PluginType<?>> newPlugins, final List<PluginType<?>> plugins) {
    162            if (plugins == null) {
    163                return;
    164            }
    165            for (final PluginType<?> pluginType : plugins) {
    166                final String key = pluginType.getKey();
    167                final PluginType<?> existing = newPlugins.get(key);
    168                if (existing == null) {
    169                    newPlugins.put(key, pluginType);
    170                } else if (!existing.getPluginClass().equals(pluginType.getPluginClass())) {
    171                    LOGGER.warn("Plugin [{}] is already mapped to {}, ignoring {}",
    172                        key, existing.getPluginClass(), pluginType.getPluginClass());
    173                }
    174            }
    175        }
    176    }