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 }