View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.index.creator;
20  
21  import javax.inject.Named;
22  import javax.inject.Singleton;
23  
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.IOException;
27  import java.security.DigestInputStream;
28  import java.security.MessageDigest;
29  import java.security.NoSuchAlgorithmException;
30  import java.util.Arrays;
31  import java.util.Collection;
32  import java.util.Enumeration;
33  import java.util.jar.Attributes;
34  import java.util.jar.Manifest;
35  import java.util.zip.ZipEntry;
36  import java.util.zip.ZipFile;
37  
38  import org.apache.lucene.document.Document;
39  import org.apache.maven.index.ArtifactContext;
40  import org.apache.maven.index.ArtifactInfo;
41  import org.apache.maven.index.IndexerField;
42  import org.apache.maven.index.IndexerFieldVersion;
43  import org.apache.maven.index.OSGI;
44  import org.codehaus.plexus.util.StringUtils;
45  
46  /**
47   * This indexCreator will index some OSGI metadatas.
48   * <br/>
49   * All jars are indexed and not only the ones with packaging bundle.
50   * <br/>
51   * <p>
52   * OSGI metadatas indexed :
53   * <ul>
54   * <li>Bundle-SymbolicName</li>
55   * <li>Bundle-Version</li>
56   * <li>Export-Package</li>
57   * <li>Export-Service</li>
58   * </ul>
59   * </p>
60   *
61   * @author Olivier Lamy
62   * @since 4.1.2
63   */
64  @Singleton
65  @Named(OsgiArtifactIndexCreator.ID)
66  public class OsgiArtifactIndexCreator extends AbstractIndexCreator {
67      public static final String ID = "osgi-metadatas";
68  
69      public static final IndexerField FLD_SHA256 = new IndexerField(
70              OSGI.SHA256,
71              IndexerFieldVersion.V4,
72              "sha256",
73              "SHA-256 (not analyzed, stored)",
74              IndexerField.KEYWORD_STORED);
75  
76      private static final String BSN = "Bundle-SymbolicName";
77  
78      public static final IndexerField FLD_BUNDLE_SYMBOLIC_NAME = new IndexerField(
79              OSGI.SYMBOLIC_NAME,
80              IndexerFieldVersion.V4,
81              BSN,
82              "Bundle-SymbolicName (indexed, stored)",
83              IndexerField.ANALYZED_STORED);
84  
85      private static final String BV = "Bundle-Version";
86  
87      public static final IndexerField FLD_BUNDLE_VERSION = new IndexerField(
88              OSGI.VERSION, IndexerFieldVersion.V4, BV, "Bundle-Version (indexed, stored)", IndexerField.ANALYZED_STORED);
89  
90      private static final String BEP = "Export-Package";
91  
92      public static final IndexerField FLD_BUNDLE_EXPORT_PACKAGE = new IndexerField(
93              OSGI.EXPORT_PACKAGE,
94              IndexerFieldVersion.V4,
95              BEP,
96              "Export-Package (indexed, stored)",
97              IndexerField.ANALYZED_STORED);
98  
99      @Deprecated
100     private static final String BES = "Export-Service";
101 
102     @Deprecated
103     public static final IndexerField FLD_BUNDLE_EXPORT_SERVIVE = new IndexerField(
104             OSGI.EXPORT_SERVICE,
105             IndexerFieldVersion.V4,
106             BES,
107             "Export-Service (indexed, stored)",
108             IndexerField.ANALYZED_STORED);
109 
110     private static final String BD = "Bundle-Description";
111 
112     public static final IndexerField FLD_BUNDLE_DESCRIPTION = new IndexerField(
113             OSGI.DESCRIPTION,
114             IndexerFieldVersion.V4,
115             BD,
116             "Bundle-Description (indexed, stored)",
117             IndexerField.ANALYZED_STORED);
118 
119     private static final String BN = "Bundle-Name";
120 
121     public static final IndexerField FLD_BUNDLE_NAME = new IndexerField(
122             OSGI.NAME, IndexerFieldVersion.V4, BN, "Bundle-Name (indexed, stored)", IndexerField.ANALYZED_STORED);
123 
124     private static final String BL = "Bundle-License";
125 
126     public static final IndexerField FLD_BUNDLE_LICENSE = new IndexerField(
127             OSGI.LICENSE, IndexerFieldVersion.V4, BL, "Bundle-License (indexed, stored)", IndexerField.ANALYZED_STORED);
128 
129     private static final String BDU = "Bundle-DocURL";
130 
131     public static final IndexerField FLD_BUNDLE_DOCURL = new IndexerField(
132             OSGI.DOCURL, IndexerFieldVersion.V4, BDU, "Bundle-DocURL (indexed, stored)", IndexerField.ANALYZED_STORED);
133 
134     private static final String BIP = "Import-Package";
135 
136     public static final IndexerField FLD_BUNDLE_IMPORT_PACKAGE = new IndexerField(
137             OSGI.IMPORT_PACKAGE,
138             IndexerFieldVersion.V4,
139             BIP,
140             "Import-Package (indexed, stored)",
141             IndexerField.ANALYZED_STORED);
142 
143     private static final String BRB = "Require-Bundle";
144 
145     public static final IndexerField FLD_BUNDLE_REQUIRE_BUNDLE = new IndexerField(
146             OSGI.REQUIRE_BUNDLE,
147             IndexerFieldVersion.V4,
148             BRB,
149             "Require-Bundle (indexed, stored)",
150             IndexerField.ANALYZED_STORED);
151 
152     private static final String PROVIDE_CAPABILITY = "Provide-Capability";
153 
154     public static final IndexerField FLD_BUNDLE_PROVIDE_CAPABILITY = new IndexerField(
155             OSGI.PROVIDE_CAPABILITY,
156             IndexerFieldVersion.V4,
157             PROVIDE_CAPABILITY,
158             "Provide-Capability (indexed, stored)",
159             IndexerField.ANALYZED_STORED);
160 
161     private static final String REQUIRE_CAPABILITY = "Require-Capability";
162 
163     public static final IndexerField FLD_BUNDLE_REQUIRE_CAPABILITY = new IndexerField(
164             OSGI.REQUIRE_CAPABILITY,
165             IndexerFieldVersion.V4,
166             REQUIRE_CAPABILITY,
167             "Require-Capability (indexed, stored)",
168             IndexerField.ANALYZED_STORED);
169 
170     private static final String FRAGMENT_HOST = "Fragment-Host";
171 
172     public static final IndexerField FLD_BUNDLE_FRAGMENT_HOST = new IndexerField(
173             OSGI.FRAGMENT_HOST,
174             IndexerFieldVersion.V4,
175             FRAGMENT_HOST,
176             "Fragment-Host (indexed, stored)",
177             IndexerField.ANALYZED_STORED);
178 
179     private static final String BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT = "Bundle-RequiredExecutionEnvironment";
180 
181     public static final IndexerField FLD_BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT = new IndexerField(
182             OSGI.BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT,
183             IndexerFieldVersion.V4,
184             BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT,
185             "Bundle-RequiredExecutionEnvironment (indexed, stored)",
186             IndexerField.ANALYZED_STORED);
187 
188     public Collection<IndexerField> getIndexerFields() {
189         return Arrays.asList(
190                 FLD_BUNDLE_SYMBOLIC_NAME,
191                 FLD_BUNDLE_VERSION,
192                 FLD_BUNDLE_EXPORT_PACKAGE,
193                 FLD_BUNDLE_EXPORT_SERVIVE,
194                 FLD_BUNDLE_DESCRIPTION,
195                 FLD_BUNDLE_NAME,
196                 FLD_BUNDLE_LICENSE,
197                 FLD_BUNDLE_DOCURL,
198                 FLD_BUNDLE_IMPORT_PACKAGE,
199                 FLD_BUNDLE_REQUIRE_BUNDLE,
200                 FLD_BUNDLE_PROVIDE_CAPABILITY,
201                 FLD_BUNDLE_REQUIRE_CAPABILITY,
202                 FLD_BUNDLE_FRAGMENT_HOST,
203                 FLD_BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT,
204                 FLD_SHA256);
205     }
206 
207     public OsgiArtifactIndexCreator() {
208         super(ID);
209     }
210 
211     public void populateArtifactInfo(ArtifactContext artifactContext) throws IOException {
212         ArtifactInfo ai = artifactContext.getArtifactInfo();
213 
214         File artifactFile = artifactContext.getArtifact();
215 
216         // TODO : olamy : supports only jars ?
217 
218         if (artifactFile != null
219                 && artifactFile.isFile()
220                 && artifactFile.getName().endsWith(".jar")) {
221             updateArtifactInfo(ai, artifactFile);
222         }
223     }
224 
225     public void updateDocument(ArtifactInfo artifactInfo, Document document) {
226 
227         if (artifactInfo.getBundleSymbolicName() != null) {
228             document.add(FLD_BUNDLE_SYMBOLIC_NAME.toField(artifactInfo.getBundleSymbolicName()));
229         }
230 
231         if (artifactInfo.getBundleVersion() != null) {
232             document.add(FLD_BUNDLE_VERSION.toField(artifactInfo.getBundleVersion()));
233         }
234 
235         if (artifactInfo.getBundleExportPackage() != null) {
236             document.add(FLD_BUNDLE_EXPORT_PACKAGE.toField(artifactInfo.getBundleExportPackage()));
237         }
238 
239         if (artifactInfo.getBundleExportService() != null) {
240             document.add(FLD_BUNDLE_EXPORT_SERVIVE.toField(artifactInfo.getBundleExportService()));
241         }
242 
243         if (artifactInfo.getBundleDescription() != null) {
244             document.add(FLD_BUNDLE_DESCRIPTION.toField(artifactInfo.getBundleDescription()));
245         }
246 
247         if (artifactInfo.getBundleName() != null) {
248             document.add(FLD_BUNDLE_NAME.toField(artifactInfo.getBundleName()));
249         }
250 
251         if (artifactInfo.getBundleLicense() != null) {
252             document.add(FLD_BUNDLE_LICENSE.toField(artifactInfo.getBundleLicense()));
253         }
254 
255         if (artifactInfo.getBundleDocUrl() != null) {
256             document.add(FLD_BUNDLE_DOCURL.toField(artifactInfo.getBundleDocUrl()));
257         }
258 
259         if (artifactInfo.getBundleImportPackage() != null) {
260             document.add(FLD_BUNDLE_IMPORT_PACKAGE.toField(artifactInfo.getBundleImportPackage()));
261         }
262 
263         if (artifactInfo.getBundleRequireBundle() != null) {
264             document.add(FLD_BUNDLE_REQUIRE_BUNDLE.toField(artifactInfo.getBundleRequireBundle()));
265         }
266 
267         if (artifactInfo.getBundleProvideCapability() != null) {
268             document.add(FLD_BUNDLE_PROVIDE_CAPABILITY.toField(artifactInfo.getBundleProvideCapability()));
269         }
270 
271         if (artifactInfo.getBundleRequireCapability() != null) {
272             document.add(FLD_BUNDLE_REQUIRE_CAPABILITY.toField(artifactInfo.getBundleRequireCapability()));
273         }
274 
275         if (artifactInfo.getBundleFragmentHost() != null) {
276             document.add(FLD_BUNDLE_FRAGMENT_HOST.toField(artifactInfo.getBundleFragmentHost()));
277         }
278 
279         String bree = artifactInfo.getBundleRequiredExecutionEnvironment();
280         if (bree != null) {
281             document.add(FLD_BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT.toField(bree));
282         }
283 
284         if (artifactInfo.getSha256() != null) {
285             document.add(FLD_SHA256.toField(artifactInfo.getSha256()));
286         }
287     }
288 
289     public boolean updateArtifactInfo(Document document, ArtifactInfo artifactInfo) {
290         boolean updated = false;
291 
292         String bundleSymbolicName = document.get(FLD_BUNDLE_SYMBOLIC_NAME.getKey());
293 
294         if (bundleSymbolicName != null) {
295             artifactInfo.setBundleSymbolicName(bundleSymbolicName);
296 
297             updated = true;
298         }
299 
300         String bundleVersion = document.get(FLD_BUNDLE_VERSION.getKey());
301 
302         if (bundleVersion != null) {
303             artifactInfo.setBundleVersion(bundleVersion);
304 
305             updated = true;
306         }
307 
308         String bundleExportPackage = document.get(FLD_BUNDLE_EXPORT_PACKAGE.getKey());
309 
310         if (bundleExportPackage != null) {
311             artifactInfo.setBundleExportPackage(bundleExportPackage);
312 
313             updated = true;
314         }
315 
316         String bundleExportService = document.get(FLD_BUNDLE_EXPORT_SERVIVE.getKey());
317 
318         if (bundleExportService != null) {
319             artifactInfo.setBundleExportService(bundleExportService);
320 
321             updated = true;
322         }
323 
324         String bundleDescription = document.get(FLD_BUNDLE_DESCRIPTION.getKey());
325 
326         if (bundleDescription != null) {
327             artifactInfo.setBundleDescription(bundleDescription);
328 
329             updated = true;
330         }
331 
332         String bundleName = document.get(FLD_BUNDLE_NAME.getKey());
333 
334         if (bundleName != null) {
335             artifactInfo.setBundleName(bundleName);
336 
337             updated = true;
338         }
339 
340         String bundleLicense = document.get(FLD_BUNDLE_LICENSE.getKey());
341 
342         if (bundleLicense != null) {
343             artifactInfo.setBundleLicense(bundleLicense);
344 
345             updated = true;
346         }
347 
348         String bundleDocUrl = document.get(FLD_BUNDLE_DOCURL.getKey());
349 
350         if (bundleDocUrl != null) {
351             artifactInfo.setBundleDocUrl(bundleDocUrl);
352 
353             updated = true;
354         }
355 
356         String bundleImportPackage = document.get(FLD_BUNDLE_IMPORT_PACKAGE.getKey());
357 
358         if (bundleImportPackage != null) {
359             artifactInfo.setBundleImportPackage(bundleImportPackage);
360 
361             updated = true;
362         }
363 
364         String bundleRequireBundle = document.get(FLD_BUNDLE_REQUIRE_BUNDLE.getKey());
365 
366         if (bundleRequireBundle != null) {
367             artifactInfo.setBundleRequireBundle(bundleRequireBundle);
368 
369             updated = true;
370         }
371 
372         String bundleProvideCapability = document.get(FLD_BUNDLE_PROVIDE_CAPABILITY.getKey());
373 
374         if (bundleProvideCapability != null) {
375             artifactInfo.setBundleProvideCapability(bundleProvideCapability);
376 
377             updated = true;
378         }
379 
380         String bundleRequireCapability = document.get(FLD_BUNDLE_REQUIRE_CAPABILITY.getKey());
381 
382         if (bundleRequireCapability != null) {
383             artifactInfo.setBundleRequireCapability(bundleRequireCapability);
384 
385             updated = true;
386         }
387 
388         String bundleFragmentHost = document.get(FLD_BUNDLE_FRAGMENT_HOST.getKey());
389 
390         if (bundleFragmentHost != null) {
391             artifactInfo.setBundleFragmentHost(bundleFragmentHost);
392 
393             updated = true;
394         }
395 
396         String bundleRequiredExecutionEnvironment = document.get(FLD_BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT.getKey());
397 
398         if (bundleRequiredExecutionEnvironment != null) {
399             artifactInfo.setBundleRequiredExecutionEnvironment(bundleRequiredExecutionEnvironment);
400 
401             updated = true;
402         }
403 
404         String sha256 = document.get(FLD_SHA256.getKey());
405 
406         if (sha256 != null) {
407             artifactInfo.setSha256(sha256);
408 
409             updated = true;
410         }
411 
412         return updated;
413     }
414 
415     private boolean updateArtifactInfo(ArtifactInfo ai, File f) throws IOException {
416         boolean updated = false;
417 
418         try (ZipFile zipFile = new ZipFile(f)) {
419             final Enumeration<? extends ZipEntry> entries = zipFile.entries();
420 
421             while (entries.hasMoreElements()) {
422                 ZipEntry zipEntry = entries.nextElement();
423 
424                 if (zipEntry.getName().equals("META-INF/MANIFEST.MF")) {
425                     Manifest manifest = new Manifest(zipFile.getInputStream(zipEntry));
426 
427                     Attributes mainAttributes = manifest.getMainAttributes();
428 
429                     if (mainAttributes != null) {
430                         String attValue = mainAttributes.getValue(BSN);
431                         if (StringUtils.isNotBlank(attValue)) {
432                             ai.setBundleSymbolicName(attValue);
433                             updated = true;
434                         } else {
435                             ai.setBundleSymbolicName(null);
436                         }
437 
438                         attValue = mainAttributes.getValue(BV);
439                         if (StringUtils.isNotBlank(attValue)) {
440                             ai.setBundleVersion(attValue);
441                             updated = true;
442                         } else {
443                             ai.setBundleVersion(null);
444                         }
445 
446                         attValue = mainAttributes.getValue(BEP);
447                         if (StringUtils.isNotBlank(attValue)) {
448                             ai.setBundleExportPackage(attValue);
449                             updated = true;
450                         } else {
451                             ai.setBundleExportPackage(null);
452                         }
453 
454                         attValue = mainAttributes.getValue(BES);
455                         if (StringUtils.isNotBlank(attValue)) {
456                             ai.setBundleExportService(attValue);
457                             updated = true;
458                         } else {
459                             ai.setBundleExportService(null);
460                         }
461 
462                         attValue = mainAttributes.getValue(BD);
463                         if (StringUtils.isNotBlank(attValue)) {
464                             ai.setBundleDescription(attValue);
465                             updated = true;
466                         } else {
467                             ai.setBundleDescription(null);
468                         }
469 
470                         attValue = mainAttributes.getValue(BN);
471                         if (StringUtils.isNotBlank(attValue)) {
472                             ai.setBundleName(attValue);
473                             updated = true;
474                         } else {
475                             ai.setBundleName(null);
476                         }
477 
478                         attValue = mainAttributes.getValue(BL);
479                         if (StringUtils.isNotBlank(attValue)) {
480                             ai.setBundleLicense(attValue);
481                             updated = true;
482                         } else {
483                             ai.setBundleLicense(null);
484                         }
485 
486                         attValue = mainAttributes.getValue(BDU);
487                         if (StringUtils.isNotBlank(attValue)) {
488                             ai.setBundleDocUrl(attValue);
489                             updated = true;
490                         } else {
491                             ai.setBundleDocUrl(null);
492                         }
493 
494                         attValue = mainAttributes.getValue(BIP);
495                         if (StringUtils.isNotBlank(attValue)) {
496                             ai.setBundleImportPackage(attValue);
497                             updated = true;
498                         } else {
499                             ai.setBundleImportPackage(null);
500                         }
501 
502                         attValue = mainAttributes.getValue(BRB);
503                         if (StringUtils.isNotBlank(attValue)) {
504                             ai.setBundleRequireBundle(attValue);
505                             updated = true;
506                         } else {
507                             ai.setBundleRequireBundle(null);
508                         }
509 
510                         attValue = mainAttributes.getValue(PROVIDE_CAPABILITY);
511                         if (StringUtils.isNotBlank(attValue)) {
512                             ai.setBundleProvideCapability(attValue);
513                             updated = true;
514                         } else {
515                             ai.setBundleProvideCapability(null);
516                         }
517 
518                         attValue = mainAttributes.getValue(REQUIRE_CAPABILITY);
519                         if (StringUtils.isNotBlank(attValue)) {
520                             ai.setBundleRequireCapability(attValue);
521                             updated = true;
522                         } else {
523                             ai.setBundleRequireCapability(null);
524                         }
525 
526                         attValue = mainAttributes.getValue(FRAGMENT_HOST);
527                         if (StringUtils.isNotBlank(attValue)) {
528                             ai.setBundleFragmentHost(attValue);
529                             updated = true;
530                         } else {
531                             ai.setBundleFragmentHost(null);
532                         }
533 
534                         attValue = mainAttributes.getValue(BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT);
535                         if (StringUtils.isNotBlank(attValue)) {
536                             ai.setBundleRequiredExecutionEnvironment(attValue);
537                             updated = true;
538                         } else {
539                             ai.setBundleRequiredExecutionEnvironment(null);
540                         }
541                     }
542                 }
543             }
544         }
545 
546         // only calculate sha256 digest for if we are indexing a bundle.
547         if (ai.getBundleSymbolicName() != null) {
548             String sha256 = computeSha256(f);
549             if (sha256 != null) {
550                 ai.setSha256(sha256);
551                 updated = true;
552             } else {
553                 ai.setSha256(null);
554             }
555         }
556 
557         return updated;
558     }
559 
560     private String computeSha256(File f) throws IOException {
561         String sha256 = null;
562         try {
563             MessageDigest digest = MessageDigest.getInstance("SHA-256");
564 
565             try (DigestInputStream in = new DigestInputStream(new FileInputStream(f), digest)) {
566                 byte[] buf = new byte[8192];
567                 while (in.read(buf) >= 0) {
568                     // nop
569                 }
570                 byte[] digestBytes = digest.digest();
571                 StringBuilder builder = new StringBuilder(64);
572                 for (int b : digestBytes) {
573                     b &= 0xff;
574                     builder.append(String.format("%02x", b));
575                     sha256 = builder.toString();
576                 }
577             }
578 
579         } catch (NoSuchAlgorithmException e) {
580         }
581         return sha256;
582     }
583 
584     @Override
585     public String toString() {
586         return ID;
587     }
588 }