    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     */
    017    package org.apache.logging.log4j.util;
    018
    019    import java.net.URL;
    020    import java.security.Permission;
    021    import java.util.List;
    022
    023    import org.apache.logging.log4j.Logger;
    024    import org.apache.logging.log4j.spi.LoggerContextFactory;
    025    import org.apache.logging.log4j.status.StatusLogger;
    026    import org.osgi.framework.AdaptPermission;
    027    import org.osgi.framework.AdminPermission;
    028    import org.osgi.framework.Bundle;
    029    import org.osgi.framework.BundleActivator;
    030    import org.osgi.framework.BundleContext;
    031    import org.osgi.framework.BundleEvent;
    032    import org.osgi.framework.SynchronousBundleListener;
    033    import org.osgi.framework.wiring.BundleWire;
    034    import 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     */
    042    public 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    }