View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  
18  package org.apache.logging.log4j.core.config.plugins.util;
19  
20  import org.apache.logging.log4j.Logger;
21  import org.apache.logging.log4j.status.StatusLogger;
22  import org.apache.logging.log4j.util.Strings;
23  
24  import java.util.Collection;
25  import java.util.HashMap;
26  import java.util.LinkedHashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.concurrent.CopyOnWriteArrayList;
30  
31  /**
32   * Loads and manages all the plugins.
33   */
34  public class PluginManager {
35  
36      private static final CopyOnWriteArrayList<String> PACKAGES = new CopyOnWriteArrayList<String>();
37      private static final String LOG4J_PACKAGES = "org.apache.logging.log4j.core";
38  
39      private static final Logger LOGGER = StatusLogger.getLogger();
40  
41      private Map<String, PluginType<?>> plugins = new HashMap<String, PluginType<?>>();
42      private final String category;
43  
44      /**
45       * Constructs a PluginManager for the plugin category name given.
46       * 
47       * @param category The plugin category name.
48       */
49      public PluginManager(final String category) {
50          this.category = category;
51      }
52  
53      /**
54       * Process annotated plugins.
55       * 
56       * @deprecated Use {@link org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor} instead. To do so,
57       *             simply include {@code log4j-core} in your dependencies and make sure annotation processing is not
58       *             disabled. By default, supported Java compilers will automatically use that plugin processor provided
59       *             {@code log4j-core} is on the classpath.
60       */
61      @Deprecated
62      // use PluginProcessor instead
63      public static void main(final String[] args) {
64          System.err.println("ERROR: this tool is superseded by the annotation processor included in log4j-core.");
65          System.err.println("If the annotation processor does not work for you, please see the manual page:");
66          System.err.println("http://logging.apache.org/log4j/2.x/manual/configuration.html#ConfigurationSyntax");
67          System.exit(-1);
68      }
69  
70      /**
71       * Adds a package name to be scanned for plugins. Must be invoked prior to plugins being collected.
72       * 
73       * @param p The package name. Ignored if {@code null} or empty.
74       */
75      public static void addPackage(final String p) {
76          if (Strings.isBlank(p)) {
77              return;
78          }
79          PACKAGES.addIfAbsent(p);
80      }
81  
82      /**
83       * Adds a list of package names to be scanned for plugins. Convenience method for {@link #addPackage(String)}.
84       *
85       * @param packages collection of package names to add. Empty and null package names are ignored.
86       */
87      public static void addPackages(final Collection<String> packages) {
88          for (final String pkg : packages) {
89              if (Strings.isNotBlank(pkg)) {
90                  PACKAGES.addIfAbsent(pkg);
91              }
92          }
93      }
94  
95      /**
96       * Returns the type of a specified plugin.
97       * 
98       * @param name The name of the plugin.
99       * @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 }