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}