/*******************************************************************************
* Copyright (c) 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Jan S. Rellermeyer, IBM Research - initial API and implementation
*******************************************************************************/
package org.eclipse.concierge;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.concierge.BundleImpl.Revision;
import org.eclipse.concierge.BundleImpl.Revision.WovenClassImpl;
import org.eclipse.concierge.ConciergeCollections.MultiMap;
import org.eclipse.concierge.ConciergeCollections.ParseResult;
import org.eclipse.concierge.Resources.BundleCapabilityImpl;
import org.eclipse.concierge.Resources.ConciergeBundleWiring;
import org.eclipse.concierge.Resources.HostedBundleCapability;
import org.eclipse.concierge.compat.LegacyBundleProcessing;
import org.eclipse.concierge.compat.service.XargsFileLauncher;
import org.eclipse.concierge.service.log.LogServiceImpl;
import org.osgi.framework.AllServiceListener;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.UnfilteredServiceListener;
import org.osgi.framework.Version;
import org.osgi.framework.hooks.bundle.CollisionHook;
import org.osgi.framework.hooks.bundle.EventHook;
import org.osgi.framework.hooks.resolver.ResolverHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.hooks.service.EventListenerHook;
import org.osgi.framework.hooks.service.FindHook;
import org.osgi.framework.hooks.service.ListenerHook;
import org.osgi.framework.hooks.service.ListenerHook.ListenerInfo;
import org.osgi.framework.hooks.weaving.WeavingException;
import org.osgi.framework.hooks.weaving.WeavingHook;
import org.osgi.framework.launch.Framework;
import org.osgi.framework.namespace.AbstractWiringNamespace;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.framework.startlevel.BundleStartLevel;
import org.osgi.framework.startlevel.FrameworkStartLevel;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.framework.wiring.FrameworkWiring;
import org.osgi.resource.Capability;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
import org.osgi.resource.Resource;
import org.osgi.resource.Wire;
import org.osgi.resource.Wiring;
import org.osgi.service.log.LogReaderService;
import org.osgi.service.log.LogService;
import org.osgi.service.resolver.HostedCapability;
import org.osgi.service.resolver.ResolutionException;
import org.osgi.service.resolver.ResolveContext;
import org.osgi.service.resolver.Resolver;
/**
* The core class of the Concierge OSGi framework. Implements the system bundle,
* maintains the central bundle and service registry.
*
* @author Jan S. Rellermeyer
* @author Jochen Hiller - added argument parsing
*/
public final class Concierge extends AbstractBundle implements Framework,
BundleRevision, FrameworkWiring, FrameworkStartLevel, BundleActivator {
// deprecated core framework constants.
@SuppressWarnings("deprecation")
private static final String FRAMEWORK_EXECUTIONENVIRONMENT = Constants.FRAMEWORK_EXECUTIONENVIRONMENT;
@SuppressWarnings("deprecation")
private static final String BUNDLE_REQUIREDEXECUTIONENVIRONMENT = Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT;
private static final String BUNDLE_SYMBOLIC_NAME = "org.eclipse.concierge";
/**
* Version displayed upon startup and returned by System Bundle
*/
private static final String FRAMEWORK_VERSION = "5.1.0.qualifier";
@SuppressWarnings("deprecation")
private static Class<?> SERVICE_EVENT_HOOK_CLASS = org.osgi.framework.hooks.service.EventHook.class;
// URLStreamHandlerFactory
/**
* This static variable contains a URL stream handler factory, which will be
* dispatched for the current running Concierge instance.
*/
private static ConciergeURLStreamHandlerFactory conciergeURLStreamHandlerFactory = new ConciergeURLStreamHandlerFactory();
// the runtime args
public static boolean SUPPORTS_EXTENSIONS;
/**
* framework basedir.
*/
private String BASEDIR;
/**
* bundle location.
*/
public String BUNDLE_LOCATION = "file:.";
private int BEGINNING_STARTLEVEL;
/**
* the location where the storage resides.
*/
String STORAGE_LOCATION;
/**
* classloader buffer size.
*/
static int CLASSLOADER_BUFFER_SIZE;
/**
* logging enabled.
*/
boolean LOG_ENABLED;
/**
* log service.
*/
LogService logger;
/**
* log buffer size.
*/
int LOG_BUFFER_SIZE;
/**
* log quiet ? (= no logging to System.out)
*/
boolean LOG_QUIET;
/**
* always decompress the bundles, great for testing
*
* FIXME: combine with decompress embedded into a single property, e.g.,
* with values: NEVER, EMBEDDED_JARS, ALWAYS
*/
boolean ALWAYS_DECOMPRESS;
/**
* decompress bundles with embedded jars.
*/
boolean DECOMPRESS_EMBEDDED;
/**
* log level.
*/
int LOG_LEVEL;
/**
* security.
*/
boolean SECURITY_ENABLED;
/**
* debug outputs from bundles ?
*/
boolean DEBUG_BUNDLES;
/**
* debug outputs from packages ?
*/
boolean DEBUG_PACKAGES;
/**
* debug outputs from class loading ?
*/
boolean DEBUG_CLASSLOADING;
/**
* debug outputs from services ?
*/
boolean DEBUG_SERVICES;
/**
* debug outputs from services ?
*/
boolean DEBUG_RESOLVER;
/**
* the profile.
*/
private String PROFILE;
private final String[] bootdelegationAbs;
private final String[] bootdelegationPrefix;
private String[] libraryExtensions;
private String execPermission;
private Pattern execPermissionPattern;
private static final int COLLISION_POLICY_SINGLE = -1;
private static final int COLLISION_POLICY_NONE = 0;
private static final int COLLISION_POLICY_MULTIPLE = 1;
private static final int COLLISION_POLICY_MANAGED = 2;
int collisionPolicy;
private Hashtable<String, String> headers;
private final Version version = new Version("1.5.0");
static final HashSet<String> SUPPORTED_EE = new HashSet<String>();
static Comparator<AbstractBundle> VERSION_COMPARATOR = new Comparator<AbstractBundle>() {
public int compare(final AbstractBundle b1, final AbstractBundle b2) {
return b2.getVersion().compareTo(b1.getVersion());
}
};
protected final static Pattern FILTER_ASSERT_MATCHER = Pattern
.compile("\\(([^&\\!|=<>~\\(\\)]*)[=|<=|>=|~=]");
// registry data structures
/**
* the bundles.
*/
List<AbstractBundle> bundles = new ArrayList<AbstractBundle>(2);
/**
* bundleID -> bundle.
*/
Map<Long, AbstractBundle> bundleID_bundles = new HashMap<Long, AbstractBundle>(
2);
/**
* location -> bundle.
*/
Map<String, AbstractBundle> location_bundles = Collections
.synchronizedMap(new HashMap<String, AbstractBundle>(2));
/**
* symbolicName -> List of bundles
*/
MultiMap<String, AbstractBundle> symbolicName_bundles = new MultiMap<String, AbstractBundle>(
2);
/**
* class name string -> service reference.
*/
final MultiMap<String, ServiceReference<?>> serviceRegistry = new MultiMap<String, ServiceReference<?>>(
3);
/**
* class name string -> service reference.
*/
final MultiMap<String, ServiceReference<?>> microServices = new MultiMap<String, ServiceReference<?>>(
3);
/**
* bundle listeners.
*/
protected final List<BundleListener> bundleListeners = new ArrayList<BundleListener>(
1);
/**
* synchronous bundle listeners.
*/
protected final List<SynchronousBundleListener> syncBundleListeners = new ArrayList<SynchronousBundleListener>(
1);
protected final MultiMap<BundleContext, BundleListener> bundleListenerMap = new MultiMap<BundleContext, BundleListener>();
/**
* service listeners.
*/
protected final List<ServiceListenerEntry> serviceListeners = new ArrayList<ServiceListenerEntry>(
1);
/**
* Map of unattached fragments in the system. HostName => List of fragments
*/
private final MultiMap<String, Revision> fragmentIndex = new MultiMap<String, Revision>(
1);
private final ArrayList<BundleImpl> extensionBundles = new ArrayList<BundleImpl>(
0);
/**
* framework listeners.
*/
protected final List<FrameworkListener> frameworkListeners = new ArrayList<FrameworkListener>(
1);
CapabilityRegistry capabilityRegistry = new CapabilityRegistry();
Map<Resource, Wiring> wirings = new HashMap<Resource, Wiring>();
// the fields
/**
* next bundle ID.
*/
private long nextBundleID = 1;
/**
* the initial startlevel for installed bundles.
*/
int initStartlevel = 1;
/**
* restart ?
*/
public boolean restart = false;
// system bundle
/**
* the symbolicName of the system bundle
*/
public static final String FRAMEWORK_SYMBOLIC_NAME = "org.eclipse.concierge";
private final List<BundleCapability> systemBundleCapabilities = new ArrayList<BundleCapability>();
private static Properties defaultProperties;
String osname;
Version osversion;
String language;
String processor;
final Properties properties;
/*
* hook support
*/
// @formatter:off
// bundle hooks
private final List<ServiceReferenceImpl<CollisionHook>> bundleCollisionHooks = new ArrayList<ServiceReferenceImpl<CollisionHook>>(0);
private final List<ServiceReferenceImpl<org.osgi.framework.hooks.bundle.EventHook>> bundleEventHooks = new ArrayList<ServiceReferenceImpl<org.osgi.framework.hooks.bundle.EventHook>>(0);
protected final List<ServiceReferenceImpl<org.osgi.framework.hooks.bundle.FindHook>> bundleFindHooks = new ArrayList<ServiceReferenceImpl<org.osgi.framework.hooks.bundle.FindHook>>(0);
// resolver hook
protected List<ServiceReferenceImpl<ResolverHookFactory>> resolverHookFactories = new ArrayList<ServiceReferenceImpl<ResolverHookFactory>>(0);
// service hooks
@SuppressWarnings("deprecation")
protected List<ServiceReferenceImpl<org.osgi.framework.hooks.service.EventHook>> serviceEventHooks = new ArrayList<ServiceReferenceImpl<org.osgi.framework.hooks.service.EventHook>>(0);
protected List<ServiceReferenceImpl<ListenerHook>> serviceListenerHooks = new ArrayList<ServiceReferenceImpl<ListenerHook>>(0);
protected List<ServiceReferenceImpl<EventListenerHook>> serviceEventListenerHooks = new ArrayList<ServiceReferenceImpl<EventListenerHook>>(0);
protected List<ServiceReferenceImpl<FindHook>> serviceFindHooks = new ArrayList<ServiceReferenceImpl<FindHook>>(0);
// weaving hooks
private final List<ServiceReferenceImpl<WeavingHook>> weavingHooks = new ArrayList<ServiceReferenceImpl<WeavingHook>>(0);
// "hooks registry"
protected final HashMap<String, List<?>> hooks = new HashMap<String, List<?>>();
// @formatter:on
static final Dictionary<String, Object> props2Dict(final Properties props) {
final Hashtable<String, Object> table = new Hashtable<String, Object>();
for (final Object key : Collections.list(props.propertyNames())) {
final String keyStr = (String) key;
table.put(keyStr, props.getProperty(keyStr));
}
return table;
}
private FrameworkEvent stopEvent = new FrameworkEvent(
FrameworkEvent.STOPPED, this, null);
private final BundleStartLevel systemBundleStartLevel = new SystemBundleStartLevel();
private final ResolverImpl resolver = new ResolverImpl();
private final Method addURL;
final ClassLoader parentClassLoader;
final ClassLoader systemBundleClassLoader;
protected static final Comparator<? super Capability> BUNDLE_VERSION = new Comparator<Capability>() {
public int compare(final Capability cap1, final Capability cap2) {
final Version cap1Version = (Version) cap1.getAttributes().get(
AbstractWiringNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
final Version cap2Version = (Version) cap2.getAttributes().get(
AbstractWiringNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE);
return cap2Version.compareTo(cap1Version);
}
};
protected static final Comparator<? super Capability> EXPORT_ORDER = new Comparator<Capability>() {
// reverts the order so that we can
// retrieve the 0st item to get the best
// match
public int compare(final Capability c1, final Capability c2) {
if (!(c1 instanceof BundleCapability
&& c2 instanceof BundleCapability)) {
return 0;
}
final BundleCapability cap1 = (BundleCapability) c1;
final BundleCapability cap2 = (BundleCapability) c2;
final int cap1Resolved = cap1.getResource().getWiring() == null ? 0
: 1;
final int cap2Resolved = cap2.getResource().getWiring() == null ? 0
: 1;
int score = cap2Resolved - cap1Resolved;
if (score != 0) {
return score;
}
final Version cap1Version = (Version) cap1.getAttributes()
.get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
final Version cap2Version = (Version) cap2.getAttributes()
.get(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
score = cap2Version.compareTo(cap1Version);
if (score != 0) {
return score;
}
final long cap1BundleId = cap1.getRevision().getBundle()
.getBundleId();
final long cap2BundleId = cap2.getRevision().getBundle()
.getBundleId();
return (int) (cap1BundleId - cap2BundleId);
}
};
public static final String DIR_INTERNAL = "x-int";
/** Return code from main when OK. */
private static final int MAIN_RC_OK = 0;
/** Return code from main when printing usage message. */
private static final int MAIN_RC_USAGE = 1;
private boolean firstInit = true;
/**
* Main method to start Concierge. This class will delegate that to an
* internal method, which will create framework instance, but will not wait
* until stopped. This allows better testability.
*/
public static void main(final String[] args) throws Exception {
final Concierge framework = doMain(args);
if (framework != null) {
// wait until framework stopped
framework.waitForStop(0);
// exit with OK
System.exit(MAIN_RC_OK);
} else {
// exit with usage message
System.exit(MAIN_RC_USAGE);
}
}
/**
* Processing of command line arguments. It will create a framework instance
* based on given arguments.
*
* @param args
* command line arguments.
* @throws Exception
*/
public static Concierge doMain(final String[] args) throws Exception {
// TODO: temporary solution to use xargs file launcher for argument
// processing
final XargsFileLauncher xargsLauncher = new XargsFileLauncher(System.err);
String xargsFile = null;
final StringBuffer argsBuf = new StringBuffer();
for (int i = 0; args != null && i < args.length; i++) {
if ("-help".equalsIgnoreCase(args[i])) {
// if -help show usage message
System.err.println(""
+ "Concierge usage: org.eclipse.concierge.Concierge {arguments}\n"
+ " {file.xargs} "
+ "Loads xargs file, must end with .xargs\n"
+ " {-install|-start|-istart} {bundle-jar-file} "
+ "Install and start one bundle (can be used multiple times, in specified order)\n"
+ " {-all} {directory} "
+ "Install and start all bundles from specified directory\n"
+ " {-Dprop=value} "
+ "Specify one or more props just for Concierge (can be used multiple times)\n"
+ "Sample: org.eclipse.concierge.Concierge -Dorg.osgi.framework.bootdelegation=sun.*,javax.* -istart mybundle.jar\n");
// TODO allow -all with directory location
return null;
} else if (args[i].endsWith(".xargs")) {
// if arg is name of an xargs file: use this, stop further
// processing
xargsFile = args[i];
break;
} else {
argsBuf.append(args[i]);
if (args[i].startsWith("-D")) {
argsBuf.append('\n');
} else if (args[i].equalsIgnoreCase("-profile")
|| args[i].equalsIgnoreCase("-install")
|| args[i].equalsIgnoreCase("-istart")
|| args[i].equalsIgnoreCase("-start")
|| args[i].equalsIgnoreCase("-all")) {
// append next argument to same line
if (i - 1 < args.length) {
i++;
argsBuf.append(' ');
argsBuf.append(args[i]);
argsBuf.append('\n');
}
}
}
}
// no arguments? Try to use init.xargs or other file
if (xargsFile == null && argsBuf.length() == 0) {
xargsFile = System.getProperty("org.eclipse.concierge.init.xargs");
if (xargsFile == null) {
xargsFile = "init.xargs";
}
}
// now start framework with given arguments
final Concierge fw;
if (xargsFile != null) {
// take args from file
final File xargs = new File(xargsFile);
if (xargs.exists()) {
fw = xargsLauncher.processXargsFile(xargs);
} else {
System.err.println("Concierge: xargs file '" + xargs.toString()
+ "' not found, starting without arguments");
fw = (Concierge) new Factory().newFramework(null);
fw.init();
fw.start();
}
} else {
// some arguments have been defined
InputStream inputStream = new ByteArrayInputStream(
argsBuf.toString().getBytes(Charset.forName("UTF-8")));
// TODO support really props as command line args?
// we have to preserve the properties for later variable and
// wildcard replacement
final Map<String, String> passedProperties = xargsLauncher
.getPropertiesFromXargsInputStream(inputStream);
// now process again for install/start options with given properties
inputStream = new ByteArrayInputStream(
argsBuf.toString().getBytes(Charset.forName("UTF-8")));
fw = xargsLauncher.processXargsInputStream(passedProperties,
inputStream);
}
return fw;
}
Concierge(final Map<String, String> passedProperties) {
hooks.put(CollisionHook.class.getName(), bundleCollisionHooks);
hooks.put(org.osgi.framework.hooks.bundle.FindHook.class.getName(),
bundleFindHooks);
hooks.put(EventHook.class.getName(), bundleEventHooks);
hooks.put(ResolverHookFactory.class.getName(), resolverHookFactories);
hooks.put(SERVICE_EVENT_HOOK_CLASS.getName(), serviceEventHooks);
hooks.put(EventListenerHook.class.getName(), serviceEventListenerHooks);
hooks.put(FindHook.class.getName(), serviceFindHooks);
hooks.put(ListenerHook.class.getName(), serviceListenerHooks);
hooks.put(WeavingHook.class.getName(), weavingHooks);
defaultProperties = new Properties(System.getProperties());
defaultProperties.setProperty(Constants.FRAMEWORK_BOOTDELEGATION,
"java.*, sun.*, com.sun.*");
defaultProperties.setProperty(Constants.FRAMEWORK_BUNDLE_PARENT,
Constants.FRAMEWORK_BUNDLE_PARENT_BOOT);
defaultProperties.setProperty(Constants.FRAMEWORK_BEGINNING_STARTLEVEL,
"1");
final String feeStr = defaultProperties
.getProperty(FRAMEWORK_EXECUTIONENVIRONMENT);
final StringBuffer myEEs = new StringBuffer();
final StringBuffer seVersionList = new StringBuffer();
final StringBuffer compact1VersionList = new StringBuffer();
final StringBuffer compact2VersionList = new StringBuffer();
final StringBuffer compact3VersionList = new StringBuffer();
final StringBuffer minVersionList = new StringBuffer();
final int minor;
int parsed = 0;
try {
parsed = Integer.parseInt(System
.getProperty("java.specification.version").substring(2));
} catch (final NumberFormatException nfe) {
nfe.printStackTrace();
} finally {
minor = parsed;
}
if (System.getProperty("java.specification.name")
.equals("J2ME Foundation Specification")) {
switch (minor) {
case 1:
myEEs.append("CDC-1.1/Foundation-1.1,");
case 0:
myEEs.append("CDC-1.0/Foundation-1.0");
}
} else {
switch (minor) {
case 8:
myEEs.append("J2SE-1.8,");
myEEs.append("JavaSE-1.8,");
// also add the valid compact profiles
try {
// Figure out the profile by loading some classes from the profile
// Is there any other way to discover the profile of the JRE?
myEEs.append("JavaSE-1.8/compact1,");
compact1VersionList.append("1.8,");
this.getClass().getClassLoader().loadClass("org.w3c.dom.Document");
myEEs.append("JavaSE-1.8/compact2,");
compact2VersionList.append("1.8,");
this.getClass().getClassLoader().loadClass("javax.management.Descriptor");
myEEs.append("JavaSE-1.8/compact3,");
compact3VersionList.append("1.8,");
this.getClass().getClassLoader().loadClass("javax.imageio.ImageIO");
} catch(ClassNotFoundException e){
}
seVersionList.append("1.8,");
case 7:
myEEs.append("J2SE-1.7,");
myEEs.append("JavaSE-1.7,");
seVersionList.append("1.7,");
case 6:
myEEs.append("J2SE-1.6,");
myEEs.append("JavaSE-1.6,");
seVersionList.append("1.6,");
case 5:
myEEs.append("J2SE-1.5,");
seVersionList.append("1.5,");
case 4:
myEEs.append("J2SE-1.4,");
myEEs.append("OSGi/Minimum-1.1,");
seVersionList.append("1.4,");
minVersionList.append("1.2,1.1,");
case 3:
myEEs.append("J2SE-1.3,");
seVersionList.append("1.3,");
case 2:
myEEs.append("J2SE-1.2,");
myEEs.append("OSGi/Minimum-1.0,");
seVersionList.append("1.2,");
minVersionList.append("1.0");
case 1:
myEEs.append("JRE-1.1");
seVersionList.append("1.1");
}
}
defaultProperties.setProperty(FRAMEWORK_EXECUTIONENVIRONMENT,
myEEs.toString());
// load micro-services
try {
final InputStream in = getClass().getClassLoader()
.getResourceAsStream("META-INF/micro-services");
final BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
try {
final String[] tokens = Utils.splitString(line, ' ');
final Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(Constants.VERSION_ATTRIBUTE,
Version.parseVersion(tokens[2]));
final Class<?> cls = Class.forName(tokens[1]);
final Object service = cls.newInstance();
final ServiceReference<?> ref = new ServiceReferenceImpl<Object>(
this, this, service, props,
new String[] { tokens[0] });
microServices.insert(tokens[0], ref);
} catch (final Exception e) {
e.printStackTrace();
}
}
serviceRegistry.insertMap(microServices);
} catch (final IOException ioe) {
ioe.printStackTrace();
}
// populate osgi.ee namespace
try {
if (seVersionList.length() > 0) {
final BundleCapabilityImpl eeCap = new BundleCapabilityImpl(
this,
"osgi.ee; osgi.ee=\"JavaSE\"; version:List<Version>=\""
+ seVersionList.toString() + "\"");
systemBundleCapabilities.add(eeCap);
}
if(compact1VersionList.length() > 0){
final BundleCapabilityImpl eeCap = new BundleCapabilityImpl(
this,
"osgi.ee; osgi.ee=\"JavaSE/compact1\"; version:List<Version>=\""
+ compact1VersionList.toString() + "\"");
systemBundleCapabilities.add(eeCap);
}
if(compact2VersionList.length() > 0){
final BundleCapabilityImpl eeCap = new BundleCapabilityImpl(
this,
"osgi.ee; osgi.ee=\"JavaSE/compact2\"; version:List<Version>=\""
+ compact2VersionList.toString() + "\"");
systemBundleCapabilities.add(eeCap);
}
if(compact3VersionList.length() > 0){
final BundleCapabilityImpl eeCap = new BundleCapabilityImpl(
this,
"osgi.ee; osgi.ee=\"JavaSE/compact3\"; version:List<Version>=\""
+ compact3VersionList.toString() + "\"");
systemBundleCapabilities.add(eeCap);
}
if (minVersionList.length() > 0) {
final BundleCapabilityImpl eeCap = new BundleCapabilityImpl(
this,
"osgi.ee; osgi.ee=\"OSGi/Minimum\"; version:List<Version>=\""
+ minVersionList.toString() + "\"");
systemBundleCapabilities.add(eeCap);
}
} catch (final BundleException be) {
// too early for log service
be.printStackTrace();
}
if (feeStr != null) {
final LegacyBundleProcessing proc = getService(
LegacyBundleProcessing.class,
LegacyBundleProcessing.VERSION_TWO);
if (proc == null) {
warning("Framework execution environment is ignored because no bundle manifest version 2 processor is available in this deployment");
} else {
final List<BundleCapability> feeCaps = proc
.translateToCapability(this,
FRAMEWORK_EXECUTIONENVIRONMENT, feeStr);
systemBundleCapabilities.addAll(feeCaps);
}
}
defaultProperties.setProperty(Constants.FRAMEWORK_SYSTEMPACKAGES,
"org.osgi.framework;version=1.7,org.osgi.framework.hooks.bundle;version=1.1,org.osgi.framework.hooks.resolver;version=1.0,org.osgi.framework.hooks.service;version=1.1,org.osgi.framework.hooks.weaving;version=1.0,org.osgi.framework.launch;version=1.1,org.osgi.framework.namespace;version=1.0,org.osgi.framework.startlevel;version=1.0,org.osgi.framework.wiring;version=1.1,org.osgi.resource;version=1.0,org.osgi.service.log;version=1.3,org.osgi.service.packageadmin;version=1.2,org.osgi.service.startlevel;version=1.1,org.osgi.service.url;version=1.0,org.osgi.service.resolver;version=1.0,org.osgi.util.tracker;version=1.5.1,META-INF.services");
Object obj;
defaultProperties.put(Constants.FRAMEWORK_OS_NAME,
(obj = System.getProperty("os.name")) != null ? obj
: "undefined");
// Normalize to framework.processor according to OSGi R5 spec table 4.4
if ("Mac OS X".equals(System.getProperty("os.name"))) {
defaultProperties.put(Constants.FRAMEWORK_OS_NAME, "MacOSX");
} else if ("Mac OS".equals(System.getProperty("os.name"))) {
defaultProperties.put(Constants.FRAMEWORK_OS_NAME, "MacOS");
}
defaultProperties.put(Constants.FRAMEWORK_OS_VERSION,
(obj = System.getProperty("os.version")) != null ? obj
: "undefined");
defaultProperties.put(Constants.FRAMEWORK_PROCESSOR,
(obj = System.getProperty("os.arch")) != null ? obj
: "undefined");
final String lang = java.util.Locale.getDefault().getLanguage();
defaultProperties.setProperty(Constants.FRAMEWORK_LANGUAGE,
lang != null ? lang : "en");
defaultProperties.setProperty(Constants.FRAMEWORK_BEGINNING_STARTLEVEL,
"3");
defaultProperties.setProperty(Constants.FRAMEWORK_STORAGE, "storage");
// properties
properties = new Properties(defaultProperties) {
private static final long serialVersionUID = -3319768973242656809L;
public String getProperty(final String key) {
final Object launchO = get(key);
final String launchS = launchO instanceof String
? (String) launchO : null;
if (launchS != null) {
return launchS;
}
final String system = System.getProperty(key);
return system == null ? defaults.getProperty(key) : system;
}
};
if (passedProperties != null) {
for (final Map.Entry<String, String> entry : passedProperties
.entrySet()) {
if (entry.getValue() != null) {
properties.put(entry.getKey(), entry.getValue());
}
}
}
// set parent classloader
systemBundleClassLoader = getClass().getClassLoader();
final String p = properties
.getProperty(Constants.FRAMEWORK_BUNDLE_PARENT);
if (Constants.FRAMEWORK_BUNDLE_PARENT_APP.equals(p)) {
parentClassLoader = ClassLoader.getSystemClassLoader();
} else if (Constants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK.equals(p)) {
parentClassLoader = systemBundleClassLoader;
} else if (Constants.FRAMEWORK_BUNDLE_PARENT_EXT.equals(p)) {
ClassLoader c = ClassLoader.getSystemClassLoader();
while (c.getParent() != null) {
c = c.getParent();
}
parentClassLoader = c;
} else {
parentClassLoader = new ClassLoader(Object.class.getClassLoader()) {
};
}
properties.setProperty(Constants.SUPPORTS_FRAMEWORK_EXTENSION,
Boolean.toString(false));
Method m = null;
if (getClass().getClassLoader() instanceof URLClassLoader) {
try {
m = URLClassLoader.class.getDeclaredMethod("addURL",
new Class[] { URL.class });
m.setAccessible(true);
properties.setProperty(Constants.SUPPORTS_FRAMEWORK_EXTENSION,
Boolean.toString(true));
SUPPORTS_EXTENSIONS = true;
} catch (final Exception e) {
logger.log(LogService.LOG_WARNING,
"Could not hijack classloader for framework extensions",
e);
}
}
addURL = m;
// apply constants
properties.setProperty(Constants.FRAMEWORK_VERSION, version.toString());
properties.setProperty(Constants.FRAMEWORK_VENDOR,
"Eclipse Foundation");
properties.setProperty(Constants.SUPPORTS_BOOTCLASSPATH_EXTENSION,
Boolean.toString(false));
properties.setProperty(Constants.SUPPORTS_FRAMEWORK_FRAGMENT,
Boolean.toString(true));
properties.setProperty(Constants.SUPPORTS_FRAMEWORK_REQUIREBUNDLE,
Boolean.toString(true));
// set instance properties
PROFILE = properties.getProperty("org.eclipse.concierge.profile",
"default");
BASEDIR = properties.getProperty("org.eclipse.concierge.basedir", ".");
BUNDLE_LOCATION = properties.getProperty("org.eclipse.concierge.jars",
"file:" + BASEDIR);
CLASSLOADER_BUFFER_SIZE = getProperty(
"org.eclipse.concierge.classloader.buffersize", 2048);
LOG_ENABLED = getProperty("org.eclipse.concierge.log.enabled", false);
LOG_QUIET = getProperty("org.eclipse.concierge.log.quiet", false);
LOG_BUFFER_SIZE = getProperty("org.eclipse.concierge.log.buffersize",
10);
LOG_LEVEL = getProperty("org.eclipse.concierge.log.level",
LogService.LOG_ERROR);
DEBUG_BUNDLES = getProperty("org.eclipse.concierge.debug.bundles",
false);
DEBUG_PACKAGES = getProperty("org.eclipse.concierge.debug.packages",
false);
DEBUG_SERVICES = getProperty("org.eclipse.concierge.debug.services",
false);
DEBUG_RESOLVER = getProperty("org.eclipse.concierge.debug.resolver",
false);
DEBUG_CLASSLOADING = getProperty(
"org.eclipse.concierge.debug.classloading", false);
if (getProperty("org.eclipse.concierge.debug", false)) {
System.out.println("SETTING ALL DEBUG FLAGS");
LOG_ENABLED = true;
LOG_LEVEL = LogService.LOG_DEBUG;
DEBUG_BUNDLES = true;
DEBUG_PACKAGES = true;
DEBUG_SERVICES = true;
DEBUG_CLASSLOADING = true;
DEBUG_RESOLVER = true;
LOG_LEVEL = 4;
}
ALWAYS_DECOMPRESS = getProperty(
"org.eclipse.concierge.alwaysDecompress", false);
DECOMPRESS_EMBEDDED = getProperty(
"org.eclipse.concierge.decompressEmbedded", true);
SECURITY_ENABLED = getProperty("org.eclipse.concierge.security.enabled",
false);
final String bsl = properties
.getProperty(Constants.FRAMEWORK_BEGINNING_STARTLEVEL);
try {
BEGINNING_STARTLEVEL = Integer.parseInt(bsl);
} catch (final NumberFormatException nfe) {
warning("Invalid initial startlevel " + bsl);
System.err.println(
"FALLING BACK TO DEFAULT BEGINNING STARTLEVEL (=1)");
BEGINNING_STARTLEVEL = 1;
}
// sort out the boot delegations
final String[] bds = Utils.splitString(
properties.getProperty(Constants.FRAMEWORK_BOOTDELEGATION),
',');
final ArrayList<String> bdsAbs = new ArrayList<String>();
final ArrayList<String> bdsRel = new ArrayList<String>();
int pos;
for (int i = 0; i < bds.length; i++) {
if ((pos = bds[i].indexOf('*')) < 0) {
bdsAbs.add(bds[i]);
} else {
if (pos < bds[i].length() - 1) {
throw new IllegalArgumentException(
"Framework bootdelegation " + bds[i]
+ " is not supported");
}
bdsRel.add(bds[i].substring(0, pos));
}
}
// remove the fast paths
bdsRel.remove("java.");
bootdelegationAbs = bdsAbs.toArray(new String[bdsAbs.size()]);
bootdelegationPrefix = bdsRel.toArray(new String[bdsRel.size()]);
// sanity checks
if (!LOG_ENABLED) {
if (DEBUG_BUNDLES || DEBUG_PACKAGES || DEBUG_SERVICES
|| DEBUG_CLASSLOADING) {
System.err.println("Logger disabled, ignoring debug flags.");
DEBUG_BUNDLES = false;
DEBUG_PACKAGES = false;
DEBUG_SERVICES = false;
DEBUG_CLASSLOADING = false;
}
}
if (System.getSecurityManager() == null) {
if (SECURITY_ENABLED) {
warning("No security manager set, ignoring security flag.");
SECURITY_ENABLED = false;
}
}
location = Constants.SYSTEM_BUNDLE_LOCATION;
state = Bundle.INSTALLED;
context = new BundleContextImpl(this);
domain = Concierge.class.getProtectionDomain();
headers = new Hashtable<String, String>(5);
}
/**
* get a boolean property.
*
* @param key
* the key.
* @param defaultVal
* the default.
* @return the value.
*/
private boolean getProperty(final String key, final boolean defaultVal) {
final String val = properties.getProperty(key);
return val != null ? Boolean.valueOf(val).booleanValue() : defaultVal;
}
/**
* get an int property.
*
* @param key
* the key.
* @param defaultVal
* the default.
* @return the value.
*/
private int getProperty(final String key, final int defaultVal) {
final String val = properties.getProperty(key);
return val != null ? Integer.parseInt(val) : defaultVal;
}
/**
* write a warning or throw an Exception
*
* @param message
* @throws BundleException
*/
private void warning(final String message) throws RuntimeException {
if (getProperty("org.eclipse.concierge.strictStartup", false)) {
throw new RuntimeException(message);
}
System.err.println("WARNING: " + message);
}
// Framework
/**
*
* @see org.osgi.framework.launch.Framework#init()
* @category Framework
*/
public void init() throws BundleException {
if (state == Bundle.ACTIVE || state == Bundle.STARTING
|| state == Bundle.STOPPING) {
return;
}
final StringTokenizer t = new StringTokenizer(
properties.getProperty(FRAMEWORK_EXECUTIONENVIRONMENT), ",");
while (t.hasMoreTokens()) {
SUPPORTED_EE.add(t.nextToken().trim());
}
// TODO: check if there is a security manager set and
// Constants.FRAMEWORK_SECURITY; is set
STORAGE_LOCATION = properties.getProperty(
"org.eclipse.concierge.storage",
properties.getProperty(Constants.FRAMEWORK_STORAGE,
BASEDIR + File.separatorChar + "storage"))
+ File.separatorChar + PROFILE + File.separatorChar;
// clean the storage if requested
final File storage = new File(STORAGE_LOCATION);
if (storage.exists()) {
if (firstInit && Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT
.equals(properties
.getProperty(Constants.FRAMEWORK_STORAGE_CLEAN))) {
deleteDirectory(storage);
} else {
restart = true;
}
}
if (!storage.exists() && !storage.mkdirs()) {
throw new BundleException(
"Could not create storage directory " + storage);
}
firstInit = false;
// set start level 0
startlevel = 0;
// enable event handling
// set the collision policy
final String bsnversion = properties
.getProperty(Constants.FRAMEWORK_BSNVERSION);
if (bsnversion == null) {
collisionPolicy = COLLISION_POLICY_NONE;
} else {
if (bsnversion.equals(Constants.FRAMEWORK_BSNVERSION_SINGLE)) {
collisionPolicy = COLLISION_POLICY_SINGLE;
} else if (bsnversion
.equals(Constants.FRAMEWORK_BSNVERSION_MULTIPLE)) {
collisionPolicy = COLLISION_POLICY_MULTIPLE;
} else
if (bsnversion.equals(Constants.FRAMEWORK_BSNVERSION_MANAGED)) {
collisionPolicy = COLLISION_POLICY_MANAGED;
}
}
// set the framework props
final String os = properties.getProperty(Constants.FRAMEWORK_OS_NAME);
osversion = new Version(sanitizeVersion(
properties.getProperty(Constants.FRAMEWORK_OS_VERSION)));
language = new Locale(
properties.getProperty(Constants.FRAMEWORK_LANGUAGE), "")
.getLanguage();
final String cpu = properties.getProperty(Constants.FRAMEWORK_PROCESSOR)
.intern();
if (os.toLowerCase().startsWith("win")) {
osname = "Windows";
} else {
osname = os;
}
if (cpu == "pentium" || cpu == "i386" || cpu == "i486" || cpu == "i586"
|| cpu == "i686") {
processor = "x86";
} else if (cpu == "amd64" || cpu == "em64t" || cpu == "x86_64"
|| cpu == "x86-64") {
processor = "x86-64";
} else {
processor = cpu;
}
// get the library extensions if set
final String libExtStr = properties
.getProperty(Constants.FRAMEWORK_LIBRARY_EXTENSIONS);
if (libExtStr != null) {
libraryExtensions = Utils.splitString(libExtStr, ',');
}
// set execpermission if set
execPermission = properties
.getProperty(Constants.FRAMEWORK_EXECPERMISSION);
if (execPermission != null) {
execPermissionPattern = Pattern.compile("\\$\\{"
+ properties.getProperty(
Constants.FRAMEWORK_COMMAND_ABSPATH, "abspath")
+ "\\}");
}
// create the system bundle
headers.put(Constants.BUNDLE_MANIFESTVERSION, "2");
headers.put(Constants.BUNDLE_NAME, BUNDLE_SYMBOLIC_NAME);
headers.put(Constants.BUNDLE_VERSION, FRAMEWORK_VERSION);
headers.put(BUNDLE_REQUIREDEXECUTIONENVIRONMENT,
"OSGi/Minimum-1.1, CDC-1.1/Foundation-1.1");
headers.put(Constants.BUNDLE_SYMBOLICNAME, "org.eclipse.concierge");
final String extraPkgs = properties
.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
final String sysPkgs = extraPkgs == null
? properties.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES)
: properties.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES)
+ "," + extraPkgs;
// initialize the system bundle capabilities
final String[] framework_pkgs = Utils.splitString(sysPkgs, ',');
exportSystemBundlePackages(framework_pkgs);
headers.put(Constants.EXPORT_PACKAGE, sysPkgs);
headers.put(Constants.BUNDLE_VENDOR, "Eclipse Foundation");
bundleID_bundles.put(new Long(0), this);
location_bundles.put(Constants.SYSTEM_BUNDLE_LOCATION, this);
symbolicName_bundles.insert(Constants.SYSTEM_BUNDLE_SYMBOLICNAME, this);
symbolicName_bundles.insert(BUNDLE_SYMBOLIC_NAME, this);
final String extraCaps = properties
.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES_EXTRA);
if (extraCaps != null) {
final String[] capStrs = Utils.splitString(extraCaps, ',');
for (final String capStr : capStrs) {
try {
final BundleCapabilityImpl cap = new BundleCapabilityImpl(
this, capStr);
systemBundleCapabilities.add(cap);
} catch (final BundleException be) {
// TODO: to log
be.printStackTrace();
}
}
}
// system bundle symbolic name
final BundleCapabilityImpl sysbundleCap = new BundleCapabilityImpl(this,
"osgi.wiring.bundle; osgi.wiring.bundle="
+ Constants.SYSTEM_BUNDLE_SYMBOLICNAME);
systemBundleCapabilities.add(sysbundleCap);
// default org.wiring.host property
final BundleCapabilityImpl sysbundleDefaultHostCap = new BundleCapabilityImpl(
this, "osgi.wiring.host; osgi.wiring.host="
+ Constants.SYSTEM_BUNDLE_SYMBOLICNAME);
systemBundleCapabilities.add(sysbundleDefaultHostCap);
// concierge-specific org.wiring.host property
final BundleCapabilityImpl sysbundleHostCap = new BundleCapabilityImpl(
this,
"osgi.wiring.host; osgi.wiring.host=org.eclipse.concierge");
systemBundleCapabilities.add(sysbundleHostCap);
publishCapabilities(systemBundleCapabilities);
// add to framework wiring
wirings.put(this, new ConciergeBundleWiring(this, null));
// initialize the system bundle services
registeredServices = new ArrayList<ServiceReference<?>>(2);
// start the logger
if (LOG_ENABLED) {
final LogServiceImpl impl = new LogServiceImpl(LOG_BUFFER_SIZE,
LOG_LEVEL, LOG_QUIET);
final ServiceReference<LogReaderService> readerref = new ServiceReferenceImpl<LogReaderService>(
Concierge.this, this, impl, null,
new String[] { LogReaderService.class.getName() });
logger = impl.factory.getService(null, null);
final ServiceReference<?> logref = new ServiceReferenceImpl<Object>(
Concierge.this, this, impl.factory, null,
new String[] { LogService.class.getName() });
synchronized (serviceRegistry) {
serviceRegistry.insert(LogReaderService.class.getName(),
readerref);
serviceRegistry.insert(LogService.class.getName(), logref);
}
registeredServices.add(logref);
registeredServices.add(readerref);
if (DEBUG_SERVICES) {
logger.log(LogService.LOG_DEBUG,
"Framework has registered LogService and LogReaderService.");
}
}
// set the URLStreamHandlerFactory
try {
conciergeURLStreamHandlerFactory.setConcierge(this);
URL.setURLStreamHandlerFactory(conciergeURLStreamHandlerFactory);
} catch (final Error e) {
// already set...
}
// set the UUID
// TODO: need workaround for Java 1.4
properties.setProperty(Constants.FRAMEWORK_UUID,
UUID.randomUUID().toString());
state = Bundle.STARTING;
if (restart) {
// have bundle objects for all installed bundles
restoreProfile();
}
}
private void exportSystemBundlePackages(final String[] pkgs)
throws BundleException {
for (final String pkg : pkgs) {
final String[] literals = Utils.splitString(pkg, ';');
if (literals.length > 0) {
final ParseResult parseResult = Utils.parseLiterals(literals,
1);
final HashMap<String, Object> attrs = parseResult
.getAttributes();
attrs.put(PackageNamespace.PACKAGE_NAMESPACE,
literals[0].trim());
systemBundleCapabilities.add(new BundleCapabilityImpl(this,
PackageNamespace.PACKAGE_NAMESPACE,
parseResult.getDirectives(), attrs,
Constants.EXPORT_PACKAGE + ' ' + pkg));
}
}
}
private static String sanitizeVersion(final String verStr) {
int dot = 0;
final int len = verStr.length();
final char[] chars = verStr.toCharArray();
for (int c = 0; c < len; c++) {
if (chars[c] == '.' && dot < 2) {
dot++;
continue;
}
if (!Character.isDigit(chars[c])) {
return verStr.substring(0, c);
}
}
return verStr;
}
/**
*
* @see org.osgi.framework.launch.Framework#waitForStop(long)
* @category Framework
*/
public synchronized FrameworkEvent waitForStop(final long timeout)
throws InterruptedException {
if (state == Bundle.STARTING || state == Bundle.STOPPING
|| state == Bundle.ACTIVE) {
synchronized (this) {
wait(timeout);
}
if (state != Bundle.RESOLVED && state != Bundle.INSTALLED) {
return new FrameworkEvent(FrameworkEvent.WAIT_TIMEDOUT, this,
null);
}
}
final FrameworkEvent event = stopEvent;
stopEvent = new FrameworkEvent(FrameworkEvent.STOPPED, this, null);
return event;
}
/**
*
* @see org.osgi.framework.launch.Framework#start()
* @category Framework
* @category SystemBundle
*/
public void start() throws BundleException {
// TODO: check for AdminPermission(this,EXECUTE)
if (state != Bundle.STARTING) {
init();
}
if (state == Bundle.ACTIVE) {
// does nothing because the system bundle is already started
return;
}
try {
System.out.println("------------------"
+ "---------------------------------------");
System.out.println(" Concierge OSGi " + FRAMEWORK_VERSION + " on "
+ System.getProperty("os.name") + " "
+ System.getProperty("os.version") + " starting ... ("
+ PROFILE + ") startlevel=" + BEGINNING_STARTLEVEL);
System.out.println("-------------------"
+ "--------------------------------------");
final long time = System.currentTimeMillis();
// resolve all extension bundles
for (final BundleImpl ext : extensionBundles) {
ext.state = Bundle.RESOLVED;
}
// start System bundle
start(context);
// set startlevel and start all bundles that are marked to be
// started up to the intended startlevel
setLevel(bundles.toArray(new Bundle[bundles.size()]),
BEGINNING_STARTLEVEL, false);
// save the metadata
if (!restart) {
storeProfile();
}
final float timediff = (System.currentTimeMillis() - time)
/ (float) 1000.00;
System.out.println("-----------------------"
+ "----------------------------------");
System.out.println(
" Framework " + (restart ? "restarted" : "started")
+ " in " + timediff + " seconds.");
System.out.println("---------------------------"
+ "------------------------------");
System.out.flush();
} catch (final Exception e) {
notifyFrameworkListeners(FrameworkEvent.ERROR, this,
new BundleException("Exception during framework start",
BundleException.STATECHANGE_ERROR, e));
}
state = Bundle.ACTIVE;
notifyFrameworkListeners(FrameworkEvent.STARTED, this, null);
}
/**
* store the profile.
*
*/
private void storeProfile() {
final BundleImpl[] bundleArray = bundles
.toArray(new BundleImpl[bundles.size()]);
for (int i = 0; i < bundleArray.length; i++) {
if (bundleArray[i].state != Bundle.UNINSTALLED) {
bundleArray[i].updateMetadata();
}
}
storeMetadata();
}
/**
* store the framework metadata.
*
*/
void storeMetadata() {
try {
final DataOutputStream out = new DataOutputStream(
new FileOutputStream(new File(STORAGE_LOCATION, "meta")));
out.writeLong(nextBundleID);
out.close();
} catch (final IOException ioe) {
ioe.printStackTrace();
}
}
/**
* restore a profile.
*
*/
private void restoreProfile() {
try {
if (DEBUG_BUNDLES) {
logger.log(LogService.LOG_DEBUG,
"restoring profile " + PROFILE);
}
final File file = new File(STORAGE_LOCATION, "meta");
if (!file.exists()) {
warning("Profile " + PROFILE
+ " not found, performing clean start ...");
restart = false;
return;
}
final DataInputStream in = new DataInputStream(
new FileInputStream(file));
nextBundleID = in.readLong();
in.close();
final File storageDir = new File(STORAGE_LOCATION);
final File[] bundleDirs = storageDir.listFiles();
for (int i = 0; i < bundleDirs.length; i++) {
if (bundleDirs[i].isDirectory()) {
final File meta = new File(bundleDirs[i], "meta");
if (meta.exists()) {
try {
final AbstractBundle bundle = new BundleImpl(this,
meta);
if (DEBUG_BUNDLES) {
logger.log(LogService.LOG_DEBUG,
"RESTORED BUNDLE " + bundle.location);
}
bundles.add(bundle);
bundleID_bundles.put(new Long(bundle.bundleId),
bundle);
} catch (final Exception e) {
// too early for logger
e.printStackTrace();
}
}
}
}
} catch (final IOException ioe) {
ioe.printStackTrace();
}
}
boolean bootdelegation(final String pkg) {
for (int i = 0; i < bootdelegationPrefix.length; i++) {
if (pkg.startsWith(bootdelegationPrefix[i])) {
return true;
}
}
for (int i = 0; i < bootdelegationAbs.length; i++) {
if (pkg.equals(bootdelegationAbs[i])) {
return true;
}
}
return false;
}
/**
* @see org.osgi.framework.launch.Framework#start(int)
* @category Framework
* @category SystemBundle
*/
public void start(final int options) throws BundleException {
start();
}
/**
* @see org.osgi.framework.launch.Framework#stop()
* @category Framework
* @category SystemBundle
*/
public void stop() throws BundleException {
// TODO: check for AdminPermission(this,EXECUTE)
new Thread() {
public void run() {
stop0(false);
}
}.start();
}
/**
* @see org.osgi.framework.launch.Framework#stop(int)
* @category Framework
* @category SystemBundle
*/
public void stop(final int options) throws BundleException {
stop();
}
protected void stop0(final boolean update) {
state = Bundle.STOPPING;
if (!update) {
System.out.println("----------------------------"
+ "-----------------------------");
System.out.println(" Concierge OSGi shutting down ...");
System.out.println(" Bye !");
System.out.println("----------------------------"
+ "-----------------------------");
System.out.flush();
}
try {
setLevel(bundles.toArray(new Bundle[bundles.size()]), 0, true);
state = Bundle.RESOLVED;
// stop System bundle
stop(context);
// release all resources
for (final AbstractBundle bundle : bundles) {
for (final BundleRevision rev : bundle.getRevisions()) {
((Revision) rev).close();
}
}
bundles.clear();
bundleID_bundles.clear();
serviceRegistry.clear();
// restore micro-services
serviceRegistry.insertMap(microServices);
// reset the used Concierge instance in URL stream handler factory
conciergeURLStreamHandlerFactory.setConcierge(null);
stopEvent = new FrameworkEvent(update
? FrameworkEvent.STOPPED_UPDATE : FrameworkEvent.STOPPED,
this, null);
// notify waiting threads
synchronized (Concierge.this) {
Concierge.this.notify();
}
} catch (final Exception e) {
stopEvent = new FrameworkEvent(FrameworkEvent.ERROR, Concierge.this,
new BundleException("Exception during framework start", e));
Concierge.this.notify();
}
}
/**
* @see org.osgi.framework.launch.Framework#uninstall()
* @category Framework
* @category SystemBundle
*/
public void uninstall() throws BundleException {
// TODO: check for AdminPermission(this,LIFECYCLE)
throw new BundleException("System bundle cannot be uninstalled.");
}
/**
* @see org.osgi.framework.launch.Framework#update()
* @category Framework
* @category SystemBundle
*/
public void update() throws BundleException {
// TODO: check for AdminPermission(this,EXECUTE)
final int state = Concierge.this.state;
new Thread() {
public void run() {
stop0(true);
try {
if (state == Bundle.STARTING) {
Concierge.this.init();
} else if (state == Bundle.ACTIVE) {
Concierge.this.start();
}
} catch (final BundleException be) {
// FIXME: to log
be.printStackTrace();
}
Concierge.this.state = state;
}
}.start();
}
/**
* @see org.osgi.framework.launch.Framework#update(java.io.InputStream)
* @category Framework
* @category SystemBundle
*/
public void update(final InputStream in) throws BundleException {
try {
in.close();
} catch (final IOException ioe) {
// silently ignore
}
update();
}
// public long getBundleId() through AbstractBundle
// public String getLocation() through AbstractBundle
/**
* @see org.osgi.framework.Bundle#getSymbolicName()
* @category Framework SystemBundle
*/
public String getSymbolicName() {
return FRAMEWORK_SYMBOLIC_NAME;
}
/**
*
* @see org.osgi.framework.Bundle#getEntryPaths(java.lang.String)
* @category Framework
* @category SystemBundle
*/
public Enumeration<String> getEntryPaths(final String path) {
return null;
}
/**
* @see org.osgi.framework.Bundle#findEntries(java.lang.String,
* java.lang.String, boolean)
* @category Framework
* @category SystemBundle
*/
public Enumeration<URL> findEntries(final String path,
final String filePattern, final boolean recurse) {
return null;
}
/**
*
* @see org.osgi.framework.Bundle#getEntry(java.lang.String)
* @category Framework
* @category SystemBundle
*/
public URL getEntry(final String path) {
return null;
}
/**
* @see org.osgi.framework.Bundle#adapt(java.lang.Class)
* @category Framework
* @category SystemBundle
*/
@SuppressWarnings("unchecked")
public <A> A adapt(final Class<A> type) {
if (type == BundleStartLevel.class) {
return (A) systemBundleStartLevel;
}
if (type == BundleWiring.class) {
return (A) wirings.get(this);
}
if (type.isInstance(this)) {
return (A) this;
}
return null;
}
// Bundle
// public final int getState() in AbstractBundle
/**
*
* @see org.osgi.framework.Bundle#getHeaders()
* @category SystemBundle
*/
public Dictionary<String, String> getHeaders() {
return headers;
}
// public long getBundleId() in AbstractBundle
// public String getLocation() in AbstractBundle
// public ServiceReference<?>[] getRegisteredServices() in AbstractBundle
/**
*
* @see org.osgi.framework.Bundle#getServicesInUse()
* @category SystemBundle
*/
public ServiceReference<?>[] getServicesInUse() {
return null;
}
// public boolean hasPermission(final Object permission) in AbstractBundle
/**
* @see org.osgi.framework.Bundle#getResource(java.lang.String)
* @category SystemBundle
*/
public URL getResource(final String name) {
return systemBundleClassLoader.getResource(name);
}
/**
* @see org.osgi.framework.Bundle#getHeaders(java.lang.String)
* @category SystemBundle
*/
public Dictionary<String, String> getHeaders(final String locale) {
return headers;
}
/**
* @see org.osgi.framework.Bundle#loadClass(java.lang.String)
* @category SystemBundle
*/
public Class<?> loadClass(final String name) throws ClassNotFoundException {
return systemBundleClassLoader.loadClass(name);
}
/**
* @see org.osgi.framework.Bundle#getResources(java.lang.String)
* @category SystemBundle
*/
public Enumeration<URL> getResources(final String name) throws IOException {
return systemBundleClassLoader.getResources(name);
}
// public long getLastModified() in AbstractBundle
// public final BundleContext getBundleContext() in AbstractBundle
/**
* @see org.osgi.framework.Bundle#getSignerCertificates(int)
* @category SystemBundle
*/
public Map<X509Certificate, List<X509Certificate>> getSignerCertificates(
final int signersType) {
return null;
}
/**
* @see org.osgi.framework.Bundle#getVersion()
* @category SystemBundle
*/
public Version getVersion() {
return version;
}
// public File getDataFile(final String filename) in AbstractBundle
/**
* @see java.lang.Object#toString()
* @category SystemBundle
*/
public String toString() {
return "Concierge System Bundle";
}
// BundleStartLevel
final class SystemBundleStartLevel implements BundleStartLevel {
/**
* @see org.osgi.framework.BundleReference#getBundle()
* @category BundleStartLevel
*/
public Bundle getBundle() {
return Concierge.this;
}
/**
* @see org.osgi.framework.startlevel.BundleStartLevel#getStartLevel()
* @category BundleStartLevel
*/
public int getStartLevel() {
return 0;
}
/**
* @see org.osgi.framework.startlevel.BundleStartLevel#setStartLevel(int)
* @category BundleStartLevel
*/
public void setStartLevel(final int startlevel) {
throw new IllegalArgumentException();
}
/**
* @see org.osgi.framework.startlevel.BundleStartLevel#isPersistentlyStarted()
* @category BundleStartLevel
*/
public boolean isPersistentlyStarted() {
return true;
}
/**
* @see org.osgi.framework.startlevel.BundleStartLevel#isActivationPolicyUsed()
* @category BundleStartLevel
*/
public boolean isActivationPolicyUsed() {
return true;
}
}
// FrameworkStartLevel
/**
* @see org.osgi.framework.startlevel.FrameworkStartLevel#getStartLevel()
* @category FrameworkStartLevel
*/
public int getStartLevel() {
return startlevel;
}
/**
* @see org.osgi.framework.startlevel.FrameworkStartLevel#setStartLevel(int,
* org.osgi.framework.FrameworkListener[])
* @category FrameworkStartLevel
*/
public void setStartLevel(final int targetLevel,
final FrameworkListener... listeners) {
// TODO: check AdminPermission(this, STARTLEVEL);
if (targetLevel <= 0) {
throw new IllegalArgumentException(
"Start level " + targetLevel + " is not a valid level");
}
new Thread() {
public void run() {
setLevel(bundles.toArray(new Bundle[bundles.size()]),
targetLevel, false);
notifyFrameworkListeners(FrameworkEvent.STARTLEVEL_CHANGED,
Concierge.this, null);
if (listeners != null) {
notifyFrameworkListeners(listeners,
FrameworkEvent.STARTLEVEL_CHANGED, Concierge.this,
null);
}
storeMetadata();
}
}.start();
}
/**
* @see org.osgi.framework.startlevel.FrameworkStartLevel#getInitialBundleStartLevel()
* @category FrameworkStartLevel
*/
public int getInitialBundleStartLevel() {
return initStartlevel;
}
/**
* @see org.osgi.framework.startlevel.FrameworkStartLevel#setInitialBundleStartLevel(int)
* @category FrameworkStartLevel
*/
public void setInitialBundleStartLevel(final int targetStartLevel) {
// TODO: check AdminPermission(this, STARTLEVEL);
if (targetStartLevel <= 0) {
throw new IllegalArgumentException("Start level " + targetStartLevel
+ " is not a valid level");
}
initStartlevel = targetStartLevel;
}
/**
* set the current startlevel but does not update the metadata.
*
* @param targetLevel
* the startlevel.
*
*/
protected void setLevel(final Bundle[] bundleArray, final int targetLevel,
final boolean all) {
if (startlevel == targetLevel) {
return;
}
final boolean up = targetLevel > startlevel;
final int levels = up ? targetLevel - startlevel
: startlevel - targetLevel;
final MultiMap<Integer, AbstractBundle> startLevels = new MultiMap<Integer, AbstractBundle>(
0);
// prepare startlevels
for (int i = 0; i < bundleArray.length; i++) {
final AbstractBundle bundle = (AbstractBundle) bundleArray[i];
if (bundle == Concierge.this || bundle.state == Bundle.UNINSTALLED
|| up && bundle.autostart == AUTOSTART_STOPPED
|| !up && bundle.state == Bundle.RESOLVED) {
continue;
}
final int offset;
if (up) {
offset = bundle.startlevel - startlevel - 1;
} else {
offset = startlevel - bundle.startlevel;
}
if (offset >= 0 && offset < levels) {
startLevels.insert(new Integer(offset), bundle);
}
}
for (int i = 0; i < levels; i++) {
if (up) {
startlevel++;
} else {
startlevel--;
}
final List<AbstractBundle> list = startLevels.get(new Integer(i));
if (list == null) {
continue;
}
final BundleImpl[] toProcess = list
.toArray(new BundleImpl[list.size()]);
for (int j = 0; j < toProcess.length; j++) {
try {
if (up) {
// transient is implicit
toProcess[j]
.activate(toProcess[j].isActivationPolicyUsed()
? Bundle.START_ACTIVATION_POLICY : 0);
} else {
if (toProcess[toProcess.length - j - 1]
.getState() == Bundle.UNINSTALLED) {
continue;
}
// transient is implicit
toProcess[toProcess.length - j - 1].stopBundle();
}
} catch (final BundleException be) {
if (be.getNestedException() != null) {
be.getNestedException().printStackTrace();
}
be.printStackTrace();
notifyFrameworkListeners(FrameworkEvent.ERROR,
up ? toProcess[j]
: toProcess[toProcess.length - j - 1],
be);
} catch (final Throwable t) {
t.printStackTrace();
notifyFrameworkListeners(FrameworkEvent.ERROR,
up ? toProcess[j]
: toProcess[toProcess.length - j - 1],
t);
}
}
}
startlevel = targetLevel;
}
// BundleRevision
/**
* @see org.osgi.framework.wiring.BundleRevision#getDeclaredCapabilities(java.lang.String)
* @category BundleRevision
*/
public List<BundleCapability> getDeclaredCapabilities(
final String namespace) {
final ArrayList<BundleCapability> filteredCapabilities = new ArrayList<BundleCapability>();
if (namespace != null) {
for (final BundleCapability c : systemBundleCapabilities) {
if (c.getNamespace().equals(namespace)) {
filteredCapabilities.add(c);
}
}
} else {
filteredCapabilities.addAll(systemBundleCapabilities);
}
return Collections.unmodifiableList(filteredCapabilities);
}
/**
* @see org.osgi.framework.wiring.BundleRevision#getDeclaredRequirements(java.lang.String)
* @category BundleRevision
*/
public List<BundleRequirement> getDeclaredRequirements(
final String namespace) {
return Collections.emptyList();
}
/**
* @see org.osgi.framework.wiring.BundleRevision#getTypes()
* @category BundleRevision
*/
public int getTypes() {
return 0;
}
/**
* @see org.osgi.framework.wiring.BundleRevision#getWiring()
* @category BundleRevision
*/
public BundleWiring getWiring() {
return (BundleWiring) wirings.get(this);
}
/**
* @see org.osgi.framework.wiring.BundleRevision#getCapabilities(java.lang.String)
* @category BundleRevision
*/
public List<Capability> getCapabilities(final String namespace) {
return Collections.unmodifiableList(
new ArrayList<Capability>(getDeclaredCapabilities(namespace)));
}
/**
*
* @category BundleRevision
*/
public List<Requirement> getRequirements(final String namespace) {
return Collections.emptyList();
}
// FrameworkWiring
/**
* @see org.osgi.framework.wiring.FrameworkWiring#refreshBundles(java.util.Collection,
* org.osgi.framework.FrameworkListener[])
*/
public void refreshBundles(final Collection<Bundle> bundleCollection,
final FrameworkListener... listeners) {
// TODO: check AdminPermission(this, RESOLVE)
new Thread() {
public void run() {
try {
synchronized (Concierge.this) {
Bundle[] initial;
// build the initial set of bundles
if (bundleCollection == null) {
initial = bundles
.toArray(new Bundle[bundles.size()]);
} else {
initial = bundleCollection.toArray(
new Bundle[bundleCollection.size()]);
}
final ArrayList<Bundle> toProcess = new ArrayList<Bundle>();
// filter out those which need to be updated
for (int i = 0; i < initial.length; i++) {
if (initial[i] == Concierge.this) {
// don't process (stop/start)
continue;
}
if (initial[i].getState() == Bundle.INSTALLED) {
continue;
}
final BundleImpl theBundle = (BundleImpl) initial[i];
if (bundleCollection == null) {
if (theBundle.currentRevision == null
|| theBundle.currentRevision != theBundle.revisions
.get(0)) {
toProcess.add(theBundle);
} else
if (theBundle.currentRevision.fragments != null) {
for (final Revision fragment : theBundle.currentRevision.fragments) {
if (fragment.getBundle()
.getState() == Bundle.UNINSTALLED) {
toProcess.add(initial[i]);
break;
}
}
}
} else {
// bundleArray has entries which should be
// processed anyway
toProcess.add(initial[i]);
}
}
// nothing to do ? fine, so we are done.
if (toProcess.isEmpty()) {
notifyListeners(FrameworkEvent.PACKAGES_REFRESHED,
Concierge.this, null);
return;
}
if (LOG_ENABLED && DEBUG_PACKAGES) {
logger.log(LogService.LOG_DEBUG,
"REFRESHING PACKAGES FROM BUNDLES "
+ toProcess);
}
final Collection<Bundle> updateGraph = getDependencyClosure(
toProcess);
if (LOG_ENABLED && DEBUG_PACKAGES) {
logger.log(LogService.LOG_DEBUG,
"UPDATE GRAPH IS " + updateGraph);
}
final ArrayList<Bundle> tmp = new ArrayList<Bundle>(
updateGraph);
Collections.sort(tmp);
final Bundle[] refreshArray = tmp
.toArray(new Bundle[tmp.size()]);
// stop all bundles in the restart array regarding their
// startlevels
// perform a cleanup for all bundles
// CLEANUP
final List<Bundle> restartList = new ArrayList<Bundle>();
for (int i = 0; i < refreshArray.length; i++) {
final BundleImpl bu = (BundleImpl) refreshArray[i];
try {
if (bu.state == ACTIVE) {
bu.stop();
restartList.add(0, bu);
}
if (bu.state == RESOLVED) {
bu.state = INSTALLED;
}
// bundle needs to be refreshed
bu.refresh();
if (bu.state == UNINSTALLED) {
// bundle is uninstalled
bundles.remove(bu);
} else {
notifyBundleListeners(
BundleEvent.UNRESOLVED, bu);
}
} catch (final Exception e) {
notifyListeners(FrameworkEvent.ERROR,
refreshArray[i], e);
}
}
// resolve, if possible
// FIXME: should be bulk operation
for (final Iterator<Bundle> resolveIter = restartList
.iterator(); resolveIter.hasNext();) {
final BundleImpl bu = (BundleImpl) resolveIter
.next();
try {
if (bu.state == Bundle.INSTALLED) {
final boolean success = bu.currentRevision
.resolve(false);
if (!success) {
resolveIter.remove();
}
}
} catch (final Exception e) {
resolveIter.remove();
notifyListeners(FrameworkEvent.ERROR, bu, e);
}
}
// restart all bundles regarding their startlevels
for (final Bundle bu : restartList) {
try {
bu.start();
} catch (final Exception e) {
notifyListeners(FrameworkEvent.ERROR, bu, e);
}
}
notifyListeners(FrameworkEvent.PACKAGES_REFRESHED,
Concierge.this, null);
} // end synchronized statement
} catch (final Throwable t) {
// TODO: to log
t.printStackTrace();
}
}
private void notifyListeners(final int type, final Bundle b,
final Exception e) {
if (type == FrameworkEvent.ERROR) {
// TODO: to log
e.printStackTrace();
}
notifyFrameworkListeners(type, b, e);
if (listeners != null) {
notifyFrameworkListeners(listeners, type, b, e);
}
}
}.start();
}
/**
* @see org.osgi.framework.wiring.FrameworkWiring#resolveBundles(java.util.Collection)
* @category FrameworkWiring
*/
public boolean resolveBundles(final Collection<Bundle> bundles) {
final ArrayList<BundleRevision> resources = new ArrayList<BundleRevision>();
boolean resolved = true;
for (final Bundle bundle : bundles == null ? Concierge.this.bundles
: bundles) {
if (bundle.getState() == UNINSTALLED) {
resolved = false;
continue;
}
resources.add(bundle.adapt(BundleRevision.class));
}
try {
resolved &= resolve(resources, false);
return resolved;
} catch (final BundleException e) {
// should not be thrown for critical==false
return false;
}
}
private boolean inResolve = false;
private HashMap<ResolverHook, ServiceReferenceImpl<ResolverHookFactory>> getResolverHooks(
final Collection<BundleRevision> bundles) throws Throwable {
final LinkedHashMap<ResolverHook, ServiceReferenceImpl<ResolverHookFactory>> hooks = new LinkedHashMap<ResolverHook, ServiceReferenceImpl<ResolverHookFactory>>();
@SuppressWarnings("unchecked")
final ServiceReferenceImpl<ResolverHookFactory>[] factories = resolverHookFactories
.toArray(
new ServiceReferenceImpl[resolverHookFactories.size()]);
try {
for (int i = 0; i < factories.length; i++) {
final ServiceReferenceImpl<ResolverHookFactory> sref = factories[i];
final ResolverHookFactory factory = sref
.getService(Concierge.this);
if (factory != null) {
final ResolverHook hook = factory
.begin(Collections.unmodifiableCollection(bundles));
if (hook != null) {
hooks.put(hook, sref);
}
sref.ungetService(Concierge.this);
}
}
} catch (final Throwable t) {
for (final ResolverHook hook : hooks.keySet()) {
hook.end();
}
throw t;
}
return hooks;
}
private void endResolverHooks(
final HashMap<ResolverHook, ServiceReferenceImpl<ResolverHookFactory>> hooks)
throws BundleException {
if (hooks == null) {
return;
}
Throwable error = null;
for (final Map.Entry<ResolverHook, ServiceReferenceImpl<ResolverHookFactory>> entry : hooks
.entrySet()) {
if (entry.getValue().service == null) {
// unregistered...
error = new BundleException(
"Something unregistered a hook that was in use.",
BundleException.REJECTED_BY_HOOK);
continue;
}
try {
entry.getKey().end();
} catch (final Throwable t) {
error = t;
}
}
if (error != null) {
throw new BundleException("Error", BundleException.REJECTED_BY_HOOK,
error);
}
}
List<BundleCapability> resolveDynamic(final BundleRevision trigger,
final String pkg, final String dynImportPackage,
final BundleRequirement dynImport, final boolean multiple) {
Collection<Capability> candidates = null;
try {
if (resolver.hooks == null) {
resolver.hooks = getResolverHooks(Arrays.asList(trigger));
}
final String filterStr = dynImport.getDirectives()
.get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
if (multiple) {
// we have a wildcard, this means scanning
candidates = capabilityRegistry
.getAll(PackageNamespace.PACKAGE_NAMESPACE);
} else {
// we don't have a wildcard, use the index
if (filterStr == null) {
candidates = capabilityRegistry.getByValue(
PackageNamespace.PACKAGE_NAMESPACE,
dynImportPackage);
} else {
try {
candidates = RFC1960Filter.filterWithIndex(dynImport,
filterStr, capabilityRegistry);
} catch (final InvalidSyntaxException e) {
e.printStackTrace();
}
}
}
if (candidates == null || candidates.isEmpty()) {
endResolverHooks(resolver.hooks);
return null;
}
filterCandidates(resolver.hooks.keySet(), dynImport, candidates);
final ArrayList<BundleCapability> matches = new ArrayList<BundleCapability>();
for (final Capability cap : candidates) {
final String candidatePackage = (String) cap.getAttributes()
.get(PackageNamespace.PACKAGE_NAMESPACE);
assert candidatePackage != null;
if (multiple && RFC1960Filter.stringCompare(pkg.toCharArray(),
0, candidatePackage.toCharArray(), 0) != 0) {
continue;
}
if (cap instanceof BundleCapability && Concierge.matches0(
PackageNamespace.PACKAGE_NAMESPACE, dynImport, cap,
filterStr)) {
// we have a match
// FIXME: cleanup...
if (((BundleCapability) cap).getRevision().getBundle()
.getState() == Bundle.INSTALLED) {
// need to resolve first
if (!resolve(
Collections.singletonList(
((BundleCapability) cap).getRevision()),
false)) {
continue;
}
}
matches.add((BundleCapability) cap);
}
}
endResolverHooks(resolver.hooks);
Collections.sort(matches, EXPORT_ORDER);
return matches;
} catch (final Throwable t) {
// TODO: handle
return null;
} finally {
resolver.hooks = null;
}
}
// TODO: simplify
protected void filterCandidates(final Collection<ResolverHook> hooks,
final BundleRequirement requirement,
final Collection<Capability> candidates) {
// sort candidates by providing resources
final MultiMap<BundleRevision, BundleCapability> mmap = new MultiMap<BundleRevision, BundleCapability>();
for (final Iterator<Capability> iter = candidates.iterator(); iter
.hasNext();) {
final Capability cap = iter.next();
final Resource res = cap.getResource();
if (res instanceof BundleRevision) {
mmap.insert((BundleRevision) cap.getResource(),
(BundleCapability) cap);
iter.remove();
}
}
for (final ResolverHook hook : hooks) {
hook.filterResolvable(mmap.keySet());
}
final ConciergeCollections.RemoveOnlyList<BundleCapability> filteredCandidates = new ConciergeCollections.RemoveOnlyList<BundleCapability>(
mmap.getAllValues());
for (final ResolverHook hook : hooks) {
hook.filterMatches(requirement, filteredCandidates);
}
candidates.addAll(filteredCandidates);
}
protected void filterResources(final Collection<ResolverHook> hooks,
final Collection<Resource> resources,
final Collection<Resource> removed) {
final ArrayList<BundleRevision> revisions = new ArrayList<BundleRevision>();
removed.addAll(resources);
for (final Iterator<Resource> iter = resources.iterator(); iter
.hasNext();) {
final Resource res = iter.next();
if (res instanceof BundleRevision) {
revisions.add((BundleRevision) res);
iter.remove();
}
}
final ConciergeCollections.RemoveOnlyList<BundleRevision> filteredResources = new ConciergeCollections.RemoveOnlyList<BundleRevision>(
revisions);
for (final ResolverHook hook : resolver.hooks.keySet()) {
hook.filterResolvable(filteredResources);
}
resources.addAll(filteredResources);
removed.removeAll(filteredResources);
}
synchronized boolean resolve(final Collection<BundleRevision> bundles,
final boolean critical) throws BundleException {
if (inResolve) {
throw new IllegalStateException("nested resolve call");
}
boolean cleanup = false;
try {
inResolve = true;
final MultiMap<Resource, HostedCapability> hostedCapabilities = new MultiMap<Resource, HostedCapability>();
if (resolver.hooks == null) {
resolver.hooks = getResolverHooks(bundles);
cleanup = true;
}
final MultiMap<Resource, Wire> solution = new MultiMap<Resource, Wire>();
final ArrayList<Requirement> unresolvedRequirements = new ArrayList<Requirement>();
final ArrayList<Resource> unresolvedResources = new ArrayList<Resource>();
resolver.resolve0(new ResolveContext() {
public Collection<Resource> getMandatoryResources() {
return new ArrayList<Resource>(bundles);
}
public Collection<Resource> getOptionalResources() {
return Collections.emptyList();
}
@Override
public List<Capability> findProviders(
final Requirement requirement) {
final String filterStr = requirement.getDirectives()
.get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
final List<Capability> providers;
if (filterStr == null) {
providers = capabilityRegistry
.getAll(requirement.getNamespace());
} else {
try {
providers = RFC1960Filter.filterWithIndex(
requirement, filterStr, capabilityRegistry);
} catch (final InvalidSyntaxException ise) {
// TODO: debug output
ise.printStackTrace();
return Collections.emptyList();
}
}
sortProviders(providers, requirement.getNamespace());
return providers;
}
private void sortProviders(final List<Capability> providers,
final String namespace) {
if (providers.isEmpty()) {
return;
}
if (PackageNamespace.PACKAGE_NAMESPACE.equals(namespace)) {
Collections.sort(providers, EXPORT_ORDER);
}
if (BundleNamespace.BUNDLE_NAMESPACE.equals(namespace)) {
Collections.sort(providers, BUNDLE_VERSION);
}
}
@Override
public int insertHostedCapability(
final List<Capability> capabilities,
final HostedCapability hostedCapability) {
publishCapabilities(
Collections.singletonList(hostedCapability));
capabilities.add(hostedCapability);
hostedCapabilities.insert(hostedCapability.getResource(),
hostedCapability);
if (PackageNamespace.PACKAGE_NAMESPACE
.equals(hostedCapability.getNamespace())) {
Collections.sort(capabilities, EXPORT_ORDER);
return capabilities.indexOf(hostedCapability);
}
return capabilities.size();
}
@Override
public boolean isEffective(final Requirement requirement) {
final String effective = requirement.getDirectives()
.get(Namespace.REQUIREMENT_EFFECTIVE_DIRECTIVE);
return effective == null
|| effective.equals(Namespace.EFFECTIVE_RESOLVE);
}
@Override
public Map<Resource, Wiring> getWirings() {
return wirings;
}
}, solution, unresolvedRequirements, unresolvedResources, false);
if (LOG_ENABLED && DEBUG_RESOLVER) {
logger.log(LogService.LOG_DEBUG, "Solution: " + solution);
}
// apply solution
for (final Resource resource : solution.keySet()) {
final List<Wire> wires = solution.get(resource);
if (resource instanceof Revision) {
final Revision revision = (Revision) resource;
final boolean isFragment = revision.isFragment();
if (isFragment) {
boolean attached = false;
for (final Iterator<Wire> iter = wires.iterator(); iter
.hasNext();) {
final Wire wire = iter.next();
// scan the wires for host namespace wires
if (HostNamespace.HOST_NAMESPACE.equals(
wire.getRequirement().getNamespace())) {
if (wire.getProvider() instanceof Revision) {
final Revision host = (Revision) wire
.getProvider();
try {
host.attachFragment(revision);
attached = true;
} catch (final BundleException be) { // TODO:
// remove
be.printStackTrace();
}
} else {
// host is system bundle, check
// extensionBundles
if (extensionBundles
.contains(revision.getBundle())) {
attached = true;
}
}
}
}
if (!attached) {
continue;
}
// fragment has been attached to at least one host =>
// becomes resolved.
revision.markResolved();
}
final ConciergeBundleWiring wiring;
if (revision.getWiring() == null) {
// set wiring for this bundle
wiring = new ConciergeBundleWiring(revision, wires);
revision.setWiring(wiring);
} else {
wiring = revision.addAdditionalWires(wires);
}
if (!isFragment) {
final List<HostedCapability> hostedCaps = hostedCapabilities
.lookup(resource);
for (final HostedCapability hostedCap : hostedCaps) {
// add hosted capability
wiring.addCapability(hostedCap);
revision.addHostedCapability(hostedCap);
}
}
wirings.put(resource, wiring);
} else {
// this is the system bundle
// manually add the wires to wirings
final Concierge systemBundle = (Concierge) resource;
ConciergeBundleWiring wiring = (ConciergeBundleWiring) wirings
.get(resource);
if (wiring == null) {
wiring = new ConciergeBundleWiring(systemBundle, wires);
wirings.put(systemBundle, wiring);
} else {
for (final Wire wire : wires) {
wiring.addWire((BundleWire) wire);
}
}
}
}
if (unresolvedRequirements.isEmpty()
&& unresolvedResources.isEmpty()) {
return true;
}
if (critical) {
throw new BundleException(
"Resolution failed " + unresolvedRequirements,
BundleException.RESOLVE_ERROR);
}
if (LOG_ENABLED && DEBUG_RESOLVER) {
logger.log(LogService.LOG_DEBUG,
"Unresolved Requirements: " + unresolvedRequirements);
}
return false;
} catch (final BundleException be) {
throw be;
} catch (final Throwable t) {
t.printStackTrace();
throw new BundleException("Resolve Error",
BundleException.REJECTED_BY_HOOK, t);
} finally {
try {
endResolverHooks(resolver.hooks);
} finally {
if (cleanup) {
resolver.hooks = null;
}
inResolve = false;
}
}
}
/**
* @see org.osgi.framework.wiring.FrameworkWiring#getRemovalPendingBundles()
* @category FrameworkWiring
*/
public Collection<Bundle> getRemovalPendingBundles() {
final ArrayList<Bundle> removalPending = new ArrayList<Bundle>();
bundleLoop: for (final AbstractBundle bundle : bundles) {
if (bundle instanceof BundleImpl) {
final List<BundleRevision> revisions = bundle.getRevisions();
for (final BundleRevision rev : revisions) {
final BundleWiring wiring = rev.getWiring();
if (wiring != null && !wiring.isCurrent()
&& wiring.isInUse()) {
removalPending.add(bundle);
continue bundleLoop;
}
}
}
}
return removalPending;
}
/**
* @see org.osgi.framework.wiring.FrameworkWiring#getDependencyClosure(java.util.Collection)
* @category FrameworkWiring
*/
public Collection<Bundle> getDependencyClosure(
final Collection<Bundle> bundles) {
return getDependencies(bundles, false);
}
private Collection<Bundle> getDependencies(final Collection<Bundle> bundles,
final boolean allRevisions) {
// build up the dependency graph. See specs for details.
final ArrayList<Bundle> toProcess = new ArrayList<Bundle>(bundles);
final Set<Bundle> dependencySet = new HashSet<Bundle>();
while (!toProcess.isEmpty()) {
final Bundle b = toProcess.remove(0);
if (b == this) {
dependencySet.add(b);
continue;
}
if (dependencySet.contains(b)) {
continue;
}
if (!(b instanceof BundleImpl)) {
throw new IllegalArgumentException(
"Bundles were not created by this framework instance "
+ b.getClass().getName());
}
dependencySet.add(b);
final BundleImpl bundle = (BundleImpl) b;
for (final BundleRevision brev : bundle.revisions) {
final BundleWiring wiring = brev.getWiring();
// all package exports
if (wiring != null) {
for (final BundleRevision rev : ((ConciergeBundleWiring) wiring).inUseSet) {
toProcess.add(rev.getBundle());
}
/*
* final List<BundleWire> importWires = wiring
* .getProvidedWires(null);
*
* if (importWires != null) { for (final BundleWire
* importWire : importWires) {
* toProcess.add(importWire.getRequirer().getBundle()); } }
*/
final List<BundleWire> hostWires = wiring
.getRequiredWires(HostNamespace.HOST_NAMESPACE);
if (hostWires != null) {
for (final BundleWire hostWire : hostWires) {
toProcess.add(hostWire.getProvider().getBundle());
}
}
}
}
}
return dependencySet;
}
public class ResolverImpl implements Resolver {
protected HashMap<ResolverHook, ServiceReferenceImpl<ResolverHookFactory>> hooks;
public synchronized Map<Resource, List<Wire>> resolve(
final ResolveContext context) throws ResolutionException {
if (context == null) {
throw new IllegalArgumentException("context is null");
}
final MultiMap<Resource, Wire> solution = new MultiMap<Resource, Wire>();
final ArrayList<Requirement> unresolvedRequirements = new ArrayList<Requirement>();
final ArrayList<Resource> unresolvedResources = new ArrayList<Resource>();
resolve0(context, solution, unresolvedRequirements,
unresolvedResources, true);
if (!unresolvedRequirements.isEmpty()
|| !unresolvedResources.isEmpty()) {
throw new ResolutionException("Could not resolve.", null,
unresolvedRequirements);
}
return solution.getFlatMap();
}
protected void resolve0(final ResolveContext context,
final MultiMap<Resource, Wire> solution,
final ArrayList<Requirement> unresolvedRequirements,
final ArrayList<Resource> unresolvedResources,
final boolean standalone) {
final Collection<Resource> mandatory = context
.getMandatoryResources();
final Collection<Resource> optional = context
.getOptionalResources();
if (hooks != null && !hooks.isEmpty()) {
filterResources(hooks.keySet(), mandatory, unresolvedResources);
}
if (!(mandatory.isEmpty() && optional.isEmpty())) {
final Map<Resource, Wiring> existingWirings = context
.getWirings();
for (final Resource resource : mandatory) {
if (resource == null) {
continue;
}
try {
if (resource instanceof Revision
&& !((Revision) resource)
.resolveMetadata(false)) {
unresolvedResources.add(resource);
continue;
}
} catch (final BundleException e) {
// should not happen for critical==false
e.printStackTrace();
}
if (resource instanceof BundleRevision) {
if (!checkSingleton((BundleRevision) resource)) {
unresolvedResources.add(resource);
continue;
}
}
final Collection<Requirement> unres = resolveResource(
context, resource, existingWirings, solution,
new HashSet<Resource>(), standalone);
unresolvedRequirements.addAll(unres);
}
if (!unresolvedRequirements.isEmpty()
|| !unresolvedResources.isEmpty()) {
return;
}
for (final Resource resource : optional) {
resolveResource(context, resource, existingWirings,
solution, new HashSet<Resource>(), standalone);
}
}
}
private boolean checkSingleton(final BundleRevision resource) {
try {
final List<Capability> identities = resource
.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
if (identities == null || identities.isEmpty()) {
return true;
}
final BundleCapability identity = (BundleCapability) identities
.get(0);
if (!"true".equals(identity.getDirectives().get(
IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE))) {
return true;
}
final List<BundleCapability> col = new ArrayList<BundleCapability>();
if (DEBUG_BUNDLES) {
logger.log(LogService.LOG_DEBUG,
"RESOLVING " + resource.getSymbolicName() + " - "
+ resource.getVersion() + " /.//"
+ resource);
}
final List<AbstractBundle> existing = new ArrayList<AbstractBundle>(
getBundleWithSymbolicName(resource.getSymbolicName()));
existing.remove(resource.getBundle());
if (existing.isEmpty()) {
return true;
}
for (final AbstractBundle bundle : existing) {
if (bundle.state != Bundle.INSTALLED) {
final BundleCapability existingIdentity = (BundleCapability) bundle.currentRevision
.getCapabilities(
IdentityNamespace.IDENTITY_NAMESPACE)
.get(0);
if ("true".equals(existingIdentity.getDirectives().get(
IdentityNamespace.CAPABILITY_SINGLETON_DIRECTIVE))) {
col.add(existingIdentity);
}
}
}
if (hooks != null && hooks.isEmpty()) {
return col.isEmpty();
}
final ConciergeCollections.RemoveOnlyList<BundleCapability> collisions = new ConciergeCollections.RemoveOnlyList<BundleCapability>(
col);
for (final ResolverHook hook : hooks.keySet()) {
hook.filterSingletonCollisions(identity, collisions);
}
if (!collisions.isEmpty()) {
return false;
}
for (final BundleCapability cap : col) {
final ConciergeCollections.RemoveOnlyList<BundleCapability> identityList = new ConciergeCollections.RemoveOnlyList<BundleCapability>(
Collections.singletonList(identity));
for (final ResolverHook hook : hooks.keySet()) {
hook.filterSingletonCollisions(cap, identityList);
}
if (!identityList.isEmpty()) {
return false;
}
}
return true;
} catch (final Throwable t) {
t.printStackTrace();
throw new RuntimeException(t.getMessage());
}
}
private final Collection<Requirement> resolveResource(
final ResolveContext context, final Resource resource,
final Map<Resource, Wiring> existingWirings,
final MultiMap<Resource, Wire> solution,
final HashSet<Resource> inResolution,
final boolean standalone) {
inResolution.add(resource);
if (solution.containsKey(resource)) {
return Collections.emptyList();
}
final Collection<Requirement> unresolvedRequirements = new ArrayList<Requirement>();
final MultiMap<Resource, Wire> newWires = new MultiMap<Resource, Wire>();
boolean isFragment = false;
if (resource instanceof Revision) {
final Revision revision = (Revision) resource;
isFragment = revision.isFragment();
if (!isFragment) {
// check which fragments can be attached to the bundles
if (revision.allowsFragmentAttachment()) {
for (final Revision frag : getFragments(revision)) {
final ArrayList<Capability> capList = new ArrayList<Capability>();
capList.add(revision
.getCapabilities(
HostNamespace.HOST_NAMESPACE)
.get(0));
if (hooks != null && !hooks.isEmpty()) {
filterCandidates(hooks.keySet(),
(BundleRequirement) frag
.getRequirements(
HostNamespace.HOST_NAMESPACE)
.get(0),
capList);
}
if (capList.isEmpty()) {
continue;
}
try {
if (!revision.attachFragment(frag)) {
continue;
}
hostFragment(context, frag, revision, solution);
} catch (final BundleException e) {
// does not attach...
if (LOG_ENABLED) {
logger.log(LogService.LOG_ERROR,
"Unsuccessfully attempted to attach "
+ frag + " to " + revision,
e);
}
}
}
}
}
} else {
isFragment = !resource
.getRequirements(HostNamespace.HOST_NAMESPACE)
.isEmpty();
}
final Collection<Requirement> requirements = resource
.getRequirements(null);
final HashSet<Requirement> skip = new HashSet<Requirement>();
for (final Requirement requirement : requirements) {
// skip requirements that are already resolved through uses
// constraints
if (skip.contains(requirement)) {
continue;
}
// skip requirements which are not effective
if (!context.isEffective(requirement)) {
continue;
}
if (isFragment && !HostNamespace.HOST_NAMESPACE
.equals(requirement.getNamespace())) {
// skip fragment requirements
continue;
}
// find candidates for the requirement
final Collection<Capability> candidates = context
.findProviders(requirement);
// filter through the resolver hooks if there are any
if (hooks != null && !hooks.isEmpty()
&& requirement instanceof BundleRequirement) {
filterCandidates(hooks.keySet(),
(BundleRequirement) requirement, candidates);
}
boolean resolved = false;
final boolean multiple = Namespace.CARDINALITY_MULTIPLE
.equals(requirement.getDirectives().get(
Namespace.REQUIREMENT_CARDINALITY_DIRECTIVE));
for (final Capability capability : candidates) {
if (isFragment) {
final Revision revision = (Revision) resource;
if (capability.getResource() instanceof Revision) {
final Revision host = (Revision) capability
.getResource();
try {
if (!host.attachFragment(revision)) {
resolved = true;
continue;
}
} catch (final BundleException be) {
// cannot attach
continue;
}
} else {
// case of system bundle extension is handled in
// Concierge.addFragment
}
resolved = true;
hostFragment(context, revision,
(BundleRevision) capability.getResource(),
solution);
// don't trigger resolution of the host
continue;
}
// handling potential uses constraints
if (capability instanceof BundleCapability) {
// FIXME: CLEANUP!!!, OPTIMIZE!!!
final ArrayList<BundleCapability> caps = new ArrayList<BundleCapability>();
if (BundleNamespace.BUNDLE_NAMESPACE
.equals(capability.getNamespace())) {
caps.addAll(((BundleCapability) capability)
.getResource().getDeclaredCapabilities(
PackageNamespace.PACKAGE_NAMESPACE));
} else {
caps.add((BundleCapability) capability);
}
final ArrayList<BundleCapability> impliedConstraints = new ArrayList<BundleCapability>();
final HashSet<BundleCapability> seen = new HashSet<BundleCapability>();
while (!caps.isEmpty()) {
final BundleCapability cap = caps.remove(0);
if (seen.contains(cap)) {
continue;
}
seen.add(cap);
final String usesStr = cap.getDirectives()
.get(Namespace.CAPABILITY_USES_DIRECTIVE);
if (usesStr != null) {
final String[] usesConstraints = Utils
.splitString(usesStr, ',');
final HashSet<String> usesSet = new HashSet<String>();
usesSet.addAll(Arrays.asList(usesConstraints));
final BundleWiring wiring = cap.getResource()
.getWiring();
// TODO: what does it mean that wiring is null
// at this point???
if (wiring != null && wiring.isInUse()) {
final List<BundleWire> wires = wiring
.getRequiredWires(
PackageNamespace.PACKAGE_NAMESPACE);
final HashSet<Object> requireSet = new HashSet<Object>();
for (final BundleWire wire : wires) {
final Object pkg = wire.getCapability()
.getAttributes()
.get(PackageNamespace.PACKAGE_NAMESPACE);
if (usesSet.contains(pkg)) {
impliedConstraints
.add(wire.getCapability());
caps.add(wire.getCapability());
requireSet.add(pkg);
}
}
final List<BundleCapability> caps2 = wiring
.getCapabilities(
PackageNamespace.PACKAGE_NAMESPACE);
for (final Capability cap2 : caps2) {
final Object pkg = cap2.getAttributes()
.get(PackageNamespace.PACKAGE_NAMESPACE);
if (usesSet.contains(pkg)
&& !requireSet.contains(pkg)) { // don't
// include
// cap
// if
// it
// was
// already
// imported
// as
// requirement
impliedConstraints.add(
(BundleCapability) cap2);
caps.add((BundleCapability) cap2);
}
}
}
}
}
if (!impliedConstraints.isEmpty()) {
// go over implied constraints
for (final BundleCapability implied : impliedConstraints) {
for (final Requirement req : requirements) {
if (matches(req, implied)) {
for (final Map.Entry<Resource, List<Wire>> entry : newWires
.entrySet()) {
for (final Iterator<Wire> iter = entry
.getValue().iterator(); iter
.hasNext();) {
final Wire wire = iter.next();
if (wire.getRequirement() == req) {
iter.remove();
}
}
}
skip.add(req);
final Wire wire = Resources
.createWire(implied, req);
newWires.insert(resource, wire);
}
}
}
}
}
// check if the provider is already resolved
if (existingWirings.get(capability.getResource()) != null) {
final Wire wire = Resources.createWire(capability,
requirement);
newWires.insert(resource, wire);
newWires.insertUnique(capability.getResource(), wire);
resolved = true;
if (!multiple) {
break;
}
} else {
// try to recursively resolve the provider
try {
if (inResolution.contains(capability.getResource())
|| (!(capability
.getResource() instanceof Revision)
|| ((Revision) capability
.getResource())
.resolveMetadata(
false))
&& resolveResource(context,
capability.getResource(),
existingWirings, solution,
inResolution, standalone)
.isEmpty()) {
final Wire wire = Resources
.createWire(capability, requirement);
newWires.insert(resource, wire);
if (!standalone) {
newWires.insertUnique(
capability.getResource(), wire);
}
resolved = true;
if (!multiple) {
break;
}
}
} catch (final BundleException be) {
// ignore
}
}
}
if (!resolved && !Namespace.RESOLUTION_OPTIONAL
.equals(requirement.getDirectives().get(
Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE))) {
unresolvedRequirements.add(requirement);
}
}
if (unresolvedRequirements.isEmpty()) {
// resolution successful, add wires to solution
if (newWires.isEmpty()) {
solution.insertEmpty(resource);
} else {
solution.insertMap(newWires);
}
if (resource instanceof Revision) {
((Revision) resource).markResolved();
}
}
return unresolvedRequirements;
}
private void hostFragment(final ResolveContext context,
final BundleRevision fragment, final BundleRevision host,
final MultiMap<Resource, Wire> solution) {
// host the capabilities
for (final Capability cap : fragment.getCapabilities(null)) {
if (!IdentityNamespace.IDENTITY_NAMESPACE
.equals(cap.getNamespace())) {
final HostedBundleCapability hostedCap = new HostedBundleCapability(
host, cap);
context.insertHostedCapability(
new ArrayList<Capability>(host.getCapabilities(
PackageNamespace.PACKAGE_NAMESPACE)),
hostedCap);
}
}
// create host wire
final Capability hostCapability = host
.getCapabilities(HostNamespace.HOST_NAMESPACE).get(0);
final Requirement hostRequirement = fragment
.getRequirements(HostNamespace.HOST_NAMESPACE).get(0);
final Wire wire = Resources.createWire(hostCapability,
hostRequirement);
solution.insert(fragment, wire);
solution.insert(host, wire);
}
}
// URLStreamHandlerFactory
protected static class ConciergeURLStreamHandlerFactory
implements URLStreamHandlerFactory {
Concierge frameworkInstance = null;
public void setConcierge(final Concierge concierge) {
this.frameworkInstance = concierge;
}
/**
* @see java.net.URLStreamHandlerFactory#createURLStreamHandler(java.lang.String)
* @category URLStreamHandlerFactory
*/
public URLStreamHandler createURLStreamHandler(final String protocol) {
// check the service registry, java.protocol.handler.pkgs, etc.
if ("bundle".equals(protocol)) {
return new URLStreamHandler() {
protected URLConnection openConnection(final URL u)
throws IOException {
try {
final String host = u.getHost();
// FIXME: unsafe!
final String[] s = Utils.splitString(host, '.');
final Long bundleId = Long.parseLong(s[0]);
final int rev = Integer.parseInt(s[1]);
if (ConciergeURLStreamHandlerFactory.this.frameworkInstance == null) {
throw new IllegalStateException(
"ConciergeURLStreamHandlerFactory "
+ "is not linked to a Concierge framework");
}
final BundleImpl bundle = (BundleImpl) ConciergeURLStreamHandlerFactory.this.frameworkInstance.bundleID_bundles
.get(bundleId);
if (bundle == null) {
throw new IllegalStateException(
"Bundle for URL " + u
+ " can not be found");
}
return new URLConnection(u) {
private InputStream inputStream;
private boolean isConnected;
public void connect() throws IOException {
inputStream = bundle.getURLResource(u, rev);
isConnected = true;
}
public int getContentLength() {
return (int) bundle.getResourceLength(u,
rev);
}
public InputStream getInputStream()
throws IOException {
if (!isConnected) {
connect();
}
return inputStream;
}
};
} catch (final NumberFormatException nfe) {
throw new IOException(
"Malformed host " + u.getHost());
}
}
};
}
return null;
}
}
// full match
static boolean matches(final Requirement req, final Capability cap) {
final String reqNamespace = req.getNamespace();
final String capNamespace = cap.getNamespace();
if (!reqNamespace.equals(capNamespace)) {
return false;
}
/*
* final String effective = req.getDirectives().get(
* Namespace.REQUIREMENT_EFFECTIVE_DIRECTIVE); if (!(effective == null
* || effective .equals(Namespace.EFFECTIVE_RESOLVE))) { return false; }
*/
final String filter = req.getDirectives()
.get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
try {
if (!(filter == null || RFC1960Filter.fromString(filter)
.matches(cap.getAttributes()))) {
return false;
}
return matches0(capNamespace, req, cap, filter);
} catch (final InvalidSyntaxException e) {
// TODO: to log
e.printStackTrace();
return false;
}
}
// match on a prefiltered result
static boolean matches0(final String namespace, final Requirement req,
final Capability cap, final String filterStr) {
if (!namespace.startsWith("osgi.wiring.")) {
return true;
}
final String mandatory = cap.getDirectives()
.get(Namespace.RESOLUTION_MANDATORY);
if (mandatory == null) {
return true;
}
final Set<String> mandatoryAttributes = new HashSet<String>(
Arrays.asList(Utils.splitString(
Utils.unQuote(mandatory).toLowerCase(), ',')));
final Matcher matcher = FILTER_ASSERT_MATCHER
.matcher(filterStr == null ? "" : filterStr);
while (matcher.find()) {
mandatoryAttributes.remove(matcher.group(1));
}
return mandatoryAttributes.isEmpty();
}
BundleContextImpl createBundleContext(final AbstractBundle bundle) {
return new BundleContextImpl(bundle);
}
List<AbstractBundle> getBundleWithSymbolicName(final String symbolicName) {
final List<AbstractBundle> list = symbolicName_bundles
.lookup(symbolicName);
return list;
}
/**
* Add an installed Fragment to the framework. From here it can be attached
* to potential host bundles.
*
* @param bundle
* the bundle object of the fragment
* @param fragmentStr
* the fragment-host description of the fragment
* @param exports
* the fragment's exports
* @param imports
* the fragment's imports
* @param dynamicImports
* the fragment's dynamic imports
* @param requireBundles
* the fragment's require bundle
* @param classpathStrings
* the fragment's class paths
*/
void addFragment(final Revision fragment) throws BundleException {
if (fragment.isExtensionBundle()) {
try {
addURL.invoke(systemBundleClassLoader,
fragment.createURL("/", null));
} catch (final Exception e) {
// FIXME: to log
e.printStackTrace();
}
extensionBundles.add((BundleImpl) fragment.getBundle());
}
final String fragmentHostName = fragment.getFragmentHost();
fragmentIndex.insert(fragmentHostName, fragment);
}
/**
* Remove a fragment from the map of unattached fragments.
*
* @param fragment
* the fragment bundle to remove
*/
void removeFragment(final Revision fragment) {
fragmentIndex.remove(fragment.getFragmentHost(), fragment);
final String fragmentHostName = fragment.getFragmentHost();
if (fragmentHostName.equals(Constants.SYSTEM_BUNDLE_SYMBOLICNAME)
|| fragmentHostName.equals(FRAMEWORK_SYMBOLIC_NAME)) {
extensionBundles.remove(fragment.getBundle());
}
}
/**
* Get Fragments matching a host bundle.
*
* @param hostBundle
* , the host bundle, for which fragments should be found
* @return an array of fragments, which should be attached to the host
* bundle
*/
List<Revision> getFragments(final BundleRevision hostBundle) {
final List<Revision> candidates = new ArrayList<Revision>(
fragmentIndex.lookup(hostBundle.getSymbolicName()));
if (!candidates.isEmpty()) {
final Capability cap = hostBundle
.getCapabilities(HostNamespace.HOST_NAMESPACE).get(0);
for (final Iterator<Revision> iter = candidates.iterator(); iter
.hasNext();) {
final Requirement req = iter.next()
.getRequirements(HostNamespace.HOST_NAMESPACE).get(0);
if (!matches(req, cap)) {
iter.remove();
}
}
}
return candidates;
}
/**
* delete a directory with all subdirs.
*
* @param path
* the directory.
*/
static void deleteDirectory(final File path) {
final File[] files = path.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
deleteDirectory(files[i]);
} else {
files[i].delete();
}
}
path.delete();
}
/*
* framework operations
*/
/**
* unregister a service.
*
* @param sref
* the service reference.
*/
void unregisterService(final ServiceReference<?> sref) {
// remove all class entries
final String[] clazzes = (String[]) sref
.getProperty(Constants.OBJECTCLASS);
serviceRegistry.removeAll(clazzes, sref);
boolean isHook = false;
for (int i = 0; i < clazzes.length; i++) {
@SuppressWarnings("unchecked")
final List<ServiceReference<?>> hookList = (List<ServiceReference<?>>) hooks
.get(clazzes[i]);
if (hookList != null) {
isHook = true;
hookList.remove(sref);
}
}
final AbstractBundle bundle = (AbstractBundle) sref.getBundle();
bundle.registeredServices.remove(sref);
// dispose list, if empty
if (bundle.registeredServices.isEmpty()) {
bundle.registeredServices = null;
}
if (!isHook) {
notifyServiceListeners(ServiceEvent.UNREGISTERING, sref, null);
}
if (LOG_ENABLED && DEBUG_SERVICES) {
logger.log(LogService.LOG_INFO,
"Framework: UNREGISTERED SERVICE " + sref);
}
}
void notifyBundleListeners(final int state, final Bundle bundle) {
notifyBundleListeners(state, bundle, bundle);
}
/**
* notify all bundle listeners.
*
* @param state
* the new state.
* @param bundle
* the bundle.
*/
void notifyBundleListeners(final int state, final Bundle bundle,
final Bundle origin) {
if (syncBundleListeners.isEmpty() && bundleListeners.isEmpty()) {
return;
}
final BundleEvent event = new BundleEvent(state, bundle, origin);
final SynchronousBundleListener[] syncs;
final BundleListener[] asyncs;
// call the hooks, if any
if (!bundleEventHooks.isEmpty()) {
final ArrayList<SynchronousBundleListener> syncListeners = new ArrayList<SynchronousBundleListener>(
syncBundleListeners);
final ArrayList<BundleListener> asyncListeners = new ArrayList<BundleListener>(
bundleListeners);
final ConciergeCollections.DeltaTrackingRemoveOnlyList<BundleContext> contexts = new ConciergeCollections.DeltaTrackingRemoveOnlyList<BundleContext>(
bundleListenerMap.keySet());
for (final ServiceReferenceImpl<org.osgi.framework.hooks.bundle.EventHook> sref : bundleEventHooks) {
final org.osgi.framework.hooks.bundle.EventHook eventHook = sref
.getService(Concierge.this);
if (eventHook != null) {
try {
eventHook.event(event, contexts);
} catch (final Throwable t) {
// TODO: to log?
}
}
sref.ungetService(Concierge.this);
}
for (final BundleContext removed : contexts.getRemoved()) {
for (final BundleListener listener : bundleListenerMap
.get(removed)) {
syncListeners.remove(listener);
asyncListeners.remove(listener);
}
}
syncs = syncListeners.toArray(
new SynchronousBundleListener[syncListeners.size()]);
asyncs = asyncListeners
.toArray(new BundleListener[asyncListeners.size()]);
} else {
syncs = syncBundleListeners.toArray(
new SynchronousBundleListener[syncBundleListeners.size()]);
asyncs = bundleListeners
.toArray(new BundleListener[bundleListeners.size()]);
}
for (int i = 0; i < syncs.length; i++) {
syncs[i].bundleChanged(event);
}
// asynchronous listeners do not get these events
final int type = event.getType();
if (bundleListeners.isEmpty() || (type & (BundleEvent.STARTING
| BundleEvent.STOPPING | BundleEvent.LAZY_ACTIVATION)) > 0) {
return;
}
for (int i = 0; i < asyncs.length; i++) {
asyncs[i].bundleChanged(event);
}
}
void notifyFrameworkListeners(final int state, final Bundle bundle,
final Throwable throwable) {
notifyFrameworkListeners(
frameworkListeners.toArray(
new FrameworkListener[frameworkListeners.size()]),
state, bundle, throwable);
}
void publishCapabilities(final List<? extends Capability> caps) {
for (final Capability cap : caps) {
capabilityRegistry.add(cap);
}
}
// void removeCapabilities(final List<? extends Capability> caps) {
// for (final Capability cap : caps) {
// capabilityRegistry.remove(cap);
// }
// }
void removeCapabilities(final Revision resource) {
capabilityRegistry.removeAll(resource);
for (final HostedCapability hosted : resource.getHostedCapabilities()) {
capabilityRegistry.remove(hosted);
}
}
void checkForCollision(final int operation, final Bundle contextOwner,
final BundleRevision revision) throws BundleException {
assert context != null;
if (revision == null) {
throw new IllegalArgumentException("revision==null");
}
if (collisionPolicy == COLLISION_POLICY_MULTIPLE) {
return;
}
final Version version = revision.getVersion();
final ArrayList<Bundle> collisions = new ArrayList<Bundle>();
final List<AbstractBundle> existing = symbolicName_bundles
.get(revision.getSymbolicName());
if (existing == null) {
return;
}
if (version == null) {
throw new IllegalStateException("version==null");
}
for (final AbstractBundle b : existing) {
if (version.equals(b.getVersion())) {
collisions.add(b);
}
}
if (operation == CollisionHook.UPDATING) {
collisions.remove(revision.getBundle());
}
if (collisions.isEmpty()) {
return;
}
if (collisionPolicy == COLLISION_POLICY_SINGLE) {
throw new BundleException(
"Bundle with same symbolic name and same version is already installed",
BundleException.DUPLICATE_BUNDLE_ERROR);
} else if (collisionPolicy == COLLISION_POLICY_NONE) {
final ConciergeCollections.RemoveOnlyList<Bundle> list = new ConciergeCollections.RemoveOnlyList<Bundle>(
collisions);
for (final ServiceReferenceImpl<CollisionHook> hookRef : bundleCollisionHooks) {
final CollisionHook hook = hookRef.getService(this);
if (hook != null) {
hook.filterCollisions(operation, contextOwner, list);
}
hookRef.ungetService(this);
}
if (!list.isEmpty()) {
throw new BundleException(
"Bundle with same symbolic name and same version is already installed",
BundleException.DUPLICATE_BUNDLE_ERROR);
}
}
}
/**
* notify all framework listeners.
*
* @param state
* the new state.
* @param bundle
* the bundle.
* @param throwable
* a throwable.
*/
protected void notifyFrameworkListeners(final FrameworkListener[] listeners,
final int state, final Bundle bundle, final Throwable throwable) {
if (listeners.length == 0) {
return;
}
final FrameworkEvent event = new FrameworkEvent(state, bundle,
throwable);
for (int i = 0; i < listeners.length; i++) {
final FrameworkListener listener = listeners[i];
if (SECURITY_ENABLED) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
listener.frameworkEvent(event);
return null;
}
});
} else {
listener.frameworkEvent(event);
}
}
}
/**
* notify all service listeners.
*
* @param state
* the new state.
* @param reference
* the service reference.
*/
@SuppressWarnings("deprecation")
void notifyServiceListeners(final int state,
final ServiceReference<?> reference,
final Map<String, ?> oldProperties) {
if (serviceListeners.isEmpty()) {
return;
}
final ServiceEvent event = new ServiceEvent(state, reference);
final ServiceEvent endmatchEvent = state == ServiceEvent.MODIFIED
? new ServiceEvent(ServiceEvent.MODIFIED_ENDMATCH, reference)
: null;
final ServiceListenerEntry[] entries;
if (serviceEventListenerHooks.isEmpty()
&& serviceEventHooks.isEmpty()) {
entries = serviceListeners
.toArray(new ServiceListenerEntry[serviceListeners.size()]);
} else {
// prepare the data structures
final MultiMap<BundleContext, ListenerInfo> mmap = new MultiMap<BundleContext, ListenerInfo>();
final List<ServiceListenerEntry> serviceListenersCopy = new ArrayList<Concierge.ServiceListenerEntry>(serviceListeners);
for (final Iterator<ServiceListenerEntry> iter = serviceListenersCopy
.iterator(); iter.hasNext();) {
final ServiceListenerEntry entry = iter.next();
mmap.insert(entry.bundle.context, entry);
}
final ConciergeCollections.RemoveOnlyMap<BundleContext, Collection<ListenerInfo>> map = new ConciergeCollections.RemoveOnlyMap<BundleContext, Collection<ListenerInfo>>();
for (final BundleContext ctx : mmap.keySet()) {
final Collection<ListenerInfo> col = new ConciergeCollections.RemoveOnlyList<ListenerInfo>(
mmap.get(ctx));
map.put(ctx, col);
}
map.seal();
// first call the event hooks
final List<ServiceReferenceImpl<org.osgi.framework.hooks.service.EventHook>> serviceEventHooksCopy
= new ArrayList<ServiceReferenceImpl<org.osgi.framework.hooks.service.EventHook>>(serviceEventHooks);
for (final ServiceReferenceImpl<org.osgi.framework.hooks.service.EventHook> eventHook : serviceEventHooksCopy) {
try {
final org.osgi.framework.hooks.service.EventHook hook = eventHook
.getService(this);
hook.event(event, map.keySet());
} catch (final Throwable t) {
notifyFrameworkListeners(FrameworkEvent.ERROR,
Concierge.this, t);
} finally {
eventHook.ungetService(this);
}
}
// then call the event listener hooks
final List<ServiceReferenceImpl<EventListenerHook>> serviceEventListenerHooksCopy
= new ArrayList<ServiceReferenceImpl<EventListenerHook>>(serviceEventListenerHooks);
for (final Iterator<ServiceReferenceImpl<EventListenerHook>> iter = serviceEventListenerHooksCopy
.iterator(); iter.hasNext();) {
final ServiceReferenceImpl<EventListenerHook> hookRef = iter
.next();
try {
final EventListenerHook hook = hookRef.getService(this);
hook.event(event, map);
} catch (final Throwable t) {
notifyFrameworkListeners(FrameworkEvent.ERROR,
Concierge.this, t);
} finally {
hookRef.ungetService(this);
}
}
final ArrayList<ServiceListenerEntry> list = new ArrayList<ServiceListenerEntry>();
for (final Iterator<ServiceListenerEntry> iter = serviceListenersCopy
.iterator(); iter.hasNext();) {
final ServiceListenerEntry entry = iter.next();
final Collection<ListenerInfo> listeners = map
.get(entry.bundle.context);
if (listeners != null && listeners.contains(entry)) {
list.add(entry);
}
}
entries = list.toArray(new ServiceListenerEntry[list.size()]);
}
final ServiceReferenceImpl<?> ref = (ServiceReferenceImpl<?>) reference;
for (int i = 0; i < entries.length; i++) {
// check if the listener can receive the service event
if (!(entries[i].listener instanceof AllServiceListener)) {
final String[] clazzes = (String[]) reference
.getProperty(Constants.OBJECTCLASS);
if (!ref.isAssignableTo(entries[i].bundle, clazzes)) {
continue;
}
}
if (entries[i].listener instanceof UnfilteredServiceListener
|| entries[i].filter == null
|| entries[i].filter.matches(ref.properties)) {
final ServiceListener listener = entries[i].listener;
if (SECURITY_ENABLED) {
AccessController
.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
listener.serviceChanged(event);
return null;
}
});
} else {
listener.serviceChanged(event);
}
} else if (state == ServiceEvent.MODIFIED) {
if (entries[i].filter.matches(oldProperties)) {
entries[i].listener.serviceChanged(endmatchEvent);
}
}
}
}
/**
* clear all traces of a bundle.
*
* @param bundle
* the bundle.
*/
void clearBundleTrace(final AbstractBundle bundle) {
// remove all registered listeners
if (bundle.registeredFrameworkListeners != null) {
frameworkListeners.removeAll(bundle.registeredFrameworkListeners);
bundle.registeredFrameworkListeners = null;
}
if (bundle.registeredServiceListeners != null) {
serviceListeners.removeAll(bundle.registeredServiceListeners);
bundle.registeredServiceListeners = null;
}
final List<BundleListener> bundleListeners = bundleListenerMap
.get(bundle.context);
if (bundleListeners != null) {
bundleListeners.removeAll(bundleListeners);
syncBundleListeners.removeAll(bundleListeners);
bundleListenerMap.remove(bundle.context);
}
// unregister registered services
final ServiceReference<?>[] regs = bundle.getRegisteredServices();
if (regs != null) {
for (int i = 0; i < regs.length; i++) {
unregisterService(regs[i]);
((ServiceReferenceImpl<?>) regs[i]).invalidate();
}
bundle.registeredServices = null;
}
// unget all using services
final ServiceReference<?>[] refs = bundle.getServicesInUse();
if (refs != null) {
for (int i = 0; i < refs.length; i++) {
((ServiceReferenceImpl<?>) refs[i]).ungetService(bundle);
}
}
}
/**
* install a bundle.
*
* @param location
* the bundle location.
* @return a Bundle object.
* @throws BundleException
* if the installation failed.
*/
BundleImpl installNewBundle(final BundleContext context,
final String location) throws BundleException {
try {
final String location2 = location.indexOf(":") > -1 ? location
: BUNDLE_LOCATION + File.separatorChar + location;
return installNewBundle(context, location2,
new URL(location2).openConnection().getInputStream());
} catch (final IOException e) {
throw new BundleException("Cannot retrieve bundle from " + location,
BundleException.READ_ERROR, e);
}
}
/**
* install a bundle from input stream.
*
* @param location
* the bundle location.
* @param in
* the input stream.
* @return a Bundle object.
* @throws BundleException
* if the installation failed.
*/
synchronized BundleImpl installNewBundle(final BundleContext context,
final String location, final InputStream in)
throws BundleException {
final AbstractBundle cached;
if ((cached = location_bundles.get(location)) != null) {
if (!bundleFindHooks.isEmpty()) {
final Bundle[] test = filterWithBundleHooks(context,
Arrays.asList((Bundle) cached));
if (test.length == 0) {
throw new BundleException(
"Existing bundle rejected by find hooks",
BundleException.REJECTED_BY_HOOK);
}
}
return (BundleImpl) cached;
}
final BundleImpl bundle = new BundleImpl(this, context, location,
nextBundleID++, in);
bundle.install();
// notify the listeners
notifyBundleListeners(BundleEvent.INSTALLED, bundle,
context.getBundle());
storeMetadata();
return bundle;
}
protected Bundle[] filterWithBundleHooks(final BundleContext context,
final Collection<Bundle> bundles) {
final ConciergeCollections.RemoveOnlyList<Bundle> list = new ConciergeCollections.RemoveOnlyList<Bundle>(
bundles);
for (final ServiceReferenceImpl<org.osgi.framework.hooks.bundle.FindHook> sref : bundleFindHooks) {
final org.osgi.framework.hooks.bundle.FindHook findHook = sref
.getService(Concierge.this);
if (findHook != null) {
try {
findHook.find(context, list);
} catch (final Throwable t) {
// TODO: log?
}
}
sref.ungetService(Concierge.this);
}
return list.toArray(new Bundle[list.size()]);
}
@SuppressWarnings({ "unchecked" })
protected <T> T getService(final Class<T> cls, final Version version) {
final List<ServiceReference<?>> refs = serviceRegistry
.lookup(cls.getName());
for (final ServiceReference<?> ref : refs) {
final Version other = (Version) ref
.getProperty(Constants.VERSION_ATTRIBUTE);
if (other != null && other.compareTo(version) == 0) {
return (T) ((ServiceReferenceImpl<?>) ref).service;
}
}
return null;
}
/*
* inner classes
*/
/**
* The bundle context implementation.
*
* @author Jan S. Rellermeyer
*
*/
final class BundleContextImpl implements BundleContext {
/**
* is the context valid ?
*/
boolean isValid = true;
/**
* the bundle.
*/
final AbstractBundle bundle;
protected BundleContextImpl(final AbstractBundle bundle) {
this.bundle = bundle;
}
/**
* check, if the context is valid.
*/
private void checkValid() {
if (!isValid) {
throw new IllegalStateException("BundleContext of bundle "
+ bundle
+ " used after bundle has been stopped or uninstalled.");
}
}
/**
* add a bundle listener.
*
* @param listener
* a bundle listener.
* @see org.osgi.framework.BundleContext#addBundleListener(org.osgi.framework.BundleListener)
*/
public void addBundleListener(final BundleListener listener) {
checkValid();
final List<BundleListener> registered = bundleListenerMap.get(this);
if (registered == null || !registered.contains(listener)) {
if (listener instanceof SynchronousBundleListener) {
syncBundleListeners
.add((SynchronousBundleListener) listener);
} else {
bundleListeners.add(listener);
}
bundleListenerMap.insert(this, listener);
}
}
/**
* add a framework listener.
*
* @param listener
* a framework listener.
* @see org.osgi.framework.BundleContext#addFrameworkListener(org.osgi.framework.FrameworkListener)
*
*/
public void addFrameworkListener(final FrameworkListener listener) {
checkValid();
// if (bundle == Concierge.this) {
// return;
// }
if (bundle.registeredFrameworkListeners == null) {
bundle.registeredFrameworkListeners = new ArrayList<FrameworkListener>(
1);
}
if (!bundle.registeredFrameworkListeners.contains(listener)) {
frameworkListeners.add(listener);
bundle.registeredFrameworkListeners.add(listener);
}
}
/**
* add a service listener.
*
* @param listener
* the service listener.
* @param filterExpr
* the filter String.
* @throws InvalidSyntaxException
* if the filter string is invalid.
* @see org.osgi.framework.BundleContext#addServiceListener(org.osgi.framework.ServiceListener,
* java.lang.String)
*
*/
public void addServiceListener(final ServiceListener listener,
final String filterExpr) throws InvalidSyntaxException {
checkValid();
final ServiceListenerEntry entry = new ServiceListenerEntry(bundle,
listener, filterExpr);
if (bundle.registeredServiceListeners == null) {
bundle.registeredServiceListeners = new ArrayList<ServiceListenerEntry>(
1);
}
synchronized (bundle.registeredServiceListeners) {
final ServiceListenerEntry existing = getRegisteredServiceListener(
listener);
if (existing != null) {
removeServiceListener(listener);
}
bundle.registeredServiceListeners.add(entry);
serviceListeners.add(entry);
}
informListenerHooks(serviceListenerHooks,
new ServiceListenerEntry[] { entry }, true);
}
private void informListenerHooks(
final Collection<ServiceReferenceImpl<ListenerHook>> hooks,
final ServiceListenerEntry[] entries, final boolean added) {
if (hooks == null || hooks.isEmpty()) {
return;
}
if (!added) {
for (final ServiceListenerEntry entry : entries) {
entry.removed = true;
}
}
final Collection<ListenerInfo> c = new ConciergeCollections.RemoveOnlyList<ListenerInfo>(
Arrays.asList(entries));
for (final Iterator<ServiceReferenceImpl<ListenerHook>> iter = hooks
.iterator(); iter.hasNext();) {
final ServiceReferenceImpl<ListenerHook> hookRef = iter.next();
final ListenerHook hook = getService(hookRef);
try {
if (added) {
hook.added(c);
} else {
hook.removed(c);
}
} catch (final Throwable t) {
notifyFrameworkListeners(FrameworkEvent.ERROR,
Concierge.this, t);
}
ungetService(hookRef);
}
}
/**
* Determine if given service listener has been registered.
*
* @param listener
* @return <code>true</code> if the listener is registered.
*/
private ServiceListenerEntry getRegisteredServiceListener(
final ServiceListener listener) {
final ServiceListenerEntry[] listeners = bundle.registeredServiceListeners
.toArray(
new ServiceListenerEntry[bundle.registeredServiceListeners
.size()]);
for (int i = 0; i < listeners.length; i++) {
if (listeners[i].bundle == bundle
&& listeners[i].listener == listener) {
return listeners[i];
}
}
return null;
}
/**
* add a service listener.
*
* @param listener
* the service listener.
* @see org.osgi.framework.BundleContext#addServiceListener(org.osgi.framework.ServiceListener)
*
*/
public void addServiceListener(final ServiceListener listener) {
checkValid();
try {
addServiceListener(listener, null);
} catch (final InvalidSyntaxException e) {
// does not happen
}
}
/**
* create a filter.
*
* @param filter
* the filter string.
* @return a Filter object.
* @throws InvalidSyntaxException
* if the filter string is invalid.
* @see org.osgi.framework.BundleContext#createFilter(java.lang.String)
*
*/
public Filter createFilter(final String filter)
throws InvalidSyntaxException {
if (filter == null) {
throw new NullPointerException();
}
return RFC1960Filter.fromString(filter);
}
/**
* get the bundle.
*
* @return the bundle.
* @see org.osgi.framework.BundleContext#getBundle()
*
*/
public Bundle getBundle() {
return bundle;
}
/**
* get a bundle by id.
*
* @param id
* the bundle id.
* @return the bundle object.
* @see org.osgi.framework.BundleContext#getBundle(long)
*
*/
public Bundle getBundle(final long id) {
checkValid();
final Bundle bundle = bundleID_bundles.get(new Long(id));
if (bundle == null || bundleFindHooks.isEmpty()) {
return bundle;
}
final Bundle[] bundles = filterWithBundleHooks(this,
Arrays.asList(bundle));
return bundles.length == 0 ? null : bundles[0];
}
/**
* get all bundles.
*
* @return the array of bundles.
* @see org.osgi.framework.BundleContext#getBundles()
*
*/
public Bundle[] getBundles() {
checkValid();
final ArrayList<Bundle> bundleList = new ArrayList<Bundle>(bundles);
bundleList.add(0, Concierge.this);
if (bundleFindHooks.isEmpty()) {
return bundleList.toArray(new Bundle[bundleList.size()]);
}
return filterWithBundleHooks(this, bundleList);
}
/**
* get a data file.
*
* @param filename
* the name of the file
* @return a File object.
* @see org.osgi.framework.BundleContext#getDataFile(java.lang.String)
*
*/
public File getDataFile(final String filename) {
checkValid();
final String path;
if (bundle == Concierge.this) {
path = Concierge.this.STORAGE_LOCATION;
} else {
path = bundle.storageLocation;
}
try {
final File file = new File(path + "/data", filename);
// ensure directory is available
// handle filename = "", create /data folder in this case
if (filename.isEmpty()) {
file.mkdirs();
} else {
// TODO Hmm, create all subdirs too, or only "/data" folder?
file.getParentFile().mkdirs();
}
return file;
} catch (final Exception e) {
e.printStackTrace();
return null;
}
}
/**
* get a system property.
*
* @param key
* the key.
* @return the value.
* @see org.osgi.framework.BundleContext#getProperty(java.lang.String)
*
*/
public String getProperty(final String key) {
return Concierge.this.properties.getProperty(key);
}
/**
* get the service object.
*
* @param reference
* the service reference.
* @return the service object.
* @see org.osgi.framework.BundleContext#getService(org.osgi.framework.ServiceReference)
*
*/
public <S> S getService(final ServiceReference<S> reference) {
checkValid();
if (reference == null) {
throw new NullPointerException("Null service reference.");
}
if (SECURITY_ENABLED) {
final String[] clazzes = (String[]) reference
.getProperty(Constants.OBJECTCLASS);
for (int i = 0; i < clazzes.length; i++) {
try {
AccessController.checkPermission(new ServicePermission(
clazzes[i], ServicePermission.GET));
return ((ServiceReferenceImpl<S>) reference)
.getService(bundle);
} catch (final SecurityException se) {
continue;
}
}
throw new SecurityException(
"Caller does not have permissions for getting service from "
+ reference);
}
return ((ServiceReferenceImpl<S>) reference).getService(bundle);
}
/**
* get all service references matching a filter.
*
* @param clazz
* The class name with which the service was registered or
* <code>null</code> for all services.
* @param filter
* The filter criteria.
* @return An array of <code>ServiceReference</code> objects or
* <code>null</code> if no services are registered which satisfy
* the search.
* @throws InvalidSyntaxException
* If <code>filter</code> contains an invalid filter string
* that cannot be parsed.
* @throws IllegalStateException
* If this BundleContext is no longer valid.
* @since 1.3
* @see org.osgi.framework.BundleContext#getAllServiceReferences(java.lang.String,
* java.lang.String)
*
*/
public ServiceReference<?>[] getAllServiceReferences(final String clazz,
final String filter) throws InvalidSyntaxException {
return getServiceReferences(clazz, filter, true);
}
/**
* get all service references matching a filter.
*
* @param clazz
* the class name.
* @param filter
* the filter.
* @return the array of matching service references.
* @throws InvalidSyntaxException
* if the filter string is invalid.
* @see org.osgi.framework.BundleContext#getServiceReferences(java.lang.String,
* java.lang.String)
*
*/
public ServiceReference<?>[] getServiceReferences(final String clazz,
final String filter) throws InvalidSyntaxException {
return getServiceReferences(clazz, filter, false);
}
private final ServiceReference<?>[] getServiceReferences(
final String clazz, final String filter, final boolean all)
throws InvalidSyntaxException {
checkValid();
final Filter theFilter = RFC1960Filter.fromString(filter);
final Collection<ServiceReference<?>> references;
if (clazz == null) {
references = serviceRegistry.getAllValues();
} else {
references = serviceRegistry.get(clazz);
}
final List<ServiceReference<?>> result = new ArrayList<ServiceReference<?>>();
if (references != null) {
final ServiceReferenceImpl<?>[] refs = references
.toArray(new ServiceReferenceImpl[references.size()]);
for (int i = 0; i < refs.length; i++) {
if (theFilter.match(refs[i]) && (all
|| refs[i].isAssignableTo(bundle, (String[]) refs[i]
.getProperty(Constants.OBJECTCLASS)))) {
result.add(refs[i]);
}
}
}
if (!serviceFindHooks.isEmpty()) {
final Collection<ServiceReference<?>> c = new ConciergeCollections.RemoveOnlyList<ServiceReference<?>>(
result);
for (final Iterator<ServiceReferenceImpl<FindHook>> iter = serviceFindHooks
.iterator(); iter.hasNext();) {
final ServiceReferenceImpl<FindHook> hookRef = iter.next();
final FindHook hook = getService(hookRef);
try {
hook.find(this, clazz, filter, all, c);
} catch (final Throwable t) {
notifyFrameworkListeners(FrameworkEvent.ERROR,
Concierge.this, t);
}
ungetService(hookRef);
}
return c.size() == 0 ? null
: (ServiceReference[]) c
.toArray(new ServiceReference[c.size()]);
}
if (LOG_ENABLED && DEBUG_SERVICES) {
logger.log(LogService.LOG_INFO,
"Framework: REQUESTED SERVICES "
+ (clazz == null ? "(no class)" : clazz) + " "
+ (filter == null ? "(no filter)"
: "filter=" + filter));
logger.log(LogService.LOG_INFO, "\tRETURNED " + result);
}
return result.size() == 0 ? null
: (ServiceReference[]) result
.toArray(new ServiceReference[result.size()]);
}
/**
* get a service reference.
*
* @param clazz
* the class name.
* @return the service reference or null if no such service is
* registered.
*
* @see org.osgi.framework.BundleContext#getServiceReference(java.lang.String)
*
*/
public ServiceReference<?> getServiceReference(final String clazz) {
checkValid();
ServiceReference<?> winner = null;
int maxRanking = -1;
long lastServiceID = Long.MAX_VALUE;
ServiceReference<?>[] list = null;
try {
list = getServiceReferences(clazz, null, true);
} catch (final InvalidSyntaxException e) {
}
if (list == null) {
return null;
}
final ServiceReference<?>[] candidates = list;
for (int i = 0; i < candidates.length; i++) {
final Integer rankProp = (Integer) candidates[i]
.getProperty(Constants.SERVICE_RANKING);
final int ranking = rankProp != null ? rankProp.intValue() : 0;
final long serviceID = ((Long) candidates[i]
.getProperty(Constants.SERVICE_ID)).longValue();
if (ranking > maxRanking
|| ranking == maxRanking && serviceID < lastServiceID) {
winner = candidates[i];
maxRanking = ranking;
lastServiceID = serviceID;
}
}
if (LOG_ENABLED && DEBUG_SERVICES) {
logger.log(LogService.LOG_INFO,
"Framework: REQUESTED SERVICE " + clazz);
logger.log(LogService.LOG_INFO, "\tRETURNED " + winner);
}
return winner;
}
/**
* install a new bundle.
*
* @param location
* the bundle location.
* @return the bundle object.
* @throws BundleException
* if something goes wrong.
* @see org.osgi.framework.BundleContext#installBundle(java.lang.String)
*
*/
public Bundle installBundle(final String location)
throws BundleException {
if (location == null) {
throw new IllegalArgumentException("Location must not be null");
}
checkValid();
// TODO: check AdminPermission(new bundle, LIFECYCLE)
return installNewBundle(this, location);
}
/**
* install a new bundle from input stream.
*
* @param location
* the location.
* @param in
* the input stream.
* @return the bundle object.
* @throws BundleException
* if something goes wrong.
* @see org.osgi.framework.BundleContext#installBundle(java.lang.String,
* java.io.InputStream)
*
*/
public Bundle installBundle(final String location, final InputStream in)
throws BundleException {
if (location == null) {
throw new IllegalArgumentException("Location must not be null");
}
checkValid();
// TODO: check AdminPermission(new bundle, LIFECYCLE)
return installNewBundle(this, location, in);
}
/**
* register a new service.
*
* @param clazzes
* the classes under which the service is registered.
* @param service
* the service object
* @param serviceProperties
* the properties.
* @return the service registration.
* @see org.osgi.framework.BundleContext#registerService(java.lang.String[],
* java.lang.Object, java.util.Dictionary)
* @context BundleContext
*/
public ServiceRegistration<?> registerService(final String[] clazzes,
final Object service,
final Dictionary<String, ?> serviceProperties) {
checkValid();
if (service == null) {
throw new IllegalArgumentException(
"Cannot register a null service");
}
if (SECURITY_ENABLED) {
for (int i = 0; i < clazzes.length; i++) {
AccessController.checkPermission(new ServicePermission(
clazzes[i], ServicePermission.REGISTER));
}
}
final ServiceReferenceImpl<?> sref = new ServiceReferenceImpl<Object>(
Concierge.this, bundle, service, serviceProperties,
clazzes);
// lazy initialization
if (bundle.registeredServices == null) {
bundle.registeredServices = new ArrayList<ServiceReference<?>>(
1);
}
bundle.registeredServices.add(sref);
boolean isHook = false;
// and now register the service for all classes ...
for (int counter = 0; counter < clazzes.length; counter++) {
final String clazz = clazzes[counter];
isHook = checkHook(clazz, sref, true);
serviceRegistry.insert(clazz, sref);
}
if (LOG_ENABLED && DEBUG_SERVICES) {
logger.log(LogService.LOG_INFO,
"Framework: REGISTERED SERVICE " + clazzes[0]);
}
if (!isHook) {
notifyServiceListeners(ServiceEvent.REGISTERED, sref, null);
}
return sref.registration;
}
private boolean checkHook(final String clazz,
final ServiceReference<?> sref, final boolean add) {
@SuppressWarnings("unchecked")
final List<ServiceReference<?>> hookList = (List<ServiceReference<?>>) hooks
.get(clazz);
if (hookList == null) {
return false;
}
if (add) {
hookList.add(sref);
// not required for collision hook, weaving hook,
// resolverHookFactory...
Collections.sort(hookList, Collections.reverseOrder());
} else {
// FIXME: remove!
}
// special case: ListenerHook
if (add && (Object) hookList == (Object) serviceListenerHooks) {
@SuppressWarnings("unchecked")
final ServiceReferenceImpl<ListenerHook> hookRef = (ServiceReferenceImpl<ListenerHook>) sref;
if (serviceListeners != null) {
try {
informListenerHooks(Collections.singletonList(hookRef),
serviceListeners.toArray(
new ServiceListenerEntry[serviceListeners
.size()]),
true);
} catch (final Throwable t) {
notifyFrameworkListeners(FrameworkEvent.ERROR,
sref.getBundle(), t);
}
}
}
return true;
}
/**
* register a new service.
*
* @param clazz
* the class under which the service is registered.
* @param service
* the service object.
* @param properties
* the properties.
* @return the service registration.
* @see org.osgi.framework.BundleContext#registerService(java.lang.String,
* java.lang.Object, java.util.Dictionary)
*
*/
public ServiceRegistration<?> registerService(final String clazz,
final Object service, final Dictionary<String, ?> properties) {
return registerService(new String[] { clazz }, service, properties);
}
/**
* remove a bundle listener.
*
* @param listener
* a bundle listener.
* @see org.osgi.framework.BundleContext#removeBundleListener(org.osgi.framework.BundleListener)
*
*/
public void removeBundleListener(final BundleListener listener) {
checkValid();
if (bundle == Concierge.this) {
return;
}
(listener instanceof SynchronousBundleListener ? syncBundleListeners
: bundleListeners).remove(listener);
bundleListenerMap.remove(this, listener);
}
/**
* remove a framework listener.
*
* @param listener
* a framework listener.
* @see org.osgi.framework.BundleContext#removeFrameworkListener(org.osgi.framework.FrameworkListener)
*
*/
public void removeFrameworkListener(final FrameworkListener listener) {
checkValid();
if (bundle == Concierge.this) {
return;
}
final AbstractBundle b = bundle;
frameworkListeners.remove(listener);
if (b.registeredFrameworkListeners != null) {
b.registeredFrameworkListeners.remove(listener);
if (b.registeredFrameworkListeners.isEmpty()) {
b.registeredFrameworkListeners = null;
}
}
}
/**
* remove a service listener.
*
* @param listener
* the service listener.
* @see org.osgi.framework.BundleContext#removeServiceListener(org.osgi.framework.ServiceListener)
*
*/
public void removeServiceListener(final ServiceListener listener) {
checkValid();
final ServiceListenerEntry entry;
synchronized (bundle.registeredServiceListeners) {
entry = getRegisteredServiceListener(listener);
if (entry == null) {
return;
}
entry.removed = true;
serviceListeners.remove(entry);
bundle.registeredServiceListeners.remove(entry);
if (bundle.registeredServiceListeners.isEmpty()) {
bundle.registeredServiceListeners = null;
}
}
informListenerHooks(serviceListenerHooks,
new ServiceListenerEntry[] { entry }, false);
}
/**
* unget a service.
*
* @param reference
* the service reference of the service
* @return true is the service is still in use by other bundles, false
* otherwise.
* @see org.osgi.framework.BundleContext#ungetService(org.osgi.framework.ServiceReference)
*
*/
public synchronized boolean ungetService(
final ServiceReference<?> reference) {
checkValid();
return ((ServiceReferenceImpl<?>) reference).ungetService(bundle);
}
// FIXME: should be the other way around...
@SuppressWarnings("unchecked")
public <S> ServiceRegistration<S> registerService(final Class<S> clazz,
final S service, final Dictionary<String, ?> properties) {
return (ServiceRegistration<S>) registerService(clazz.getName(),
service, properties);
}
// FIXME: should be the other way around...
@SuppressWarnings("unchecked")
public <S> ServiceReference<S> getServiceReference(
final Class<S> clazz) {
return (ServiceReference<S>) getServiceReference(
clazz == null ? null : clazz.getName());
}
// FIXME: should be the other way around...
@SuppressWarnings({ "unchecked", "rawtypes" })
public <S> Collection<ServiceReference<S>> getServiceReferences(
final Class<S> clazz, final String filter)
throws InvalidSyntaxException {
final ServiceReference[] refs = getServiceReferences(
clazz.getName(), filter);
if (refs == null) {
return Collections.EMPTY_LIST;
} else {
return (Collection) Arrays.asList(refs);
}
}
/**
* @see org.osgi.framework.BundleContext#getBundle(java.lang.String)
* @since 1.6
*/
public Bundle getBundle(final String location) {
return location_bundles.get(location);
}
}
/**
* An entry consisting of service listener and filter.
*
* @author Jan S. Rellermeyer
*/
static final class ServiceListenerEntry
implements EventListener, ListenerHook.ListenerInfo {
final AbstractBundle bundle;
/**
* the listener.
*/
final ServiceListener listener;
/**
* the filter.
*/
final Filter filter;
boolean removed;
/**
* create a new entry.
*
* @param listener
* the listener.
* @param filter
* the filter.
* @throws InvalidSyntaxException
* if the filter cannot be parsed.
*/
protected ServiceListenerEntry(final AbstractBundle bundle,
final ServiceListener listener, final String filter)
throws InvalidSyntaxException {
this.bundle = bundle;
this.listener = listener;
this.removed = false;
this.filter = filter == null ? null
: RFC1960Filter.fromString(filter);
}
/**
* check for equality.
*
* @param other
* the other object.
* @return true, if the two objects are equal.
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(final Object other) {
if (other instanceof ServiceListenerEntry) {
final ServiceListenerEntry entry = (ServiceListenerEntry) other;
return bundle == entry.bundle
&& listener.equals(entry.listener);
}
return false;
}
/**
* get the hash code.
*
* @return the hash code.
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return listener.hashCode()
+ (filter != null ? filter.hashCode() >> 8 : 0);
}
/**
* get a string representation.
*
* @return a string representation.
* @see java.lang.Object#toString()
*/
public String toString() {
return listener + " " + filter;
}
public BundleContext getBundleContext() {
return bundle.context;
}
public String getFilter() {
return filter == null ? null : filter.toString();
}
public boolean isRemoved() {
return removed;
}
}
protected final boolean isSecurityEnabled() {
return SECURITY_ENABLED;
}
static class CapabilityRegistry {
// namespace -> list of capability
private final MultiMap<String, Capability> capabilities = new MultiMap<String, Capability>();
// namespace -> value (of canonical attribute) -> list of capability
private final HashMap<String, MultiMap<String, Capability>> defaultAttributeIndex = new HashMap<String, MultiMap<String, Capability>>();
void add(final Capability cap) {
final String namespace = cap.getNamespace();
capabilities.insert(namespace, cap);
final Object defaultAttribute = cap.getAttributes().get(namespace);
if (defaultAttribute instanceof String) {
MultiMap<String, Capability> attributeIndex = defaultAttributeIndex
.get(namespace);
if (attributeIndex == null) {
attributeIndex = new MultiMap<String, Capability>();
defaultAttributeIndex.put(namespace, attributeIndex);
}
attributeIndex.insert((String) defaultAttribute, cap);
}
}
void addAll(final Resource res) {
for (final Capability cap : res.getCapabilities(null)) {
add(cap);
}
}
boolean remove(final Capability cap) {
final String namespace = cap.getNamespace();
capabilities.remove(namespace, cap);
final Object defaultAttribute = cap.getAttributes().get(namespace);
final MultiMap<String, Capability> attributeIndex = defaultAttributeIndex
.get(namespace);
if (attributeIndex == null) {
return false;
}
if (defaultAttribute != null
&& defaultAttribute instanceof String) {
final boolean success = attributeIndex.remove(defaultAttribute,
cap);
if (success) {
if (attributeIndex.isEmpty()) {
defaultAttributeIndex.remove(namespace);
}
}
return success;
} else {
return false;
}
}
void removeAll(final Resource res) {
for (final Capability cap : res.getCapabilities(null)) {
remove(cap);
}
}
public List<Capability> getByValue(final String namespace,
final String value) {
final MultiMap<String, Capability> attributeIndex = defaultAttributeIndex
.get(namespace);
final List<Capability> result = attributeIndex.get(value);
return result == null ? Collections.<Capability> emptyList()
: new ArrayList<Capability>(result);
}
public List<Capability> getByKey(final String namespace,
final String value) {
final MultiMap<String, Capability> caps = defaultAttributeIndex
.get(namespace);
return caps == null ? Collections.<Capability> emptyList()
: caps.get(value);
}
public List<Capability> getAll(final String namespace) {
final List<Capability> result = capabilities.get(namespace);
return result == null ? Collections.<Capability> emptyList()
: new ArrayList<Capability>(result);
}
@Override
public String toString() {
return capabilities.toString();
}
}
boolean hasWeavingHooks() {
return !weavingHooks.isEmpty();
}
void callWeavingHooks(final WovenClassImpl wovenClass) {
Collections.sort(weavingHooks, Collections.reverseOrder());
final List<ServiceReferenceImpl<WeavingHook>> wHooks = new ArrayList<ServiceReferenceImpl<WeavingHook>>();
wHooks.addAll(weavingHooks);
for (final ServiceReferenceImpl<WeavingHook> sref : wHooks) {
final WeavingHook hook = sref.getService(this);
try {
hook.weave(wovenClass);
} catch (final Throwable t) {
if (!(t instanceof WeavingException)) {
// blacklist the hook
weavingHooks.remove(sref);
}
// framework event
notifyFrameworkListeners(FrameworkEvent.ERROR, sref.bundle, t);
// mark as complete
wovenClass.setComplete();
final ClassFormatError err = new ClassFormatError(
"Error while invoking weaving hook");
err.initCause(t);
throw err;
} finally {
sref.ungetService(this);
}
}
wovenClass.setComplete();
}
/**
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
* @category BundleActivator
*/
public void start(final BundleContext context) throws Exception {
context.registerService(Resolver.class, resolver, null);
}
/**
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
* @category BundleActivator
*/
public void stop(final BundleContext context) throws Exception {
}
String[] getLibraryName(final String libname) {
if (libraryExtensions == null) {
return new String[] { System.mapLibraryName(libname) };
}
final String[] result = new String[libraryExtensions.length + 1];
result[0] = System.mapLibraryName(libname);
for (int i = 0; i < libraryExtensions.length; i++) {
result[i + 1] = libname + "." + libraryExtensions[i];
}
return result;
}
void execPermission(final File libfile) {
if (execPermission == null) {
return;
}
final String cmd = execPermissionPattern.matcher(execPermission)
.replaceAll(
Matcher.quoteReplacement(libfile.getAbsolutePath()));
try {
Runtime.getRuntime().exec(cmd).waitFor();
} catch (final Throwable t) {
t.printStackTrace();
}
}
}