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