View Javadoc
1   package org.apache.maven.archiver;
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 org.apache.maven.artifact.Artifact;
23  import org.apache.maven.artifact.DependencyResolutionRequiredException;
24  import org.apache.maven.execution.MavenSession;
25  import org.apache.maven.project.MavenProject;
26  import org.codehaus.plexus.archiver.ArchiverException;
27  import org.codehaus.plexus.archiver.jar.JarArchiver;
28  import org.codehaus.plexus.archiver.jar.Manifest;
29  import org.codehaus.plexus.archiver.jar.ManifestException;
30  import org.codehaus.plexus.interpolation.InterpolationException;
31  import org.codehaus.plexus.interpolation.Interpolator;
32  import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
33  import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
34  import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource;
35  import org.codehaus.plexus.interpolation.RecursionInterceptor;
36  import org.codehaus.plexus.interpolation.StringSearchInterpolator;
37  import org.codehaus.plexus.interpolation.ValueSource;
38  import org.apache.maven.shared.utils.StringUtils;
39  
40  import java.io.File;
41  import java.io.IOException;
42  import java.util.ArrayList;
43  import java.util.Collections;
44  import java.util.List;
45  import java.util.Map;
46  import java.util.Properties;
47  import java.util.Set;
48  
49  /**
50   * @author <a href="evenisse@apache.org">Emmanuel Venisse</a>
51   * @author kama
52   * @version $Revision: 1705932 $ $Date: 2015-09-29 23:57:46 +0200 (Tue, 29 Sep 2015) $
53   */
54  public class MavenArchiver
55  {
56  
57      /**
58       * The simply layout.
59       */
60      public static final String SIMPLE_LAYOUT =
61          "${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}";
62  
63      /**
64       * Repository layout.
65       */
66      public static final String REPOSITORY_LAYOUT =
67          "${artifact.groupIdPath}/${artifact.artifactId}/" + "${artifact.baseVersion}/${artifact.artifactId}-"
68              + "${artifact.version}${dashClassifier?}.${artifact.extension}";
69  
70      /**
71       * simple layout non unique.
72       */
73      public static final String SIMPLE_LAYOUT_NONUNIQUE =
74          "${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}";
75  
76      /**
77       * Repository layout non unique.
78       */
79      public static final String REPOSITORY_LAYOUT_NONUNIQUE =
80          "${artifact.groupIdPath}/${artifact.artifactId}/" + "${artifact.baseVersion}/${artifact.artifactId}-"
81              + "${artifact.baseVersion}${dashClassifier?}.${artifact.extension}";
82  
83      private static final List<String> ARTIFACT_EXPRESSION_PREFIXES;
84  
85      static
86      {
87          List<String> artifactExpressionPrefixes = new ArrayList<String>();
88          artifactExpressionPrefixes.add( "artifact." );
89  
90          ARTIFACT_EXPRESSION_PREFIXES = artifactExpressionPrefixes;
91      }
92  
93      private JarArchiver archiver;
94  
95      private File archiveFile;
96  
97      /**
98       * Return a pre-configured manifest
99       *
100      * @param project the project
101      * @param config the configuration to use
102      * @return a manifest, clients are recommended to use java.util.jar.Manifest datatype.
103      * @throws org.apache.maven.artifact.DependencyResolutionRequiredException .
104      * @throws org.codehaus.plexus.archiver.jar.ManifestException .
105      * @todo Add user attributes list and user groups list
106      * @deprecated
107      */
108     @SuppressWarnings( "UnusedDeclaration" )
109     public Manifest getManifest( MavenProject project, MavenArchiveConfiguration config )
110         throws ManifestException, DependencyResolutionRequiredException
111     {
112         return getManifest( null, project, config );
113     }
114 
115     /**
116      * @param session The Maven Session.
117      * @param project The Maven Project.
118      * @param config The MavenArchiveConfiguration
119      * @return The {@link Manifest}
120      * @throws ManifestException In case of a failure.
121      * @throws DependencyResolutionRequiredException Resolution failure.
122      */
123     public Manifest getManifest( MavenSession session, MavenProject project, MavenArchiveConfiguration config )
124         throws ManifestException, DependencyResolutionRequiredException
125     {
126         boolean hasManifestEntries = !config.isManifestEntriesEmpty();
127         Map<String, String> entries =
128             hasManifestEntries ? config.getManifestEntries() : Collections.<String, String>emptyMap();
129 
130         Manifest manifest = getManifest( session, project, config.getManifest(), entries );
131 
132         // any custom manifest entries in the archive configuration manifest?
133         if ( hasManifestEntries )
134         {
135 
136             for ( Map.Entry<String, String> entry : entries.entrySet() )
137             {
138                 String key = entry.getKey();
139                 String value = entry.getValue();
140                 Manifest.Attribute attr = manifest.getMainSection().getAttribute( key );
141                 if ( key.equals( "Class-Path" ) && attr != null )
142                 {
143                     // Merge the user-supplied Class-Path value with the programmatically
144                     // generated Class-Path. Note that the user-supplied value goes first
145                     // so that resources there will override any in the standard Class-Path.
146                     attr.setValue( value + " " + attr.getValue() );
147                 }
148                 else
149                 {
150                     addManifestAttribute( manifest, key, value );
151                 }
152             }
153         }
154 
155         // any custom manifest sections in the archive configuration manifest?
156         if ( !config.isManifestSectionsEmpty() )
157         {
158             for ( ManifestSection section : config.getManifestSections() )
159             {
160                 Manifest.Section theSection = new Manifest.Section();
161                 theSection.setName( section.getName() );
162 
163                 if ( !section.isManifestEntriesEmpty() )
164                 {
165                     Map<String, String> sectionEntries = section.getManifestEntries();
166 
167                     for ( Map.Entry<String, String> entry : sectionEntries.entrySet() )
168                     {
169                         String key = entry.getKey();
170                         String value = entry.getValue();
171                         Manifest.Attribute attr = new Manifest.Attribute( key, value );
172                         theSection.addConfiguredAttribute( attr );
173                     }
174                 }
175 
176                 manifest.addConfiguredSection( theSection );
177             }
178         }
179 
180         return manifest;
181     }
182 
183     /**
184      * Return a pre-configured manifest
185      *
186      * @todo Add user attributes list and user groups list
187      * @param project {@link MavenProject}
188      * @param config {@link ManifestConfiguration}
189      * @return {@link Manifest}
190      * @throws ManifestException Manifest exception.
191      * @throws DependencyResolutionRequiredException Dependency resolution exception.
192      */
193     public Manifest getManifest( MavenProject project, ManifestConfiguration config )
194         throws ManifestException, DependencyResolutionRequiredException
195     {
196         return getManifest( null, project, config, Collections.<String, String>emptyMap() );
197     }
198 
199     /**
200      * @param mavenSession {@link MavenSession}
201      * @param project {@link MavenProject}
202      * @param config {@link ManifestConfiguration}
203      * @return {@link Manifest}
204      * @throws ManifestException The manifest exception.
205      * @throws DependencyResolutionRequiredException The dependency resolution required exception.
206      */
207     public Manifest getManifest( MavenSession mavenSession, MavenProject project, ManifestConfiguration config )
208         throws ManifestException, DependencyResolutionRequiredException
209     {
210         return getManifest( mavenSession, project, config, Collections.<String, String>emptyMap() );
211     }
212 
213     private void addManifestAttribute( Manifest manifest, Map<String, String> map, String key, String value )
214         throws ManifestException
215     {
216         if ( map.containsKey( key ) )
217         {
218             return; // The map value will be added later
219         }
220         addManifestAttribute( manifest, key, value );
221     }
222 
223     private void addManifestAttribute( Manifest manifest, String key, String value )
224         throws ManifestException
225     {
226         if ( !StringUtils.isEmpty( value ) )
227         {
228             Manifest.Attribute attr = new Manifest.Attribute( key, value );
229             manifest.addConfiguredAttribute( attr );
230         }
231         else
232         {
233             // if the value is empty we have create an entry with an empty string
234             // to prevent null print in the manifest file
235             Manifest.Attribute attr = new Manifest.Attribute( key, "" );
236             manifest.addConfiguredAttribute( attr );
237         }
238     }
239 
240     /**
241      * @param session {@link MavenSession}
242      * @param project {@link MavenProject}
243      * @param config {@link ManifestConfiguration}
244      * @param entries The entries.
245      * @return {@link Manifest}
246      * @throws ManifestException The manifest exception.
247      * @throws DependencyResolutionRequiredException The dependency resolution required exception.
248      */
249     protected Manifest getManifest( MavenSession session, MavenProject project, ManifestConfiguration config,
250                                     Map<String, String> entries )
251                                         throws ManifestException, DependencyResolutionRequiredException
252     {
253         // TODO: Should we replace "map" with a copy? Note, that we modify it!
254 
255         // Added basic entries
256         Manifest m = new Manifest();
257         addCreatedByEntry( session, m, entries );
258 
259         addCustomEntries( m, entries, config );
260 
261         if ( config.isAddClasspath() )
262         {
263             StringBuilder classpath = new StringBuilder();
264 
265             List<String> artifacts = project.getRuntimeClasspathElements();
266             String classpathPrefix = config.getClasspathPrefix();
267             String layoutType = config.getClasspathLayoutType();
268             String layout = config.getCustomClasspathLayout();
269 
270             Interpolator interpolator = new StringSearchInterpolator();
271 
272             for ( String artifactFile : artifacts )
273             {
274                 File f = new File( artifactFile );
275                 if ( f.getAbsoluteFile().isFile() )
276                 {
277                     Artifact artifact = findArtifactWithFile( project.getArtifacts(), f );
278 
279                     if ( classpath.length() > 0 )
280                     {
281                         classpath.append( " " );
282                     }
283                     classpath.append( classpathPrefix );
284 
285                     // NOTE: If the artifact or layout type (from config) is null, give up and use the file name by
286                     // itself.
287                     if ( artifact == null || layoutType == null )
288                     {
289                         classpath.append( f.getName() );
290                     }
291                     else
292                     {
293                         List<ValueSource> valueSources = new ArrayList<ValueSource>();
294 
295                         valueSources.add( new PrefixedObjectValueSource( ARTIFACT_EXPRESSION_PREFIXES, artifact,
296                                                                          true ) );
297                         valueSources.add( new PrefixedObjectValueSource( ARTIFACT_EXPRESSION_PREFIXES,
298                                                                          artifact.getArtifactHandler(), true ) );
299 
300                         Properties extraExpressions = new Properties();
301                         // FIXME: This query method SHOULD NOT affect the internal
302                         // state of the artifact version, but it does.
303                         if ( !artifact.isSnapshot() )
304                         {
305                             extraExpressions.setProperty( "baseVersion", artifact.getVersion() );
306                         }
307 
308                         extraExpressions.setProperty( "groupIdPath", artifact.getGroupId().replace( '.', '/' ) );
309                         if ( StringUtils.isNotEmpty( artifact.getClassifier() ) )
310                         {
311                             extraExpressions.setProperty( "dashClassifier", "-" + artifact.getClassifier() );
312                             extraExpressions.setProperty( "dashClassifier?", "-" + artifact.getClassifier() );
313                         }
314                         else
315                         {
316                             extraExpressions.setProperty( "dashClassifier", "" );
317                             extraExpressions.setProperty( "dashClassifier?", "" );
318                         }
319                         valueSources.add( new PrefixedPropertiesValueSource( ARTIFACT_EXPRESSION_PREFIXES,
320                                                                              extraExpressions, true ) );
321 
322                         for ( ValueSource vs : valueSources )
323                         {
324                             interpolator.addValueSource( vs );
325                         }
326 
327                         RecursionInterceptor recursionInterceptor =
328                             new PrefixAwareRecursionInterceptor( ARTIFACT_EXPRESSION_PREFIXES );
329 
330                         try
331                         {
332                             if ( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_SIMPLE.equals( layoutType ) )
333                             {
334                                 if ( config.isUseUniqueVersions() )
335                                 {
336                                     classpath.append( interpolator.interpolate( SIMPLE_LAYOUT, recursionInterceptor ) );
337                                 }
338                                 else
339                                 {
340                                     classpath.append( interpolator.interpolate( SIMPLE_LAYOUT_NONUNIQUE,
341                                                                                 recursionInterceptor ) );
342                                 }
343                             }
344                             else if ( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_REPOSITORY.equals( layoutType ) )
345                             {
346                                 // we use layout /$groupId[0]/../${groupId[n]/$artifactId/$version/{fileName}
347                                 // here we must find the Artifact in the project Artifacts to generate the maven layout
348                                 if ( config.isUseUniqueVersions() )
349                                 {
350                                     classpath.append( interpolator.interpolate( REPOSITORY_LAYOUT,
351                                                                                 recursionInterceptor ) );
352                                 }
353                                 else
354                                 {
355                                     classpath.append( interpolator.interpolate( REPOSITORY_LAYOUT_NONUNIQUE,
356                                                                                 recursionInterceptor ) );
357                                 }
358                             }
359                             else if ( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM.equals( layoutType ) )
360                             {
361                                 if ( layout == null )
362                                 {
363                                     throw new ManifestException( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM
364                                         + " layout type was declared, but custom layout expression was not"
365                                         + " specified. Check your <archive><manifest><customLayout/> element." );
366                                 }
367 
368                                 classpath.append( interpolator.interpolate( layout, recursionInterceptor ) );
369                             }
370                             else
371                             {
372                                 throw new ManifestException( "Unknown classpath layout type: '" + layoutType
373                                     + "'. Check your <archive><manifest><layoutType/> element." );
374                             }
375                         }
376                         catch ( InterpolationException e )
377                         {
378                             ManifestException error =
379                                 new ManifestException( "Error interpolating artifact path for classpath entry: "
380                                     + e.getMessage() );
381 
382                             error.initCause( e );
383                             throw error;
384                         }
385                         finally
386                         {
387                             for ( ValueSource vs : valueSources )
388                             {
389                                 interpolator.removeValuesSource( vs );
390                             }
391                         }
392                     }
393                 }
394             }
395 
396             if ( classpath.length() > 0 )
397             {
398                 // Class-Path is special and should be added to manifest even if
399                 // it is specified in the manifestEntries section
400                 addManifestAttribute( m, "Class-Path", classpath.toString() );
401             }
402         }
403 
404         if ( config.isAddDefaultSpecificationEntries() )
405         {
406             addManifestAttribute( m, entries, "Specification-Title", project.getName() );
407             addManifestAttribute( m, entries, "Specification-Version", project.getVersion() );
408 
409             if ( project.getOrganization() != null )
410             {
411                 addManifestAttribute( m, entries, "Specification-Vendor", project.getOrganization().getName() );
412             }
413         }
414 
415         if ( config.isAddDefaultImplementationEntries() )
416         {
417             addManifestAttribute( m, entries, "Implementation-Title", project.getName() );
418             addManifestAttribute( m, entries, "Implementation-Version", project.getVersion() );
419             // MJAR-5
420             addManifestAttribute( m, entries, "Implementation-Vendor-Id", project.getGroupId() );
421 
422             if ( project.getOrganization() != null )
423             {
424                 addManifestAttribute( m, entries, "Implementation-Vendor", project.getOrganization().getName() );
425             }
426 
427             if ( project.getUrl() != null )
428             {
429                 addManifestAttribute( m, entries, "Implementation-URL", project.getUrl() );
430             }
431         }
432 
433         String mainClass = config.getMainClass();
434         if ( mainClass != null && !"".equals( mainClass ) )
435         {
436             addManifestAttribute( m, entries, "Main-Class", mainClass );
437         }
438 
439         // Added extensions
440         if ( config.isAddExtensions() )
441         {
442             // TODO: this is only for applets - should we distinguish them as a packaging?
443             StringBuilder extensionsList = new StringBuilder();
444             Set<Artifact> artifacts = (Set<Artifact>) project.getArtifacts();
445 
446             for ( Artifact artifact : artifacts )
447             {
448                 if ( !Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
449                 {
450                     if ( "jar".equals( artifact.getType() ) )
451                     {
452                         if ( extensionsList.length() > 0 )
453                         {
454                             extensionsList.append( " " );
455                         }
456                         extensionsList.append( artifact.getArtifactId() );
457                     }
458                 }
459             }
460 
461             if ( extensionsList.length() > 0 )
462             {
463                 addManifestAttribute( m, entries, "Extension-List", extensionsList.toString() );
464             }
465 
466             for ( Object artifact1 : artifacts )
467             {
468                 // TODO: the correct solution here would be to have an extension type, and to read
469                 // the real extension values either from the artifact's manifest or some part of the POM
470                 Artifact artifact = (Artifact) artifact1;
471                 if ( "jar".equals( artifact.getType() ) )
472                 {
473                     String artifactId = artifact.getArtifactId().replace( '.', '_' );
474                     String ename = artifactId + "-Extension-Name";
475                     addManifestAttribute( m, entries, ename, artifact.getArtifactId() );
476                     String iname = artifactId + "-Implementation-Version";
477                     addManifestAttribute( m, entries, iname, artifact.getVersion() );
478 
479                     if ( artifact.getRepository() != null )
480                     {
481                         iname = artifactId + "-Implementation-URL";
482                         String url = artifact.getRepository().getUrl() + "/" + artifact.toString();
483                         addManifestAttribute( m, entries, iname, url );
484                     }
485                 }
486             }
487         }
488 
489         return m;
490     }
491 
492     private void addCustomEntries( Manifest m, Map<String, String> entries, ManifestConfiguration config )
493         throws ManifestException
494     {
495         addManifestAttribute( m, entries, "Built-By", System.getProperty( "user.name" ) );
496         addManifestAttribute( m, entries, "Build-Jdk", System.getProperty( "java.version" ) );
497 
498         /*
499          * TODO: rethink this, it wasn't working Artifact projectArtifact = project.getArtifact(); if (
500          * projectArtifact.isSnapshot() ) { Manifest.Attribute buildNumberAttr = new Manifest.Attribute( "Build-Number",
501          * "" + project.getSnapshotDeploymentBuildNumber() ); m.addConfiguredAttribute( buildNumberAttr ); }
502          */
503         if ( config.getPackageName() != null )
504         {
505             addManifestAttribute( m, entries, "Package", config.getPackageName() );
506         }
507     }
508 
509     /**
510      * @return {@link JarArchiver}
511      */
512     public JarArchiver getArchiver()
513     {
514         return archiver;
515     }
516 
517     /**
518      * @param archiver {@link JarArchiver}
519      */
520     public void setArchiver( JarArchiver archiver )
521     {
522         this.archiver = archiver;
523     }
524 
525     /**
526      * @param outputFile Set output file.
527      */
528     public void setOutputFile( File outputFile )
529     {
530         archiveFile = outputFile;
531     }
532 
533     /**
534      * @deprecated
535      * @param project {@link MavenProject}
536      * @param archiveConfiguration {@link MavenArchiveConfiguration}
537      * @throws ArchiverException Archiver Exception.
538      * @throws ManifestException Manifest Exception.
539      * @throws IOException IO Exception.
540      * @throws DependencyResolutionRequiredException Dependency resolution exception.
541      */
542     @SuppressWarnings( "JavaDoc" )
543     public void createArchive( MavenProject project, MavenArchiveConfiguration archiveConfiguration )
544         throws ArchiverException, ManifestException, IOException, DependencyResolutionRequiredException
545     {
546         createArchive( null, project, archiveConfiguration );
547     }
548 
549     /**
550      * @param session {@link MavenSession}
551      * @param project {@link MavenProject}
552      * @param archiveConfiguration {@link MavenArchiveConfiguration}
553      * @throws ArchiverException Archiver Exception.
554      * @throws ManifestException Manifest Exception.
555      * @throws IOException IO Exception.
556      * @throws DependencyResolutionRequiredException Dependency resolution exception.
557      */
558     public void createArchive( MavenSession session, MavenProject project,
559                                MavenArchiveConfiguration archiveConfiguration )
560                                    throws ArchiverException, ManifestException, IOException,
561                                    DependencyResolutionRequiredException
562     {
563         // we have to clone the project instance so we can write out the pom with the deployment version,
564         // without impacting the main project instance...
565         MavenProject workingProject = null;
566         workingProject = (MavenProject) project.clone();
567 
568         boolean forced = archiveConfiguration.isForced();
569         if ( archiveConfiguration.isAddMavenDescriptor() )
570         {
571             // ----------------------------------------------------------------------
572             // We want to add the metadata for the project to the JAR in two forms:
573             //
574             // The first form is that of the POM itself. Applications that wish to
575             // access the POM for an artifact using maven tools they can.
576             //
577             // The second form is that of a properties file containing the basic
578             // top-level POM elements so that applications that wish to access
579             // POM information without the use of maven tools can do so.
580             // ----------------------------------------------------------------------
581 
582             if ( workingProject.getArtifact().isSnapshot() )
583             {
584                 workingProject.setVersion( workingProject.getArtifact().getVersion() );
585             }
586 
587             String groupId = workingProject.getGroupId();
588 
589             String artifactId = workingProject.getArtifactId();
590 
591             archiver.addFile( project.getFile(), "META-INF/maven/" + groupId + "/" + artifactId + "/pom.xml" );
592 
593             // ----------------------------------------------------------------------
594             // Create pom.properties file
595             // ----------------------------------------------------------------------
596 
597             File pomPropertiesFile = archiveConfiguration.getPomPropertiesFile();
598             if ( pomPropertiesFile == null )
599             {
600                 File dir = new File( workingProject.getBuild().getDirectory(), "maven-archiver" );
601                 pomPropertiesFile = new File( dir, "pom.properties" );
602             }
603             new PomPropertiesUtil().createPomProperties( workingProject, archiver, pomPropertiesFile, forced );
604         }
605 
606         // ----------------------------------------------------------------------
607         // Create the manifest
608         // ----------------------------------------------------------------------
609 
610         File manifestFile = archiveConfiguration.getManifestFile();
611 
612         if ( manifestFile != null )
613         {
614             archiver.setManifest( manifestFile );
615         }
616 
617         Manifest manifest = getManifest( session, workingProject, archiveConfiguration );
618 
619         // Configure the jar
620         archiver.addConfiguredManifest( manifest );
621 
622         archiver.setCompress( archiveConfiguration.isCompress() );
623 
624         archiver.setRecompressAddedZips( archiveConfiguration.isRecompressAddedZips() );
625 
626         archiver.setIndex( archiveConfiguration.isIndex() );
627 
628         archiver.setDestFile( archiveFile );
629 
630         // make the archiver index the jars on the classpath, if we are adding that to the manifest
631         if ( archiveConfiguration.getManifest().isAddClasspath() )
632         {
633             List<String> artifacts = project.getRuntimeClasspathElements();
634             for ( String artifact : artifacts )
635             {
636                 File f = new File( artifact );
637                 archiver.addConfiguredIndexJars( f );
638             }
639         }
640 
641         archiver.setForced( forced );
642         if ( !archiveConfiguration.isForced() && archiver.isSupportingForced() )
643         {
644             // TODO Should issue a warning here, but how do we get a logger?
645             // TODO getLog().warn(
646             // "Forced build is disabled, but disabling the forced mode isn't supported by the archiver." );
647         }
648 
649         // create archive
650         archiver.createArchive();
651     }
652 
653     private void addCreatedByEntry( MavenSession session, Manifest m, Map<String, String> entries )
654         throws ManifestException
655     {
656         String createdBy = "Apache Maven";
657         if ( session != null ) // can be null due to API backwards compatibility
658         {
659             String mavenVersion = session.getUserProperties().getProperty( "maven.version" );
660             if ( mavenVersion != null )
661             {
662                 createdBy += " " + mavenVersion;
663             }
664         }
665         addManifestAttribute( m, entries, "Created-By", createdBy );
666     }
667 
668     private Artifact findArtifactWithFile( Set<Artifact> artifacts, File file )
669     {
670         for ( Artifact artifact : artifacts )
671         {
672             // normally not null but we can check
673             if ( artifact.getFile() != null )
674             {
675                 if ( artifact.getFile().equals( file ) )
676                 {
677                     return artifact;
678                 }
679             }
680         }
681         return null;
682     }
683 }