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    package org.apache.logging.log4j.core.config.plugins.util;
018    
019    import java.io.BufferedInputStream;
020    import java.io.DataInputStream;
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.net.URL;
024    import java.util.Enumeration;
025    import java.util.HashMap;
026    import java.util.Map;
027    import java.util.concurrent.ConcurrentMap;
028    
029    import org.apache.logging.log4j.Logger;
030    import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor;
031    import org.apache.logging.log4j.core.util.ClassLoaderResourceLoader;
032    import org.apache.logging.log4j.core.util.Closer;
033    import org.apache.logging.log4j.core.util.Loader;
034    import org.apache.logging.log4j.core.util.ResourceLoader;
035    import org.apache.logging.log4j.status.StatusLogger;
036    
037    /**
038     * Loads and manages all the plugins.
039     */
040    public class PluginManager {
041    
042        // TODO: re-use PluginCache code from plugin processor
043        private static final PluginRegistry<PluginType<?>> REGISTRY =
044            new PluginRegistry<PluginType<?>>();
045    
046        private static final Logger LOGGER = StatusLogger.getLogger();
047    
048        private Map<String, PluginType<?>> plugins = new HashMap<String, PluginType<?>>();
049        private final String category;
050    
051        /**
052         * Constructs a PluginManager for the plugin category name given.
053         * @param category The plugin category name.
054         */
055        public PluginManager(final String category) {
056            this.category = category;
057        }
058    
059        /**
060         * Process annotated plugins.
061         * @deprecated Use {@link org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor} instead. To do
062         * so, simply include {@code log4j-core} in your dependencies and make sure annotation processing is not disabled.
063         * By default, supported Java compilers will automatically use that plugin processor provided {@code log4j-core}
064         * is on the classpath.
065         */
066        @Deprecated // use PluginProcessor instead
067        public static void main(final String[] args) {
068            System.err.println("WARNING: this tool is superseded by the annotation processor included in log4j-core.");
069            System.exit(-1);
070        }
071    
072        /**
073         * Adds a package name to be scanned for plugins. Must be invoked prior to plugins being collected.
074         * @param p The package name.
075         */
076        @Deprecated // no more need for this method due to PluginProcessor
077        public static void addPackage(final String p) {
078        }
079    
080        /**
081         * Returns the type of a specified plugin.
082         * @param name The name of the plugin.
083         * @return The plugin's type.
084         */
085        public PluginType<?> getPluginType(final String name) {
086            return plugins.get(name.toLowerCase());
087        }
088    
089        /**
090         * Returns all the matching plugins.
091         * @return A Map containing the name of the plugin and its type.
092         */
093        public Map<String, PluginType<?>> getPlugins() {
094            return plugins;
095        }
096    
097        /**
098         * Locates all the plugins.
099         */
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    }