001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.util;
018
019import java.net.URL;
020import java.security.Permission;
021import java.util.Collection;
022import java.util.List;
023
024import org.apache.logging.log4j.Logger;
025import org.apache.logging.log4j.spi.LoggerContextFactory;
026import org.apache.logging.log4j.spi.Provider;
027import org.apache.logging.log4j.status.StatusLogger;
028import org.osgi.framework.AdaptPermission;
029import org.osgi.framework.AdminPermission;
030import org.osgi.framework.Bundle;
031import org.osgi.framework.BundleActivator;
032import org.osgi.framework.BundleContext;
033import org.osgi.framework.BundleEvent;
034import org.osgi.framework.InvalidSyntaxException;
035import org.osgi.framework.ServiceReference;
036import org.osgi.framework.SynchronousBundleListener;
037import org.osgi.framework.wiring.BundleWire;
038import org.osgi.framework.wiring.BundleWiring;
039
040/**
041 * <em>Consider this class private.</em>
042 * OSGi bundle activator. Used for locating an implementation of
043 * {@link org.apache.logging.log4j.spi.LoggerContextFactory} et al. that have corresponding
044 * {@code META-INF/log4j-provider.properties} files. As with all OSGi BundleActivator classes, this class is not for
045 * public use and is only useful in an OSGi framework environment.
046 */
047public class Activator implements BundleActivator, SynchronousBundleListener {
048
049    private static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
050
051    private static final Logger LOGGER = StatusLogger.getLogger();
052
053    // until we have at least one Provider, we'll lock ProviderUtil which locks LogManager.<clinit> by extension.
054    // this variable needs to be reset once the lock has been released
055    private boolean lockingProviderUtil;
056
057    private static void checkPermission(final Permission permission) {
058        if (SECURITY_MANAGER != null) {
059            SECURITY_MANAGER.checkPermission(permission);
060        }
061    }
062
063    private void loadProvider(final Bundle bundle) {
064        if (bundle.getState() == Bundle.UNINSTALLED) {
065            return;
066        }
067        try {
068            checkPermission(new AdminPermission(bundle, AdminPermission.RESOURCE));
069            checkPermission(new AdaptPermission(BundleWiring.class.getName(), bundle, AdaptPermission.ADAPT));
070            final BundleContext bundleContext = bundle.getBundleContext();
071            if (bundleContext == null) {
072                LOGGER.debug("Bundle {} has no context (state={}), skipping loading provider", bundle.getSymbolicName(), toStateString(bundle.getState()));
073            } else {
074                loadProvider(bundleContext, bundle.adapt(BundleWiring.class));
075            }
076        } catch (final SecurityException e) {
077            LOGGER.debug("Cannot access bundle [{}] contents. Ignoring.", bundle.getSymbolicName(), e);
078        } catch (final Exception e) {
079            LOGGER.warn("Problem checking bundle {} for Log4j 2 provider.", bundle.getSymbolicName(), e);
080        }
081    }
082
083    private String toStateString(final int state) {
084        switch (state) {
085        case Bundle.UNINSTALLED:
086            return "UNINSTALLED";
087        case Bundle.INSTALLED:
088            return "INSTALLED";
089        case Bundle.RESOLVED:
090            return "RESOLVED";
091        case Bundle.STARTING:
092            return "STARTING";
093        case Bundle.STOPPING:
094            return "STOPPING";
095        case Bundle.ACTIVE:
096            return "ACTIVE";
097        default:
098            return Integer.toString(state);
099        }
100    }
101
102    private void loadProvider(final BundleContext bundleContext, final BundleWiring bundleWiring) {
103        final String filter = "(APIVersion>=2.6.0)";
104        try {
105            final Collection<ServiceReference<Provider>> serviceReferences = bundleContext.getServiceReferences(Provider.class, filter);
106            Provider maxProvider = null;
107            for (final ServiceReference<Provider> serviceReference : serviceReferences) {
108                final Provider provider = bundleContext.getService(serviceReference);
109                if (maxProvider == null || provider.getPriority() > maxProvider.getPriority()) {
110                    maxProvider = provider;
111                }
112            }
113            if (maxProvider != null) {
114                ProviderUtil.addProvider(maxProvider);
115            }
116        } catch (final InvalidSyntaxException ex) {
117            LOGGER.error("Invalid service filter: " + filter, ex);
118        }
119        final List<URL> urls = bundleWiring.findEntries("META-INF", "log4j-provider.properties", 0);
120        for (final URL url : urls) {
121            ProviderUtil.loadProvider(url, bundleWiring.getClassLoader());
122        }
123    }
124
125    @Override
126    public void start(final BundleContext bundleContext) throws Exception {
127        ProviderUtil.STARTUP_LOCK.lock();
128        lockingProviderUtil = true;
129        final BundleWiring self = bundleContext.getBundle().adapt(BundleWiring.class);
130        final List<BundleWire> required = self.getRequiredWires(LoggerContextFactory.class.getName());
131        for (final BundleWire wire : required) {
132            loadProvider(bundleContext, wire.getProviderWiring());
133        }
134        bundleContext.addBundleListener(this);
135        final Bundle[] bundles = bundleContext.getBundles();
136        for (final Bundle bundle : bundles) {
137            loadProvider(bundle);
138        }
139        unlockIfReady();
140    }
141
142    private void unlockIfReady() {
143        if (lockingProviderUtil && !ProviderUtil.PROVIDERS.isEmpty()) {
144            ProviderUtil.STARTUP_LOCK.unlock();
145            lockingProviderUtil = false;
146        }
147    }
148
149    @Override
150    public void stop(final BundleContext bundleContext) throws Exception {
151        bundleContext.removeBundleListener(this);
152        unlockIfReady();
153    }
154
155    @Override
156    public void bundleChanged(final BundleEvent event) {
157        switch (event.getType()) {
158            case BundleEvent.STARTED:
159                loadProvider(event.getBundle());
160                unlockIfReady();
161                break;
162
163            default:
164                break;
165        }
166    }
167
168}