    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.processor;
    019
    020    import org.apache.logging.log4j.core.util.Closer;
    021
    022    import java.io.BufferedInputStream;
    023    import java.io.BufferedOutputStream;
    024    import java.io.DataInputStream;
    025    import java.io.DataOutputStream;
    026    import java.io.IOException;
    027    import java.io.OutputStream;
    028    import java.net.URL;
    029    import java.util.Enumeration;
    030    import java.util.LinkedHashMap;
    031    import java.util.Map;
    032
    033    /**
    034     *
    035     */
    036    public class PluginCache {
    037        private final Map<String, Map<String, PluginEntry>> categories =
    038            new LinkedHashMap<String, Map<String, PluginEntry>>();
    039
    040        /**
    041         * Returns all categories of plugins in this cache.
    042         *
    043         * @return all categories of plugins in this cache.
    044         * @since 2.1
    045         */
    046        public Map<String, Map<String, PluginEntry>> getAllCategories() {
    047            return categories;
    048        }
    049
    050        /**
    051         * Gets or creates a category of plugins.
    052         *
    053         * @param category name of category to look up.
    054         * @return plugin mapping of names to plugin entries.
    055         */
    056        public Map<String, PluginEntry> getCategory(final String category) {
    057            final String key = category.toLowerCase();
    058            if (!categories.containsKey(key)) {
    059                categories.put(key, new LinkedHashMap<String, PluginEntry>());
    060            }
    061            return categories.get(key);
    062        }
    063
    064        /**
    065         * Stores the plugin cache to a given OutputStream.
    066         *
    067         * @param os destination to save cache to.
    068         * @throws IOException
    069         */
    070        // NOTE: if this file format is to be changed, the filename should change and this format should still be readable
    071        public void writeCache(final OutputStream os) throws IOException {
    072            final DataOutputStream out = new DataOutputStream(new BufferedOutputStream(os));
    073            try {
    074                // See PluginManager.readFromCacheFiles for the corresponding decoder. Format may not be changed
    075                // without breaking existing Log4j2Plugins.dat files.
    076                out.writeInt(categories.size());
    077                for (final Map.Entry<String, Map<String, PluginEntry>> category : categories.entrySet()) {
    078                    out.writeUTF(category.getKey());
    079                    final Map<String, PluginEntry> m = category.getValue();
    080                    out.writeInt(m.size());
    081                    for (final Map.Entry<String, PluginEntry> entry : m.entrySet()) {
    082                        final PluginEntry plugin = entry.getValue();
    083                        out.writeUTF(plugin.getKey());
    084                        out.writeUTF(plugin.getClassName());
    085                        out.writeUTF(plugin.getName());
    086                        out.writeBoolean(plugin.isPrintable());
    087                        out.writeBoolean(plugin.isDefer());
    088                    }
    089                }
    090            } finally {
    091                Closer.closeSilently(out);
    092            }
    093        }
    094
    095        /**
    096         * Loads and merges all the Log4j plugin cache files specified. Usually, this is obtained via a ClassLoader.
    097         *
    098         * @param resources URLs to all the desired plugin cache files to load.
    099         * @throws IOException
    100         */
    101        public void loadCacheFiles(final Enumeration<URL> resources) throws IOException {
    102            categories.clear();
    103            while (resources.hasMoreElements()) {
    104                final URL url = resources.nextElement();
    105                final DataInputStream in = new DataInputStream(new BufferedInputStream(url.openStream()));
    106                try {
    107                    final int count = in.readInt();
    108                    for (int i = 0; i < count; i++) {
    109                        final String category = in.readUTF();
    110                        final Map<String, PluginEntry> m = getCategory(category);
    111                        final int entries = in.readInt();
    112                        for (int j = 0; j < entries; j++) {
    113                            final PluginEntry entry = new PluginEntry();
    114                            entry.setKey(in.readUTF());
    115                            entry.setClassName(in.readUTF());
    116                            entry.setName(in.readUTF());
    117                            entry.setPrintable(in.readBoolean());
    118                            entry.setDefer(in.readBoolean());
    119                            entry.setCategory(category);
    120                            if (!m.containsKey(entry.getKey())) {
    121                                m.put(entry.getKey(), entry);
    122                            }
    123                        }
    124                    }
    125                } finally {
    126                    Closer.closeSilently(in);
    127                }
    128            }
    129        }
    130
    131        /**
    132         * Gets the number of plugin categories registered.
    133         *
    134         * @return number of plugin categories in cache.
    135         */
    136        public int size() {
    137            return categories.size();
    138        }
    139    }