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    }