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}