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