/*******************************************************************************
* Copyright (c) 2008, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware Inc. - initial contribution
*******************************************************************************/
package org.eclipse.virgo.kernel.osgi.region;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.launch.Framework;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.framework.CompositeBundle;
import org.osgi.service.framework.CompositeBundleFactory;
import org.osgi.service.framework.SurrogateBundle;
import org.eclipse.virgo.osgi.launcher.parser.ArgumentParser;
import org.eclipse.virgo.osgi.launcher.parser.BundleEntry;
import org.eclipse.virgo.medic.eventlog.EventLogger;
import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
import org.eclipse.virgo.kernel.core.Shutdown;
import org.eclipse.virgo.kernel.osgi.framework.OsgiFrameworkLogEvents;
/**
* Creates and manages the user {@link Region regions}.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
*
* Threadsafe.
*
*/
@SuppressWarnings("deprecation")
final class RegionManager {
private static final String USER_REGION_CONFIGURATION_PID = "org.eclipse.virgo.kernel.userregion";
private static final String PLUGGABLE_CLASS_LOADING_HOOK_CLASS_NAME = "org.eclipse.virgo.osgi.extensions.equinox.hooks.PluggableClassLoadingHook";
private static final String USER_REGION_BASE_BUNDLES_PROPERTY = "baseBundles";
private static final String USER_REGION_PACKAGE_IMPORTS_PROPERTY = "packageImports";
private static final String USER_REGION_SERVICE_IMPORTS_PROPERTY = "serviceImports";
private static final String USER_REGION_SERVICE_EXPORTS_PROPERTY = "serviceExports";
private static final String USER_REGION_PROPERTIES_PROPERTY = "inheritedFrameworkProperties";
private static final String REGION_USER = "org.eclipse.virgo.region.user";
private static final String EVENT_REGION_STARTING = "org/eclipse/virgo/kernel/region/STARTING";
private static final Object EVENT_PROPERTY_REGION_BUNDLECONTEXT = "region.bundleContext";
private final ServiceRegistrationTracker tracker = new ServiceRegistrationTracker();
private final BundleContext bundleContext;
private final CompositeBundleFactory compositeBundleFactory;
private final ArgumentParser parser = new ArgumentParser();
private final EventAdmin eventAdmin;
private final ServiceFactory eventLoggerServiceFactory;
private volatile Framework childFramework;
private Dictionary<String, String> userRegionProperties;
private String regionBundles;
private String regionImports;
private String regionServiceImports;
private String regionServiceExports;
private String regionInheritedProperties;
public RegionManager(BundleContext bundleContext, CompositeBundleFactory compositeBundleFactory, EventAdmin eventAdmin,
ServiceFactory eventLoggerServiceFactory, ConfigurationAdmin configAdmin, EventLogger eventLogger, Shutdown shutdown) {
this.bundleContext = bundleContext;
this.compositeBundleFactory = compositeBundleFactory;
this.eventAdmin = eventAdmin;
this.eventLoggerServiceFactory = eventLoggerServiceFactory;
getRegionConfiguration(configAdmin, eventLogger, shutdown);
}
private void getRegionConfiguration(ConfigurationAdmin configAdmin, EventLogger eventLogger, Shutdown shutdown) {
try {
Configuration config = configAdmin.getConfiguration(USER_REGION_CONFIGURATION_PID);
@SuppressWarnings("unchecked")
Dictionary<String, String> properties = (Dictionary<String, String>) config.getProperties();
if (properties != null) {
this.userRegionProperties = properties;
this.regionBundles = properties.get(USER_REGION_BASE_BUNDLES_PROPERTY);
this.regionImports = properties.get(USER_REGION_PACKAGE_IMPORTS_PROPERTY);
this.regionServiceImports = properties.get(USER_REGION_SERVICE_IMPORTS_PROPERTY);
this.regionServiceExports = properties.get(USER_REGION_SERVICE_EXPORTS_PROPERTY);
this.regionInheritedProperties = properties.get(USER_REGION_PROPERTIES_PROPERTY);
} else {
eventLogger.log(OsgiFrameworkLogEvents.USER_REGION_CONFIGURATION_UNAVAILABLE);
shutdown.immediateShutdown();
}
} catch (Exception e) {
eventLogger.log(OsgiFrameworkLogEvents.USER_REGION_CONFIGURATION_UNAVAILABLE, e);
shutdown.immediateShutdown();
}
}
public void start() throws BundleException {
createAndPublishUserRegion();
}
private void createAndPublishUserRegion() throws BundleException {
CompositeBundle compositeBundle = this.compositeBundleFactory.installCompositeBundle(createChildFrameworkConfig(), REGION_USER,
createCompositeBundleManifest());
childFramework = compositeBundle.getCompositeFramework();
compositeBundle.start();
childFramework.start();
SurrogateBundle surrogateBundle = compositeBundle.getSurrogateBundle();
BundleContext surrogateBundleContext = surrogateBundle.getBundleContext();
Properties properties = new Properties();
properties.put(EVENT_PROPERTY_REGION_BUNDLECONTEXT, surrogateBundleContext);
this.eventAdmin.sendEvent(new Event(EVENT_REGION_STARTING, properties));
setUserRegionBundleParentClassLoader(surrogateBundleContext);
registerEventLoggerServiceFactory(surrogateBundleContext);
initialiseUserRegionBundles(surrogateBundleContext);
registerRegionService(new ImmutableRegion(REGION_USER, surrogateBundleContext));
publishUserRegionsBundleContext(surrogateBundleContext);
}
private void registerEventLoggerServiceFactory(BundleContext surrogateBundleContext) {
surrogateBundleContext.registerService(EventLogger.class.getName(), this.eventLoggerServiceFactory, null);
}
/**
* @param surrogateBundleContext
*/
private void publishUserRegionsBundleContext(BundleContext surrogateBundleContext) {
Properties properties = new Properties();
properties.put("org.eclipse.virgo.kernel.regionContext", "true");
this.bundleContext.registerService(BundleContext.class.getName(), surrogateBundleContext, properties);
}
private void setUserRegionBundleParentClassLoader(BundleContext surrogateBundleContext) throws BundleException {
ClassLoader surrogateClassLoader = surrogateBundleContext.getClass().getClassLoader();
try {
setUserRegionHookBundleParentClassLoader(surrogateClassLoader);
} catch (Exception e) {
throw new BundleException("Error setting user region hook bundle parent class loader", e);
}
}
private void setUserRegionHookBundleParentClassLoader(ClassLoader parentClassLoader) throws ClassNotFoundException, SecurityException,
NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Class<?> pluggableClassLoadingHookClass = parentClassLoader.loadClass(PLUGGABLE_CLASS_LOADING_HOOK_CLASS_NAME);
Object pluggableClassLoadingHookInstance = invokeGetInstance(pluggableClassLoadingHookClass);
invokeSetParent(pluggableClassLoadingHookClass, pluggableClassLoadingHookInstance, parentClassLoader);
}
private Object invokeGetInstance(Class<?> pluggableClassLoadingHookClass) throws SecurityException, NoSuchMethodException,
IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Class<?>[] parmTypes = {};
Method getInstanceMethod = pluggableClassLoadingHookClass.getDeclaredMethod("getInstance", parmTypes);
Object[] args = {};
return getInstanceMethod.invoke(null, args);
}
private void invokeSetParent(Class<?> pluggableClassLoadingHookClass, Object pluggableClassLoadingHookInstance, ClassLoader parentClassLoader)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
Class<?>[] parmTypes = { ClassLoader.class };
Method setParentMethod = pluggableClassLoadingHookClass.getDeclaredMethod("setBundleClassLoaderParent", parmTypes);
Object[] args = { parentClassLoader };
setParentMethod.invoke(pluggableClassLoadingHookInstance, args);
}
private Map<String, String> createChildFrameworkConfig() {
HashMap<String, String> frameworkConfig = new HashMap<String, String>();
setUserConfiguredUserRegionProperties(frameworkConfig);
if (this.regionInheritedProperties != null) {
String[] inheritedProperties = this.regionInheritedProperties.split(",");
for (String property : inheritedProperties) {
propagatePropertyToUserRegion(frameworkConfig, property);
}
}
String userRegionImportsProperty = this.bundleContext.getProperty(USER_REGION_PROPERTIES_PROPERTY);
if (userRegionImportsProperty != null) {
for (String property : userRegionImportsProperty.split(",")) {
propagatePropertyToUserRegion(frameworkConfig, property);
}
}
return frameworkConfig;
}
private void setUserConfiguredUserRegionProperties(HashMap<String, String> frameworkConfig) {
if (this.userRegionProperties != null) {
Enumeration<String> keys = this.userRegionProperties.keys();
while (keys.hasMoreElements()) {
String propertyName = keys.nextElement();
String propertyValue = this.userRegionProperties.get(propertyName);
frameworkConfig.put(propertyName, propertyValue);
}
}
}
private void propagatePropertyToUserRegion(HashMap<String, String> frameworkConfig, String propertyName) {
String propertyValue = this.bundleContext.getProperty(propertyName);
if (propertyValue != null) {
frameworkConfig.put(propertyName, propertyValue);
}
}
private Map<String, String> createCompositeBundleManifest() {
Map<String, String> compositeManifest = new HashMap<String, String>();
compositeManifest.put(Constants.BUNDLE_SYMBOLICNAME, REGION_USER);
String userRegionImportsProperty = this.regionImports != null ? this.regionImports
: this.bundleContext.getProperty(USER_REGION_PACKAGE_IMPORTS_PROPERTY);
if (userRegionImportsProperty != null) {
String expandedUserRegionImportsProperty = PackageImportWildcardExpander.expandPackageImportsWildcards(userRegionImportsProperty,
this.bundleContext);
compositeManifest.put(Constants.IMPORT_PACKAGE, expandedUserRegionImportsProperty);
}
configureServiceImportFilter(compositeManifest);
configureServiceExportFilter(compositeManifest);
return compositeManifest;
}
private void configureServiceImportFilter(Map<String, String> compositeManifest) {
String[] serviceImports = splitServices(this.regionServiceImports);
if (serviceImports != null) {
compositeManifest.put(CompositeBundleFactory.COMPOSITE_SERVICE_FILTER_IMPORT, createObjectClassesServiceFilter(serviceImports));
}
}
private void configureServiceExportFilter(Map<String, String> compositeManifest) {
String[] serviceExports = splitServices(this.regionServiceExports);
if (serviceExports != null) {
compositeManifest.put(CompositeBundleFactory.COMPOSITE_SERVICE_FILTER_EXPORT, createObjectClassesServiceFilter(serviceExports));
}
}
private String[] splitServices(String serviceString) {
String[] services = null;
if (serviceString != null) {
services = serviceString.split(",");
}
return services;
}
private String createObjectClassesServiceFilter(String[] serviceClassNames) {
StringBuffer importFilter = new StringBuffer();
importFilter.append("(|");
for (String className : serviceClassNames) {
importFilter.append(createObjectClassFilter(className));
}
importFilter.append(")");
return importFilter.toString();
}
private String createObjectClassFilter(String className) {
return "(objectClass=" + className + ")";
}
private void initialiseUserRegionBundles(BundleContext surrogateBundleContext) throws BundleException {
String userRegionBundlesProperty = this.regionBundles != null ? this.regionBundles
: this.bundleContext.getProperty(USER_REGION_BASE_BUNDLES_PROPERTY);
if (userRegionBundlesProperty != null) {
List<Bundle> bundlesToStart = new ArrayList<Bundle>();
for (BundleEntry entry : this.parser.parseBundleEntries(userRegionBundlesProperty)) {
Bundle bundle = surrogateBundleContext.installBundle(entry.getURI().toString());
if (entry.isAutoStart()) {
bundlesToStart.add(bundle);
}
}
for (Bundle bundle : bundlesToStart) {
try {
bundle.start();
} catch (BundleException e) {
throw new BundleException("Failed to start bundle " + bundle.getSymbolicName() + " " + bundle.getVersion(), e);
}
}
}
}
private void registerRegionService(Region region) {
Properties props = new Properties();
props.setProperty("org.eclipse.virgo.kernel.region.name", region.getName());
this.tracker.track(this.bundleContext.registerService(Region.class.getName(), region, props));
}
public void stop() {
this.tracker.unregisterAll();
}
private static class ImmutableRegion implements Region {
private final String name;
private final BundleContext bundleContext;
public ImmutableRegion(String name, BundleContext bundleContext) {
this.name = name;
this.bundleContext = bundleContext;
}
public String getName() {
return name;
}
public BundleContext getBundleContext() {
return this.bundleContext;
}
}
}