blob: 2e68a9a1c917070672b9714e0b7f6e4c1f33f615 [file] [log] [blame]
Clement Escoffier491a1232007-12-04 16:09:03 +00001/*
2 * $Id: BundleInfo.java 44 2007-07-13 20:49:41Z hargrave@us.ibm.com $
3 *
4 * Copyright (c) OSGi Alliance (2002, 2006, 2007). All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
Stefano Lenzi476013d2007-09-21 23:59:54 +000018package org.osgi.impl.bundle.obr.resource;
19
20import java.io.*;
21import java.net.URL;
22import java.util.*;
23import java.util.zip.*;
24
25import org.osgi.service.obr.Resource;
26
27/**
28 * Convert a bundle to a generic resource description and store its local
29 * dependencies (like for example a license file in the JAR) in a zip file.
30 *
Clement Escoffier491a1232007-12-04 16:09:03 +000031 * @version $Revision: 44 $
Stefano Lenzi476013d2007-09-21 23:59:54 +000032 */
33public class BundleInfo {
34 Manifest manifest;
35 File bundleJar;
36 ZipFile jar;
37 String license;
38 Properties localization;
39 RepositoryImpl repository;
40
41 /**
42 * Parse a zipFile from the file system. We only need the manifest and the
43 * localization. So a zip file is used to minimze memory consumption.
44 *
45 * @param bundleJar Path name
46 * @throws Exception Any errors that occur
47 */
48 public BundleInfo(RepositoryImpl repository, File bundleJar) throws Exception {
49 this.bundleJar = bundleJar;
50 this.repository = repository;
51
52 if (!this.bundleJar.exists())
53 throw new FileNotFoundException(bundleJar.toString());
54
55 jar = new ZipFile(bundleJar);
56 ZipEntry entry = jar.getEntry("META-INF/MANIFEST.MF");
57 if (entry == null)
58 throw new FileNotFoundException("No Manifest in "
59 + bundleJar.toString());
60 manifest = new Manifest(jar.getInputStream(entry));
61 }
62
63 public BundleInfo(Manifest manifest) throws Exception {
64 this.manifest = manifest;
65 }
66
67 /**
68 * Convert the bundle to a Resource. All URIs are going to be abslute, but
69 * could be local.
70 *
71 * @return the resource
72 * @throws Exception
73 */
74 public ResourceImpl build() throws Exception {
75 ResourceImpl resource;
76 // Setup the manifest
77 // and create a resource
78 resource = new ResourceImpl(repository, manifest.getSymbolicName(), manifest
79 .getVersion());
80
81 try {
82
83 // Calculate the location URL of the JAR
84 URL location = new URL("jar:" + bundleJar.toURL().toString() + "!/");
85 resource.setURL(bundleJar.toURL());
86 resource.setFile(bundleJar);
87
88 doReferences(resource, location);
89 doSize(resource);
90 doCategories(resource);
91 doImportExportServices(resource);
92 doDeclarativeServices(resource);
93 doFragment(resource);
94 doRequires(resource);
95 doBundle(resource);
96 doExports(resource);
97 doImports(resource);
98 doExecutionEnvironment(resource);
99
100 return resource;
101 }
102 finally {
103 try {
104 jar.close();
105 }
106 catch (Exception e) {
107 // ignore
108 }
109 }
110 }
111
112 /**
113 * Check the size and add it.
114 *
115 * @param resource
116 */
117 void doSize(ResourceImpl resource) {
118 long size = bundleJar.length();
119 if (size > 0)
120 resource.setSize(size);
121 }
122
123 /**
124 * Find the categories, break them up and add them.
125 *
126 * @param resource
127 */
128 void doCategories(ResourceImpl resource) {
129 for (int i = 0; i < manifest.getCategories().length; i++) {
130 String category = manifest.getCategories()[i];
131 resource.addCategory(category);
132 }
133 }
134
135 void doReferences(ResourceImpl resource, URL location) {
136 // Presentation name
137 String name = translated("Bundle-Name");
138 if (name != null)
139 resource.setPresentationName(name);
140
141 // Handle license. -l allows a global license
142 // set when no license is included.
143
144 String license = translated("Bundle-License");
145 if (license != null)
146 resource.setLicense(toURL(location, license));
147 else if (this.license != null)
148 resource.setLicense(toURL(location, this.license));
149
150 String description = translated("Bundle-Description");
151 if (description != null)
152 resource.setDescription(description);
153
154 String copyright = translated("Bundle-Copyright");
155 if (copyright != null)
156 resource.setCopyright(copyright);
157
158 String documentation = translated("Bundle-DocURL");
159 if (documentation != null)
160 resource.setDocumentation(toURL(location, documentation));
161
162 String source = manifest.getValue("Bundle-Source");
163 if (source != null)
164 resource.setSource(toURL(location, source));
165 }
166
167 URL toURL(URL location, String source) {
168 try {
169 return new URL(location, source);
170 }
171 catch (Exception e) {
172 System.err.println("Error in converting url: " + location + " : "
173 + source);
174 return null;
175 }
176 }
177
178 void doDeclarativeServices(ResourceImpl resource) throws Exception {
179 String serviceComponent = manifest.getValue("service-component");
180 if (serviceComponent == null)
181 return;
182
183 StringTokenizer st = new StringTokenizer(serviceComponent, " ,\t");
184 String parts[] = new String[st.countTokens()];
185 for (int i = 0; i < parts.length; i++)
186 parts[i] = st.nextToken();
187
188 for (int i = 0; i < parts.length; i++) {
189 ZipEntry entry = jar.getEntry(parts[i]);
190 if (entry == null) {
191 System.err.println("Bad Service-Component header: "
192 + serviceComponent + ", no such file " + parts[i]);
193 }
194 InputStream in = jar.getInputStream(entry);
195 // TODO parse declarative services files.
196 in.close();
197 }
198 }
199
200 void doImportExportServices(ResourceImpl resource) throws IOException {
201 String importServices = manifest.getValue("import-service");
202 if (importServices != null) {
203 List entries = manifest.getEntries(importServices);
204 for (Iterator i = entries.iterator(); i.hasNext();) {
205 ManifestEntry entry = (ManifestEntry) i.next();
206 RequirementImpl ri = new RequirementImpl("service");
207 ri.setFilter(createServiceFilter(entry));
208 ri.setComment("Import Service " + entry.getName());
209
210 // TODO the following is arbitrary
Clement Escoffier491a1232007-12-04 16:09:03 +0000211 ri.setOptional(false);
212 ri.setMultiple(true);
Stefano Lenzi476013d2007-09-21 23:59:54 +0000213 resource.addRequirement(ri);
214 }
215 }
216
217 String exportServices = manifest.getValue("export-service");
218 if (exportServices != null) {
219 List entries = manifest.getEntries(exportServices);
220 for (Iterator i = entries.iterator(); i.hasNext();) {
221 ManifestEntry entry = (ManifestEntry) i.next();
222 CapabilityImpl cap = createServiceCapability(entry);
223 resource.addCapability(cap);
224 }
225 }
226 }
227
228 String translated(String key) {
229 return translate(manifest.getValue(key));
230 }
231
232 void doFragment(ResourceImpl resource) {
233 // Check if we are a fragment
234 ManifestEntry entry = manifest.getHost();
235 if (entry == null) {
236 return;
237 }
238 else {
239 // We are a fragment, create a requirement
240 // to our host.
241 RequirementImpl r = new RequirementImpl("bundle");
242 StringBuffer sb = new StringBuffer();
243 sb.append("(&(symbolicname=");
244 sb.append(entry.getName());
245 sb.append(")(version>=");
246 sb.append(entry.getVersion());
247 sb.append("))");
248 r.setFilter(sb.toString());
249 r.setComment("Required Host " + entry.getName() );
250 r.setExtend(true);
251 r.setOptional(false);
252 r.setMultiple(false);
253 resource.addRequirement(r);
254
255 // And insert a capability that we are available
256 // as a fragment. ### Do we need that with extend?
257 CapabilityImpl capability = new CapabilityImpl("fragment");
258 capability.addProperty("host", entry.getName());
259 capability.addProperty("version", entry.getVersion());
260 resource.addCapability(capability);
261 }
262 }
263
264 void doRequires(ResourceImpl resource) {
265 List entries = manifest.getRequire();
266 if (entries == null)
267 return;
268
269 for (Iterator i = entries.iterator(); i.hasNext();) {
270 ManifestEntry entry = (ManifestEntry) i.next();
271 RequirementImpl r = new RequirementImpl("bundle");
272
273 StringBuffer sb = new StringBuffer();
274 sb.append("(&(symbolicname=");
275 sb.append(entry.getName());
276 sb.append(")(version>=");
277 sb.append(entry.getVersion());
278 sb.append("))");
279 r.setFilter(sb.toString());
280 r.setComment("Require Bundle " + entry.getName() + "; "
281 + entry.getVersion());
282 if (entry.directives == null
283 || "true".equalsIgnoreCase((String) entry.directives
284 .get("resolution")))
285 r.setOptional(false);
286 else
287 r.setOptional(true);
288 resource.addRequirement(r);
289 }
290 }
291
292 void doExecutionEnvironment(ResourceImpl resource) {
293 String[] parts = manifest.getRequiredExecutionEnvironments();
294 if (parts == null)
295 return;
296
297 StringBuffer sb = new StringBuffer();
298 sb.append("(|");
299 for (int i = 0; i < parts.length; i++) {
300 String part = parts[i];
301 sb.append("(ee=");
302 sb.append(part);
303 sb.append(")");
304 }
305 sb.append(")");
306
307 RequirementImpl req = new RequirementImpl("ee");
308 req.setFilter(sb.toString());
309 req.setComment("Execution Environment " + sb.toString());
310 resource.addRequirement(req);
311 }
312
313 void doImports(ResourceImpl resource) {
314 List requirements = new ArrayList();
315 List packages = manifest.getImports();
316 if (packages == null)
317 return;
318
319 for (Iterator i = packages.iterator(); i.hasNext();) {
320 ManifestEntry pack = (ManifestEntry) i.next();
321 RequirementImpl requirement = new RequirementImpl("package");
322
323 createImportFilter(requirement, "package", pack);
324 requirement.setComment("Import package " + pack);
325 requirements.add(requirement);
326 }
327 for (Iterator i = requirements.iterator(); i.hasNext();)
328 resource.addRequirement((RequirementImpl) i.next());
329 }
330
331 String createServiceFilter(ManifestEntry pack) {
332 StringBuffer filter = new StringBuffer();
333 filter.append("(service=");
334 filter.append(pack.getName());
335 filter.append(")");
336 return filter.toString();
337 }
338
339 void createImportFilter(RequirementImpl req, String name, ManifestEntry pack) {
340 StringBuffer filter = new StringBuffer();
341 filter.append("(&(");
342 filter.append(name);
343 filter.append("=");
344 filter.append(pack.getName());
345 filter.append(")");
Clement Escoffier491a1232007-12-04 16:09:03 +0000346 VersionRange version = pack.getVersion();
Stefano Lenzi476013d2007-09-21 23:59:54 +0000347 if (version != null) {
Clement Escoffier491a1232007-12-04 16:09:03 +0000348 if ( version.isRange() ) {
Stefano Lenzi476013d2007-09-21 23:59:54 +0000349 filter.append("(version");
350 filter.append(">");
Clement Escoffier491a1232007-12-04 16:09:03 +0000351 if (version.includeLow())
Stefano Lenzi476013d2007-09-21 23:59:54 +0000352 filter.append("=");
Clement Escoffier491a1232007-12-04 16:09:03 +0000353 filter.append(version.low);
Stefano Lenzi476013d2007-09-21 23:59:54 +0000354 filter.append(")");
355
356 filter.append("(version");
357 filter.append("<");
Clement Escoffier491a1232007-12-04 16:09:03 +0000358 if (version.includeHigh())
Stefano Lenzi476013d2007-09-21 23:59:54 +0000359 filter.append("=");
Clement Escoffier491a1232007-12-04 16:09:03 +0000360 filter.append(version.high);
Stefano Lenzi476013d2007-09-21 23:59:54 +0000361 filter.append(")");
362 }
363 else {
364 filter.append("(version>=");
365 filter.append(pack.getVersion());
366 filter.append(")");
367 }
368 }
369 Map attributes = pack.getAttributes();
370 Set attrs = doImportPackageAttributes(req, filter, attributes);
371 if (attrs.size() > 0) {
372 String del = "";
373 filter.append("(mandatory:<*");
374 for (Iterator i = attrs.iterator(); i.hasNext();) {
375 filter.append(del);
376 filter.append(i.next());
377 del = ", ";
378 }
379 filter.append(")");
380 }
381 filter.append(")");
382 req.setFilter(filter.toString());
383 }
384
385 Set doImportPackageAttributes(RequirementImpl req, StringBuffer filter,
386 Map attributes) {
387 HashSet set = new HashSet();
388
389 if (attributes != null)
390 for (Iterator i = attributes.keySet().iterator(); i.hasNext();) {
391 String attribute = (String) i.next();
392 String value = (String) attributes.get(attribute);
393 if (attribute.equalsIgnoreCase("specification-version")
394 || attribute.equalsIgnoreCase("version"))
395 continue;
396 else if (attribute.equalsIgnoreCase("resolution:")) {
397 req.setOptional(value.equalsIgnoreCase("optional"));
398 }
399 if (attribute.endsWith(":")) {
400 // Ignore
401 }
402 else {
403 filter.append("(");
404 filter.append(attribute);
405 filter.append("=");
406 filter.append(attributes.get(attribute));
407 filter.append(")");
408 set.add(attribute);
409 }
410 }
411 return set;
412 }
413
414 void doBundle(ResourceImpl resource) {
415 CapabilityImpl capability = new CapabilityImpl("bundle");
416 capability.addProperty("symbolicname", manifest.getSymbolicName());
417 if (manifest.getValue("Bundle-Name") != null)
418 capability.addProperty(
419 Resource.PRESENTATION_NAME,
420 translated("Bundle-Name"));
421 capability.addProperty("version", manifest.getVersion());
422 capability
423 .addProperty("manifestversion", manifest.getManifestVersion());
424
425 /**
426 * Is this needed TODO
427 */
428 ManifestEntry host = manifest.getHost();
429 if (host != null) {
430 capability.addProperty("host", host.getName());
431 if (host.getVersion() != null)
432 capability.addProperty("version", host.getVersion());
433 }
434 resource.addCapability(capability);
435 }
436
437 void doExports(ResourceImpl resource) {
438 List capabilities = new ArrayList();
439 List packages = manifest.getExports();
440 if (packages != null) {
441 for (Iterator i = packages.iterator(); i.hasNext();) {
442 ManifestEntry pack = (ManifestEntry) i.next();
443 CapabilityImpl capability = createCapability("package", pack);
444 capabilities.add(capability);
445 }
446 }
447 for (Iterator i = capabilities.iterator(); i.hasNext();)
448 resource.addCapability((CapabilityImpl) i.next());
449 }
450
451 CapabilityImpl createServiceCapability(ManifestEntry pack) {
452 CapabilityImpl capability = new CapabilityImpl("service");
453 capability.addProperty("service", pack.getName());
454 return capability;
455 }
456
457 CapabilityImpl createCapability(String name, ManifestEntry pack) {
458 CapabilityImpl capability = new CapabilityImpl(name);
459 capability.addProperty(name, pack.getName());
460 capability.addProperty("version", pack.getVersion());
461 Map attributes = pack.getAttributes();
462 if (attributes != null)
463 for (Iterator at = attributes.keySet().iterator(); at.hasNext();) {
464 String key = (String) at.next();
465 if (key.equalsIgnoreCase("specification-version")
466 || key.equalsIgnoreCase("version"))
467 continue;
468 else {
469 Object value = attributes.get(key);
470 capability.addProperty(key, value);
471 }
472 }
473 return capability;
474 }
475
476 String translate(String s) {
477 if (s == null)
478 return null;
479
480 if (!s.startsWith("%")) {
481 return s;
482 }
483
484 if (localization == null)
485 try {
486 localization = new Properties();
487 String path = manifest
488 .getValue("Bundle-Localization", "bundle");
489 path += ".properties";
490 InputStream in = jar.getInputStream(new ZipEntry(path));
491 if (in != null) {
492 localization.load(in);
493 in.close();
494 }
495 }
496 catch (IOException e) {
497 e.printStackTrace();
498 }
499 s = s.substring(1);
500 return localization.getProperty(s, s);
501 }
502
503 File getZipFile() {
504 return bundleJar;
505 }
506}