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    }