Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Watson2008-12-19 21:10:00 +0000
committerThomas Watson2008-12-19 21:10:00 +0000
commitc0bf408b216d2511900a0e48b4185d50fb6017e2 (patch)
treecc91e6e2a43514f5088471048125db7503fed0b8
parentf1a814066412e3ae14174179eea848452496bb34 (diff)
downloadrt.equinox.framework-c0bf408b216d2511900a0e48b4185d50fb6017e2.tar.gz
rt.equinox.framework-c0bf408b216d2511900a0e48b4185d50fb6017e2.tar.xz
rt.equinox.framework-c0bf408b216d2511900a0e48b4185d50fb6017e2.zip
Bug 254021 Implement new LinkBundleFactory service (rfc 138)
-rw-r--r--bundles/org.eclipse.osgi/.classpath1
-rw-r--r--bundles/org.eclipse.osgi/META-INF/MANIFEST.MF5
-rw-r--r--bundles/org.eclipse.osgi/build.properties1
-rw-r--r--bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleData.java6
-rw-r--r--bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeBase.java111
-rw-r--r--bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeClassLoader.java142
-rw-r--r--bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeConfigurator.java182
-rw-r--r--bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeHelper.java263
-rw-r--r--bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeImpl.java280
-rw-r--r--bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeServiceTracker.java178
-rw-r--r--bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/SurrogateImpl.java93
-rw-r--r--bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/service/internal/composite/CompositeModule.java28
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/AbstractBundle.java6
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleHost.java18
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java33
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/loader/BundleLoaderProxy.java3
-rw-r--r--bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/launch/EquinoxFWClassLoader.java2
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/BaseStorageHook.java11
-rw-r--r--bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/SystemBundleData.java4
-rw-r--r--bundles/org.eclipse.osgi/hookconfigurators.properties3
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/CompositeBundle.java151
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/CompositeBundleFactory.java147
-rw-r--r--bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/SurrogateBundle.java75
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/CompositeResolveHelper.java17
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/CompositeResolveHelperRegistry.java17
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java8
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java124
-rw-r--r--bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java12
28 files changed, 1856 insertions, 65 deletions
diff --git a/bundles/org.eclipse.osgi/.classpath b/bundles/org.eclipse.osgi/.classpath
index 9bffd6a97..0629761ab 100644
--- a/bundles/org.eclipse.osgi/.classpath
+++ b/bundles/org.eclipse.osgi/.classpath
@@ -8,6 +8,7 @@
<classpathentry kind="src" path="supplement/src"/>
<classpathentry kind="src" path="core/adaptor"/>
<classpathentry kind="src" path="core/framework"/>
+ <classpathentry kind="src" path="core/composite"/>
<classpathentry kind="src" path="resolver/src"/>
<classpathentry kind="src" path="defaultAdaptor/src"/>
<classpathentry kind="src" path="eclipseAdaptor/src"/>
diff --git a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
index c43cffc6c..1d4c49726 100644
--- a/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.osgi/META-INF/MANIFEST.MF
@@ -21,6 +21,7 @@ Export-Package: org.eclipse.osgi.event;version="1.0",
org.osgi.framework.launch; version="1.0",
org.osgi.framework.hooks.service; version="1.0",
org.osgi.service.condpermadmin;version="1.1",
+ org.osgi.service.framework; version="1.0",
org.osgi.service.packageadmin;version="1.2",
org.osgi.service.permissionadmin;version="1.2",
org.osgi.service.startlevel;version="1.1",
@@ -43,6 +44,7 @@ Export-Package: org.eclipse.osgi.event;version="1.0",
org.eclipse.osgi.framework.internal.reliablefile;x-internal:=true,
org.eclipse.osgi.framework.util;x-internal:=true,
org.eclipse.osgi.internal.baseadaptor;x-internal:=true,
+ org.eclipse.osgi.internal.composite; x-internal:=true,
org.eclipse.osgi.internal.loader;x-internal:=true,
org.eclipse.osgi.internal.loader.buddy; x-internal:=true,
org.eclipse.osgi.internal.module;x-internal:=true,
@@ -53,7 +55,8 @@ Export-Package: org.eclipse.osgi.event;version="1.0",
org.eclipse.osgi.internal.provisional.service.security; x-friends:="org.eclipse.equinox.security.ui";version="1.0.0",
org.eclipse.osgi.internal.provisional.verifier;x-friends:="org.eclipse.update.core,org.eclipse.ui.workbench,org.eclipse.equinox.p2.artifact.repository",
org.eclipse.osgi.internal.service.security;x-friends:="org.eclipse.equinox.security.ui",
- org.eclipse.osgi.internal.signedcontent; x-internal:=true
+ org.eclipse.osgi.internal.signedcontent; x-internal:=true,
+ org.eclipse.osgi.service.internal.composite; x-internal:=true
Export-Service: org.osgi.service.packageadmin.PackageAdmin,
org.osgi.service.permissionadmin.PermissionAdmin,
org.osgi.service.startlevel.StartLevel,
diff --git a/bundles/org.eclipse.osgi/build.properties b/bundles/org.eclipse.osgi/build.properties
index f5b1e7614..70326c98f 100644
--- a/bundles/org.eclipse.osgi/build.properties
+++ b/bundles/org.eclipse.osgi/build.properties
@@ -22,6 +22,7 @@ src.includes = about.html,\
source.. = osgi/src,\
core/adaptor/,\
core/framework/,\
+ core/composite/,\
resolver/src/,\
defaultAdaptor/src/,\
eclipseAdaptor/src/,\
diff --git a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleData.java b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleData.java
index 6c1d0ddac..ff33a0232 100644
--- a/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleData.java
+++ b/bundles/org.eclipse.osgi/core/adaptor/org/eclipse/osgi/framework/adaptor/BundleData.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2003, 2007 IBM Corporation and others.
+ * Copyright (c) 2003, 2008 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
* which accompanies this distribution, and is available at
@@ -42,6 +42,10 @@ public interface BundleData {
public static final int TYPE_SINGLETON = 0x00000008;
/** The BundleData is for an extension classpath bundle */
public static final int TYPE_EXTCLASSPATH_EXTENSION = 0x00000010;
+ /** The BundleData is for a composite bundle */
+ public static final int TYPE_COMPOSITEBUNDLE = 0x00000020;
+ /** The BundleData is for a composite bundle surrogate */
+ public static final int TYPE_SURROGATEBUNDLE = 0x00000040;
/**
* Creates the ClassLoader for the BundleData. The ClassLoader created
diff --git a/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeBase.java b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeBase.java
new file mode 100644
index 000000000..9ca68c91b
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeBase.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.composite;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.framework.internal.core.BundleHost;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.internal.module.CompositeResolveHelper;
+import org.eclipse.osgi.service.internal.composite.CompositeModule;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.framework.CompositeBundle;
+
+/**
+ * This is a base class for both composite and surrogate bundles.
+ */
+public abstract class CompositeBase extends BundleHost implements CompositeResolveHelper, CompositeModule {
+ protected static String PROP_COMPOSITE = "org.eclipse.equinox.Composite"; //$NON-NLS-1$
+ protected static String PROP_PARENTFRAMEWORK = "org.eclipse.equinox.parentFramework"; //$NON-NLS-1$
+
+ protected final Framework companionFramework;
+ protected final long companionID;
+ protected final ThreadLocal refreshing = new ThreadLocal();
+
+ public CompositeBase(BundleData bundledata, org.eclipse.osgi.framework.internal.core.Framework framework) throws BundleException {
+ super(bundledata, framework);
+ this.companionFramework = findCompanionFramework(framework, bundledata);
+ this.companionID = isSurrogate() ? ((CompositeBundle) FrameworkProperties.getProperties().get(PROP_COMPOSITE)).getBundleId() : 1;
+ }
+
+ /*
+ * Finds the companion framework for the composite/surrogate.
+ * For surrogate bundles this is the parent framework.
+ * For composite bundles this is the child framework.
+ */
+ protected abstract Framework findCompanionFramework(org.eclipse.osgi.framework.internal.core.Framework thisFramework, BundleData thisData) throws BundleException;
+
+ /*
+ * Gets the companion bundle for the composite/surrogate.
+ * For surrogate bundles this is the composite bundle.
+ * For composite bundles this is the surrogate bundle.
+ */
+ Bundle getCompanionBundle() {
+ return companionFramework.getBundleContext().getBundle(companionID);
+ }
+
+ protected boolean isSurrogate() {
+ return false;
+ }
+
+ public BundleDescription getCompositeDescription() {
+ return getBundleDescription();
+ }
+
+ public ClassLoaderDelegate getDelegate() {
+ return getBundleLoader();
+ }
+
+ public void refreshContent(boolean synchronously) {
+ if (synchronously)
+ refreshing.set(Boolean.TRUE);
+ try {
+ framework.getPackageAdmin().refreshPackages(new Bundle[] {this}, synchronously);
+ } finally {
+ if (synchronously)
+ refreshing.set(null);
+ }
+ }
+
+ public boolean resolveContent() {
+ return framework.getPackageAdmin().resolveBundles(new Bundle[] {this});
+ }
+
+ public void started(CompositeModule surrogate) {
+ // nothing
+ }
+
+ public void stopped(CompositeModule surrogate) {
+ // nothing
+ }
+
+ public void updateContent(InputStream content) throws BundleException {
+ super.update(content);
+ }
+
+ public void update() throws BundleException {
+ throw new BundleException("Cannot update composite bundles", BundleException.INVALID_OPERATION);
+ }
+
+ public void update(InputStream in) throws BundleException {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ throw new BundleException("Cannot update composite bundles", BundleException.INVALID_OPERATION);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeClassLoader.java b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeClassLoader.java
new file mode 100644
index 000000000..ab2a6d772
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeClassLoader.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.composite;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.*;
+import org.eclipse.osgi.baseadaptor.BaseData;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile;
+import org.eclipse.osgi.baseadaptor.loader.*;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+
+public class CompositeClassLoader extends ClassLoader implements BaseClassLoader {
+
+ private final ClassLoaderDelegate delegate;
+ private final ClasspathManager manager;
+ private final ClassLoaderDelegate companionDelegate;
+ //Support to cut class / resource loading cycles in the context of one thread. The contained object is a set of classname
+ private final ThreadLocal beingLoaded = new ThreadLocal();
+
+ public CompositeClassLoader(ClassLoader parent, ClassLoaderDelegate delegate, ClassLoaderDelegate companionDelegate, BaseData data) {
+ super(parent);
+ this.delegate = delegate;
+ this.manager = new ClasspathManager(data, new String[0], this);
+ this.companionDelegate = companionDelegate;
+ }
+
+ public ClasspathEntry createClassPathEntry(BundleFile bundlefile, ProtectionDomain cpDomain) {
+ // nothing
+ return null;
+ }
+
+ public Class defineClass(String name, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry) {
+ // nothing
+ return null;
+ }
+
+ public ClasspathManager getClasspathManager() {
+ return manager;
+ }
+
+ public ProtectionDomain getDomain() {
+ // no domain
+ return null;
+ }
+
+ public Object publicDefinePackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase) {
+ return definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
+ }
+
+ public Class publicFindLoaded(String classname) {
+ return findLoadedClass(classname);
+ }
+
+ public Object publicGetPackage(String pkgname) {
+ return getPackage(pkgname);
+ }
+
+ public void attachFragment(BundleData bundledata, ProtectionDomain domain, String[] classpath) {
+ // nothing
+ }
+
+ public void close() {
+ // nothing
+ }
+
+ public Class findLocalClass(String classname) throws ClassNotFoundException {
+ if (!startLoading(classname))
+ throw new ClassNotFoundException(classname);
+ try {
+ return companionDelegate.findClass(classname);
+ } finally {
+ stopLoading(classname);
+ }
+ }
+
+ public URL findLocalResource(String resource) {
+ if (!startLoading(resource))
+ return null;
+ try {
+ return companionDelegate.findResource(resource);
+ } finally {
+ stopLoading(resource);
+ }
+ }
+
+ public Enumeration findLocalResources(String resource) {
+ if (!startLoading(resource))
+ return null;
+ try {
+ return companionDelegate.findResources(resource);
+ } catch (IOException e) {
+ return null;
+ } finally {
+ stopLoading(resource);
+ }
+ }
+
+ public ClassLoaderDelegate getDelegate() {
+ return delegate;
+ }
+
+ public URL getResource(String name) {
+ return delegate.findResource(name);
+ }
+
+ public void initialize() {
+ manager.initialize();
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return delegate.findClass(name);
+ }
+
+ private boolean startLoading(String name) {
+ Set classesAndResources = (Set) beingLoaded.get();
+ if (classesAndResources != null && classesAndResources.contains(name))
+ return false;
+
+ if (classesAndResources == null) {
+ classesAndResources = new HashSet(3);
+ beingLoaded.set(classesAndResources);
+ }
+ classesAndResources.add(name);
+ return true;
+ }
+
+ private void stopLoading(String name) {
+ ((Set) beingLoaded.get()).remove(name);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeConfigurator.java b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeConfigurator.java
new file mode 100644
index 000000000..b317ece24
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeConfigurator.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.composite;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLConnection;
+import java.security.AllPermission;
+import java.security.ProtectionDomain;
+import java.util.*;
+import org.eclipse.osgi.baseadaptor.*;
+import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
+import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook;
+import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingHook;
+import org.eclipse.osgi.baseadaptor.loader.*;
+import org.eclipse.osgi.framework.adaptor.*;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.osgi.internal.module.*;
+import org.eclipse.osgi.service.internal.composite.CompositeModule;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.osgi.framework.*;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.framework.CompositeBundle;
+import org.osgi.service.framework.CompositeBundleFactory;
+
+public class CompositeConfigurator implements HookConfigurator, AdaptorHook, ClassLoadingHook, CompositeBundleFactory, CompositeResolveHelperRegistry {
+
+ // the base adaptor
+ private BaseAdaptor adaptor;
+ // the composite bundle factory service reference
+ private ServiceRegistration factoryService;
+ // the system bundle context
+ private BundleContext systemContext;
+
+ public void addHooks(HookRegistry hookRegistry) {
+ // this is an adaptor hook to register the composite factory and
+ // to shutdown child frameworks on shutdown
+ hookRegistry.addAdaptorHook(this);
+ // this is a class loading hook in order to create special class loaders for composites
+ hookRegistry.addClassLoadingHook(this);
+ }
+
+ public void addProperties(Properties properties) {
+ // nothing
+ }
+
+ public FrameworkLog createFrameworkLog() {
+ // nothing
+ return null;
+ }
+
+ /**
+ * @throws BundleException
+ */
+ public void frameworkStart(BundleContext context) throws BundleException {
+ this.systemContext = context;
+ // this is a composite resolve helper registry; add it to the resolver
+ ((ResolverImpl) adaptor.getState().getResolver()).setCompositeResolveHelperRegistry(this);
+ // register this as the composite bundle factory
+ factoryService = context.registerService(new String[] {CompositeBundleFactory.class.getName()}, this, null);
+ }
+
+ public void frameworkStop(BundleContext context) {
+ // unregister the factory
+ if (factoryService != null)
+ factoryService.unregister();
+ factoryService = null;
+ // stop any child frameworks than may still be running.
+ stopFrameworks();
+ }
+
+ public void frameworkStopping(BundleContext context) {
+ // nothing
+ }
+
+ public void handleRuntimeError(Throwable error) {
+ // nothing
+ }
+
+ public void initialize(BaseAdaptor initAdaptor) {
+ this.adaptor = initAdaptor;
+ }
+
+ public URLConnection mapLocationToURLConnection(String location) {
+ // nothing
+ return null;
+ }
+
+ public boolean matchDNChain(String pattern, String[] dnChain) {
+ // nothing
+ return false;
+ }
+
+ public CompositeBundle installCompositeBundle(Map frameworkConfig, String location, Map compositeManifest) throws BundleException {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ // must have AllPermission to do this
+ sm.checkPermission(new AllPermission());
+ // make a local copy of the manifest first
+ compositeManifest = new HashMap(compositeManifest);
+ // make sure the manifest is valid
+ CompositeHelper.validateCompositeManifest(compositeManifest);
+
+ try {
+ // get an in memory input stream to jar content of the composite we want to install
+ InputStream content = CompositeHelper.getCompositeInput(frameworkConfig, compositeManifest);
+ CompositeBundle result = (CompositeBundle) systemContext.installBundle(location, content);
+ // set the permissions
+ CompositeHelper.setCompositePermissions(location, systemContext);
+ return result;
+ } catch (IOException e) {
+ throw new BundleException("Error creating composite bundle", e);
+ }
+ }
+
+ private void stopFrameworks() {
+ Bundle[] allBundles = systemContext.getBundles();
+ // stop each child framework
+ for (int i = 0; i < allBundles.length; i++) {
+ if (!(allBundles[i] instanceof CompositeBundle))
+ continue;
+ CompositeBundle composite = (CompositeBundle) allBundles[i];
+ try {
+ Framework child = composite.getCompositeFramework();
+ child.stop();
+ // need to wait for each child to stop
+ child.waitForStop(30000);
+ // TODO need to figure out a way to invalid the child
+ } catch (Throwable t) {
+ // TODO consider logging
+ t.printStackTrace();
+ }
+ }
+ }
+
+ public CompositeResolveHelper getCompositeResolveHelper(BundleDescription bundle) {
+ // EquinoxComposite bundles implement the resolver helper
+ Bundle composite = systemContext.getBundle(bundle.getBundleId());
+ // If we found a resolver helper bundle; return it
+ return (CompositeResolveHelper) ((!(composite instanceof CompositeResolveHelper)) ? null : composite);
+ }
+
+ public boolean addClassPathEntry(ArrayList cpEntries, String cp, ClasspathManager hostmanager, BaseData sourcedata, ProtectionDomain sourcedomain) {
+ // nothing
+ return false;
+ }
+
+ public BaseClassLoader createClassLoader(ClassLoader parent, ClassLoaderDelegate delegate, BundleProtectionDomain domain, BaseData data, String[] bundleclasspath) {
+ if ((data.getType() & (BundleData.TYPE_COMPOSITEBUNDLE | BundleData.TYPE_SURROGATEBUNDLE)) == 0)
+ return null;
+ // only create composite class loaders for bundles that are of type composite | surrogate
+ ClassLoaderDelegate companionDelegate = ((CompositeModule) ((CompositeBase) data.getBundle()).getCompanionBundle()).getDelegate();
+ return new CompositeClassLoader(parent, delegate, companionDelegate, data);
+ }
+
+ public String findLibrary(BaseData data, String libName) {
+ // nothing
+ return null;
+ }
+
+ public ClassLoader getBundleClassLoaderParent() {
+ // nothing
+ return null;
+ }
+
+ public void initializedClassLoader(BaseClassLoader baseClassLoader, BaseData data) {
+ // nothing
+ }
+
+ public byte[] processClass(String name, byte[] classbytes, ClasspathEntry classpathEntry, BundleEntry entry, ClasspathManager manager) {
+ // nothing
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeHelper.java b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeHelper.java
new file mode 100644
index 000000000..52f38a758
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeHelper.java
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.composite;
+
+import java.io.*;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.jar.*;
+import org.eclipse.osgi.internal.baseadaptor.BaseStorageHook;
+import org.eclipse.osgi.service.resolver.*;
+import org.osgi.framework.*;
+import org.osgi.service.permissionadmin.PermissionAdmin;
+import org.osgi.service.permissionadmin.PermissionInfo;
+
+public class CompositeHelper {
+ private static final PermissionInfo[] COMPOSITE_PERMISSIONS = new PermissionInfo[] {new PermissionInfo(PackagePermission.class.getName(), "*", PackagePermission.EXPORT), new PermissionInfo(ServicePermission.class.getName(), "*", ServicePermission.REGISTER + ',' + ServicePermission.GET)}; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String COMPOSITE_POLICY = "org.eclipse.osgi.composite"; //$NON-NLS-1$
+ private static String ELEMENT_SEPARATOR = "; "; //$NON-NLS-1$
+ private static final Object EQUALS_QUOTE = "=\""; //$NON-NLS-1$
+ private static final String[] INVALID_COMPOSITE_HEADERS = new String[] {Constants.DYNAMICIMPORT_PACKAGE, Constants.FRAGMENT_HOST, Constants.REQUIRE_BUNDLE, Constants.BUNDLE_NATIVECODE, Constants.BUNDLE_CLASSPATH, Constants.BUNDLE_ACTIVATOR, Constants.BUNDLE_LOCALIZATION, Constants.BUNDLE_ACTIVATIONPOLICY};
+
+ private static Manifest getCompositeManifest(Map compositeManifest) {
+ Manifest manifest = new Manifest();
+ Attributes attributes = manifest.getMainAttributes();
+ attributes.putValue("Manifest-Version", "1.0"); //$NON-NLS-1$//$NON-NLS-2$
+ // get the common headers Bundle-ManifestVersion, Bundle-SymbolicName and Bundle-Version
+ // get the manifest version from the map
+ String manifestVersion = (String) compositeManifest.remove(Constants.BUNDLE_MANIFESTVERSION);
+ // here we assume the validation got the correct version for us
+ attributes.putValue(Constants.BUNDLE_MANIFESTVERSION, manifestVersion);
+ // Ignore the Equinox composite bundle header
+ compositeManifest.remove(BaseStorageHook.COMPOSITE_HEADER);
+ attributes.putValue(BaseStorageHook.COMPOSITE_HEADER, BaseStorageHook.COMPOSITE_BUNDLE);
+ for (Iterator entries = compositeManifest.entrySet().iterator(); entries.hasNext();) {
+ Map.Entry entry = (Entry) entries.next();
+ if (entry.getKey() instanceof String && entry.getValue() instanceof String)
+ attributes.putValue((String) entry.getKey(), (String) entry.getValue());
+ }
+ return manifest;
+ }
+
+ private static Manifest getSurrogateManifest(Dictionary compositeManifest, BundleDescription compositeDesc, ExportPackageDescription[] matchingExports) {
+ Manifest manifest = new Manifest();
+ Attributes attributes = manifest.getMainAttributes();
+ attributes.putValue("Manifest-Version", "1.0"); //$NON-NLS-1$//$NON-NLS-2$
+ // Ignore the manifest version from the map
+ // always use bundle manifest version 2
+ attributes.putValue(Constants.BUNDLE_MANIFESTVERSION, "2"); //$NON-NLS-1$
+ // Ignore the Equinox composite bundle header
+ attributes.putValue(BaseStorageHook.COMPOSITE_HEADER, BaseStorageHook.SURROGATE_BUNDLE);
+
+ if (compositeDesc != null && matchingExports != null) {
+ // convert the exports from the composite into imports
+ addImports(attributes, compositeDesc, matchingExports);
+
+ // convert the matchingExports from the composite into exports
+ addExports(attributes, matchingExports);
+ }
+
+ // add the rest
+ for (Enumeration keys = compositeManifest.keys(); keys.hasMoreElements();) {
+ Object header = keys.nextElement();
+ if (Constants.BUNDLE_MANIFESTVERSION.equals(header) || BaseStorageHook.COMPOSITE_HEADER.equals(header) || Constants.IMPORT_PACKAGE.equals(header) || Constants.EXPORT_PACKAGE.equals(header))
+ continue;
+ if (header instanceof String && compositeManifest.get(header) instanceof String)
+ attributes.putValue((String) header, (String) compositeManifest.get(header));
+ }
+ return manifest;
+ }
+
+ static InputStream getCompositeInput(Map frameworkConfig, Map compositeManifest) throws IOException {
+ // use an in memory stream to store the content
+ ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+ // the composite bundles only consist of a manifest describing the packages they import and export
+ // and a framework config properties file
+ Manifest manifest = CompositeHelper.getCompositeManifest(compositeManifest);
+ JarOutputStream jarOut = new JarOutputStream(bytesOut, manifest);
+ try {
+ // store the framework config
+ Properties fwProps = new Properties();
+ if (frameworkConfig != null)
+ fwProps.putAll(frameworkConfig);
+ JarEntry entry = new JarEntry(CompositeImpl.COMPOSITE_CONFIGURATION);
+ jarOut.putNextEntry(entry);
+ fwProps.store(jarOut, null);
+ jarOut.closeEntry();
+ jarOut.flush();
+ } finally {
+ try {
+ jarOut.close();
+ } catch (IOException e) {
+ // nothing
+ }
+ }
+ return new ByteArrayInputStream(bytesOut.toByteArray());
+ }
+
+ static InputStream getSurrogateInput(Dictionary compositeManifest, BundleDescription compositeDesc, ExportPackageDescription[] matchingExports) throws IOException {
+ // use an in memory stream to store the content
+ ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+ Manifest manifest = CompositeHelper.getSurrogateManifest(compositeManifest, compositeDesc, matchingExports);
+ JarOutputStream jarOut = new JarOutputStream(bytesOut, manifest);
+ jarOut.flush();
+ jarOut.close();
+ return new ByteArrayInputStream(bytesOut.toByteArray());
+ }
+
+ private static void addImports(Attributes attrigutes, BundleDescription compositeDesc, ExportPackageDescription[] matchingExports) {
+ ExportPackageDescription[] exports = compositeDesc.getExportPackages();
+ List systemExports = getSystemExports(matchingExports);
+ if (exports.length == 0 && systemExports.size() == 0)
+ return;
+ StringBuffer importStatement = new StringBuffer();
+ Collection importedNames = new ArrayList(exports.length);
+ int i = 0;
+ for (; i < exports.length; i++) {
+ if (i != 0)
+ importStatement.append(',');
+ importedNames.add(exports[i].getName());
+ getImportFrom(exports[i], importStatement);
+ }
+ for (Iterator iSystemExports = systemExports.iterator(); iSystemExports.hasNext();) {
+ ExportPackageDescription systemExport = (ExportPackageDescription) iSystemExports.next();
+ if (!importedNames.contains(systemExport.getName())) {
+ if (i != 0)
+ importStatement.append(',');
+ i++;
+ importStatement.append(systemExport.getName()).append(ELEMENT_SEPARATOR).append(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE).append('=').append(Constants.SYSTEM_BUNDLE_SYMBOLICNAME);
+ }
+ }
+ attrigutes.putValue(Constants.IMPORT_PACKAGE, importStatement.toString());
+ }
+
+ private static List getSystemExports(ExportPackageDescription[] matchingExports) {
+ ArrayList list = null;
+ for (int i = 0; i < matchingExports.length; i++) {
+ if (matchingExports[i].getExporter().getBundleId() != 0)
+ continue;
+ if (list == null)
+ list = new ArrayList();
+ list.add(matchingExports[i]);
+ }
+ return list == null ? Collections.EMPTY_LIST : list;
+ }
+
+ private static void getImportFrom(ExportPackageDescription export, StringBuffer importStatement) {
+ importStatement.append(export.getName()).append(ELEMENT_SEPARATOR);
+ Version version = export.getVersion();
+ importStatement.append(Constants.VERSION_ATTRIBUTE).append(EQUALS_QUOTE).append('[').append(version).append(',').append(new Version(version.getMajor(), version.getMinor(), version.getMicro() + 1)).append(')').append('\"');
+ addMap(importStatement, export.getAttributes(), "="); //$NON-NLS-1$
+ }
+
+ private static void addExports(Attributes attributes, ExportPackageDescription[] matchingExports) {
+ if (matchingExports.length == 0)
+ return;
+ StringBuffer exportStatement = new StringBuffer();
+ for (int i = 0; i < matchingExports.length; i++) {
+ if (i != 0)
+ exportStatement.append(',');
+ getExportFrom(matchingExports[i], exportStatement);
+ }
+ attributes.putValue(Constants.EXPORT_PACKAGE, exportStatement.toString());
+ }
+
+ private static void getExportFrom(ExportPackageDescription export, StringBuffer exportStatement) {
+ exportStatement.append(export.getName()).append(ELEMENT_SEPARATOR);
+ exportStatement.append(Constants.VERSION_ATTRIBUTE).append(EQUALS_QUOTE).append(export.getVersion()).append('\"');
+ addMap(exportStatement, export.getDirectives(), ":="); //$NON-NLS-1$
+ addMap(exportStatement, export.getAttributes(), "="); //$NON-NLS-1$
+ }
+
+ private static void addMap(StringBuffer manifest, Map values, String assignment) {
+ if (values == null)
+ return; // nothing to add
+ for (Iterator iEntries = values.entrySet().iterator(); iEntries.hasNext();) {
+ manifest.append(ELEMENT_SEPARATOR);
+ Map.Entry entry = (Entry) iEntries.next();
+ manifest.append(entry.getKey()).append(assignment).append('\"');
+ Object value = entry.getValue();
+ if (value instanceof String[]) {
+ String[] strings = (String[]) value;
+ for (int i = 0; i < strings.length; i++) {
+ if (i != 0)
+ manifest.append(',');
+ manifest.append(strings[i]);
+ }
+ } else {
+ manifest.append(value);
+ }
+ manifest.append('\"');
+ }
+ }
+
+ static void setCompositePermissions(String bundleLocation, BundleContext systemContext) {
+ ServiceReference ref = systemContext.getServiceReference(PermissionAdmin.class.getName());
+ PermissionAdmin permAdmin = (PermissionAdmin) (ref == null ? null : systemContext.getService(ref));
+ if (permAdmin == null)
+ throw new RuntimeException("No Permission Admin service is available");
+ try {
+ permAdmin.setPermissions(bundleLocation, COMPOSITE_PERMISSIONS);
+ } finally {
+ systemContext.ungetService(ref);
+ }
+ }
+
+ static void setDisabled(boolean disable, Bundle bundle, BundleContext systemContext) {
+ ServiceReference ref = systemContext.getServiceReference(PlatformAdmin.class.getName());
+ PlatformAdmin pa = (PlatformAdmin) (ref == null ? null : systemContext.getService(ref));
+ if (pa == null)
+ throw new RuntimeException("No Platform Admin service is available.");
+ try {
+ State state = pa.getState(false);
+ BundleDescription desc = state.getBundle(bundle.getBundleId());
+ setDisabled(disable, desc);
+ } finally {
+ systemContext.ungetService(ref);
+ }
+ }
+
+ static void setDisabled(boolean disable, BundleDescription bundle) {
+ State state = bundle.getContainingState();
+ if (disable) {
+ state.addDisabledInfo(new DisabledInfo(COMPOSITE_POLICY, "Composite companion bundle is not resolved.", bundle));
+ } else {
+ DisabledInfo toRemove = state.getDisabledInfo(bundle, COMPOSITE_POLICY);
+ if (toRemove != null)
+ state.removeDisabledInfo(toRemove);
+ }
+ }
+
+ static void validateCompositeManifest(Map compositeManifest) throws BundleException {
+ if (compositeManifest == null)
+ throw new BundleException("The composite manifest cannot be null.", BundleException.MANIFEST_ERROR);
+ // check for symbolic name
+ if (compositeManifest.get(Constants.BUNDLE_SYMBOLICNAME) == null)
+ throw new BundleException("The composite manifest must contain a Bundle-SymbolicName header.", BundleException.MANIFEST_ERROR);
+ // check for invalid manifests headers
+ for (int i = 0; i < INVALID_COMPOSITE_HEADERS.length; i++)
+ if (compositeManifest.get(INVALID_COMPOSITE_HEADERS[i]) != null)
+ throw new BundleException("The composite manifest must not contain the header " + INVALID_COMPOSITE_HEADERS[i], BundleException.MANIFEST_ERROR);
+ // validate manifest version
+ String manifestVersion = (String) compositeManifest.get(Constants.BUNDLE_MANIFESTVERSION);
+ if (manifestVersion == null) {
+ compositeManifest.put(Constants.BUNDLE_MANIFESTVERSION, "2"); //$NON-NLS-1$
+ } else {
+ try {
+ Integer parsed = Integer.valueOf(manifestVersion);
+ if (parsed.intValue() > 2 || parsed.intValue() < 2)
+ throw new BundleException("Invalid Bundle-ManifestVersion: " + manifestVersion);
+ } catch (NumberFormatException e) {
+ throw new BundleException("Invalid Bundle-ManifestVersion: " + manifestVersion);
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeImpl.java b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeImpl.java
new file mode 100644
index 000000000..4b840716e
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeImpl.java
@@ -0,0 +1,280 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.composite;
+
+import java.io.*;
+import java.net.URL;
+import java.util.Map;
+import java.util.Properties;
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.internal.core.Constants;
+import org.eclipse.osgi.internal.loader.BundleLoader;
+import org.eclipse.osgi.internal.loader.BundleLoaderProxy;
+import org.eclipse.osgi.launch.Equinox;
+import org.eclipse.osgi.service.internal.composite.CompositeModule;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.osgi.framework.*;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.framework.*;
+
+public class CompositeImpl extends CompositeBase implements CompositeBundle {
+ private static String COMPOSITE_STORAGE = "store"; //$NON-NLS-1$
+ public static String COMPOSITE_CONFIGURATION = "compositeConfig.properties"; //$NON-NLS-1$
+
+ private final ServiceTrackerManager trackerManager = new ServiceTrackerManager();
+
+ public CompositeImpl(BundleData bundledata, org.eclipse.osgi.framework.internal.core.Framework framework) throws BundleException {
+ super(bundledata, framework);
+ }
+
+ protected Framework findCompanionFramework(org.eclipse.osgi.framework.internal.core.Framework thisFramework, BundleData thisData) throws BundleException {
+ // allocate storage area for the composite framework
+ File compositeStorage = thisData.getDataFile(COMPOSITE_STORAGE);
+ boolean firstTime = false;
+ if (!compositeStorage.exists())
+ // the child storage area has not been allocated; this is the first time
+ firstTime = true;
+ // find the configuration properties
+ URL childConfig = bundledata.getEntry(COMPOSITE_CONFIGURATION);
+ Properties props = new Properties();
+ try {
+ props.load(childConfig.openStream());
+ } catch (IOException e) {
+ throw new BundleException("Could not load child configuration", e);
+ }
+ props.put(Constants.FRAMEWORK_STORAGE, compositeStorage.getAbsolutePath());
+ // save the parent framework so the parent companion bundle can find it
+ props.put(PROP_PARENTFRAMEWORK, thisFramework.getSystemBundleContext().getBundle());
+ // TODO leaks "this" out of the constructor
+ props.put(PROP_COMPOSITE, this);
+ Equinox equinox = new Equinox(props);
+ if (!firstTime)
+ // if not the first time then we are done
+ return equinox;
+ equinox.init();
+ installSurrogate(equinox.getBundleContext(), thisData);
+ return equinox;
+ }
+
+ private void installSurrogate(BundleContext companionContext, BundleData thisData) throws BundleException {
+ Bundle surrogate;
+ try {
+ InputStream surrogateContent = CompositeHelper.getSurrogateInput(thisData.getManifest(), null, null);
+ surrogate = companionContext.installBundle(thisData.getLocation(), surrogateContent);
+ } catch (IOException e) {
+ throw new BundleException("Error installing parent companion composite bundle", e);
+ }
+ // disable the surrogate initially since we know we have not resolved the composite yet.
+ CompositeHelper.setDisabled(true, surrogate, companionContext);
+ // set the permissions of the surrogate bundle
+ CompositeHelper.setCompositePermissions(thisData.getLocation(), companionContext);
+ }
+
+ private boolean updateSurrogate(BundleData thisData, BundleDescription child, ExportPackageDescription[] matchingExports) throws BundleException {
+ // update the surrogate content with the matching exports provided by the composite
+ InputStream surrogateContent;
+ try {
+ surrogateContent = CompositeHelper.getSurrogateInput(thisData.getManifest(), child, matchingExports);
+ } catch (IOException e) {
+ throw new BundleException("Error updating surrogate bundle.", e);
+ }
+ CompositeModule surrogateComposite = (CompositeModule) getSurrogateBundle();
+ surrogateComposite.updateContent(surrogateContent);
+ // enable/disable the surrogate composite based on if we have exports handed to us
+ boolean disable = matchingExports == null ? true : false;
+ CompositeHelper.setDisabled(disable, getSurrogateBundle(), getCompositeFramework().getBundleContext());
+ // return true if we can resolve the surrogate bundle
+ return disable ? false : surrogateComposite.resolveContent();
+ }
+
+ private SurrogateBundle findSurrogateBundle() throws BundleException {
+ if ((companionFramework.getState() & (Bundle.INSTALLED | Bundle.RESOLVED)) != 0)
+ companionFramework.init();
+ return (SurrogateBundle) getCompanionBundle();
+ }
+
+ public Framework getCompositeFramework() {
+ return companionFramework;
+ }
+
+ public SurrogateBundle getSurrogateBundle() {
+ try {
+ return findSurrogateBundle();
+ } catch (BundleException e) {
+ throw new RuntimeException("Error intializing child framework", e);
+ }
+ }
+
+ public void update(Map compositeManifest) throws BundleException {
+ // validate the composite manifest
+ CompositeHelper.validateCompositeManifest(compositeManifest);
+ if (isResolved()) {
+ // force the class loader creation before updating the surrogate to cache the current state
+ // this is to allow for lazy updates of composite bundles
+ BundleLoader loader = getBundleLoader();
+ if (loader != null)
+ loader.createClassLoader();
+ }
+ try {
+ Map frameworkConfig = getFrameworkConfig();
+ // first update the parent companion and disable it
+ updateSurrogate(getBundleData(), null, null);
+ // update the content with the new manifest
+ updateContent(CompositeHelper.getCompositeInput(frameworkConfig, compositeManifest));
+ } catch (IOException e) {
+ throw new BundleException("Error updating composite.", e);
+ }
+ }
+
+ private Map getFrameworkConfig() throws IOException {
+ Properties result = new Properties();
+ URL config = getEntry(COMPOSITE_CONFIGURATION);
+ result.load(config.openStream());
+ return result;
+ }
+
+ public void uninstall() throws BundleException {
+ // ensure class loader is created if needed
+ checkClassLoader();
+ // stop first before stopping the child to let the service listener clean up
+ stop(Bundle.STOP_TRANSIENT);
+ stopChildFramework();
+ super.uninstall();
+ }
+
+ private void checkClassLoader() {
+ BundleLoaderProxy proxy = getLoaderProxy();
+ if (proxy != null && proxy.inUse() && proxy.getBundleLoader() != null)
+ proxy.getBundleLoader().createClassLoader();
+ }
+
+ protected void startHook() throws BundleException {
+ // always start the child framework
+ companionFramework.start();
+ trackerManager.startedComposite();
+ }
+
+ protected void stopHook() throws BundleException {
+ trackerManager.stoppedComposite();
+ // do not stop the framework unless we are persistently stopped
+ if ((bundledata.getStatus() & Constants.BUNDLE_STARTED) == 0)
+ stopChildFramework();
+ }
+
+ public void started(CompositeModule surrogate) {
+ if (surrogate == getSurrogateBundle())
+ trackerManager.startedSurrogate();
+ }
+
+ public void stopped(CompositeModule surrogate) {
+ if (surrogate == getSurrogateBundle())
+ trackerManager.stoppedSurrogate();
+ }
+
+ private void stopChildFramework() throws BundleException {
+ companionFramework.stop();
+ try {
+ FrameworkEvent stopped = companionFramework.waitForStop(30000);
+ switch (stopped.getType()) {
+ case FrameworkEvent.ERROR :
+ throw new BundleException("Error stopping the child framework.", stopped.getThrowable());
+ case FrameworkEvent.INFO :
+ throw new BundleException("Timed out waiting for the child framework to stop.");
+ case FrameworkEvent.STOPPED :
+ // normal stop, just return
+ return;
+ default :
+ throw new BundleException("Unexpected code returned when stopping the child framework:" + stopped.getType());
+ }
+ } catch (InterruptedException e) {
+ throw new BundleException("Error stopping child framework", e);
+ }
+ }
+
+ public boolean giveExports(ExportPackageDescription[] matchingExports) {
+ if (matchingExports == null) {
+ SurrogateBundle surrogate = getSurrogateBundle();
+ // disable the surrogate
+ CompositeHelper.setDisabled(true, getSurrogateBundle(), getCompositeFramework().getBundleContext());
+ // refresh the parent composite synchronously
+ ((CompositeModule) surrogate).refreshContent(true);
+ return true;
+ }
+ try {
+ return updateSurrogate(getBundleData(), getBundleDescription(), matchingExports);
+ } catch (BundleException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /*
+ * Listens to source and target bundles and source and target framework changes
+ */
+ class ServiceTrackerManager {
+ static final int COMPOSITE_ACTIVE = 0x01;
+ static final int SURROGATE_ACTIVE = 0x02;
+ // @GuardedBy(this)
+ private int bundlesActive = 0;
+ // @GuardedBy(this)
+ private CompositeServiceTracker shareToChildServices;
+ // @GuardedBy(this)
+ private CompositeServiceTracker shareToParentServices;
+
+ void startedComposite() throws BundleException {
+ open(COMPOSITE_ACTIVE);
+ getSurrogateBundle().start(Bundle.START_TRANSIENT);
+ }
+
+ void startedSurrogate() {
+ open(SURROGATE_ACTIVE);
+ }
+
+ void stoppedComposite() {
+ try {
+ getSurrogateBundle().stop(Bundle.STOP_TRANSIENT);
+ } catch (BundleException e) {
+ // nothing
+ } catch (IllegalStateException e) {
+ // child framework must have been stoped
+ }
+ close(COMPOSITE_ACTIVE);
+ }
+
+ void stoppedSurrogate() {
+ close(SURROGATE_ACTIVE);
+ }
+
+ private synchronized void open(int bundleActive) {
+ bundlesActive |= bundleActive;
+ if ((bundlesActive & (COMPOSITE_ACTIVE | SURROGATE_ACTIVE)) != (COMPOSITE_ACTIVE | SURROGATE_ACTIVE))
+ return;
+ // create a service tracker to track and share services from the parent framework
+ shareToChildServices = new CompositeServiceTracker(getBundleContext(), getSurrogateBundle().getBundleContext(), (String) getBundleContext().getBundle().getHeaders("").get(CompositeBundleFactory.COMPOSITE_SERVICE_FILTER_IMPORT)); //$NON-NLS-1$
+ shareToChildServices.open();
+ // create a service tracker to track and share services from the child framework
+ shareToParentServices = new CompositeServiceTracker(getSurrogateBundle().getBundleContext(), getBundleContext(), (String) getBundleContext().getBundle().getHeaders("").get(CompositeBundleFactory.COMPOSITE_SERVICE_FILTER_EXPORT)); //$NON-NLS-1$
+ shareToParentServices.open();
+
+ }
+
+ private synchronized void close(int bundleStopped) {
+ bundlesActive ^= bundleStopped;
+ // close the service tracker to stop tracking and sharing services
+ if (shareToChildServices != null)
+ shareToChildServices.close();
+ if (shareToParentServices != null)
+ shareToParentServices.close();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeServiceTracker.java b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeServiceTracker.java
new file mode 100644
index 000000000..a9a449b40
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/CompositeServiceTracker.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.composite;
+
+import java.util.*;
+import org.eclipse.osgi.util.ManifestElement;
+import org.osgi.framework.*;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+class CompositeServiceTracker implements ServiceTrackerCustomizer {
+ final BundleContext sourceContext;
+ final BundleContext targetContext;
+ final ServiceTracker[] trackers;
+ final String[] filters;
+ /* @GuardedBy("serviceComposites") */
+ final HashMap serviceComposites = new HashMap();
+
+ public CompositeServiceTracker(BundleContext sourceContext, BundleContext targetContext, String serviceFilters) {
+ this.sourceContext = sourceContext;
+ this.targetContext = targetContext;
+ filters = ManifestElement.getArrayFromList(serviceFilters, ","); //$NON-NLS-1$
+ trackers = new ServiceTracker[filters.length];
+ }
+
+ synchronized void open() {
+ for (int i = 0; i < trackers.length; i++) {
+ try {
+ trackers[i] = new ServiceTracker(sourceContext, sourceContext.createFilter(filters[i]), this);
+ trackers[i].open();
+ } catch (InvalidSyntaxException e) {
+ // TODO log
+ // we will skip this filter; note that trackers may have null entries
+ }
+ }
+ }
+
+ synchronized void close() {
+ for (int i = 0; i < trackers.length; i++) {
+ if (trackers[i] != null)
+ trackers[i].close();
+ }
+ }
+
+ public Object addingService(ServiceReference reference) {
+ ServiceLink serviceLink;
+ int useCount;
+ synchronized (serviceComposites) {
+ serviceLink = (ServiceLink) serviceComposites.get(reference);
+ if (serviceLink == null) {
+ serviceLink = new ServiceLink(reference);
+ serviceComposites.put(reference, serviceLink);
+ }
+ useCount = serviceLink.incrementUse();
+ }
+ // register service outside of the sync block
+ if (useCount == 1)
+ serviceLink.register();
+ return serviceLink;
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ ServiceLink serviceLink = (ServiceLink) service;
+ Dictionary serviceProps = null;
+ synchronized (serviceComposites) {
+ serviceProps = serviceLink.getRefreshProperties();
+ }
+ // set service properties out side the sync block
+ if (serviceProps != null)
+ ((ServiceLink) service).setServiceProperties(serviceProps);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ int useCount;
+ synchronized (serviceComposites) {
+ useCount = ((ServiceLink) service).decrementUse();
+ if (useCount == 0)
+ serviceComposites.remove(reference);
+ }
+ // unregister outside the sync block
+ if (useCount == 0)
+ ((ServiceLink) service).unregister();
+ }
+
+ class ServiceLink implements ServiceFactory {
+ private final ServiceReference reference;
+ private volatile ServiceRegistration registration;
+ /* @GuardedBy("this") */
+ private Object service;
+ /* @GuardedBy("serviceLinks") */
+ private int useCount;
+
+ ServiceLink(ServiceReference reference) {
+ this.reference = reference;
+ }
+
+ /* @GuardedBy("serviceLinks") */
+ Dictionary getRefreshProperties() {
+ Dictionary result = getServiceProperties();
+ if (useCount <= 1)
+ return result;
+ // need to do an expensive properties check to avoid multiple registration property changes
+ String[] originalKeys = registration.getReference().getPropertyKeys();
+ for (int i = 0; i < originalKeys.length; i++) {
+ if (!Constants.OBJECTCLASS.equals(originalKeys[i]) && !Constants.SERVICE_ID.equals(originalKeys[i]))
+ // identity compare is done on purpose here to catch any kind of change
+ if (registration.getReference().getProperty(originalKeys[i]) != result.get(originalKeys[i]))
+ return result;
+ }
+ for (Enumeration eKeys = result.keys(); eKeys.hasMoreElements();) {
+ String key = (String) eKeys.nextElement();
+ if (!Constants.OBJECTCLASS.equals(key) && !Constants.SERVICE_ID.equals(key))
+ // identity compare is done on purpose here to catch any kind of change
+ if (result.get(key) != registration.getReference().getProperty(key))
+ return result;
+ }
+ return null;
+ }
+
+ /* @GuardedBy("serviceLinks") */
+ int decrementUse() {
+ return --useCount;
+ }
+
+ /* @GuardedBy("serviceLinks") */
+ int incrementUse() {
+ return ++useCount;
+ }
+
+ /* @GuardedBy("serviceLinks") */
+ int getUse() {
+ return useCount;
+ }
+
+ void setServiceProperties(Dictionary props) {
+ ServiceRegistration current = registration;
+ if (current != null)
+ current.setProperties(props);
+ }
+
+ void register() {
+ Dictionary props = getServiceProperties();
+ registration = targetContext.registerService((String[]) props.get(Constants.OBJECTCLASS), this, props);
+ }
+
+ void unregister() {
+ ServiceRegistration current = registration;
+ if (current != null)
+ current.unregister();
+ }
+
+ private Dictionary getServiceProperties() {
+ String[] keys = reference.getPropertyKeys();
+ Hashtable serviceProps = new Hashtable(keys.length);
+ for (int i = 0; i < keys.length; i++)
+ serviceProps.put(keys[i], reference.getProperty(keys[i]));
+ return serviceProps;
+ }
+
+ public synchronized Object getService(Bundle bundle, ServiceRegistration reg) {
+ if (service == null)
+ service = sourceContext.getService(reference);
+ return service;
+ }
+
+ public void ungetService(Bundle bundle, ServiceRegistration reg, Object serv) {
+ // nothing
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/SurrogateImpl.java b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/SurrogateImpl.java
new file mode 100644
index 000000000..c77d687fd
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/internal/composite/SurrogateImpl.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.composite;
+
+import org.eclipse.osgi.framework.adaptor.BundleData;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.internal.module.ResolverBundle;
+import org.eclipse.osgi.service.internal.composite.CompositeModule;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+import org.osgi.framework.*;
+import org.osgi.framework.launch.Framework;
+import org.osgi.service.framework.SurrogateBundle;
+
+public class SurrogateImpl extends CompositeBase implements SurrogateBundle {
+
+ public SurrogateImpl(BundleData bundledata, org.eclipse.osgi.framework.internal.core.Framework framework) throws BundleException {
+ super(bundledata, framework);
+ }
+
+ protected Framework findCompanionFramework(org.eclipse.osgi.framework.internal.core.Framework thisFramework, BundleData thisData) {
+ // just get the property which was set when creating the child framework
+ return (Framework) FrameworkProperties.getProperties().get(PROP_PARENTFRAMEWORK);
+ }
+
+ public BundleContext getCompositeBundleContext() {
+ Bundle composite = getCompanionBundle();
+ return composite == null ? null : composite.getBundleContext();
+ }
+
+ protected boolean isSurrogate() {
+ return true;
+ }
+
+ public boolean giveExports(ExportPackageDescription[] matchingExports) {
+ if (matchingExports == null) {
+ // set the surrogate to disabled to prevent resolution this go around
+ CompositeHelper.setDisabled(true, getBundleDescription());
+ // refresh the composite bundle (in the parent framework) asynchronously and enable it
+ // should only do this if the composite is not in the process of refreshing this
+ // surrogate bundle
+ if (refreshing.get() == null)
+ ((CompositeModule) getCompanionBundle()).refreshContent(false);
+ return true;
+ }
+ return validExports(matchingExports);
+ }
+
+ private boolean validExports(ExportPackageDescription[] matchingExports) {
+ // make sure each matching exports matches the export signature of the composite
+ CompositeModule composite = (CompositeModule) getCompanionBundle();
+ BundleDescription childDesc = composite.getCompositeDescription();
+ ExportPackageDescription[] childExports = childDesc.getExportPackages();
+ for (int i = 0; i < matchingExports.length; i++) {
+ for (int j = 0; j < childExports.length; j++) {
+ if (matchingExports[i].getName().equals(childExports[j].getName())) {
+ if (!validateExport(matchingExports[i], childExports[j]))
+ return false;
+ continue;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean validateExport(ExportPackageDescription matchingExport, ExportPackageDescription childExport) {
+ Version matchingVersion = matchingExport.getVersion();
+ Version childVersion = childExport.getVersion();
+ if (!childVersion.equals(Version.emptyVersion) && !matchingVersion.equals(childVersion))
+ return false;
+ if (!ResolverBundle.equivalentMaps(childExport.getAttributes(), matchingExport.getAttributes(), false))
+ return false;
+ if (!ResolverBundle.equivalentMaps(childExport.getDirectives(), matchingExport.getDirectives(), false))
+ return false;
+ return true;
+ }
+
+ protected void startHook() {
+ ((CompositeModule) getCompanionBundle()).started(this);
+ }
+
+ protected void stopHook() {
+ ((CompositeModule) getCompanionBundle()).stopped(this);
+ }
+}
diff --git a/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/service/internal/composite/CompositeModule.java b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/service/internal/composite/CompositeModule.java
new file mode 100644
index 000000000..502ed47da
--- /dev/null
+++ b/bundles/org.eclipse.osgi/core/composite/org/eclipse/osgi/service/internal/composite/CompositeModule.java
@@ -0,0 +1,28 @@
+package org.eclipse.osgi.service.internal.composite;
+
+import java.io.InputStream;
+import org.eclipse.osgi.framework.adaptor.ClassLoaderDelegate;
+import org.eclipse.osgi.service.resolver.BundleDescription;
+import org.osgi.framework.BundleException;
+
+/**
+ * An internal interface only used by the composite implementation
+ *
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface CompositeModule {
+ public void updateContent(InputStream content) throws BundleException;
+
+ public void refreshContent(boolean synchronously);
+
+ public boolean resolveContent();
+
+ public BundleDescription getCompositeDescription();
+
+ public ClassLoaderDelegate getDelegate();
+
+ public void started(CompositeModule compositeBundle);
+
+ public void stopped(CompositeModule compositeBundle);
+}
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/AbstractBundle.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/AbstractBundle.java
index 545b03dc5..fdd41d081 100644
--- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/AbstractBundle.java
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/AbstractBundle.java
@@ -20,6 +20,8 @@ import java.util.*;
import org.eclipse.osgi.framework.adaptor.*;
import org.eclipse.osgi.framework.debug.Debug;
import org.eclipse.osgi.framework.util.KeyedElement;
+import org.eclipse.osgi.internal.composite.CompositeImpl;
+import org.eclipse.osgi.internal.composite.SurrogateImpl;
import org.eclipse.osgi.internal.loader.BundleLoader;
import org.eclipse.osgi.internal.permadmin.EquinoxProtectionDomain;
import org.eclipse.osgi.internal.permadmin.EquinoxSecurityManager;
@@ -63,6 +65,10 @@ public abstract class AbstractBundle implements Bundle, Comparable, KeyedElement
AbstractBundle result;
if ((bundledata.getType() & BundleData.TYPE_FRAGMENT) > 0)
result = new BundleFragment(bundledata, framework);
+ else if ((bundledata.getType() & BundleData.TYPE_COMPOSITEBUNDLE) > 0)
+ result = new CompositeImpl(bundledata, framework);
+ else if ((bundledata.getType() & BundleData.TYPE_SURROGATEBUNDLE) > 0)
+ result = new SurrogateImpl(bundledata, framework);
else
result = new BundleHost(bundledata, framework);
if (setBundle)
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleHost.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleHost.java
index 4e5224dd7..d85c02a8e 100644
--- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleHost.java
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/BundleHost.java
@@ -346,7 +346,7 @@ public class BundleHost extends AbstractBundle {
}
try {
context.start();
-
+ startHook();
if (framework.active) {
state = ACTIVE;
@@ -363,6 +363,7 @@ public class BundleHost extends AbstractBundle {
state = STOPPING;
framework.publishBundleEvent(BundleEvent.STOPPING, this);
+ stopHook();
context.close();
context = null;
@@ -388,6 +389,13 @@ public class BundleHost extends AbstractBundle {
}
}
+ /**
+ * @throws BundleException
+ */
+ protected void startHook() throws BundleException {
+ // do nothing by default
+ }
+
protected boolean readyToResume() {
// Return false if the bundle is not at the correct start-level
if (getStartLevel() > framework.startLevelManager.getStartLevel())
@@ -461,6 +469,7 @@ public class BundleHost extends AbstractBundle {
if (context != null)
context.stop();
} finally {
+ stopHook();
if (context != null) {
context.close();
context = null;
@@ -485,6 +494,13 @@ public class BundleHost extends AbstractBundle {
}
/**
+ * @throws BundleException
+ */
+ protected void stopHook() throws BundleException {
+ // do nothing
+ }
+
+ /**
* Provides a list of {@link ServiceReference}s for the services
* registered by this bundle
* or <code>null</code> if the bundle has no registered
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java
index af85e59c9..9483699a6 100644
--- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java
@@ -148,26 +148,35 @@ public class PackageAdminImpl implements PackageAdmin {
}
public void refreshPackages(Bundle[] input) {
+ refreshPackages(input, false);
+ }
+
+ public void refreshPackages(Bundle[] input, boolean synchronously) {
framework.checkAdminPermission(framework.systemBundle, AdminPermission.RESOLVE);
- AbstractBundle[] copy = null;
+ final AbstractBundle[] copy;
if (input != null) {
synchronized (input) {
copy = new AbstractBundle[input.length];
System.arraycopy(input, 0, copy, 0, input.length);
}
- }
-
- final AbstractBundle[] bundles = copy;
- Thread refresh = framework.secureAction.createThread(new Runnable() {
- public void run() {
- doResolveBundles(bundles, true);
- if (framework.isForcedRestart())
- framework.shutdown(FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED);
- }
- }, "Refresh Packages"); //$NON-NLS-1$
+ } else
+ copy = null;
- refresh.start();
+ if (synchronously) {
+ doResolveBundles(copy, true);
+ if (framework.isForcedRestart())
+ framework.systemBundle.stop();
+ } else {
+ Thread refresh = framework.secureAction.createThread(new Runnable() {
+ public void run() {
+ doResolveBundles(copy, true);
+ if (framework.isForcedRestart())
+ framework.shutdown(FrameworkEvent.STOPPED_BOOTCLASSPATH_MODIFIED);
+ }
+ }, "Refresh Packages"); //$NON-NLS-1$
+ refresh.start();
+ }
}
public boolean resolveBundles(Bundle[] bundles) {
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/loader/BundleLoaderProxy.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/loader/BundleLoaderProxy.java
index 55e3c9d8e..6f2e494d2 100644
--- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/loader/BundleLoaderProxy.java
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/internal/loader/BundleLoaderProxy.java
@@ -17,6 +17,7 @@ import org.eclipse.osgi.framework.internal.core.*;
import org.eclipse.osgi.framework.internal.core.Constants;
import org.eclipse.osgi.framework.util.KeyedHashSet;
import org.eclipse.osgi.framework.util.SecureAction;
+import org.eclipse.osgi.internal.composite.CompositeBase;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
import org.osgi.framework.*;
@@ -183,7 +184,7 @@ public class BundleLoaderProxy implements RequiredBundle {
}
public boolean inUse() {
- return description.getDependents().length > 0;
+ return (description.getDependents().length > 0) || ((bundle instanceof CompositeBase) && description.getResolvedImports().length > 0);
}
boolean forceSourceCreation(ExportPackageDescription export) {
diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/launch/EquinoxFWClassLoader.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/launch/EquinoxFWClassLoader.java
index 90604d5ad..2168b7fc3 100644
--- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/launch/EquinoxFWClassLoader.java
+++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/launch/EquinoxFWClassLoader.java
@@ -15,7 +15,7 @@ import java.net.URLClassLoader;
class EquinoxFWClassLoader extends URLClassLoader {
- private static final String[] DELEGATE_PARENT = {"java.", "org.osgi.", "org.eclipse.osgi.launch.", "org.eclipse.osgi.service."}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] DELEGATE_PARENT = {"java.", "org.osgi.", "org.eclipse.osgi.launch.", "org.eclipse.osgi.service.", "org.eclipse.osgi.framework.log", "org.eclipse.osgi.framework.adaptor", "org.eclipse.osgi.framework.internal.core.ReferenceInputStream"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
private static final String[] SKIP_PARENT = {"org.osgi.framework.AdminPermission", "org.osgi.framework.FrameworkUtil", "org.osgi.service.condpermadmin.BundleSignerCondition"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
private final ClassLoader parent;
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/BaseStorageHook.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/BaseStorageHook.java
index a8efe77d4..03f932ea4 100644
--- a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/BaseStorageHook.java
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/BaseStorageHook.java
@@ -43,6 +43,9 @@ public class BaseStorageHook implements StorageHook, AdaptorHook {
public static final String EXTERNAL_LIB_PREFIX = "external:"; //$NON-NLS-1$
public static final String VARIABLE_DELIM_STRING = "$"; //$NON-NLS-1$
public static final char VARIABLE_DELIM_CHAR = '$';
+ public static String COMPOSITE_HEADER = "Equinox-CompositeBundle"; //$NON-NLS-1$
+ public static String COMPOSITE_BUNDLE = "composite"; //$NON-NLS-1$
+ public static String SURROGATE_BUNDLE = "surrogate"; //$NON-NLS-1$
/** bundle's file name */
private String fileName;
@@ -110,6 +113,14 @@ public class BaseStorageHook implements StorageHook, AdaptorHook {
else if (extensionType.equals("extclasspath")) //$NON-NLS-1$
bundleType |= BundleData.TYPE_EXTCLASSPATH_EXTENSION;
}
+ } else {
+ String composite = (String) manifest.get(COMPOSITE_HEADER);
+ if (composite != null) {
+ if (COMPOSITE_BUNDLE.equals(composite))
+ bundleType |= BundleData.TYPE_COMPOSITEBUNDLE;
+ else
+ bundleType |= BundleData.TYPE_SURROGATEBUNDLE;
+ }
}
target.setType(bundleType);
target.setExecutionEnvironment((String) manifest.get(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT));
diff --git a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/SystemBundleData.java b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/SystemBundleData.java
index 75b4c3185..7148b0b6e 100644
--- a/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/SystemBundleData.java
+++ b/bundles/org.eclipse.osgi/defaultAdaptor/src/org/eclipse/osgi/internal/baseadaptor/SystemBundleData.java
@@ -165,10 +165,6 @@ public class SystemBundleData extends BaseData {
// do nothing
}
- public File getDataFile(String path) {
- return null;
- }
-
public int getStartLevel() {
return 0;
}
diff --git a/bundles/org.eclipse.osgi/hookconfigurators.properties b/bundles/org.eclipse.osgi/hookconfigurators.properties
index bb97ef345..162960246 100644
--- a/bundles/org.eclipse.osgi/hookconfigurators.properties
+++ b/bundles/org.eclipse.osgi/hookconfigurators.properties
@@ -18,5 +18,6 @@ hook.configurators= \
org.eclipse.core.runtime.internal.adaptor.EclipseClassLoadingHook,\
org.eclipse.core.runtime.internal.adaptor.EclipseLazyStarter,\
org.eclipse.core.runtime.internal.stats.StatsManager,\
- org.eclipse.osgi.internal.signedcontent.SignedBundleHook
+ org.eclipse.osgi.internal.signedcontent.SignedBundleHook,\
+ org.eclipse.osgi.internal.composite.CompositeConfigurator
builtin.hooks = true
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/CompositeBundle.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/CompositeBundle.java
new file mode 100644
index 000000000..072bbfc11
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/CompositeBundle.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) OSGi Alliance (2008). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.osgi.service.framework;
+
+import java.io.InputStream;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.launch.Framework;
+
+/**
+ * Composite bundles are composed of other bundles. The component bundles which
+ * make up the content of a composite bundle are installed into a child
+ * framework. Like a normal bundle, a composite bundle may import packages and
+ * use services from other bundles which are installed in the same framework as
+ * the composite bundle. The packages imported and the services used by a
+ * composite bundle are shared with the components of a composite bundle through
+ * the surrogate bundle installed in the child framework. Also like a normal
+ * bundle, a composite bundle may export packages and register services which
+ * can be used by bundles installed in the same framework as the composite
+ * bundle. The packages exported and the services registered by a composite
+ * bundle are acquired from the components of a composite bundle by the
+ * surrogate bundle installed in the child framework
+ * <p>
+ * A framework has one composite bundle for each of its child frameworks. A
+ * framework can have zero or more composite bundles installed. A child
+ * framework must have one and only one surrogate bundle which represents the
+ * composite bundle. In other words a parent framework can have many children
+ * frameworks but a child framework can only have one parent.
+ * <p>
+ * A composite bundle does the following as specified by the composite manifest
+ * map:
+ * <ul>
+ * <li>Exports packages to the parent framework from the child framework. These
+ * packages are imported by the surrogate bundle installed in the child
+ * framework.</li>
+ * <li>Imports packages from the parent framework. These packages are exported
+ * by the surrogate bundle installed in the child framework.</li>
+ * <li>Registers services to the parent framework from the child framework.
+ * These services are acquired by the surrogate bundle installed in the child
+ * framework.</li>
+ * <li>Acquires services from the parent framework. These services are
+ * registered by the surrogate bundle installed in the child framework.</li>
+ * </ul>
+ *
+ * A newly created child <code>Framework</code> will be in the
+ * {@link Bundle#STARTING STARTING} state. This child <code>Framework</code> can
+ * then be used to manage and control the child framework instance. The child
+ * framework instance is persistent and uses a storage area in the composite
+ * bundle's data area. The child framework's lifecycle is tied to its composite
+ * bundle's lifecycle in the following ways:
+ * <p>
+ * <ul>
+ * <li>If the composite bundle is marked to be persistently started (see
+ * StartLevel.isBundlePersistentlyStarted(Bundle)) then the child framework
+ * instance will automatically be started when the composite bundle's
+ * start-level is met.</li>
+ * <li>The child framework instance will be stopped if the composite bundle is
+ * persistently stopped or its start level is no longer met. Performing
+ * operations which transiently stop a composite bundle do not cause the child
+ * framework to stop (e.g. {@link Bundle#stop(int) stop(Bundle.STOP_TRANSIENT)},
+ * {@link Bundle#update() update}, refreshPackage etc.).</li>
+ * </ul>
+ * <p>
+ * The child framework may be persistently started and stopped by persistently
+ * starting and stopping the composite bundle, but it is still possible to
+ * initialize and start the child framework explicitly while the composite
+ * bundle is not persistently started. This allows for the child framework to be
+ * initialized and populated with a set of bundles before starting the composite
+ * bundle. The set of bundles installed into the child framework are the
+ * component bundles which compose the composite bundle.
+ * <p>
+ * If the child framework is started while the composite bundle is not
+ * persistently started then the child framework lifecycle is tied to its parent
+ * framework lifecycle. When the parent <code>Framework</code> enters the
+ * {@link Bundle#STOPPING STOPPING} state then all active child frameworks of
+ * that parent are shutdown using the to the {@link Framework#stop()} method.
+ * The parent <code>Framework</code> must not enter the {@link Bundle#RESOLVED}
+ * state until all the child frameworks have completed their shutdown process.
+ * After the parent framework has completed the shutdown process then all child
+ * framework instances become invalid and must not be allowed to re-initialize
+ * or re-start.
+ *
+ * @see SurrogateBundle
+ * @ThreadSafe
+ * @version $Revision: 1.1.2.2 $
+ */
+public interface CompositeBundle extends Bundle {
+ /**
+ * Returns the composite framework associated with this composite bundle.
+ *
+ * @return the companion framework.
+ */
+ Framework getCompositeFramework();
+
+ /**
+ * Returns the surrogate bundle associated with this composite bundle. The
+ * surrogate bundle is installed in the composite framework.
+ *
+ * @return the surrogate bundle.
+ */
+ SurrogateBundle getSurrogateBundle();
+
+ /**
+ * Updates this child composite bundle with the specified manifest.
+ *
+ * @param compositeManifest the new composite manifest.
+ * @throws BundleException If the update fails.
+ * @see CompositeBundleFactory#installCompositeBundle(Map, String, Map)
+ */
+ void update(Map /* <String, String> */compositeManifest)
+ throws BundleException;
+
+ /**
+ * This operation is not supported for composite bundles. A
+ * <code>BundleException</code> of type
+ * {@link BundleException#INVALID_OPERATION invalid operation} must be
+ * thrown.
+ */
+ void update() throws BundleException;
+
+ /**
+ * This operation is not supported for composite bundles. A
+ * <code>BundleException</code> of type
+ * {@link BundleException#INVALID_OPERATION invalid operation} must be
+ * thrown.
+ */
+ void update(InputStream input) throws BundleException;
+
+ /**
+ * Uninstalls this composite bundle and its companion bundle.
+ * <p>
+ * If this composite bundle is a child composite then the companion child
+ * framework is shutdown and its persistent storage area is deleted.
+ */
+ void uninstall() throws BundleException;
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/CompositeBundleFactory.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/CompositeBundleFactory.java
new file mode 100644
index 000000000..797327cb0
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/CompositeBundleFactory.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) OSGi Alliance (2008). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.osgi.service.framework;
+
+import java.util.Map;
+
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+
+/**
+ * Framework service that is used to create composite bundles.
+ * <p>
+ * If present, there will only be a single instance of this service registered
+ * with the Framework.
+ *
+ * @ThreadSafe
+ * @version $Revision: 1.1.2.2 $
+ */
+// TODO javadoc needs review
+public interface CompositeBundleFactory {
+ /**
+ * Manifest header (named &quot;CompositeServiceFilter-Import&quot;)
+ * identifying the service filters that are used by a child composite bundle
+ * to select services that will be registered into a child framework from a
+ * parent composite bundle.
+ */
+ public static final String COMPOSITE_SERVICE_FILTER_IMPORT = "CompositeServiceFilter-Import";
+
+ /**
+ * Manifest header (named &quot;CompositeServiceFilter-Export&quot;)
+ * identifying the service filters that are used by a parent composite
+ * bundle to select services that will be registered into a parent framework
+ * from a child composite bundle.
+ */
+ public static final String COMPOSITE_SERVICE_FILTER_EXPORT = "CompositeServiceFilter-Export";
+
+ /**
+ * Installs a <code>CompositeBundle</code>. The composite bundle has a new
+ * child <code>Framework</code> associated with it and a surrogate bundle
+ * which is installed in the child framework. Composite bundles share
+ * packages and services between parent framework they are installed in and
+ * the the child framework.
+ * <p>
+ * The following steps are required to create a composite bundle:
+ * <ol>
+ * <li>If a bundle containing the same location string is already installed,
+ * then if the Bundle object is a <code>CompositeBundle</code> then that
+ * composite bundle is returned; otherwise a BundleException is thrown
+ * indicating that an incompatible bundle is already installed at the
+ * specified location.</li>
+ * <li>The composite bundle's associated resources are allocated. The
+ * associated resources minimally consist of a unique identifier and a
+ * persistent storage area. If this step fails, a BundleException is thrown.
+ * <li>The compositeManifest map is used to provide the headers for the
+ * composite bundle and its surrogate bundle.
+ * <p>
+ * If composite manifest map does not contain the following header(s) then a
+ * BundleException is thrown:
+ * <ul>
+ * <li> {@link Constants#BUNDLE_SYMBOLICNAME Bundle-SymbolicName} the
+ * symbolic name used for the composite bundle and its surrogate bundle.
+ * </ul>
+ * </p>
+ * The composite manifest map may optionally contain the following
+ * header(s):
+ * <ul>
+ * <li> {@link Constants#BUNDLE_VERSION Bundle-Version} the bundle version
+ * used for the composite bundle and its surrogate bundle.</li>
+ * <li> {@link Constants#IMPORT_PACKAGE Import-Package} the packages which
+ * are imported from the parent framework by the composite bundle and are
+ * exported to the child framework by the surrogate bundle.</li>
+ * <li>{@link Constants#EXPORT_PACKAGE Export-Package} the packages which
+ * are imported from the child framework by the surrogate bundle and are
+ * exported to the parent framework by the composite bundle.</li>
+ * <li>{@link #COMPOSITE_SERVICE_FILTER_IMPORT
+ * CompositeServiceFilter-Import} the service filters which are acquired
+ * from the parent framework by the composite bundle and are registered in
+ * the child framework by the surrogate bundle.</li>
+ * <li>{@link #COMPOSITE_SERVICE_FILTER_EXPORT
+ * CompositeServiceFilter-Export} the service filters which are acquired
+ * from the child framework by the surrogate bundle and are registered in
+ * the parent framework by the composite bundle.</li>
+ * <li>{@link Constants#BUNDLE_MANIFESTVERSION Bundle-ManifestVersion} the
+ * bundle manifest version. If this header is not specified then the default
+ * is to use version 2. A BundleException is thrown if this header is
+ * specified and the version is less than 2.</li>
+ * </ul>
+ * The composite manifest map must not contain the following headers. If a
+ * composite manifest map does contain one of the following headers then a
+ * BundleException is thrown:
+ * <ul>
+ * <li> {@link Constants#BUNDLE_ACTIVATIONPOLICY Bundle-ActivationPolicy}</li>
+ * <li> {@link Constants#BUNDLE_ACTIVATOR Bundle-Activator}</li>
+ * <li> {@link Constants#BUNDLE_CLASSPATH Bundle-ClassPath}</li>
+ * <li> {@link Constants#BUNDLE_LOCALIZATION Bundle-Localization}</li>
+ * <li> {@link Constants#BUNDLE_NATIVECODE Bundle-NativeCode}</li>
+ * <li> {@link Constants#DYNAMICIMPORT_PACKAGE DynamicImport-Package}</li>
+ * <li> {@link Constants#FRAGMENT_HOST Fragment-Host}</li>
+ * <li> {@link Constants#REQUIRE_BUNDLE Require-Bundle}</li>
+ * </ul>
+ * <li>A child framework is created which uses a storage area under the
+ * composite bundle's associated persistent storage. Note that if the
+ * framework configuration property {@link Constants#FRAMEWORK_STORAGE
+ * org.osgi.framework.storage} is specified in the framework config then it
+ * is ignored.</li>
+ * <li>The child framework is initialized (see {@link Framework#init()}).
+ * <li>A surrogate bundle is installed into the child framework</li>
+ * <li>The composite bundle's state is set to INSTALLED.</li>
+ * <li>A bundle event of type {@link BundleEvent#INSTALLED} is fired for the
+ * composite bundle.
+ * <li>The <code>CompositeBundle</code> object for the newly composite
+ * bundle is returned
+ * </ol>
+ * <p>
+ *
+ * @param frameworkConfig the child framework configuration.
+ * @param location the bundle location used for the child composite bundle
+ * and its companion bundle.
+ * @param compositeManifest the manifest used to create the composite bundle
+ * @return A new child composite bundle.
+ * @throws BundleException If the composite manifest is invalid or there is
+ * some other problem with installing the composite bundle.
+ * @throws SecurityException If the caller does not have
+ * <code>AllPermission</code>.
+ * @see Framework
+ * @see CompositeBundle
+ */
+ CompositeBundle installCompositeBundle(
+ Map /* <String, String> */frameworkConfig, String location,
+ Map /* <String, String> */compositeManifest) throws BundleException;
+
+}
diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/SurrogateBundle.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/SurrogateBundle.java
new file mode 100644
index 000000000..5c95e7860
--- /dev/null
+++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/service/framework/SurrogateBundle.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) OSGi Alliance (2008). All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.osgi.service.framework;
+
+import java.io.InputStream;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+/**
+ * A surrogate bundle represents a composite bundle within a child framework.
+ * A surrogate bundle does the following as specified by the composite manifest:
+ * <ul>
+ * <li>Exports packages to the child framework from the parent framework. These
+ * packages are imported by the composite bundle installed in the parent
+ * framework.</li>
+ * <li>Imports packages from the child framework. These packages are exported
+ * by the composite bundle installed in the parent framework.</li>
+ * <li>Registers services with the child framework from the parent framework.
+ * These services are acquired by the composite bundle installed in the
+ * parent framework.</li>
+ * <li>Acquires services from the child framework. These services are
+ * registered by the composite bundle installed in the parent framework.</li>
+ * </ul>
+ * <p>
+ * @see CompositeBundle
+ */
+public interface SurrogateBundle extends Bundle {
+ /**
+ * Returns the bundle context of the composite bundle which this
+ * surrogate bundle represents.
+ * @return the bundle context of the composite bundle. A value
+ * of <code>null</code> is returned if the composite bundle does
+ * not have a valid bundle context.
+ */
+ BundleContext getCompositeBundleContext();
+
+ /**
+ * This operation is not supported for surrogate bundles. A
+ * <code>BundleException</code> of type
+ * {@link BundleException#INVALID_OPERATION invalid operation} must be
+ * thrown.
+ */
+ void update() throws BundleException;
+
+ /**
+ * This operation is not supported for surrogate bundles. A
+ * <code>BundleException</code> of type
+ * {@link BundleException#INVALID_OPERATION invalid operation} must be
+ * thrown.
+ */
+ void update(InputStream input) throws BundleException;
+
+ /**
+ * This operation is not supported for surrogate bundles. A
+ * <code>BundleException</code> of type
+ * {@link BundleException#INVALID_OPERATION invalid operation} must be
+ * thrown.
+ */
+ void uninstall() throws BundleException;
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/CompositeResolveHelper.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/CompositeResolveHelper.java
new file mode 100644
index 000000000..5f7cdba7d
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/CompositeResolveHelper.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.ExportPackageDescription;
+
+public interface CompositeResolveHelper {
+ public boolean giveExports(ExportPackageDescription[] matchingExports);
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/CompositeResolveHelperRegistry.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/CompositeResolveHelperRegistry.java
new file mode 100644
index 000000000..f4cd6fb80
--- /dev/null
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/CompositeResolveHelperRegistry.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.module;
+
+import org.eclipse.osgi.service.resolver.BundleDescription;
+
+public interface CompositeResolveHelperRegistry {
+ public CompositeResolveHelper getCompositeResolveHelper(BundleDescription bundle);
+}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java
index dcae3876c..d44e4416f 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverBundle.java
@@ -392,19 +392,19 @@ public class ResolverBundle extends VersionSupplier implements Comparable {
return false;
if (!existingDescription.getVersion().equals(newDescription.getVersion()))
return false;
- if (!equivalentMaps(existingDescription.getAttributes(), newDescription.getAttributes()))
+ if (!equivalentMaps(existingDescription.getAttributes(), newDescription.getAttributes(), true))
return false;
- if (!equivalentMaps(existingDescription.getDirectives(), newDescription.getDirectives()))
+ if (!equivalentMaps(existingDescription.getDirectives(), newDescription.getDirectives(), true))
return false;
return true;
}
- private boolean equivalentMaps(Map existingDirectives, Map newDirectives) {
+ public static boolean equivalentMaps(Map existingDirectives, Map newDirectives, boolean exactMatch) {
if (existingDirectives == null && newDirectives == null)
return true;
if (existingDirectives == null ? newDirectives != null : newDirectives == null)
return false;
- if (existingDirectives.size() != newDirectives.size())
+ if (exactMatch && existingDirectives.size() != newDirectives.size())
return false;
for (Iterator entries = existingDirectives.entrySet().iterator(); entries.hasNext();) {
Entry entry = (Entry) entries.next();
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
index 2d80e5263..19eff3497 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/module/ResolverImpl.java
@@ -72,6 +72,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver
private GroupingChecker groupingChecker;
private Comparator selectionPolicy;
private boolean developmentMode = false;
+ private volatile CompositeResolveHelperRegistry compositeHelpers;
public ResolverImpl(BundleContext context, boolean checkPermissions) {
this.permissionChecker = new PermissionChecker(context, checkPermissions, this);
@@ -535,46 +536,76 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver
resolveFragment(unresolved[i]);
}
checkUsesConstraints(bundles, platformProperties, rejectedSingletons);
+ checkComposites(bundles, platformProperties, rejectedSingletons);
+ }
+
+ private void checkComposites(ResolverBundle[] bundles, Dictionary[] platformProperties, ArrayList rejectedSingletons) {
+ CompositeResolveHelperRegistry helpers = getCompositeHelpers();
+ if (helpers == null)
+ return;
+ Set exclude = null;
+ for (int i = 0; i < bundles.length; i++) {
+ CompositeResolveHelper helper = helpers.getCompositeResolveHelper(bundles[i].getBundle());
+ if (helper == null)
+ continue;
+ if (!bundles[i].isResolved())
+ continue;
+ if (!helper.giveExports(getExportsWiredTo(bundles[i]))) {
+ state.addResolverError(bundles[i].getBundle(), ResolverError.DISABLED_BUNDLE, null, null);
+ bundles[i].setResolvable(false);
+ bundles[i].clearRefs();
+ setBundleUnresolved(bundles[i], false, developmentMode);
+ if (exclude == null)
+ exclude = new HashSet(1);
+ exclude.add(bundles[i]);
+ }
+ }
+ reResolveBundles(exclude, bundles, platformProperties, rejectedSingletons);
}
private void checkUsesConstraints(ResolverBundle[] bundles, Dictionary[] platformProperties, ArrayList rejectedSingletons) {
ArrayList conflictingConstraints = findBestCombination(bundles);
+ if (conflictingConstraints == null)
+ return;
Set conflictedBundles = null;
- if (conflictingConstraints != null) {
- for (Iterator conflicts = conflictingConstraints.iterator(); conflicts.hasNext();) {
- ResolverConstraint conflict = (ResolverConstraint) conflicts.next();
- if (conflict.isOptional()) {
- conflict.clearPossibleSuppliers();
- continue;
- }
+ for (Iterator conflicts = conflictingConstraints.iterator(); conflicts.hasNext();) {
+ ResolverConstraint conflict = (ResolverConstraint) conflicts.next();
+ if (conflict.isOptional()) {
+ conflict.clearPossibleSuppliers();
+ continue;
+ }
+ if (conflictedBundles == null)
conflictedBundles = new HashSet(conflictingConstraints.size());
- ResolverBundle conflictedBundle;
- if (conflict.isFromFragment())
- conflictedBundle = (ResolverBundle) bundleMapping.get(conflict.getVersionConstraint().getBundle());
- else
- conflictedBundle = conflict.getBundle();
- if (conflictedBundle != null) {
- if (DEBUG_USES)
- System.out.println("Found conflicting constraint: " + conflict + " in bundle " + conflictedBundle); //$NON-NLS-1$//$NON-NLS-2$
- conflictedBundles.add(conflictedBundle);
- int type = conflict instanceof ResolverImport ? ResolverError.IMPORT_PACKAGE_USES_CONFLICT : ResolverError.REQUIRE_BUNDLE_USES_CONFLICT;
- state.addResolverError(conflictedBundle.getBundle(), type, conflict.getVersionConstraint().toString(), conflict.getVersionConstraint());
- conflictedBundle.setResolvable(false);
- conflictedBundle.clearRefs();
- setBundleUnresolved(conflictedBundle, false, developmentMode);
- }
+ ResolverBundle conflictedBundle;
+ if (conflict.isFromFragment())
+ conflictedBundle = (ResolverBundle) bundleMapping.get(conflict.getVersionConstraint().getBundle());
+ else
+ conflictedBundle = conflict.getBundle();
+ if (conflictedBundle != null) {
+ if (DEBUG_USES)
+ System.out.println("Found conflicting constraint: " + conflict + " in bundle " + conflictedBundle); //$NON-NLS-1$//$NON-NLS-2$
+ conflictedBundles.add(conflictedBundle);
+ int type = conflict instanceof ResolverImport ? ResolverError.IMPORT_PACKAGE_USES_CONFLICT : ResolverError.REQUIRE_BUNDLE_USES_CONFLICT;
+ state.addResolverError(conflictedBundle.getBundle(), type, conflict.getVersionConstraint().toString(), conflict.getVersionConstraint());
+ conflictedBundle.setResolvable(false);
+ conflictedBundle.clearRefs();
+ setBundleUnresolved(conflictedBundle, false, developmentMode);
}
- if (conflictedBundles != null && conflictedBundles.size() > 0) {
- ArrayList remainingUnresolved = new ArrayList();
- for (int i = 0; i < bundles.length; i++) {
- if (!conflictedBundles.contains(bundles[i])) {
- setBundleUnresolved(bundles[i], false, developmentMode);
- remainingUnresolved.add(bundles[i]);
- }
- }
- resolveBundles0((ResolverBundle[]) remainingUnresolved.toArray(new ResolverBundle[remainingUnresolved.size()]), platformProperties, rejectedSingletons);
+ }
+ reResolveBundles(conflictedBundles, bundles, platformProperties, rejectedSingletons);
+ }
+
+ private void reResolveBundles(Set exclude, ResolverBundle[] bundles, Dictionary[] platformProperties, ArrayList rejectedSingletons) {
+ if (exclude == null || exclude.size() == 0)
+ return;
+ ArrayList remainingUnresolved = new ArrayList();
+ for (int i = 0; i < bundles.length; i++) {
+ if (!exclude.contains(bundles[i])) {
+ setBundleUnresolved(bundles[i], false, developmentMode);
+ remainingUnresolved.add(bundles[i]);
}
}
+ resolveBundles0((ResolverBundle[]) remainingUnresolved.toArray(new ResolverBundle[remainingUnresolved.size()]), platformProperties, rejectedSingletons);
}
private ArrayList findBestCombination(ResolverBundle[] bundles) {
@@ -1434,12 +1465,7 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver
ExportPackageDescription[] substitutedExportsArray = (ExportPackageDescription[]) substitutedExports.toArray(new ExportPackageDescription[substitutedExports.size()]);
// Gather exports that have been wired to
- ResolverImport[] imports = rb.getImportPackages();
- ArrayList exportsWiredTo = new ArrayList(imports.length);
- for (int i = 0; i < imports.length; i++)
- if (imports[i].getSelectedSupplier() != null)
- exportsWiredTo.add(imports[i].getSelectedSupplier().getBaseDescription());
- ExportPackageDescription[] exportsWiredToArray = (ExportPackageDescription[]) exportsWiredTo.toArray(new ExportPackageDescription[exportsWiredTo.size()]);
+ ExportPackageDescription[] exportsWiredToArray = getExportsWiredTo(rb);
// Gather bundles that have been wired to
BundleConstraint[] requires = rb.getRequires();
@@ -1472,6 +1498,16 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver
state.resolveBundle(rb.getBundle(), rb.isResolved(), hostBundles, selectedExportsArray, substitutedExportsArray, bundlesWiredToArray, exportsWiredToArray);
}
+ private static ExportPackageDescription[] getExportsWiredTo(ResolverBundle rb) {
+ // Gather exports that have been wired to
+ ResolverImport[] imports = rb.getImportPackages();
+ ArrayList exportsWiredTo = new ArrayList(imports.length);
+ for (int i = 0; i < imports.length; i++)
+ if (imports[i].getSelectedSupplier() != null)
+ exportsWiredTo.add(imports[i].getSelectedSupplier().getBaseDescription());
+ return (ExportPackageDescription[]) exportsWiredTo.toArray(new ExportPackageDescription[exportsWiredTo.size()]);
+ }
+
// Resolve dynamic import
public synchronized ExportPackageDescription resolveDynamicImport(BundleDescription importingBundle, String requestedPackage) {
if (state == null)
@@ -1616,6 +1652,12 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver
if (!bundle.getBundle().isResolved() && !developmentMode)
return;
+ CompositeResolveHelperRegistry currentLinks = compositeHelpers;
+ if (currentLinks != null) {
+ CompositeResolveHelper helper = currentLinks.getCompositeResolveHelper(bundle.getBundle());
+ if (helper != null)
+ helper.giveExports(null);
+ }
// if not removed then add to the list of unresolvedBundles,
// passing false for devmode because we need all fragments detached
setBundleUnresolved(bundle, removed, false);
@@ -1739,4 +1781,12 @@ public class ResolverImpl implements org.eclipse.osgi.service.resolver.Resolver
public Comparator getSelectionPolicy() {
return selectionPolicy;
}
+
+ public void setCompositeResolveHelperRegistry(CompositeResolveHelperRegistry compositeHelpers) {
+ this.compositeHelpers = compositeHelpers;
+ }
+
+ CompositeResolveHelperRegistry getCompositeHelpers() {
+ return compositeHelpers;
+ }
}
diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java
index 39b8c553f..d369399c9 100644
--- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java
+++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateImpl.java
@@ -19,6 +19,7 @@ import org.eclipse.osgi.framework.internal.core.Constants;
import org.eclipse.osgi.framework.internal.core.FilterImpl;
import org.eclipse.osgi.framework.util.*;
import org.eclipse.osgi.internal.baseadaptor.StateManager;
+import org.eclipse.osgi.internal.loader.BundleLoaderProxy;
import org.eclipse.osgi.service.resolver.*;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
@@ -121,7 +122,7 @@ public abstract class StateImpl implements State {
if (getSystemBundle().equals(newDescription.getSymbolicName()))
resetSystemExports();
if (resolver != null) {
- boolean pending = existing.getDependents().length > 0;
+ boolean pending = isInUse(existing);
resolver.bundleUpdated(newDescription, existing, pending);
if (pending) {
getDelta().recordBundleRemovalPending(existing);
@@ -161,7 +162,7 @@ public abstract class StateImpl implements State {
getDelta().recordBundleRemoved((BundleDescriptionImpl) toRemove);
((BundleDescriptionImpl) toRemove).setStateBit(BundleDescriptionImpl.REMOVAL_PENDING, true);
if (resolver != null) {
- boolean pending = toRemove.getDependents().length > 0;
+ boolean pending = isInUse(toRemove);
resolver.bundleRemoved(toRemove, pending);
if (pending) {
getDelta().recordBundleRemovalPending((BundleDescriptionImpl) toRemove);
@@ -182,6 +183,13 @@ public abstract class StateImpl implements State {
}
}
+ private boolean isInUse(BundleDescription bundle) {
+ Object userObject = bundle.getUserObject();
+ if (userObject instanceof BundleLoaderProxy)
+ return ((BundleLoaderProxy) userObject).inUse();
+ return bundle.getDependents().length > 0;
+ }
+
public StateDelta getChanges() {
synchronized (this.monitor) {
return getDelta();

Back to the top