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  package org.apache.logging.log4j.core.config.plugins.util;
18  
19  import java.io.BufferedInputStream;
20  import java.io.DataInputStream;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URL;
24  import java.util.Enumeration;
25  import java.util.HashMap;
26  import java.util.Map;
27  import java.util.concurrent.ConcurrentMap;
28  
29  import org.apache.logging.log4j.Logger;
30  import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor;
31  import org.apache.logging.log4j.core.util.ClassLoaderResourceLoader;
32  import org.apache.logging.log4j.core.util.Closer;
33  import org.apache.logging.log4j.core.util.Loader;
34  import org.apache.logging.log4j.core.util.ResourceLoader;
35  import org.apache.logging.log4j.status.StatusLogger;
36  
37  /**
38   * Loads and manages all the plugins.
39   */
40  public class PluginManager {
41  
42      // TODO: re-use PluginCache code from plugin processor
43      private static final PluginRegistry<PluginType<?>> REGISTRY =
44          new PluginRegistry<PluginType<?>>();
45  
46      private static final Logger LOGGER = StatusLogger.getLogger();
47  
48      private Map<String, PluginType<?>> plugins = new HashMap<String, PluginType<?>>();
49      private final String category;
50  
51      /**
52       * Constructs a PluginManager for the plugin category name given.
53       * @param category The plugin category name.
54       */
55      public PluginManager(final String category) {
56          this.category = category;
57      }
58  
59      /**
60       * Process annotated plugins.
61       * @deprecated Use {@link org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor} instead. To do
62       * so, simply include {@code log4j-core} in your dependencies and make sure annotation processing is not disabled.
63       * By default, supported Java compilers will automatically use that plugin processor provided {@code log4j-core}
64       * is on the classpath.
65       */
66      @Deprecated // use PluginProcessor instead
67      public static void main(final String[] args) {
68          System.err.println("WARNING: this tool is superseded by the annotation processor included in log4j-core.");
69          System.exit(-1);
70      }
71  
72      /**
73       * Adds a package name to be scanned for plugins. Must be invoked prior to plugins being collected.
74       * @param p The package name.
75       */
76      @Deprecated // no more need for this method due to PluginProcessor
77      public static void addPackage(final String p) {
78      }
79  
80      /**
81       * Returns the type of a specified plugin.
82       * @param name The name of the plugin.
83       * @return The plugin's type.
84       */
85      public PluginType<?> getPluginType(final String name) {
86          return plugins.get(name.toLowerCase());
87      }
88  
89      /**
90       * Returns all the matching plugins.
91       * @return A Map containing the name of the plugin and its type.
92       */
93      public Map<String, PluginType<?>> getPlugins() {
94          return plugins;
95      }
96  
97      /**
98       * Locates all the plugins.
99       */
100     public void collectPlugins() {
101         collectPlugins(true);
102     }
103 
104     /**
105      * Collects plugins, optionally obtaining them from a preload map.
106      * @param preLoad if true, plugins will be obtained from the preload map.
107      *
108      */
109     public void collectPlugins(boolean preLoad) {
110         if (REGISTRY.hasCategory(category)) {
111             plugins = REGISTRY.getCategory(category);
112             preLoad = false;
113         }
114         if (preLoad) {
115             final ResourceLoader loader = new ClassLoaderResourceLoader(Loader.getClassLoader());
116             loadPlugins(loader);
117         }
118         plugins = REGISTRY.getCategory(category);
119     }
120 
121     public static void loadPlugins(final ResourceLoader loader) {
122         final PluginRegistry<PluginType<?>> registry = decode(loader);
123         if (registry != null) {
124             for (final Map.Entry<String, ConcurrentMap<String, PluginType<?>>> entry : registry.getCategories()) {
125                 REGISTRY.getCategory(entry.getKey()).putAll(entry.getValue());
126             }
127         } else {
128             LOGGER.info("Plugin preloads not available from class loader {}", loader);
129         }
130     }
131 
132     private static PluginRegistry<PluginType<?>> decode(final ResourceLoader loader) {
133         final Enumeration<URL> resources;
134         try {
135             resources = loader.getResources(PluginProcessor.PLUGIN_CACHE_FILE);
136             if (resources == null) {
137                 return null;
138             }
139         } catch (final IOException ioe) {
140             LOGGER.warn("Unable to preload plugins", ioe);
141             return null;
142         }
143         final PluginRegistry<PluginType<?>> map = new PluginRegistry<PluginType<?>>();
144         while (resources.hasMoreElements()) {
145             final URL url = resources.nextElement();
146             LOGGER.debug("Found Plugin Map at {}", url.toExternalForm());
147             final InputStream is;
148             try {
149                 is = url.openStream();
150             } catch (final IOException e) {
151                 LOGGER.warn("Unable to open {}", url.toExternalForm(), e);
152                 continue;
153             }
154             final DataInputStream dis = new DataInputStream(new BufferedInputStream(is));
155             try {
156                 final int count = dis.readInt();
157                 for (int j = 0; j < count; ++j) {
158                     final String category = dis.readUTF();
159                     final int entries = dis.readInt();
160                     final Map<String, PluginType<?>> types = map.getCategory(category);
161                     for (int i = 0; i < entries; ++i) {
162                         final String key = dis.readUTF();
163                         final String className = dis.readUTF();
164                         final String name = dis.readUTF();
165                         final boolean printable = dis.readBoolean();
166                         final boolean defer = dis.readBoolean();
167                         try {
168                             final Class<?> clazz = loader.loadClass(className);
169                             @SuppressWarnings({"unchecked","rawtypes"})
170                             final PluginType<?> pluginType = new PluginType(clazz, name, printable, defer);
171                             types.put(key, pluginType);
172                         } catch (final ClassNotFoundException e) {
173                             LOGGER.info("Plugin [{}] could not be loaded due to missing classes.", className, e);
174                         }
175                     }
176                 }
177             } catch (final IOException ex) {
178                 LOGGER.warn("Unable to preload plugins", ex);
179             } finally {
180                 Closer.closeSilent(dis);
181             }
182         }
183         return map.isEmpty() ? null : map;
184     }
185 
186 }