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.config.Configuration;
32 import org.apache.logging.log4j.core.helpers.Loader;
33 import org.apache.logging.log4j.core.impl.ContextAnchor;
34 import org.apache.logging.log4j.core.impl.ReflectiveCallerClassUtility;
35 import org.apache.logging.log4j.status.StatusLogger;
36
37
38
39
40
41
42
43
44
45
46
47
48 public class ClassLoaderContextSelector implements ContextSelector {
49
50 private static final AtomicReference<LoggerContext> CONTEXT = new AtomicReference<LoggerContext>();
51
52 private static final PrivateSecurityManager SECURITY_MANAGER;
53
54 private static final StatusLogger LOGGER = StatusLogger.getLogger();
55
56 private static final ConcurrentMap<String, AtomicReference<WeakReference<LoggerContext>>> CONTEXT_MAP =
57 new ConcurrentHashMap<String, AtomicReference<WeakReference<LoggerContext>>>();
58
59 static {
60 if (ReflectiveCallerClassUtility.isSupported()) {
61 SECURITY_MANAGER = null;
62 } else {
63 PrivateSecurityManager securityManager;
64 try {
65 securityManager = new PrivateSecurityManager();
66 if (securityManager.getCaller(ClassLoaderContextSelector.class.getName()) == null) {
67
68 securityManager = null;
69 LOGGER.error("Unable to obtain call stack from security manager.");
70 }
71 } catch (final Exception e) {
72 securityManager = null;
73 LOGGER.debug("Unable to install security manager", e);
74 }
75 SECURITY_MANAGER = securityManager;
76 }
77 }
78
79 @Override
80 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
81 return getContext(fqcn, loader, currentContext, null);
82 }
83
84 @Override
85 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext,
86 final URI configLocation) {
87 if (currentContext) {
88 final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
89 if (ctx != null) {
90 return ctx;
91 }
92 return getDefault();
93 } else if (loader != null) {
94 return locateContext(loader, configLocation);
95 } else {
96 if (ReflectiveCallerClassUtility.isSupported()) {
97 try {
98 Class<?> clazz = Class.class;
99 boolean next = false;
100 for (int index = 2; clazz != null; ++index) {
101 clazz = ReflectiveCallerClassUtility.getCaller(index);
102 if (clazz == null) {
103 break;
104 }
105 if (clazz.getName().equals(fqcn)) {
106 next = true;
107 continue;
108 }
109 if (next) {
110 break;
111 }
112 }
113 if (clazz != null) {
114 return locateContext(clazz.getClassLoader(), configLocation);
115 }
116 } catch (final Exception ex) {
117
118 }
119 }
120
121 if (SECURITY_MANAGER != null) {
122 final Class<?> clazz = SECURITY_MANAGER.getCaller(fqcn);
123 if (clazz != null) {
124 final ClassLoader ldr = clazz.getClassLoader() != null ? clazz.getClassLoader() :
125 ClassLoader.getSystemClassLoader();
126 return locateContext(ldr, configLocation);
127 }
128 }
129
130 final Throwable t = new Throwable();
131 boolean next = false;
132 String name = null;
133 for (final StackTraceElement element : t.getStackTrace()) {
134 if (element.getClassName().equals(fqcn)) {
135 next = true;
136 continue;
137 }
138 if (next) {
139 name = element.getClassName();
140 break;
141 }
142 }
143 if (name != null) {
144 try {
145 return locateContext(Loader.loadClass(name).getClassLoader(), configLocation);
146 } catch (final ClassNotFoundException ignore) {
147
148 }
149 }
150 final LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
151 if (lc != null) {
152 return lc;
153 }
154 return getDefault();
155 }
156 }
157
158 @Override
159 public void removeContext(final LoggerContext context) {
160 for (final Map.Entry<String, AtomicReference<WeakReference<LoggerContext>>> entry : CONTEXT_MAP.entrySet()) {
161 final LoggerContext ctx = entry.getValue().get().get();
162 if (ctx == context) {
163 CONTEXT_MAP.remove(entry.getKey());
164 }
165 }
166 }
167
168 @Override
169 public List<LoggerContext> getLoggerContexts() {
170 final List<LoggerContext> list = new ArrayList<LoggerContext>();
171 final Collection<AtomicReference<WeakReference<LoggerContext>>> coll = CONTEXT_MAP.values();
172 for (final AtomicReference<WeakReference<LoggerContext>> ref : coll) {
173 final LoggerContext ctx = ref.get().get();
174 if (ctx != null) {
175 list.add(ctx);
176 }
177 }
178 return Collections.unmodifiableList(list);
179 }
180
181 private LoggerContext locateContext(final ClassLoader loader, final URI configLocation) {
182 final String name = loader.toString();
183 AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
184 if (ref == null) {
185 if (configLocation == null) {
186 ClassLoader parent = loader.getParent();
187 while (parent != null) {
188
189 ref = CONTEXT_MAP.get(parent.toString());
190 if (ref != null) {
191 final WeakReference<LoggerContext> r = ref.get();
192 final LoggerContext ctx = r.get();
193 if (ctx != null) {
194 return ctx;
195 }
196 }
197 parent = parent.getParent();
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215 }
216 }
217 LoggerContext ctx = new LoggerContext(name, null, configLocation);
218 final AtomicReference<WeakReference<LoggerContext>> r =
219 new AtomicReference<WeakReference<LoggerContext>>();
220 r.set(new WeakReference<LoggerContext>(ctx));
221 CONTEXT_MAP.putIfAbsent(loader.toString(), r);
222 ctx = CONTEXT_MAP.get(name).get().get();
223 return ctx;
224 }
225 final WeakReference<LoggerContext> r = ref.get();
226 LoggerContext ctx = r.get();
227 if (ctx != null) {
228 if (ctx.getConfigLocation() == null && configLocation != null) {
229 LOGGER.debug("Setting configuration to {}", configLocation);
230 ctx.setConfigLocation(configLocation);
231 } else if (ctx.getConfigLocation() != null && configLocation != null &&
232 !ctx.getConfigLocation().equals(configLocation)) {
233 LOGGER.warn("locateContext called with URI {}. Existing LoggerContext has URI {}", configLocation,
234 ctx.getConfigLocation());
235 }
236 return ctx;
237 }
238 ctx = new LoggerContext(name, null, configLocation);
239 ref.compareAndSet(r, new WeakReference<LoggerContext>(ctx));
240 return ctx;
241 }
242
243 private LoggerContext getDefault() {
244 final LoggerContext ctx = CONTEXT.get();
245 if (ctx != null) {
246 return ctx;
247 }
248 CONTEXT.compareAndSet(null, new LoggerContext("Default"));
249 return CONTEXT.get();
250 }
251
252
253
254
255 private static class PrivateSecurityManager extends SecurityManager {
256
257 public Class<?> getCaller(final String fqcn) {
258 final Class<?>[] classes = getClassContext();
259 boolean next = false;
260 for (final Class<?> clazz : classes) {
261 if (clazz.getName().equals(fqcn)) {
262 next = true;
263 continue;
264 }
265 if (next) {
266 return clazz;
267 }
268 }
269 return null;
270 }
271 }
272
273 }