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.spi;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Map;
22  import java.util.Objects;
23  import java.util.WeakHashMap;
24  import java.util.concurrent.ConcurrentHashMap;
25  import java.util.concurrent.ConcurrentMap;
26  
27  import org.apache.logging.log4j.message.MessageFactory;
28  
29  /**
30   * Convenience class to be used by {@code LoggerContext} implementations.
31   */
32  public class LoggerRegistry<T extends ExtendedLogger> {
33      private static final String DEFAULT_FACTORY_KEY = AbstractLogger.DEFAULT_MESSAGE_FACTORY_CLASS.getName();
34      private final MapFactory<T> factory;
35      private final Map<String, Map<String, T>> map;
36  
37      /**
38       * Interface to control the data structure used by the registry to store the Loggers.
39       * @param <T> subtype of {@code ExtendedLogger}
40       */
41      public interface MapFactory<T extends ExtendedLogger> {
42          Map<String, T> createInnerMap();
43  
44          Map<String, Map<String, T>> createOuterMap();
45  
46          void putIfAbsent(Map<String, T> innerMap, String name, T logger);
47      }
48  
49      /**
50       * Generates ConcurrentHashMaps for use by the registry to store the Loggers.
51       * @param <T> subtype of {@code ExtendedLogger}
52       */
53      public static class ConcurrentMapFactory<T extends ExtendedLogger> implements MapFactory<T> {
54          @Override
55          public Map<String, T> createInnerMap() {
56              return new ConcurrentHashMap<>();
57          }
58  
59          @Override
60          public Map<String, Map<String, T>> createOuterMap() {
61              return new ConcurrentHashMap<>();
62          }
63  
64          @Override
65          public void putIfAbsent(final Map<String, T> innerMap, final String name, final T logger) {
66              ((ConcurrentMap<String, T>) innerMap).putIfAbsent(name, logger);
67          }
68      }
69  
70      /**
71       * Generates WeakHashMaps for use by the registry to store the Loggers.
72       * @param <T> subtype of {@code ExtendedLogger}
73       */
74      public static class WeakMapFactory<T extends ExtendedLogger> implements MapFactory<T> {
75          @Override
76          public Map<String, T> createInnerMap() {
77              return new WeakHashMap<>();
78          }
79  
80          @Override
81          public Map<String, Map<String, T>> createOuterMap() {
82              return new WeakHashMap<>();
83          }
84  
85          @Override
86          public void putIfAbsent(final Map<String, T> innerMap, final String name, final T logger) {
87              innerMap.put(name, logger);
88          }
89      }
90  
91      public LoggerRegistry() {
92          this(new ConcurrentMapFactory<T>());
93      }
94  
95      public LoggerRegistry(final MapFactory<T> factory) {
96          this.factory = Objects.requireNonNull(factory, "factory");
97          this.map = factory.createOuterMap();
98      }
99  
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 }