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.List;
022
023import org.apache.logging.log4j.Logger;
024import org.apache.logging.log4j.spi.LoggerContextFactory;
025import org.apache.logging.log4j.status.StatusLogger;
026import org.osgi.framework.AdaptPermission;
027import org.osgi.framework.AdminPermission;
028import org.osgi.framework.Bundle;
029import org.osgi.framework.BundleActivator;
030import org.osgi.framework.BundleContext;
031import org.osgi.framework.BundleEvent;
032import org.osgi.framework.SynchronousBundleListener;
033import org.osgi.framework.wiring.BundleWire;
034import org.osgi.framework.wiring.BundleWiring;
035
036/**
037 * OSGi bundle activator. Used for locating an implementation of
038 * {@link org.apache.logging.log4j.spi.LoggerContextFactory} et al. that have corresponding
039 * {@code META-INF/log4j-provider.properties} files. As with all OSGi BundleActivator classes, this class is not for
040 * public use and is only useful in an OSGi framework environment.
041 */
042public class Activator implements BundleActivator, SynchronousBundleListener {
043
044    private static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
045
046    private static final Logger LOGGER = StatusLogger.getLogger();
047
048    // until we have at least one Provider, we'll lock ProviderUtil which locks LogManager.<clinit> by extension.
049    // this variable needs to be reset once the lock has been released
050    private boolean lockingProviderUtil;
051
052    private static void checkPermission(final Permission permission) {
053        if (SECURITY_MANAGER != null) {
054            SECURITY_MANAGER.checkPermission(permission);
055        }
056    }
057
058    private void loadProvider(final Bundle bundle) {
059        if (bundle.getState() == Bundle.UNINSTALLED) {
060            return;
061        }
062        try {
063            checkPermission(new AdminPermission(bundle, AdminPermission.RESOURCE));
064            checkPermission(new AdaptPermission(BundleWiring.class.getName(), bundle, AdaptPermission.ADAPT));
065            loadProvider(bundle.adapt(BundleWiring.class));
066        } catch (final SecurityException e) {
067            LOGGER.debug("Cannot access bundle [{}] contents. Ignoring.", bundle.getSymbolicName(), e);
068        } catch (final Exception e) {
069            LOGGER.warn("Problem checking bundle {} for Log4j 2 provider.", bundle.getSymbolicName(), e);
070        }
071    }
072
073    private void loadProvider(final BundleWiring provider) {
074        final List<URL> urls = provider.findEntries("META-INF", "log4j-provider.properties", 0);
075        for (final URL url : urls) {
076            ProviderUtil.loadProvider(url, provider.getClassLoader());
077        }
078    }
079
080    @Override
081    public void start(final BundleContext context) throws Exception {
082        ProviderUtil.STARTUP_LOCK.lock();
083        lockingProviderUtil = true;
084        final BundleWiring self = context.getBundle().adapt(BundleWiring.class);
085        final List<BundleWire> required = self.getRequiredWires(LoggerContextFactory.class.getName());
086        for (final BundleWire wire : required) {
087            loadProvider(wire.getProviderWiring());
088        }
089        context.addBundleListener(this);
090        final Bundle[] bundles = context.getBundles();
091        for (final Bundle bundle : bundles) {
092            loadProvider(bundle);
093        }
094        unlockIfReady();
095    }
096
097    private void unlockIfReady() {
098        if (lockingProviderUtil && !ProviderUtil.PROVIDERS.isEmpty()) {
099            ProviderUtil.STARTUP_LOCK.unlock();
100            lockingProviderUtil = false;
101        }
102    }
103
104    @Override
105    public void stop(final BundleContext context) throws Exception {
106        context.removeBundleListener(this);
107        unlockIfReady();
108    }
109
110    @Override
111    public void bundleChanged(final BundleEvent event) {
112        switch (event.getType()) {
113            case BundleEvent.STARTED:
114                loadProvider(event.getBundle());
115                unlockIfReady();
116                break;
117
118            default:
119                break;
120        }
121    }
122
123}