/* * 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.apache.aries.subsystem.core.archive; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.jar.Attributes; import java.util.jar.Manifest; import org.apache.aries.subsystem.core.internal.OsgiIdentityCapability; import org.apache.aries.util.manifest.ManifestProcessor; import org.osgi.framework.Constants; import org.osgi.framework.Version; import org.osgi.resource.Capability; import org.osgi.resource.Requirement; import org.osgi.resource.Resource; import org.osgi.service.subsystem.SubsystemConstants; public class SubsystemManifest { public static class Builder { private Map<String, Header<?>> headers = new HashMap<String, Header<?>>(); public SubsystemManifest build() { return new SubsystemManifest(headers); } public Builder content(String value) { return value == null ? this : content(new SubsystemContentHeader(value)); } public Builder content(Collection<Resource> value) { return value == null || value.isEmpty() ? this : content(SubsystemContentHeader.newInstance(value)); } public Builder content(SubsystemContentHeader value) { return header(value); } public Builder header(Header<?> value) { if (value != null) headers.put(value.getName(), value); return this; } public Builder manifest(SubsystemManifest value) { for (Entry<String, Header<?>> entry : value.getHeaders().entrySet()) header(entry.getValue()); return this; } public Builder symbolicName(String value) { return value == null ? this : symbolicName(new SubsystemSymbolicNameHeader(value)); } public Builder symbolicName(SubsystemSymbolicNameHeader value) { return header(value); } public Builder type(String value) { return value == null ? this : type(new SubsystemTypeHeader(value)); } public Builder type(SubsystemTypeHeader value) { return header(value); } public Builder version(String value) { return value == null ? this : version(Version.parseVersion(value)); } public Builder version(Version value) { return value == null ? this : version(new SubsystemVersionHeader(value)); } public Builder version(SubsystemVersionHeader value) { return header(value); } } public static final String EXPORT_PACKAGE = Constants.EXPORT_PACKAGE; public static final String IMPORT_PACKAGE = Constants.IMPORT_PACKAGE; public static final String PREFERRED_PROVIDER = SubsystemConstants.PREFERRED_PROVIDER; public static final String PROVIDE_CAPABILITY = Constants.PROVIDE_CAPABILITY; public static final String REQUIRE_BUNDLE = Constants.REQUIRE_BUNDLE; public static final String REQUIRE_CAPABILITY = Constants.REQUIRE_CAPABILITY; public static final String SUBSYSTEM_CONTENT = SubsystemConstants.SUBSYSTEM_CONTENT; public static final String SUBSYSTEM_DESCRIPTION = SubsystemConstants.SUBSYSTEM_DESCRIPTION; public static final String SUBSYSTEM_EXPORTSERVICE = SubsystemConstants.SUBSYSTEM_EXPORTSERVICE; public static final String SUBSYSTEM_IMPORTSERVICE = SubsystemConstants.SUBSYSTEM_IMPORTSERVICE; public static final String SUBSYTEM_LOCALIZATION = SubsystemConstants.SUBSYSTEM_LOCALIZATION; public static final String SUBSYSTEM_MANIFESTVERSION = SubsystemConstants.SUBSYSTEM_MANIFESTVERSION; public static final String SUBSYSTEM_NAME = SubsystemConstants.SUBSYSTEM_NAME; public static final String SUBSYSTEM_SYMBOLICNAME = SubsystemConstants.SUBSYSTEM_SYMBOLICNAME; public static final String SUBSYSTEM_TYPE = SubsystemConstants.SUBSYSTEM_TYPE; public static final String SUBSYSTEM_VERSION = SubsystemConstants.SUBSYSTEM_VERSION; private static Map<String, Header<?>> parseHeaders(java.util.jar.Manifest manifest) { Map<String, Header<?>> result = new HashMap<String, Header<?>>(); for (Entry<Object, Object> entry : manifest.getMainAttributes().entrySet()) { String key = String.valueOf(entry.getKey()); result.put(key, HeaderFactory.createHeader(key, String.valueOf(entry.getValue()))); } return result; } private static void fillInDefaults(Map<String, Header<?>> headers) { Header<?> header = headers.get(SUBSYSTEM_VERSION); if (header == null) { headers.put(SUBSYSTEM_VERSION, SubsystemVersionHeader.DEFAULT); } header = headers.get(SUBSYSTEM_TYPE); if (header == null) headers.put(SUBSYSTEM_TYPE, SubsystemTypeHeader.DEFAULT); header = headers.get(SUBSYTEM_LOCALIZATION); if (header == null) headers.put(SUBSYTEM_LOCALIZATION, SubsystemLocalizationHeader.DEFAULT); } private final Map<String, Header<?>> headers; private SubsystemManifest(Map<String, Header<?>> headers) { Map<String, Header<?>> map = new HashMap<String, Header<?>>(headers); fillInDefaults(map); this.headers = Collections.unmodifiableMap(map); } public SubsystemManifest(java.util.jar.Manifest manifest) { this(parseHeaders(manifest)); } public SubsystemManifest(File file) throws FileNotFoundException, IOException { this(new FileInputStream(file)); } public SubsystemManifest(InputStream in) throws IOException { Manifest manifest = ManifestProcessor.parseManifest(in); Attributes attributes = manifest.getMainAttributes(); Map<String, Header<?>> headers = new HashMap<String, Header<?>>(attributes.size() + 4); // Plus the # of potentially derived headers. for (Entry<Object, Object> entry : attributes.entrySet()) { String key = String.valueOf(entry.getKey()); headers.put(key, HeaderFactory.createHeader(key, String.valueOf(entry.getValue()))); } fillInDefaults(headers); this.headers = Collections.unmodifiableMap(headers); } public SubsystemManifest(String symbolicName, Version version, Collection<Resource> content) { this(null, symbolicName, version, content); } public SubsystemManifest(SubsystemManifest manifest, String symbolicName, Version version, Collection<Resource> content) { Map<String, Header<?>> headers; if (manifest == null) { headers = new HashMap<String, Header<?>>(4); } else { headers = new HashMap<String, Header<?>>(manifest.headers); } Header<?> header = headers.get(SUBSYSTEM_SYMBOLICNAME); if (header == null) headers.put(SUBSYSTEM_SYMBOLICNAME, new SubsystemSymbolicNameHeader(symbolicName)); header = headers.get(SUBSYSTEM_VERSION); if (header == null && !(version == null)) { headers.put(SUBSYSTEM_VERSION, new SubsystemVersionHeader(version)); } header = headers.get(SUBSYSTEM_CONTENT); if (header == null && content != null && !content.isEmpty()) { headers.put(SubsystemContentHeader.NAME, SubsystemContentHeader.newInstance(content)); } fillInDefaults(headers); this.headers = Collections.unmodifiableMap(headers); } public Map<String, Header<?>> getHeaders() { return headers; } public ExportPackageHeader getExportPackageHeader() { return (ExportPackageHeader)getHeaders().get(EXPORT_PACKAGE); } public ImportPackageHeader getImportPackageHeader() { return (ImportPackageHeader)getHeaders().get(IMPORT_PACKAGE); } public PreferredProviderHeader getPreferredProviderHeader() { return (PreferredProviderHeader)getHeaders().get(PREFERRED_PROVIDER); } public ProvideCapabilityHeader getProvideCapabilityHeader() { return (ProvideCapabilityHeader)getHeaders().get(PROVIDE_CAPABILITY); } public RequireBundleHeader getRequireBundleHeader() { return (RequireBundleHeader)getHeaders().get(REQUIRE_BUNDLE); } public RequireCapabilityHeader getRequireCapabilityHeader() { return (RequireCapabilityHeader)getHeaders().get(REQUIRE_CAPABILITY); } public SubsystemContentHeader getSubsystemContentHeader() { return (SubsystemContentHeader)getHeaders().get(SUBSYSTEM_CONTENT); } public SubsystemExportServiceHeader getSubsystemExportServiceHeader() { return (SubsystemExportServiceHeader)getHeaders().get(SUBSYSTEM_EXPORTSERVICE); } public SubsystemImportServiceHeader getSubsystemImportServiceHeader() { return (SubsystemImportServiceHeader)getHeaders().get(SUBSYSTEM_IMPORTSERVICE); } public SubsystemLocalizationHeader getSubsystemLocalizationHeader() { return (SubsystemLocalizationHeader)getHeaders().get(SUBSYTEM_LOCALIZATION); } public SubsystemSymbolicNameHeader getSubsystemSymbolicNameHeader() { return (SubsystemSymbolicNameHeader)getHeaders().get(SUBSYSTEM_SYMBOLICNAME); } public SubsystemTypeHeader getSubsystemTypeHeader() { return (SubsystemTypeHeader)getHeaders().get(SUBSYSTEM_TYPE); } public SubsystemVersionHeader getSubsystemVersionHeader() { return (SubsystemVersionHeader)getHeaders().get(SUBSYSTEM_VERSION); } public List<Capability> toCapabilities(Resource resource) { ArrayList<Capability> capabilities = new ArrayList<Capability>(); for (Header<?> header : headers.values()) if (header instanceof CapabilityHeader) capabilities.addAll(((CapabilityHeader<?>)header).toCapabilities(resource)); capabilities.add(new OsgiIdentityCapability( resource, getSubsystemSymbolicNameHeader().getSymbolicName(), getSubsystemVersionHeader().getVersion(), getSubsystemTypeHeader().getType())); capabilities.trimToSize(); return capabilities; } public List<Requirement> toRequirements(Resource resource) { ArrayList<Requirement> requirements = new ArrayList<Requirement>(); for (Header<?> header : headers.values()) if (header instanceof RequirementHeader && !((header instanceof SubsystemContentHeader) || (header instanceof PreferredProviderHeader))) requirements.addAll(((RequirementHeader<?>)header).toRequirements(resource)); requirements.trimToSize(); return requirements; } @Override public String toString() { StringBuilder builder = new StringBuilder("[Subsystem Manifest: "); Iterator<Header<?>> iterator = headers.values().iterator(); if (iterator.hasNext()) { Header<?> header = iterator.next(); builder.append(header.getName()).append('=').append(header.getValue()); while (iterator.hasNext()) { header = iterator.next(); builder.append(", ").append(header.getName()).append('=').append(header.getValue()); } } return builder.append(']').toString(); } public void write(OutputStream out) throws IOException { Manifest manifest = new Manifest(); Attributes attributes = manifest.getMainAttributes(); // The manifest won't write anything unless the following header is present. attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); for (Entry<String, Header<?>> entry : headers.entrySet()) { attributes.putValue(entry.getKey(), entry.getValue().getValue()); } manifest.write(out); } @Override public int hashCode() { return 31 * 17 + headers.hashCode(); } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof SubsystemManifest)) { return false; } SubsystemManifest that = (SubsystemManifest)o; return that.headers.equals(this.headers); } }