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