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 }