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 */
017package org.apache.logging.log4j.spi;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Map;
022import java.util.Objects;
023import java.util.WeakHashMap;
024import java.util.concurrent.ConcurrentHashMap;
025import java.util.concurrent.ConcurrentMap;
026
027import org.apache.logging.log4j.message.MessageFactory;
028
029/**
030 * Convenience class to be used by {@code LoggerContext} implementations.
031 */
032public class LoggerRegistry<T extends ExtendedLogger> {
033    private static final String DEFAULT_FACTORY_KEY = AbstractLogger.DEFAULT_MESSAGE_FACTORY_CLASS.getName();
034    private final MapFactory<T> factory;
035    private final Map<String, Map<String, T>> map;
036
037    /**
038     * Interface to control the data structure used by the registry to store the Loggers.
039     * @param <T> subtype of {@code ExtendedLogger}
040     */
041    public interface MapFactory<T extends ExtendedLogger> {
042        Map<String, T> createInnerMap();
043
044        Map<String, Map<String, T>> createOuterMap();
045
046        void putIfAbsent(Map<String, T> innerMap, String name, T logger);
047    }
048
049    /**
050     * Generates ConcurrentHashMaps for use by the registry to store the Loggers.
051     * @param <T> subtype of {@code ExtendedLogger}
052     */
053    public static class ConcurrentMapFactory<T extends ExtendedLogger> implements MapFactory<T> {
054        @Override
055        public Map<String, T> createInnerMap() {
056            return new ConcurrentHashMap<>();
057        }
058
059        @Override
060        public Map<String, Map<String, T>> createOuterMap() {
061            return new ConcurrentHashMap<>();
062        }
063
064        @Override
065        public void putIfAbsent(final Map<String, T> innerMap, final String name, final T logger) {
066            ((ConcurrentMap<String, T>) innerMap).putIfAbsent(name, logger);
067        }
068    }
069
070    /**
071     * Generates WeakHashMaps for use by the registry to store the Loggers.
072     * @param <T> subtype of {@code ExtendedLogger}
073     */
074    public static class WeakMapFactory<T extends ExtendedLogger> implements MapFactory<T> {
075        @Override
076        public Map<String, T> createInnerMap() {
077            return new WeakHashMap<>();
078        }
079
080        @Override
081        public Map<String, Map<String, T>> createOuterMap() {
082            return new WeakHashMap<>();
083        }
084
085        @Override
086        public void putIfAbsent(final Map<String, T> innerMap, final String name, final T logger) {
087            innerMap.put(name, logger);
088        }
089    }
090
091    public LoggerRegistry() {
092        this(new ConcurrentMapFactory<T>());
093    }
094
095    public LoggerRegistry(final MapFactory<T> factory) {
096        this.factory = Objects.requireNonNull(factory, "factory");
097        this.map = factory.createOuterMap();
098    }
099
100    private static String factoryClassKey(final Class<? extends MessageFactory> messageFactoryClass) {
101        return messageFactoryClass == null ? DEFAULT_FACTORY_KEY : messageFactoryClass.getName();
102    }
103
104    private static String factoryKey(final MessageFactory messageFactory) {
105        return messageFactory == null ? DEFAULT_FACTORY_KEY : messageFactory.getClass().getName();
106    }
107
108    /**
109     * Returns an ExtendedLogger.
110     * @param name The name of the Logger to return.
111     * @return The logger with the specified name.
112     */
113    public T getLogger(final String name) {
114        return getOrCreateInnerMap(DEFAULT_FACTORY_KEY).get(name);
115    }
116
117    /**
118     * Returns an ExtendedLogger.
119     * @param name The name of the Logger to return.
120     * @param messageFactory The message factory is used only when creating a logger, subsequent use does not change
121     *                       the logger but will log a warning if mismatched.
122     * @return The logger with the specified name.
123     */
124    public T getLogger(final String name, final MessageFactory messageFactory) {
125        return getOrCreateInnerMap(factoryKey(messageFactory)).get(name);
126    }
127
128    public Collection<T> getLoggers() {
129        return getLoggers(new ArrayList<T>());
130    }
131
132    public Collection<T> getLoggers(final Collection<T> destination) {
133        for (final Map<String, T> inner : map.values()) {
134            destination.addAll(inner.values());
135        }
136        return destination;
137    }
138
139    private Map<String, T> getOrCreateInnerMap(final String factoryName) {
140        Map<String, T> inner = map.get(factoryName);
141        if (inner == null) {
142            inner = factory.createInnerMap();
143            map.put(factoryName, inner);
144        }
145        return inner;
146    }
147
148    /**
149     * Detects if a Logger with the specified name exists.
150     * @param name The Logger name to search for.
151     * @return true if the Logger exists, false otherwise.
152     */
153    public boolean hasLogger(final String name) {
154        return getOrCreateInnerMap(DEFAULT_FACTORY_KEY).containsKey(name);
155    }
156
157    /**
158     * Detects if a Logger with the specified name and MessageFactory exists.
159     * @param name The Logger name to search for.
160     * @param messageFactory The message factory to search for.
161     * @return true if the Logger exists, false otherwise.
162     * @since 2.5
163     */
164    public boolean hasLogger(final String name, final MessageFactory messageFactory) {
165        return getOrCreateInnerMap(factoryKey(messageFactory)).containsKey(name);
166    }
167
168    /**
169     * Detects if a Logger with the specified name and MessageFactory type exists.
170     * @param name The Logger name to search for.
171     * @param messageFactoryClass The message factory class to search for.
172     * @return true if the Logger exists, false otherwise.
173     * @since 2.5
174     */
175    public boolean hasLogger(final String name, final Class<? extends MessageFactory> messageFactoryClass) {
176        return getOrCreateInnerMap(factoryClassKey(messageFactoryClass)).containsKey(name);
177    }
178
179    public void putIfAbsent(final String name, final MessageFactory messageFactory, final T logger) {
180        factory.putIfAbsent(getOrCreateInnerMap(factoryKey(messageFactory)), name, logger);
181    }
182}