| /******************************************************************************* |
| * 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()]); |
| } |
| } |