blob: 5bd005ff61ab1e674233a35601ff4326e4bd9e7b [file] [log] [blame]
/*
* $Id: BundleInfo.java 44 2007-07-13 20:49:41Z hargrave@us.ibm.com $
*
* Copyright (c) OSGi Alliance (2002, 2006, 2007). All Rights Reserved.
*
* 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.osgi.impl.bundle.obr.resource;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.zip.*;
import org.osgi.service.obr.Resource;
import aQute.bnd.annotation.ProviderType;
/**
* Convert a bundle to a generic resource description and store its local
* dependencies (like for example a license file in the JAR) in a zip file.
*
* @version $Revision: 44 $
*/
@ProviderType
public class BundleInfo {
Manifest manifest;
File bundleJar;
ZipFile jar;
String license;
Properties localization;
RepositoryImpl repository;
/**
* Parse a zipFile from the file system. We only need the manifest and the
* localization. So a zip file is used to minimze memory consumption.
*
* @param bundleJar Path name
* @throws Exception Any errors that occur
*/
public BundleInfo(RepositoryImpl repository, File bundleJar) throws Exception {
this.bundleJar = bundleJar;
this.repository = repository;
if (!this.bundleJar.exists())
throw new FileNotFoundException(bundleJar.toString());
jar = new ZipFile(bundleJar);
ZipEntry entry = jar.getEntry("META-INF/MANIFEST.MF");
if (entry == null)
throw new FileNotFoundException("No Manifest in "
+ bundleJar.toString());
manifest = new Manifest(jar.getInputStream(entry));
}
public BundleInfo(Manifest manifest) throws Exception {
this.manifest = manifest;
}
/**
* Convert the bundle to a Resource. All URIs are going to be abslute, but
* could be local.
*
* @return the resource
* @throws Exception
*/
public ResourceImpl build() throws Exception {
ResourceImpl resource;
// Setup the manifest
// and create a resource
resource = new ResourceImpl(repository, manifest.getSymbolicName(), manifest
.getVersion());
try {
// Calculate the location URL of the JAR
URL location = new URL("jar:" + bundleJar.toURL().toString() + "!/");
resource.setURL(bundleJar.toURL());
resource.setFile(bundleJar);
doReferences(resource, location);
doSize(resource);
doCategories(resource);
doImportExportServices(resource);
doDeclarativeServices(resource);
doFragment(resource);
doRequires(resource);
doBundle(resource);
doExports(resource);
doImports(resource);
doExecutionEnvironment(resource);
return resource;
}
finally {
try {
jar.close();
}
catch (Exception e) {
// ignore
}
}
}
/**
* Check the size and add it.
*
* @param resource
*/
void doSize(ResourceImpl resource) {
long size = bundleJar.length();
if (size > 0)
resource.setSize(size);
}
/**
* Find the categories, break them up and add them.
*
* @param resource
*/
void doCategories(ResourceImpl resource) {
for (int i = 0; i < manifest.getCategories().length; i++) {
String category = manifest.getCategories()[i];
resource.addCategory(category);
}
}
void doReferences(ResourceImpl resource, URL location) {
// Presentation name
String name = translated("Bundle-Name");
if (name != null)
resource.setPresentationName(name);
// Handle license. -l allows a global license
// set when no license is included.
String license = translated("Bundle-License");
if (license != null)
resource.setLicense(toURL(location, license));
else if (this.license != null)
resource.setLicense(toURL(location, this.license));
String description = translated("Bundle-Description");
if (description != null)
resource.setDescription(description);
String copyright = translated("Bundle-Copyright");
if (copyright != null)
resource.setCopyright(copyright);
String documentation = translated("Bundle-DocURL");
if (documentation != null)
resource.setDocumentation(toURL(location, documentation));
String source = manifest.getValue("Bundle-Source");
if (source != null)
resource.setSource(toURL(location, source));
}
URL toURL(URL location, String source) {
try {
return new URL(location, source);
}
catch (Exception e) {
System.err.println("Error in converting url: " + location + " : "
+ source);
return null;
}
}
void doDeclarativeServices(ResourceImpl resource) throws Exception {
String serviceComponent = manifest.getValue("service-component");
if (serviceComponent == null)
return;
StringTokenizer st = new StringTokenizer(serviceComponent, " ,\t");
String parts[] = new String[st.countTokens()];
for (int i = 0; i < parts.length; i++)
parts[i] = st.nextToken();
for (int i = 0; i < parts.length; i++) {
ZipEntry entry = jar.getEntry(parts[i]);
if (entry == null) {
System.err.println("Bad Service-Component header: "
+ serviceComponent + ", no such file " + parts[i]);
}
InputStream in = jar.getInputStream(entry);
// TODO parse declarative services files.
in.close();
}
}
void doImportExportServices(ResourceImpl resource) throws IOException {
String importServices = manifest.getValue("import-service");
if (importServices != null) {
List entries = manifest.getEntries(importServices);
for (Iterator i = entries.iterator(); i.hasNext();) {
ManifestEntry entry = (ManifestEntry) i.next();
RequirementImpl ri = new RequirementImpl("service");
ri.setFilter(createServiceFilter(entry));
ri.setComment("Import Service " + entry.getName());
// TODO the following is arbitrary
ri.setOptional(false);
ri.setMultiple(true);
resource.addRequirement(ri);
}
}
String exportServices = manifest.getValue("export-service");
if (exportServices != null) {
List entries = manifest.getEntries(exportServices);
for (Iterator i = entries.iterator(); i.hasNext();) {
ManifestEntry entry = (ManifestEntry) i.next();
CapabilityImpl cap = createServiceCapability(entry);
resource.addCapability(cap);
}
}
}
String translated(String key) {
return translate(manifest.getValue(key));
}
void doFragment(ResourceImpl resource) {
// Check if we are a fragment
ManifestEntry entry = manifest.getHost();
if (entry == null) {
return;
}
else {
// We are a fragment, create a requirement
// to our host.
RequirementImpl r = new RequirementImpl("bundle");
StringBuffer sb = new StringBuffer();
sb.append("(&(symbolicname=");
sb.append(entry.getName());
sb.append(")(version>=");
sb.append(entry.getVersion());
sb.append("))");
r.setFilter(sb.toString());
r.setComment("Required Host " + entry.getName() );
r.setExtend(true);
r.setOptional(false);
r.setMultiple(false);
resource.addRequirement(r);
// And insert a capability that we are available
// as a fragment. ### Do we need that with extend?
CapabilityImpl capability = new CapabilityImpl("fragment");
capability.addProperty("host", entry.getName());
capability.addProperty("version", entry.getVersion());
resource.addCapability(capability);
}
}
void doRequires(ResourceImpl resource) {
List entries = manifest.getRequire();
if (entries == null)
return;
for (Iterator i = entries.iterator(); i.hasNext();) {
ManifestEntry entry = (ManifestEntry) i.next();
RequirementImpl r = new RequirementImpl("bundle");
StringBuffer sb = new StringBuffer();
sb.append("(&(symbolicname=");
sb.append(entry.getName());
sb.append(")(version>=");
sb.append(entry.getVersion());
sb.append("))");
r.setFilter(sb.toString());
r.setComment("Require Bundle " + entry.getName() + "; "
+ entry.getVersion());
if (entry.directives == null
|| "true".equalsIgnoreCase((String) entry.directives
.get("resolution")))
r.setOptional(false);
else
r.setOptional(true);
resource.addRequirement(r);
}
}
void doExecutionEnvironment(ResourceImpl resource) {
String[] parts = manifest.getRequiredExecutionEnvironments();
if (parts == null)
return;
StringBuffer sb = new StringBuffer();
sb.append("(|");
for (int i = 0; i < parts.length; i++) {
String part = parts[i];
sb.append("(ee=");
sb.append(part);
sb.append(")");
}
sb.append(")");
RequirementImpl req = new RequirementImpl("ee");
req.setFilter(sb.toString());
req.setComment("Execution Environment " + sb.toString());
resource.addRequirement(req);
}
void doImports(ResourceImpl resource) {
List requirements = new ArrayList();
List packages = manifest.getImports();
if (packages == null)
return;
for (Iterator i = packages.iterator(); i.hasNext();) {
ManifestEntry pack = (ManifestEntry) i.next();
RequirementImpl requirement = new RequirementImpl("package");
createImportFilter(requirement, "package", pack);
requirement.setComment("Import package " + pack);
requirements.add(requirement);
}
for (Iterator i = requirements.iterator(); i.hasNext();)
resource.addRequirement((RequirementImpl) i.next());
}
String createServiceFilter(ManifestEntry pack) {
StringBuffer filter = new StringBuffer();
filter.append("(service=");
filter.append(pack.getName());
filter.append(")");
return filter.toString();
}
void createImportFilter(RequirementImpl req, String name, ManifestEntry pack) {
StringBuffer filter = new StringBuffer();
filter.append("(&(");
filter.append(name);
filter.append("=");
filter.append(pack.getName());
filter.append(")");
VersionRange version = pack.getVersion();
if (version != null) {
if ( version.isRange() ) {
filter.append("(version");
filter.append(">");
if (version.includeLow())
filter.append("=");
filter.append(version.low);
filter.append(")");
filter.append("(version");
filter.append("<");
if (version.includeHigh())
filter.append("=");
filter.append(version.high);
filter.append(")");
}
else {
filter.append("(version>=");
filter.append(pack.getVersion());
filter.append(")");
}
}
Map attributes = pack.getAttributes();
Set attrs = doImportPackageAttributes(req, filter, attributes);
if (attrs.size() > 0) {
String del = "";
filter.append("(mandatory:<*");
for (Iterator i = attrs.iterator(); i.hasNext();) {
filter.append(del);
filter.append(i.next());
del = ", ";
}
filter.append(")");
}
filter.append(")");
req.setFilter(filter.toString());
}
Set doImportPackageAttributes(RequirementImpl req, StringBuffer filter,
Map attributes) {
HashSet set = new HashSet();
if (attributes != null)
for (Iterator i = attributes.keySet().iterator(); i.hasNext();) {
String attribute = (String) i.next();
String value = (String) attributes.get(attribute);
if (attribute.equalsIgnoreCase("specification-version")
|| attribute.equalsIgnoreCase("version"))
continue;
else if (attribute.equalsIgnoreCase("resolution:")) {
req.setOptional(value.equalsIgnoreCase("optional"));
}
if (attribute.endsWith(":")) {
// Ignore
}
else {
filter.append("(");
filter.append(attribute);
filter.append("=");
filter.append(attributes.get(attribute));
filter.append(")");
set.add(attribute);
}
}
return set;
}
void doBundle(ResourceImpl resource) {
CapabilityImpl capability = new CapabilityImpl("bundle");
capability.addProperty("symbolicname", manifest.getSymbolicName());
if (manifest.getValue("Bundle-Name") != null)
capability.addProperty(
Resource.PRESENTATION_NAME,
translated("Bundle-Name"));
capability.addProperty("version", manifest.getVersion());
capability
.addProperty("manifestversion", manifest.getManifestVersion());
/**
* Is this needed TODO
*/
ManifestEntry host = manifest.getHost();
if (host != null) {
capability.addProperty("host", host.getName());
if (host.getVersion() != null)
capability.addProperty("version", host.getVersion());
}
resource.addCapability(capability);
}
void doExports(ResourceImpl resource) {
List capabilities = new ArrayList();
List packages = manifest.getExports();
if (packages != null) {
for (Iterator i = packages.iterator(); i.hasNext();) {
ManifestEntry pack = (ManifestEntry) i.next();
CapabilityImpl capability = createCapability("package", pack);
capabilities.add(capability);
}
}
for (Iterator i = capabilities.iterator(); i.hasNext();)
resource.addCapability((CapabilityImpl) i.next());
}
CapabilityImpl createServiceCapability(ManifestEntry pack) {
CapabilityImpl capability = new CapabilityImpl("service");
capability.addProperty("service", pack.getName());
return capability;
}
CapabilityImpl createCapability(String name, ManifestEntry pack) {
CapabilityImpl capability = new CapabilityImpl(name);
capability.addProperty(name, pack.getName());
capability.addProperty("version", pack.getVersion());
Map attributes = pack.getAttributes();
if (attributes != null)
for (Iterator at = attributes.keySet().iterator(); at.hasNext();) {
String key = (String) at.next();
if (key.equalsIgnoreCase("specification-version")
|| key.equalsIgnoreCase("version"))
continue;
else {
Object value = attributes.get(key);
capability.addProperty(key, value);
}
}
return capability;
}
String translate(String s) {
if (s == null)
return null;
if (!s.startsWith("%")) {
return s;
}
if (localization == null)
try {
localization = new Properties();
String path = manifest
.getValue("Bundle-Localization", "bundle");
path += ".properties";
InputStream in = jar.getInputStream(new ZipEntry(path));
if (in != null) {
localization.load(in);
in.close();
}
}
catch (IOException e) {
e.printStackTrace();
}
s = s.substring(1);
return localization.getProperty(s, s);
}
File getZipFile() {
return bundleJar;
}
}