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