blob: 704ffd12cf2a0b2f76f55148d7ceccda11dc4345 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.config.plugins.util;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.status.StatusLogger;
import org.apache.logging.log4j.util.Strings;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Loads and manages all the plugins.
*/
public class PluginManager {
private static final CopyOnWriteArrayList<String> PACKAGES = new CopyOnWriteArrayList<>();
private static final String LOG4J_PACKAGES = "org.apache.logging.log4j.core";
private static final Logger LOGGER = StatusLogger.getLogger();
private Map<String, PluginType<?>> plugins = new HashMap<>();
private final String category;
/**
* Constructs a PluginManager for the plugin category name given.
*
* @param category The plugin category name.
*/
public PluginManager(final String category) {
this.category = category;
}
/**
* Process annotated plugins.
*
* @deprecated Use {@link org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor} instead. To do so,
* simply include {@code log4j-core} in your dependencies and make sure annotation processing is not
* disabled. By default, supported Java compilers will automatically use that plugin processor provided
* {@code log4j-core} is on the classpath.
*/
@Deprecated
// use PluginProcessor instead
public static void main(final String[] args) {
System.err.println("ERROR: this tool is superseded by the annotation processor included in log4j-core.");
System.err.println("If the annotation processor does not work for you, please see the manual page:");
System.err.println("http://logging.apache.org/log4j/2.x/manual/configuration.html#ConfigurationSyntax");
System.exit(-1);
}
/**
* Adds a package name to be scanned for plugins. Must be invoked prior to plugins being collected.
*
* @param p The package name. Ignored if {@code null} or empty.
*/
public static void addPackage(final String p) {
if (Strings.isBlank(p)) {
return;
}
PACKAGES.addIfAbsent(p);
}
/**
* Adds a list of package names to be scanned for plugins. Convenience method for {@link #addPackage(String)}.
*
* @param packages collection of package names to add. Empty and null package names are ignored.
*/
public static void addPackages(final Collection<String> packages) {
for (final String pkg : packages) {
if (Strings.isNotBlank(pkg)) {
PACKAGES.addIfAbsent(pkg);
}
}
}
/**
* Returns the type of a specified plugin.
*
* @param name The name of the plugin.
* @return The plugin's type.
*/
public PluginType<?> getPluginType(final String name) {
return plugins.get(name.toLowerCase());
}
/**
* Returns all the matching plugins.
*
* @return A Map containing the name of the plugin and its type.
*/
public Map<String, PluginType<?>> getPlugins() {
return plugins;
}
/**
* Locates all the plugins.
*/
public void collectPlugins() {
collectPlugins(null);
}
/**
* Locates all the plugins including search of specific packages. Warns about name collisions.
*
* @param packages the list of packages to scan for plugins
* @since 2.1
*/
public void collectPlugins(final List<String> packages) {
final String categoryLowerCase = category.toLowerCase();
final Map<String, PluginType<?>> newPlugins = new LinkedHashMap<>();
// First, iterate the Log4j2Plugin.dat files found in the main CLASSPATH
Map<String, List<PluginType<?>>> builtInPlugins = PluginRegistry.getInstance().loadFromMainClassLoader();
if (builtInPlugins.isEmpty()) {
// If we didn't find any plugins above, someone must have messed with the log4j-core.jar.
// Search the standard package in the hopes we can find our core plugins.
builtInPlugins = PluginRegistry.getInstance().loadFromPackage(LOG4J_PACKAGES);
}
mergeByName(newPlugins, builtInPlugins.get(categoryLowerCase));
// Next, iterate any Log4j2Plugin.dat files from OSGi Bundles
for (final Map<String, List<PluginType<?>>> pluginsByCategory : PluginRegistry.getInstance().getPluginsByCategoryByBundleId().values()) {
mergeByName(newPlugins, pluginsByCategory.get(categoryLowerCase));
}
// Next iterate any packages passed to the static addPackage method.
for (final String pkg : PACKAGES) {
mergeByName(newPlugins, PluginRegistry.getInstance().loadFromPackage(pkg).get(categoryLowerCase));
}
// Finally iterate any packages provided in the configuration (note these can be changed at runtime).
if (packages != null) {
for (final String pkg : packages) {
mergeByName(newPlugins, PluginRegistry.getInstance().loadFromPackage(pkg).get(categoryLowerCase));
}
}
LOGGER.debug("PluginManager '{}' found {} plugins", category, newPlugins.size());
plugins = newPlugins;
}
private static void mergeByName(final Map<String, PluginType<?>> newPlugins, final List<PluginType<?>> plugins) {
if (plugins == null) {
return;
}
for (final PluginType<?> pluginType : plugins) {
final String key = pluginType.getKey();
final PluginType<?> existing = newPlugins.get(key);
if (existing == null) {
newPlugins.put(key, pluginType);
} else if (!existing.getPluginClass().equals(pluginType.getPluginClass())) {
LOGGER.warn("Plugin [{}] is already mapped to {}, ignoring {}",
key, existing.getPluginClass(), pluginType.getPluginClass());
}
}
}
}