| /******************************************************************************* |
| * Copyright (c) 2008, 2009 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"); //$NON-NLS-1$ |
| 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."); //$NON-NLS-1$ |
| 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)); //$NON-NLS-1$ |
| } 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); //$NON-NLS-1$ |
| // 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); //$NON-NLS-1$ |
| // 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); //$NON-NLS-1$ |
| // 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); //$NON-NLS-1$ |
| } catch (NumberFormatException e) { |
| throw new BundleException("Invalid Bundle-ManifestVersion: " + manifestVersion); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |