blob: d5ad91a8d7f0e1125a7b84ec357ead8a68078b26 [file] [log] [blame]
/*******************************************************************************
* 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$
}
}
}
}