blob: dab6c3b2318b7b1520a372fc52f29b25c60fb736 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.qpid.server.plugins;
import static org.apache.felix.framework.util.FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP;
import static org.apache.felix.main.AutoProcessor.AUTO_DEPLOY_ACTION_PROPERY;
import static org.apache.felix.main.AutoProcessor.AUTO_DEPLOY_DIR_PROPERY;
import static org.apache.felix.main.AutoProcessor.AUTO_DEPLOY_INSTALL_VALUE;
import static org.apache.felix.main.AutoProcessor.AUTO_DEPLOY_START_VALUE;
import static org.apache.felix.main.AutoProcessor.process;
import static org.osgi.framework.Constants.FRAMEWORK_STORAGE;
import static org.osgi.framework.Constants.FRAMEWORK_STORAGE_CLEAN;
import static org.osgi.framework.Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT;
import static org.osgi.framework.Constants.FRAMEWORK_SYSTEMPACKAGES;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.felix.framework.Felix;
import org.apache.felix.framework.util.StringMap;
import org.apache.log4j.Logger;
import org.apache.qpid.common.Closeable;
import org.apache.qpid.common.QpidProperties;
import org.apache.qpid.server.configuration.TopicConfiguration;
import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory;
import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionConfiguration.SlowConsumerDetectionConfigurationFactory;
import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionPolicyConfiguration.SlowConsumerDetectionPolicyConfigurationFactory;
import org.apache.qpid.server.configuration.plugins.SlowConsumerDetectionQueueConfiguration.SlowConsumerDetectionQueueConfigurationFactory;
import org.apache.qpid.server.exchange.ExchangeType;
import org.apache.qpid.server.security.SecurityManager;
import org.apache.qpid.server.security.SecurityPluginFactory;
import org.apache.qpid.server.security.access.plugins.AllowAll;
import org.apache.qpid.server.security.access.plugins.DenyAll;
import org.apache.qpid.server.security.access.plugins.LegacyAccess;
import org.apache.qpid.server.security.auth.manager.AuthenticationManagerPluginFactory;
import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager;
import org.apache.qpid.server.virtualhost.plugins.SlowConsumerDetection;
import org.apache.qpid.server.virtualhost.plugins.VirtualHostPluginFactory;
import org.apache.qpid.server.virtualhost.plugins.policies.TopicDeletePolicy;
import org.apache.qpid.slowconsumerdetection.policies.SlowConsumerPolicyPluginFactory;
import org.apache.qpid.util.FileUtils;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Version;
import org.osgi.framework.launch.Framework;
import org.osgi.util.tracker.ServiceTracker;
/**
* Provides access to pluggable elements, such as exchanges
*/
@SuppressWarnings("unchecked")
public class PluginManager implements Closeable
{
private static final Logger _logger = Logger.getLogger(PluginManager.class);
private static final int FELIX_STOP_TIMEOUT = 30000;
private Framework _felix;
private ServiceTracker _exchangeTracker = null;
private ServiceTracker _securityTracker = null;
private ServiceTracker _configTracker = null;
private ServiceTracker _virtualHostTracker = null;
private ServiceTracker _policyTracker = null;
private ServiceTracker _authenticationManagerTracker = null;
private Activator _activator;
private final List<ServiceTracker> _trackers = new ArrayList<ServiceTracker>();
private Map<String, SecurityPluginFactory> _securityPlugins = new HashMap<String, SecurityPluginFactory>();
private Map<List<String>, ConfigurationPluginFactory> _configPlugins = new IdentityHashMap<List<String>, ConfigurationPluginFactory>();
private Map<String, VirtualHostPluginFactory> _vhostPlugins = new HashMap<String, VirtualHostPluginFactory>();
private Map<String, SlowConsumerPolicyPluginFactory> _policyPlugins = new HashMap<String, SlowConsumerPolicyPluginFactory>();
private Map<String, AuthenticationManagerPluginFactory<? extends Plugin>> _authenticationManagerPlugins = new HashMap<String, AuthenticationManagerPluginFactory<? extends Plugin>>();
/** The default name of the OSGI system package list. */
private static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/server/plugins/OsgiSystemPackages.properties";
/** The name of the override system property that holds the name of the OSGI system package list. */
private static final String FILE_PROPERTY = "qpid.osgisystempackages.properties";
private static final String OSGI_SYSTEM_PACKAGES;
static
{
final String filename = System.getProperty(FILE_PROPERTY);
final InputStream is = FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME,
PluginManager.class.getClassLoader());
try
{
Version qpidReleaseVersion;
try
{
qpidReleaseVersion = Version.parseVersion(QpidProperties.getReleaseVersion());
}
catch (IllegalArgumentException iae)
{
qpidReleaseVersion = null;
}
final Properties p = new Properties();
p.load(is);
final OsgiSystemPackageUtil osgiSystemPackageUtil = new OsgiSystemPackageUtil(qpidReleaseVersion, (Map)p);
OSGI_SYSTEM_PACKAGES = osgiSystemPackageUtil.getFormattedSystemPackageString();
_logger.debug("List of OSGi system packages to be added: " + OSGI_SYSTEM_PACKAGES);
}
catch (IOException e)
{
_logger.error("Error reading OSGI system package list", e);
throw new ExceptionInInitializerError(e);
}
}
public PluginManager(String pluginPath, String cachePath, BundleContext bundleContext) throws Exception
{
// Store all non-OSGi plugins
// A little gross that we have to add them here, but not all the plugins are OSGIfied
for (SecurityPluginFactory<?> pluginFactory : Arrays.asList(
AllowAll.FACTORY, DenyAll.FACTORY, LegacyAccess.FACTORY))
{
_securityPlugins.put(pluginFactory.getPluginName(), pluginFactory);
}
for (ConfigurationPluginFactory configFactory : Arrays.asList(
TopicConfiguration.FACTORY,
SecurityManager.SecurityConfiguration.FACTORY,
AllowAll.AllowAllConfiguration.FACTORY,
DenyAll.DenyAllConfiguration.FACTORY,
LegacyAccess.LegacyAccessConfiguration.FACTORY,
new SlowConsumerDetectionConfigurationFactory(),
new SlowConsumerDetectionPolicyConfigurationFactory(),
new SlowConsumerDetectionQueueConfigurationFactory(),
PrincipalDatabaseAuthenticationManager.PrincipalDatabaseAuthenticationManagerConfiguration.FACTORY))
{
_configPlugins.put(configFactory.getParentPaths(), configFactory);
}
for (SlowConsumerPolicyPluginFactory pluginFactory : Arrays.asList(
new TopicDeletePolicy.TopicDeletePolicyFactory()))
{
_policyPlugins.put(pluginFactory.getPluginName(), pluginFactory);
}
for (VirtualHostPluginFactory pluginFactory : Arrays.asList(
new SlowConsumerDetection.SlowConsumerFactory()))
{
_vhostPlugins.put(pluginFactory.getClass().getName(), pluginFactory);
}
for (AuthenticationManagerPluginFactory<? extends Plugin> pluginFactory : Arrays.asList(
PrincipalDatabaseAuthenticationManager.FACTORY))
{
_authenticationManagerPlugins.put(pluginFactory.getPluginName(), pluginFactory);
}
if(bundleContext == null)
{
// Check the plugin directory path is set and exist
if (pluginPath == null)
{
_logger.info("No plugin path specified, no plugins will be loaded.");
return;
}
File pluginDir = new File(pluginPath);
if (!pluginDir.exists())
{
_logger.warn("Plugin dir : " + pluginDir + " does not exist.");
return;
}
// Add the bundle provided service interface package and the core OSGi
// packages to be exported from the class path via the system bundle.
// Setup OSGi configuration property map
final StringMap configMap = new StringMap(false);
configMap.put(FRAMEWORK_SYSTEMPACKAGES, OSGI_SYSTEM_PACKAGES);
// No automatic shutdown hook
configMap.put("felix.shutdown.hook", "false");
// Add system activator
List<BundleActivator> activators = new ArrayList<BundleActivator>();
_activator = new Activator();
activators.add(_activator);
configMap.put(SYSTEMBUNDLE_ACTIVATORS_PROP, activators);
if (cachePath != null)
{
File cacheDir = new File(cachePath);
if (!cacheDir.exists() && cacheDir.canWrite())
{
_logger.info("Creating plugin cache directory: " + cachePath);
cacheDir.mkdir();
}
// Set plugin cache directory and empty it
_logger.info("Cache bundles in directory " + cachePath);
configMap.put(FRAMEWORK_STORAGE, cachePath);
}
configMap.put(FRAMEWORK_STORAGE_CLEAN, FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
// Set directory with plugins to auto-deploy
_logger.info("Auto deploying bundles from directory " + pluginPath);
configMap.put(AUTO_DEPLOY_DIR_PROPERY, pluginPath);
configMap.put(AUTO_DEPLOY_ACTION_PROPERY, AUTO_DEPLOY_INSTALL_VALUE + "," + AUTO_DEPLOY_START_VALUE);
// Start plugin manager
_felix = new Felix(configMap);
try
{
_logger.info("Starting plugin manager framework");
_felix.init();
process(configMap, _felix.getBundleContext());
_felix.start();
_logger.info("Started plugin manager framework");
}
catch (BundleException e)
{
throw new ConfigurationException("Could not start plugin manager: " + e.getMessage(), e);
}
bundleContext = _activator.getContext();
}
else
{
_logger.info("Using the specified external BundleContext");
}
_exchangeTracker = new ServiceTracker(bundleContext, ExchangeType.class.getName(), null);
_exchangeTracker.open();
_trackers.add(_exchangeTracker);
_securityTracker = new ServiceTracker(bundleContext, SecurityPluginFactory.class.getName(), null);
_securityTracker.open();
_trackers.add(_securityTracker);
_configTracker = new ServiceTracker(bundleContext, ConfigurationPluginFactory.class.getName(), null);
_configTracker.open();
_trackers.add(_configTracker);
_virtualHostTracker = new ServiceTracker(bundleContext, VirtualHostPluginFactory.class.getName(), null);
_virtualHostTracker.open();
_trackers.add(_virtualHostTracker);
_policyTracker = new ServiceTracker(bundleContext, SlowConsumerPolicyPluginFactory.class.getName(), null);
_policyTracker.open();
_trackers.add(_policyTracker);
_authenticationManagerTracker = new ServiceTracker(bundleContext, AuthenticationManagerPluginFactory.class.getName(), null);
_authenticationManagerTracker.open();
_trackers.add(_authenticationManagerTracker);
_logger.info("Opened service trackers");
}
private static <T> Map<String, T> getServices(ServiceTracker tracker)
{
Map<String, T> services = new HashMap<String, T>();
if ((tracker != null) && (tracker.getServices() != null))
{
for (Object service : tracker.getServices())
{
if (service instanceof PluginFactory<?>)
{
services.put(((PluginFactory<?>) service).getPluginName(), (T) service);
}
else
{
services.put(service.getClass().getName(), (T) service);
}
}
}
return services;
}
public static <T> Map<String, T> getServices(ServiceTracker tracker, Map<String, T> plugins)
{
Map<String, T> services = getServices(tracker);
services.putAll(plugins);
return services;
}
public Map<List<String>, ConfigurationPluginFactory> getConfigurationPlugins()
{
Map<List<String>, ConfigurationPluginFactory> services = new IdentityHashMap<List<String>, ConfigurationPluginFactory>();
if (_configTracker != null && _configTracker.getServices() != null)
{
for (Object service : _configTracker.getServices())
{
ConfigurationPluginFactory factory = (ConfigurationPluginFactory) service;
services.put(factory.getParentPaths(), factory);
}
}
services.putAll(_configPlugins);
return services;
}
public Map<String, VirtualHostPluginFactory> getVirtualHostPlugins()
{
return getServices(_virtualHostTracker, _vhostPlugins);
}
public Map<String, SlowConsumerPolicyPluginFactory> getSlowConsumerPlugins()
{
return getServices(_policyTracker, _policyPlugins);
}
public Map<String, ExchangeType<?>> getExchanges()
{
return getServices(_exchangeTracker);
}
public Map<String, SecurityPluginFactory> getSecurityPlugins()
{
return getServices(_securityTracker, _securityPlugins);
}
public Map<String, AuthenticationManagerPluginFactory<? extends Plugin>> getAuthenticationManagerPlugins()
{
return getServices(_authenticationManagerTracker, _authenticationManagerPlugins);
}
public void close()
{
try
{
// Close all bundle trackers
for(ServiceTracker tracker : _trackers)
{
tracker.close();
}
}
finally
{
if (_felix != null)
{
_logger.info("Stopping plugin manager framework");
try
{
// FIXME should be stopAndWait() but hangs VM, need upgrade in felix
_felix.stop();
}
catch (BundleException e)
{
// Ignore
}
try
{
_felix.waitForStop(FELIX_STOP_TIMEOUT);
}
catch (InterruptedException e)
{
// Ignore
}
_logger.info("Stopped plugin manager framework");
}
else
{
_logger.info("Plugin manager was started with an external BundleContext, " +
"skipping remaining shutdown tasks");
}
}
}
}