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.lang.reflect.Method;
21 import java.lang.reflect.Modifier;
22 import java.net.URI;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap;
30 import java.util.concurrent.atomic.AtomicReference;
31
32 import org.apache.logging.log4j.core.LoggerContext;
33 import org.apache.logging.log4j.core.helpers.Loader;
34 import org.apache.logging.log4j.core.impl.ContextAnchor;
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 PrivateSecurityManager securityManager;
53
54 private static Method getCallerClass;
55
56 private static final StatusLogger LOGGER = StatusLogger.getLogger();
57
58 private static final ConcurrentMap<String, AtomicReference<WeakReference<LoggerContext>>> CONTEXT_MAP =
59 new ConcurrentHashMap<String, AtomicReference<WeakReference<LoggerContext>>>();
60
61 static {
62 setupCallerCheck();
63 }
64
65 @Override
66 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext) {
67 return getContext(fqcn, loader, currentContext, null);
68 }
69
70 @Override
71 public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext,
72 URI configLocation) {
73 if (currentContext) {
74 final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
75 if (ctx != null) {
76 return ctx;
77 }
78 return getDefault();
79 } else if (loader != null) {
80 return locateContext(loader, configLocation);
81 } else {
82 if (getCallerClass != null) {
83 try {
84 Class clazz = Class.class;
85 boolean next = false;
86 for (int index = 2; clazz != null; ++index) {
87 final Object[] params = new Object[] {index};
88 clazz = (Class) getCallerClass.invoke(null, params);
89 if (clazz == null) {
90 break;
91 }
92 if (clazz.getName().equals(fqcn)) {
93 next = true;
94 continue;
95 }
96 if (next) {
97 break;
98 }
99 }
100 if (clazz != null) {
101 return locateContext(clazz.getClassLoader(), configLocation);
102 }
103 } catch (final Exception ex) {
104
105 }
106 }
107
108 if (securityManager != null) {
109 final Class clazz = securityManager.getCaller(fqcn);
110 if (clazz != null) {
111 final ClassLoader ldr = clazz.getClassLoader() != null ? clazz.getClassLoader() :
112 ClassLoader.getSystemClassLoader();
113 return locateContext(ldr, configLocation);
114 }
115 }
116
117 final Throwable t = new Throwable();
118 boolean next = false;
119 String name = null;
120 for (final StackTraceElement element : t.getStackTrace()) {
121 if (element.getClassName().equals(fqcn)) {
122 next = true;
123 continue;
124 }
125 if (next) {
126 name = element.getClassName();
127 break;
128 }
129 }
130 if (name != null) {
131 try {
132 return locateContext(Loader.loadClass(name).getClassLoader(), configLocation);
133 } catch (final ClassNotFoundException ex) {
134
135 }
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<LoggerContext>();
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 loader, final URI configLocation) {
169 final String name = loader.toString();
170 AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
171 if (ref == null) {
172 if (configLocation == null) {
173 ClassLoader parent = loader.getParent();
174 while (parent != null) {
175
176 ref = CONTEXT_MAP.get(parent.toString());
177 if (ref != null) {
178 final WeakReference<LoggerContext> r = ref.get();
179 LoggerContext ctx = r.get();
180 if (ctx != null) {
181 return ctx;
182 }
183 }
184 parent = parent.getParent();
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 }
203 }
204 LoggerContext ctx = new LoggerContext(name, null, configLocation);
205 final AtomicReference<WeakReference<LoggerContext>> r =
206 new AtomicReference<WeakReference<LoggerContext>>();
207 r.set(new WeakReference<LoggerContext>(ctx));
208 CONTEXT_MAP.putIfAbsent(loader.toString(), r);
209 ctx = CONTEXT_MAP.get(name).get().get();
210 return ctx;
211 } else {
212 final WeakReference<LoggerContext> r = ref.get();
213 LoggerContext ctx = r.get();
214 if (ctx != null) {
215 return ctx;
216 }
217 ctx = new LoggerContext(name, null, configLocation);
218 ref.compareAndSet(r, new WeakReference<LoggerContext>(ctx));
219 return ctx;
220 }
221 }
222
223 private static void setupCallerCheck() {
224 try {
225 final ClassLoader loader = Loader.getClassLoader();
226 final Class clazz = loader.loadClass("sun.reflect.Reflection");
227 final Method[] methods = clazz.getMethods();
228 for (final Method method : methods) {
229 final int modifier = method.getModifiers();
230 if (method.getName().equals("getCallerClass") && Modifier.isStatic(modifier)) {
231 getCallerClass = method;
232 break;
233 }
234 }
235 } catch (final ClassNotFoundException cnfe) {
236 LOGGER.debug("sun.reflect.Reflection is not installed");
237 }
238 try {
239 securityManager = new PrivateSecurityManager();
240 } catch (final Exception ex) {
241 ex.printStackTrace();
242 LOGGER.debug("Unable to install security manager", ex);
243 }
244 }
245
246 private LoggerContext getDefault() {
247 final LoggerContext ctx = CONTEXT.get();
248 if (ctx != null) {
249 return ctx;
250 }
251 CONTEXT.compareAndSet(null, new LoggerContext("Default"));
252 return CONTEXT.get();
253 }
254
255
256
257
258 private static class PrivateSecurityManager extends SecurityManager {
259
260 public Class getCaller(final String fqcn) {
261 final Class[] classes = getClassContext();
262 boolean next = false;
263 for (final Class clazz : classes) {
264 if (clazz.getName().equals(fqcn)) {
265 next = true;
266 continue;
267 }
268 if (next) {
269 return clazz;
270 }
271 }
272 return null;
273 }
274 }
275
276 }