blob: 2120a3923947eaff4db7b76f3c83959bb87dfaad [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010 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.URL;
import java.net.URLConnection;
import java.security.AccessControlContext;
import java.security.AllPermission;
import java.util.*;
import org.eclipse.osgi.framework.adaptor.BundleData;
import org.eclipse.osgi.framework.debug.Debug;
import org.eclipse.osgi.framework.internal.core.*;
import org.eclipse.osgi.framework.internal.protocol.ContentHandlerFactory;
import org.eclipse.osgi.framework.internal.protocol.StreamHandlerFactory;
import org.eclipse.osgi.internal.composite.CompositeInfo.ClassSpacePolicyInfo;
import org.eclipse.osgi.internal.composite.CompositeInfo.ServicePolicyInfo;
import org.eclipse.osgi.internal.loader.BundleLoaderProxy;
import org.eclipse.osgi.internal.permadmin.SecurityAdmin;
import org.eclipse.osgi.internal.resolver.StateBuilder;
import org.eclipse.osgi.internal.resolver.StateObjectFactoryImpl;
import org.eclipse.osgi.service.resolver.*;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.*;
import org.osgi.framework.Constants;
import org.osgi.service.composite.CompositeBundle;
import org.osgi.service.composite.CompositeConstants;
import org.osgi.service.condpermadmin.ConditionalPermissionUpdate;
public class CompositeImpl extends BundleHost implements CompositeBundle {
static final StateObjectFactory stateFactory = new StateObjectFactoryImpl();
private final ThreadLocal<Boolean> updating = new ThreadLocal<Boolean>() {
public Boolean initialValue() {
return Boolean.FALSE;
}
};
private final CompositeSystemBundle compositeSystemBundle;
private final CompositeInfo compositeInfo;
private final StartLevelManager startLevelManager;
private final SecurityAdmin securityAdmin;
private final StreamHandlerFactory streamHandlerFactory;
private final ContentHandlerFactory contentHandlerFactory;
private final List<BundleDescription> constituents = new ArrayList<BundleDescription>(0);
private final int beginningStartLevel;
private final Properties configuration;
public CompositeImpl(BundleData bundledata, Framework framework, boolean setCompositeParent) throws BundleException {
super(bundledata, framework);
compositeSystemBundle = new CompositeSystemBundle((BundleHost) framework.getBundle(0), framework);
compositeInfo = createCompositeInfo(setCompositeParent);
Object existing = framework.getBundle(bundledata.getBundleID());
if (existing != null && existing instanceof CompositeImpl) {
checkBSNAndVersions((CompositeImpl) existing, compositeInfo);
}
startLevelManager = new StartLevelManager(framework, bundledata.getBundleID(), compositeSystemBundle);
startLevelManager.initialize();
configuration = loadCompositeConfiguration();
beginningStartLevel = loadBeginningStartLevel(configuration);
if (setCompositeParent) {
try {
securityAdmin = new SecurityAdmin(framework, framework.getAdaptor().getPermissionStorage(), bundledata.getBundleID());
} catch (IOException e) {
throw new BundleException("Error creating SecurityAdmin", e); //$NON-NLS-1$
}
streamHandlerFactory = new StreamHandlerFactory(compositeSystemBundle.getBundleContext(), framework.getAdaptor());
contentHandlerFactory = new ContentHandlerFactory(compositeSystemBundle.getBundleContext(), framework.getAdaptor());
framework.getStreamHandlerFactory().registerComposite(streamHandlerFactory);
framework.getContentHandlerFactory().registerComposite(contentHandlerFactory);
} else {
securityAdmin = null;
streamHandlerFactory = null;
contentHandlerFactory = null;
}
}
private void checkBSNAndVersions(CompositeImpl existing, CompositeInfo compositeInfo) throws BundleException {
// save off original policy
CompositeInfo original = new CompositeInfo(existing.getBundleId(), null, null, null, null, null, null, null, null, null);
original.update(existing.compositeInfo);
// update current policy
existing.compositeInfo.update(compositeInfo);
try {
validateBSNAndVersions(existing);
} finally {
existing.compositeInfo.update(original);
}
}
private void validateBSNAndVersions(CompositeImpl composite) throws BundleException {
AbstractBundle[] bundles = framework.getBundles(composite.getBundleId());
// check that we still have valid BSN/Version consistency
for (AbstractBundle bundle : bundles) {
if (bundle.getBundleId() == 0) // not the system bundle
continue;
framework.validateNameAndVersion(bundle.getBundleData());
if (bundle instanceof CompositeImpl)
validateBSNAndVersions((CompositeImpl) bundle);
}
}
private int loadBeginningStartLevel(Properties config) {
String level = config.getProperty(Constants.FRAMEWORK_BEGINNING_STARTLEVEL);
if (level == null)
return 1;
return Integer.parseInt(level);
}
private Properties loadCompositeConfiguration() throws BundleException {
Properties result = new Properties();
URL configURL = bundledata.getEntry(CompositeSupport.COMPOSITE_CONFIGURATION);
if (configURL == null)
return result;
InputStream in = null;
try {
in = configURL.openStream();
result.load(configURL.openStream());
} catch (IOException e) {
throw new BundleException("Error loading composite configuration: " + configURL, e); //$NON-NLS-1$
} finally {
if (in != null)
try {
in.close();
} catch (IOException e) {
// nothing
}
}
return result;
}
private CompositeInfo createCompositeInfo(boolean setParent) throws BundleException {
Dictionary manifest = bundledata.getManifest();
String importPackage = (String) manifest.get(CompositeConstants.COMPOSITE_PACKAGE_IMPORT_POLICY);
String exportPackage = (String) manifest.get(CompositeConstants.COMPOSITE_PACKAGE_EXPORT_POLICY);
String requireBundle = (String) manifest.get(CompositeConstants.COMPOSITE_BUNDLE_REQUIRE_POLICY);
String provideBundle = (String) manifest.get(CompositeConstants.COMPOSITE_BUNDLE_PROVIDE_POLICY);
String importService = (String) manifest.get(CompositeConstants.COMPOSITE_SERVICE_IMPORT_POLICY);
String exportService = (String) manifest.get(CompositeConstants.COMPOSITE_SERVICE_EXPORT_POLICY);
BundleContext systemContext = compositeSystemBundle.getBundleContext();
BundleDescription desc = stateFactory.createBundleDescription(manifest, bundledata.getLocation(), bundledata.getBundleID());
ClassSpacePolicyInfo[] imports = createClassSpacePolicy(importPackage, stateFactory, CompositeConstants.COMPOSITE_PACKAGE_IMPORT_POLICY, desc);
ClassSpacePolicyInfo[] exports = createClassSpacePolicy(exportPackage, stateFactory, CompositeConstants.COMPOSITE_PACKAGE_EXPORT_POLICY, desc);
ClassSpacePolicyInfo[] requires = createClassSpacePolicy(requireBundle, stateFactory, CompositeConstants.COMPOSITE_BUNDLE_REQUIRE_POLICY, desc);
ClassSpacePolicyInfo[] provides = createClassSpacePolicy(provideBundle, stateFactory, CompositeConstants.COMPOSITE_BUNDLE_PROVIDE_POLICY, desc);
ServicePolicyInfo[] importServiceFilter = createServicePolicyInfo(importService, stateFactory, CompositeConstants.COMPOSITE_SERVICE_IMPORT_POLICY, systemContext);
ServicePolicyInfo[] exportServiceFilter = createServicePolicyInfo(exportService, stateFactory, CompositeConstants.COMPOSITE_SERVICE_EXPORT_POLICY, systemContext);
// set the parent info
CompositeInfo parentInfo = null;
if (setParent) {
parentInfo = framework.getCompositeSupport().compositPolicy.getCompositeInfo(bundledata.getCompositeID());
}
CompositeInfo result = new CompositeInfo(bundledata.getBundleID(), bundledata.getSymbolicName(), bundledata.getVersion(), parentInfo, imports, exports, requires, provides, importServiceFilter, exportServiceFilter);
if (setParent) {
// add the the composite info as a child of the parent.
parentInfo.addChild(result);
}
return result;
}
public BundleContext getSystemBundleContext() {
if (getState() == Bundle.UNINSTALLED)
return null;
return compositeSystemBundle.getBundleContext();
}
public void update() throws BundleException {
throw new BundleException("Must update a composite with the update(Map) method.", BundleException.UNSUPPORTED_OPERATION); //$NON-NLS-1$
}
public void update(InputStream in) throws BundleException {
if (in == RELOAD) {
super.update(RELOAD);
return;
}
try {
in.close();
} catch (IOException e) {
// do nothing
}
update();
}
public void update(Map<String, String> compositeManifest) throws BundleException {
if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
Debug.println("update location " + bundledata.getLocation()); //$NON-NLS-1$
Debug.println(" from: " + compositeManifest); //$NON-NLS-1$
}
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<String, String>(compositeManifest);
// make sure the manifest is valid
CompositeSupport.validateCompositeManifest(compositeManifest);
try {
URL configURL = bundledata.getEntry(CompositeSupport.COMPOSITE_CONFIGURATION);
Properties config = new Properties();
config.load(configURL.openStream());
// get an in memory input stream to jar content of the composite we want to install
InputStream content = CompositeSupport.getCompositeInput(config, compositeManifest);
updating.set(true);
// update with the new content
super.update(content);
} catch (IOException e) {
throw new BundleException("Error creating composite bundle", e); //$NON-NLS-1$
} finally {
updating.set(false);
}
}
@Override
protected void updateWorkerPrivileged(URLConnection source, AccessControlContext callerContext) throws BundleException {
CompositeInfo originalInfo = createCompositeInfo(false);
super.updateWorkerPrivileged(source, callerContext);
// update the composite info with the new data.
CompositeInfo updatedInfo = createCompositeInfo(false);
compositeInfo.update(updatedInfo);
AbstractBundle[] bundles = framework.getBundles(getBundleId());
// reload all the constituents
for (AbstractBundle bundle : bundles) {
if (bundle.getBundleId() == 0) // not the system bundle
continue;
try {
bundle.reload();
} catch (BundleException e) {
framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e);
}
}
}
protected void startHook() {
startLevelManager.doSetStartLevel(beginningStartLevel);
}
protected void stopHook() {
if (updating.get())
startLevelManager.update();
else
startLevelManager.shutdown();
}
public void uninstallWorkerPrivileged() throws BundleException {
// uninstall the composite first to invalidate the context
super.uninstallWorkerPrivileged();
Bundle[] bundles = framework.getBundles(getBundleId());
// uninstall all the constituents
for (int i = 0; i < bundles.length; i++) {
if (bundles[i].getBundleId() != 0) // not the system bundle
try {
bundles[i].uninstall();
} catch (BundleException e) {
framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundles[i], e);
}
}
// clean up persistent storage associated with this composite
framework.getAdaptor().setInitialBundleStartLevel(getBundleId(), -1);
securityAdmin.setDefaultPermissions(null);
String[] locations = securityAdmin.getLocations();
if (locations != null)
for (int i = 0; i < locations.length; i++)
securityAdmin.setPermissions(locations[i], null);
ConditionalPermissionUpdate update = securityAdmin.newConditionalPermissionUpdate();
update.getConditionalPermissionInfos().clear();
update.commit();
}
protected void close() {
super.close();
startLevelManager.cleanup();
// remove the composite info from the parent
compositeInfo.orphaned();
// unregister the composite factory before closing the system bundle
getFramework().getStreamHandlerFactory().unregisterComposite(streamHandlerFactory);
getFramework().getContentHandlerFactory().unregisterComposite(contentHandlerFactory);
compositeSystemBundle.close();
}
public class CompositeSystemBundle extends InternalSystemBundle {
private final BundleHost rootSystemBundle;
public CompositeSystemBundle(BundleHost systemBundle, Framework framework) throws BundleException {
super(systemBundle.getBundleData(), framework);
this.rootSystemBundle = systemBundle;
this.state = Bundle.STARTING; // Initial state must be STARTING for composite system bundle
}
protected BundleContextImpl createContext() {
CompositeContext compositeContext = new CompositeContext(this);
return compositeContext;
}
public ServiceReference[] getRegisteredServices() {
// TODO this is not scoped; do we care?
return rootSystemBundle.getRegisteredServices();
}
public ServiceReference[] getServicesInUse() {
return rootSystemBundle.getServicesInUse();
}
public void start(int options) throws BundleException {
throw new BundleException("Cannot start a composite system bundle.", BundleException.UNSUPPORTED_OPERATION); //$NON-NLS-1$
}
public void start() throws BundleException {
start(0);
}
public void stop(int options) throws BundleException {
throw new BundleException("Cannot stop a composite system bundle.", BundleException.UNSUPPORTED_OPERATION); //$NON-NLS-1$
}
public void stop() throws BundleException {
stop(0);
}
public void uninstall() throws BundleException {
throw new BundleException("Cannot uninstall a composite system bundle.", BundleException.UNSUPPORTED_OPERATION); //$NON-NLS-1$
}
public void update(InputStream input) throws BundleException {
throw new BundleException("Cannot update a composite system bundle.", BundleException.UNSUPPORTED_OPERATION); //$NON-NLS-1$
}
public void update() throws BundleException {
update(null);
}
public long getCompositeId() {
return CompositeImpl.this.getBundleId();
}
public BundleLoaderProxy getLoaderProxy() {
return rootSystemBundle.getLoaderProxy();
}
protected void close() {
super.close();
this.state = Bundle.UNINSTALLED;
}
}
public class CompositeContext extends BundleContextImpl {
protected CompositeContext(BundleHost bundle) {
super(bundle);
}
protected long getCompositeId() {
return CompositeImpl.this.getBundleId();
}
protected void start() {
// nothing;
}
protected void stop() {
// nothing
}
}
public StartLevelManager getStartLevelService() {
return startLevelManager;
}
public SecurityAdmin getSecurityAdmin() {
return securityAdmin;
}
public BundleHost getSystemBundle() {
return compositeSystemBundle;
}
public String getProperty(String key) {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPropertyAccess(key);
return configuration.getProperty(key, FrameworkProperties.getProperty(key));
}
public String setProperty(String key, String value) {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(new PropertyPermission(key, "write")); //$NON-NLS-1$
return (String) configuration.setProperty(key, value);
}
protected void refresh() {
super.refresh();
loadConstituents();
}
void loadConstituents() {
synchronized (constituents) {
constituents.clear();
AbstractBundle[] bundles = framework.getBundles(getBundleId());
for (int i = 0; i < bundles.length; i++) {
if (bundles[i].getBundleId() == 0)
continue;
BundleDescription constituent = bundles[i].getBundleDescription();
if (constituent != null)
constituents.add(constituent);
}
}
}
void addConstituent(BundleDescription description) {
synchronized (constituents) {
constituents.add(description);
}
}
BundleDescription[] getConstituentDescriptions() {
synchronized (constituents) {
return constituents.toArray(new BundleDescription[constituents.size()]);
}
}
private static ClassSpacePolicyInfo[] createClassSpacePolicy(String policySpec, StateObjectFactory factory, String header, BundleDescription desc) throws BundleException {
ManifestElement[] policy = ManifestElement.parseHeader(header, policySpec);
if (policy == null || policy.length == 0)
return null;
ArrayList<ClassSpacePolicyInfo> result = new ArrayList<ClassSpacePolicyInfo>(policy.length);
for (int i = 0; i < policy.length; i++) {
String compositeAffinityName = policy[i].getDirective(CompositeConstants.COMPOSITE_SYMBOLICNAME_DIRECTIVE);
VersionRange compositeAffinityVersion = StateBuilder.getVersionRange(policy[i].getDirective(CompositeConstants.COMPOSITE_VERSION_DIRECTIVE));
if (header == CompositeConstants.COMPOSITE_PACKAGE_IMPORT_POLICY || header == CompositeConstants.COMPOSITE_PACKAGE_EXPORT_POLICY) {
VersionRange versionRange = StateBuilder.getVersionRange(policy[i].getAttribute(Constants.VERSION_ATTRIBUTE));
String bundleSymbolicName = policy[i].getAttribute(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
VersionRange bundleVersionRange = StateBuilder.getVersionRange(policy[i].getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE));
Map attributes = StateBuilder.getAttributes(policy[i], StateBuilder.DEFINED_MATCHING_ATTRS);
String[] packageNames = policy[i].getValueComponents();
for (int j = 0; j < packageNames.length; j++) {
ImportPackageSpecification importSpec = factory.createImportPackageSpecification(packageNames[j], versionRange, bundleSymbolicName, bundleVersionRange, null, attributes, desc);
result.add(new ClassSpacePolicyInfo(compositeAffinityName, compositeAffinityVersion, importSpec));
}
} else {
String bsn = policy[i].getValue();
VersionRange bundleVersion = StateBuilder.getVersionRange(policy[i].getAttribute(Constants.BUNDLE_VERSION_ATTRIBUTE));
result.add(new ClassSpacePolicyInfo(compositeAffinityName, compositeAffinityVersion, factory.createBundleSpecification(bsn, bundleVersion, false, false)));
}
}
return result.toArray(new ClassSpacePolicyInfo[result.size()]);
}
private static ServicePolicyInfo[] createServicePolicyInfo(String policySpec, StateObjectFactory factory, String header, BundleContext context) throws BundleException {
ManifestElement[] policy = ManifestElement.parseHeader(header, policySpec);
if (policy == null || policy.length == 0)
return null;
ArrayList<ServicePolicyInfo> result = new ArrayList<ServicePolicyInfo>(policy.length);
for (int i = 0; i < policy.length; i++) {
String compositeAffinityName = policy[i].getDirective(CompositeConstants.COMPOSITE_SYMBOLICNAME_DIRECTIVE);
VersionRange compositeAffinityVersion = StateBuilder.getVersionRange(policy[i].getDirective(CompositeConstants.COMPOSITE_VERSION_DIRECTIVE));
String[] filters = policy[i].getValueComponents();
for (int j = 0; j < filters.length; j++) {
Filter filter = null;
try {
filter = context.createFilter(filters[i]);
} catch (InvalidSyntaxException e) {
throw new BundleException("Invalid service sharing policy: " + filters[i], BundleException.MANIFEST_ERROR, e); //$NON-NLS-1$
}
result.add(new ServicePolicyInfo(compositeAffinityName, compositeAffinityVersion, filter));
}
}
return result.toArray(new ServicePolicyInfo[result.size()]);
}
}