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.Reference;
20 import java.lang.ref.WeakReference;
21 import java.net.URI;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.ConcurrentMap;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.atomic.AtomicReference;
31
32 import org.apache.logging.log4j.core.LoggerContext;
33 import org.apache.logging.log4j.core.impl.ContextAnchor;
34 import org.apache.logging.log4j.spi.LoggerContextShutdownAware;
35 import org.apache.logging.log4j.status.StatusLogger;
36 import org.apache.logging.log4j.util.StackLocatorUtil;
37
38
39
40
41
42
43
44
45
46
47
48
49 public class ClassLoaderContextSelector implements ContextSelector, LoggerContextShutdownAware {
50
51 private static final AtomicReference<LoggerContext> DEFAULT_CONTEXT = new AtomicReference<>();
52
53 protected static final StatusLogger LOGGER = StatusLogger.getLogger();
54
55 protected static final ConcurrentMap<String, AtomicReference<WeakReference<LoggerContext>>> CONTEXT_MAP =
56 new ConcurrentHashMap<>();
57
58 @Override
59 public void shutdown(final String fqcn, final ClassLoader loader, final boolean currentContext,
60 final boolean allContexts) {
61 LoggerContext ctx = null;
62 if (currentContext) {
63 ctx = ContextAnchor.THREAD_CONTEXT.get();
64 } else if (loader != null) {
65 ctx = findContext(loader);
66 } else {
67 final Class<?> clazz = StackLocatorUtil.getCallerClass(fqcn);
68 if (clazz != null) {
69 ctx = findContext(clazz.getClassLoader());
70 }
71 if (ctx == null) {
72 ctx = ContextAnchor.THREAD_CONTEXT.get();
73 }
74 }
75 if (ctx != null) {
76 ctx.stop(DEFAULT_STOP_TIMEOUT, TimeUnit.MILLISECONDS);
77 }
78 }
79
80 @Override
81 public void contextShutdown(org.apache.logging.log4j.spi.LoggerContext loggerContext) {
82 if (loggerContext instanceof LoggerContext) {
83 removeContext((LoggerContext) loggerContext);
84 }
85 }
86
87 @Override
88 public boolean hasContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
89 LoggerContext ctx;
90 if (currentContext) {
91 ctx = ContextAnchor.THREAD_CONTEXT.get();
92 } else if (loader != null) {
93 ctx = findContext(loader);
94 } else {
95 final Class<?> clazz = StackLocatorUtil.getCallerClass(fqcn);
96 if (clazz != null) {
97 ctx = findContext(clazz.getClassLoader());
98 } else {
99 ctx = ContextAnchor.THREAD_CONTEXT.get();
100 }
101 }
102 return ctx != null && ctx.isStarted();
103 }
104
105 private LoggerContext findContext(ClassLoader loaderOrNull) {
106 final ClassLoader loader = loaderOrNull != null ? loaderOrNull : ClassLoader.getSystemClassLoader();
107 final String name = toContextMapKey(loader);
108 AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
109 if (ref != null) {
110 final WeakReference<LoggerContext> weakRef = ref.get();
111 return weakRef.get();
112 }
113 return null;
114 }
115
116 @Override
117 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
118 return getContext(fqcn, loader, currentContext, null);
119 }
120
121 @Override
122 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext,
123 final URI configLocation) {
124 if (currentContext) {
125 final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
126 if (ctx != null) {
127 return ctx;
128 }
129 return getDefault();
130 } else if (loader != null) {
131 return locateContext(loader, configLocation);
132 } else {
133 final Class<?> clazz = StackLocatorUtil.getCallerClass(fqcn);
134 if (clazz != null) {
135 return locateContext(clazz.getClassLoader(), configLocation);
136 }
137 final LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
138 if (lc != null) {
139 return lc;
140 }
141 return getDefault();
142 }
143 }
144
145 @Override
146 public void removeContext(final LoggerContext context) {
147 for (final Map.Entry<String, AtomicReference<WeakReference<LoggerContext>>> entry : CONTEXT_MAP.entrySet()) {
148 final LoggerContext ctx = entry.getValue().get().get();
149 if (ctx == context) {
150 CONTEXT_MAP.remove(entry.getKey());
151 }
152 }
153 }
154
155 @Override
156 public List<LoggerContext> getLoggerContexts() {
157 final List<LoggerContext> list = new ArrayList<>();
158 final Collection<AtomicReference<WeakReference<LoggerContext>>> coll = CONTEXT_MAP.values();
159 for (final AtomicReference<WeakReference<LoggerContext>> ref : coll) {
160 final LoggerContext ctx = ref.get().get();
161 if (ctx != null) {
162 list.add(ctx);
163 }
164 }
165 return Collections.unmodifiableList(list);
166 }
167
168 private LoggerContext locateContext(final ClassLoader loaderOrNull, final URI configLocation) {
169
170 final ClassLoader loader = loaderOrNull != null ? loaderOrNull : ClassLoader.getSystemClassLoader();
171 final String name = toContextMapKey(loader);
172 AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
173 if (ref == null) {
174 if (configLocation == null) {
175 ClassLoader parent = loader.getParent();
176 while (parent != null) {
177
178 ref = CONTEXT_MAP.get(toContextMapKey(parent));
179 if (ref != null) {
180 final WeakReference<LoggerContext> r = ref.get();
181 final LoggerContext ctx = r.get();
182 if (ctx != null) {
183 return ctx;
184 }
185 }
186 parent = parent.getParent();
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204 }
205 }
206 LoggerContext ctx = createContext(name, configLocation);
207 LoggerContext newContext = CONTEXT_MAP.computeIfAbsent(name,
208 k -> new AtomicReference<>(new WeakReference<>(ctx))).get().get();
209 if (newContext == ctx) {
210 ctx.addShutdownListener(this);
211 }
212 return newContext;
213 }
214 final WeakReference<LoggerContext> weakRef = ref.get();
215 LoggerContext ctx = weakRef.get();
216 if (ctx != null) {
217 if (ctx.getConfigLocation() == null && configLocation != null) {
218 LOGGER.debug("Setting configuration to {}", configLocation);
219 ctx.setConfigLocation(configLocation);
220 } else if (ctx.getConfigLocation() != null && configLocation != null
221 && !ctx.getConfigLocation().equals(configLocation)) {
222 LOGGER.warn("locateContext called with URI {}. Existing LoggerContext has URI {}", configLocation,
223 ctx.getConfigLocation());
224 }
225 return ctx;
226 }
227 ctx = createContext(name, configLocation);
228 ref.compareAndSet(weakRef, new WeakReference<>(ctx));
229 return ctx;
230 }
231
232 protected LoggerContext createContext(final String name, final URI configLocation) {
233 return new LoggerContext(name, null, configLocation);
234 }
235
236 protected String toContextMapKey(final ClassLoader loader) {
237 return Integer.toHexString(System.identityHashCode(loader));
238 }
239
240 protected LoggerContext getDefault() {
241 final LoggerContext ctx = DEFAULT_CONTEXT.get();
242 if (ctx != null) {
243 return ctx;
244 }
245 DEFAULT_CONTEXT.compareAndSet(null, createContext(defaultContextName(), null));
246 return DEFAULT_CONTEXT.get();
247 }
248
249 protected String defaultContextName() {
250 return "Default";
251 }
252 }