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            loadProvider(bundle.getBundleContext(), bundle.adapt(BundleWiring.class));
071        } catch (final SecurityException e) {
072            LOGGER.debug("Cannot access bundle [{}] contents. Ignoring.", bundle.getSymbolicName(), e);
073        } catch (final Exception e) {
074            LOGGER.warn("Problem checking bundle {} for Log4j 2 provider.", bundle.getSymbolicName(), e);
075        }
076    }
077
078    private void loadProvider(final BundleContext context, final BundleWiring bundleWiring) {
079        final String filter = "(APIVersion>=2.60)";
080        try {
081            final Collection<ServiceReference<Provider>> serviceReferences = context.getServiceReferences(Provider.class, filter);
082            Provider maxProvider = null;
083            for (final ServiceReference<Provider> serviceReference : serviceReferences) {
084                final Provider provider = context.getService(serviceReference);
085                if (maxProvider == null || provider.getPriority() > maxProvider.getPriority()) {
086                    maxProvider = provider;
087                }
088            }
089            if (maxProvider != null) {
090                ProviderUtil.addProvider(maxProvider);
091            }
092        } catch (final InvalidSyntaxException ex) {
093            LOGGER.error("Invalid service filter: " + filter, ex);
094        }
095        final List<URL> urls = bundleWiring.findEntries("META-INF", "log4j-provider.properties", 0);
096        for (final URL url : urls) {
097            ProviderUtil.loadProvider(url, bundleWiring.getClassLoader());
098        }
099    }
100
101    @Override
102    public void start(final BundleContext context) throws Exception {
103        ProviderUtil.STARTUP_LOCK.lock();
104        lockingProviderUtil = true;
105        final BundleWiring self = context.getBundle().adapt(BundleWiring.class);
106        final List<BundleWire> required = self.getRequiredWires(LoggerContextFactory.class.getName());
107        for (final BundleWire wire : required) {
108            loadProvider(context, wire.getProviderWiring());
109        }
110        context.addBundleListener(this);
111        final Bundle[] bundles = context.getBundles();
112        for (final Bundle bundle : bundles) {
113            loadProvider(bundle);
114        }
115        unlockIfReady();
116    }
117
118    private void unlockIfReady() {
119        if (lockingProviderUtil && !ProviderUtil.PROVIDERS.isEmpty()) {
120            ProviderUtil.STARTUP_LOCK.unlock();
121            lockingProviderUtil = false;
122        }
123    }
124
125    @Override
126    public void stop(final BundleContext context) throws Exception {
127        context.removeBundleListener(this);
128        unlockIfReady();
129    }
130
131    @Override
132    public void bundleChanged(final BundleEvent event) {
133        switch (event.getType()) {
134            case BundleEvent.STARTED:
135                loadProvider(event.getBundle());
136                unlockIfReady();
137                break;
138
139            default:
140                break;
141        }
142    }
143
144}