1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.selector;
18
19 import java.lang.ref.WeakReference;
20 import java.net.URI;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.ConcurrentMap;
28 import java.util.concurrent.atomic.AtomicReference;
29
30 import org.apache.logging.log4j.core.LoggerContext;
31 import org.apache.logging.log4j.core.impl.ContextAnchor;
32 import org.apache.logging.log4j.status.StatusLogger;
33 import org.apache.logging.log4j.util.ReflectionUtil;
34
35
36
37
38
39
40
41
42
43
44
45
46 public class ClassLoaderContextSelector implements ContextSelector {
47
48 private static final AtomicReference<LoggerContext> DEFAULT_CONTEXT = new AtomicReference<>();
49
50 protected static final StatusLogger LOGGER = StatusLogger.getLogger();
51
52 protected static final ConcurrentMap<String, AtomicReference<WeakReference<LoggerContext>>> CONTEXT_MAP =
53 new ConcurrentHashMap<>();
54
55 @Override
56 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
57 return getContext(fqcn, loader, currentContext, null);
58 }
59
60 @Override
61 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext,
62 final URI configLocation) {
63 if (currentContext) {
64 final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
65 if (ctx != null) {
66 return ctx;
67 }
68 return getDefault();
69 } else if (loader != null) {
70 return locateContext(loader, configLocation);
71 } else {
72 final Class<?> clazz = ReflectionUtil.getCallerClass(fqcn);
73 if (clazz != null) {
74 return locateContext(clazz.getClassLoader(), configLocation);
75 }
76 final LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
77 if (lc != null) {
78 return lc;
79 }
80 return getDefault();
81 }
82 }
83
84 @Override
85 public void removeContext(final LoggerContext context) {
86 for (final Map.Entry<String, AtomicReference<WeakReference<LoggerContext>>> entry : CONTEXT_MAP.entrySet()) {
87 final LoggerContext ctx = entry.getValue().get().get();
88 if (ctx == context) {
89 CONTEXT_MAP.remove(entry.getKey());
90 }
91 }
92 }
93
94 @Override
95 public List<LoggerContext> getLoggerContexts() {
96 final List<LoggerContext> list = new ArrayList<>();
97 final Collection<AtomicReference<WeakReference<LoggerContext>>> coll = CONTEXT_MAP.values();
98 for (final AtomicReference<WeakReference<LoggerContext>> ref : coll) {
99 final LoggerContext ctx = ref.get().get();
100 if (ctx != null) {
101 list.add(ctx);
102 }
103 }
104 return Collections.unmodifiableList(list);
105 }
106
107 private LoggerContext locateContext(final ClassLoader loaderOrNull, final URI configLocation) {
108
109 final ClassLoader loader = loaderOrNull != null ? loaderOrNull : ClassLoader.getSystemClassLoader();
110 final String name = toContextMapKey(loader);
111 AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
112 if (ref == null) {
113 if (configLocation == null) {
114 ClassLoader parent = loader.getParent();
115 while (parent != null) {
116
117 ref = CONTEXT_MAP.get(toContextMapKey(parent));
118 if (ref != null) {
119 final WeakReference<LoggerContext> r = ref.get();
120 final LoggerContext ctx = r.get();
121 if (ctx != null) {
122 return ctx;
123 }
124 }
125 parent = parent.getParent();
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143 }
144 }
145 LoggerContext ctx = createContext(name, configLocation);
146 final AtomicReference<WeakReference<LoggerContext>> r = new AtomicReference<>();
147 r.set(new WeakReference<>(ctx));
148 CONTEXT_MAP.putIfAbsent(name, r);
149 ctx = CONTEXT_MAP.get(name).get().get();
150 return ctx;
151 }
152 final WeakReference<LoggerContext> weakRef = ref.get();
153 LoggerContext ctx = weakRef.get();
154 if (ctx != null) {
155 if (ctx.getConfigLocation() == null && configLocation != null) {
156 LOGGER.debug("Setting configuration to {}", configLocation);
157 ctx.setConfigLocation(configLocation);
158 } else if (ctx.getConfigLocation() != null && configLocation != null
159 && !ctx.getConfigLocation().equals(configLocation)) {
160 LOGGER.warn("locateContext called with URI {}. Existing LoggerContext has URI {}", configLocation,
161 ctx.getConfigLocation());
162 }
163 return ctx;
164 }
165 ctx = createContext(name, configLocation);
166 ref.compareAndSet(weakRef, new WeakReference<>(ctx));
167 return ctx;
168 }
169
170 protected LoggerContext createContext(final String name, final URI configLocation) {
171 return new LoggerContext(name, null, configLocation);
172 }
173
174 protected String toContextMapKey(final ClassLoader loader) {
175 return Integer.toHexString(System.identityHashCode(loader));
176 }
177
178 protected LoggerContext getDefault() {
179 final LoggerContext ctx = DEFAULT_CONTEXT.get();
180 if (ctx != null) {
181 return ctx;
182 }
183 DEFAULT_CONTEXT.compareAndSet(null, createContext(defaultContextName(), null));
184 return DEFAULT_CONTEXT.get();
185 }
186
187 protected String defaultContextName() {
188 return "Default";
189 }
190 }