View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.osgi;
18  
19  import java.lang.ref.WeakReference;
20  import java.net.URI;
21  import java.util.Objects;
22  import java.util.concurrent.atomic.AtomicReference;
23  
24  import org.apache.logging.log4j.core.LoggerContext;
25  import org.apache.logging.log4j.core.impl.ContextAnchor;
26  import org.apache.logging.log4j.core.selector.ClassLoaderContextSelector;
27  import org.apache.logging.log4j.util.StackLocatorUtil;
28  import org.osgi.framework.Bundle;
29  import org.osgi.framework.BundleReference;
30  import org.osgi.framework.FrameworkUtil;
31  
32  /**
33   * ContextSelector for OSGi bundles. This ContextSelector works rather similarly to the
34   * {@link ClassLoaderContextSelector}, but instead of each ClassLoader having its own LoggerContext (like in a
35   * servlet container), each OSGi bundle has its own LoggerContext.
36   *
37   * @since 2.1
38   */
39  public class BundleContextSelector extends ClassLoaderContextSelector {
40  
41      @Override
42      public LoggerContext getContext(final String fqcn, final ClassLoader loader, final boolean currentContext,
43                                      final URI configLocation) {
44          if (currentContext) {
45              final LoggerContext ctx = ContextAnchor.THREAD_CONTEXT.get();
46              if (ctx != null) {
47                  return ctx;
48              }
49              return getDefault();
50          }
51          // it's quite possible that the provided ClassLoader may implement BundleReference which gives us a nice shortcut
52          if (loader instanceof BundleReference) {
53              return locateContext(((BundleReference) loader).getBundle(), configLocation);
54          }
55          final Class<?> callerClass = StackLocatorUtil.getCallerClass(fqcn);
56          if (callerClass != null) {
57              return locateContext(FrameworkUtil.getBundle(callerClass), configLocation);
58          }
59          final LoggerContext lc = ContextAnchor.THREAD_CONTEXT.get();
60          return lc == null ? getDefault() : lc;
61      }
62  
63      private static LoggerContext locateContext(final Bundle bundle, final URI configLocation) {
64          final String name = Objects.requireNonNull(bundle, "No Bundle provided").getSymbolicName();
65          final AtomicReference<WeakReference<LoggerContext>> ref = CONTEXT_MAP.get(name);
66          if (ref == null) {
67              final LoggerContext context = new LoggerContext(name, bundle, configLocation);
68              CONTEXT_MAP.putIfAbsent(name,
69                  new AtomicReference<>(new WeakReference<>(context)));
70              return CONTEXT_MAP.get(name).get().get();
71          }
72          final WeakReference<LoggerContext> r = ref.get();
73          final LoggerContext ctx = r.get();
74          if (ctx == null) {
75              final LoggerContext context = new LoggerContext(name, bundle, configLocation);
76              ref.compareAndSet(r, new WeakReference<>(context));
77              return ref.get().get();
78          }
79          final URI oldConfigLocation = ctx.getConfigLocation();
80          if (oldConfigLocation == null && configLocation != null) {
81              LOGGER.debug("Setting bundle ({}) configuration to {}", name, configLocation);
82              ctx.setConfigLocation(configLocation);
83          } else if (oldConfigLocation != null && configLocation != null && !configLocation.equals(oldConfigLocation)) {
84              LOGGER.warn("locateContext called with URI [{}], but existing LoggerContext has URI [{}]",
85                  configLocation, oldConfigLocation);
86          }
87          return ctx;
88      }
89  }