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 }