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