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.artifact.handler.ArtifactHandler;
25  import org.apache.maven.artifact.handler.DefaultArtifactHandler;
26  import org.apache.maven.artifact.repository.ArtifactRepository;
27  import org.apache.maven.execution.DefaultMavenExecutionRequest;
28  import org.apache.maven.execution.DefaultMavenExecutionResult;
29  import org.apache.maven.execution.MavenExecutionRequest;
30  import org.apache.maven.execution.MavenExecutionResult;
31  import org.apache.maven.execution.MavenSession;
32  import org.apache.maven.model.Build;
33  import org.apache.maven.model.Model;
34  import org.apache.maven.model.Organization;
35  import org.apache.maven.project.MavenProject;
36  import org.apache.maven.shared.utils.StringUtils;
37  import org.apache.maven.shared.utils.io.FileUtils;
38  import org.codehaus.plexus.archiver.jar.JarArchiver;
39  import org.codehaus.plexus.archiver.jar.ManifestException;
40  import org.eclipse.aether.DefaultRepositorySystemSession;
41  import org.eclipse.aether.RepositorySystemSession;
42  import org.junit.Test;
43  
44  import java.io.File;
45  import java.io.IOException;
46  import java.io.InputStream;
47  import java.net.URI;
48  import java.net.URL;
49  import java.util.ArrayList;
50  import java.util.Collections;
51  import java.util.Comparator;
52  import java.util.Date;
53  import java.util.HashMap;
54  import java.util.List;
55  import java.util.Map;
56  import java.util.Properties;
57  import java.util.Set;
58  import java.util.TreeSet;
59  import java.util.jar.Attributes;
60  import java.util.jar.JarFile;
61  import java.util.jar.Manifest;
62  import java.util.zip.ZipEntry;
63  
64  import static org.assertj.core.api.Assertions.assertThat;
65  import static org.junit.Assert.*;
66  
67  public class MavenArchiverTest
68  {
69      static class ArtifactComparator
70          implements Comparator<Artifact>
71      {
72          public int compare( Artifact o1, Artifact o2 )
73          {
74              return o1.getArtifactId().compareTo( o2.getArtifactId() );
75          }
76  
77          public boolean equals( Object o )
78          {
79              return false;
80          }
81      }
82  
83      @Test
84      public void testInvalidModuleNames()
85      {
86          assertThat( MavenArchiver.isValidModuleName( "" ) ).isFalse();
87          assertThat( MavenArchiver.isValidModuleName( "." ) ).isFalse();
88          assertThat( MavenArchiver.isValidModuleName( "dash-is-invalid" ) ).isFalse();
89          assertThat( MavenArchiver.isValidModuleName( "plus+is+invalid" ) ).isFalse();
90          assertThat( MavenArchiver.isValidModuleName( "colon:is:invalid" ) ).isFalse();
91          assertThat( MavenArchiver.isValidModuleName( "new.class" ) ).isFalse();
92          assertThat( MavenArchiver.isValidModuleName( "123.at.start.is.invalid" ) ).isFalse();
93          assertThat( MavenArchiver.isValidModuleName( "digit.at.123start.is.invalid" ) ).isFalse();
94      }
95  
96      @Test
97      public void testValidModuleNames()
98      {
99          assertThat( MavenArchiver.isValidModuleName( "a" ) ).isTrue();
100         assertThat( MavenArchiver.isValidModuleName( "a.b" ) ).isTrue();
101         assertThat( MavenArchiver.isValidModuleName( "a_b" ) ).isTrue();
102         assertThat( MavenArchiver.isValidModuleName( "trailing0.digits123.are456.ok789" ) ).isTrue();
103         assertThat( MavenArchiver.isValidModuleName( "UTF8.chars.are.okay.äëïöüẍ" ) ).isTrue();
104         assertThat( MavenArchiver.isValidModuleName( "ℤ€ℕ" ) ).isTrue();
105     }
106 
107     @Test
108     public void testGetManifestExtensionList()
109         throws Exception
110     {
111         MavenArchiver archiver = new MavenArchiver();
112 
113         MavenSession session = getDummySession();
114 
115         Model model = new Model();
116         model.setArtifactId( "dummy" );
117 
118         MavenProject project = new MavenProject( model );
119         // we need to sort the artifacts for test purposes
120         Set<Artifact> artifacts = new TreeSet<>( new ArtifactComparator() );
121         project.setArtifacts( artifacts );
122 
123         // there should be a mock or a setter for this field.
124         ManifestConfiguration config = new ManifestConfiguration()
125         {
126             public boolean isAddExtensions()
127             {
128                 return true;
129             }
130         };
131 
132         Manifest manifest = archiver.getManifest( session, project, config );
133 
134         assertThat( manifest.getMainAttributes() ).isNotNull();
135 
136         assertThat( manifest.getMainAttributes().getValue( "Extension-List" ) ).isNull();
137 
138         MockArtifact artifact1 = new MockArtifact();
139         artifact1.setGroupId( "org.apache.dummy" );
140         artifact1.setArtifactId( "dummy1" );
141         artifact1.setVersion( "1.0" );
142         artifact1.setType( "dll" );
143         artifact1.setScope( "compile" );
144 
145         artifacts.add( artifact1 );
146 
147         manifest = archiver.getManifest( session, project, config );
148 
149         assertThat( manifest.getMainAttributes().getValue( "Extension-List" ) ).isNull();
150 
151         MockArtifact artifact2 = new MockArtifact();
152         artifact2.setGroupId( "org.apache.dummy" );
153         artifact2.setArtifactId( "dummy2" );
154         artifact2.setVersion( "1.0" );
155         artifact2.setType( "jar" );
156         artifact2.setScope( "compile" );
157 
158         artifacts.add( artifact2 );
159 
160         manifest = archiver.getManifest( session, project, config );
161 
162         assertThat( manifest.getMainAttributes().getValue( "Extension-List" ) ).isEqualTo( "dummy2" );
163 
164         MockArtifact artifact3 = new MockArtifact();
165         artifact3.setGroupId( "org.apache.dummy" );
166         artifact3.setArtifactId( "dummy3" );
167         artifact3.setVersion( "1.0" );
168         artifact3.setScope( "test" );
169         artifact3.setType( "jar" );
170 
171         artifacts.add( artifact3 );
172 
173         manifest = archiver.getManifest( session, project, config );
174 
175         assertThat( manifest.getMainAttributes().getValue( "Extension-List" ) ).isEqualTo( "dummy2" );
176 
177         MockArtifact artifact4 = new MockArtifact();
178         artifact4.setGroupId( "org.apache.dummy" );
179         artifact4.setArtifactId( "dummy4" );
180         artifact4.setVersion( "1.0" );
181         artifact4.setType( "jar" );
182         artifact4.setScope( "compile" );
183 
184         artifacts.add( artifact4 );
185 
186         manifest = archiver.getManifest( session, project, config );
187 
188         assertThat( manifest.getMainAttributes().getValue( "Extension-List" ) ).isEqualTo( "dummy2 dummy4" );
189     }
190 
191     @Test
192     public void testMultiClassPath()
193         throws Exception
194     {
195         final File tempFile = File.createTempFile( "maven-archiver-test-", ".jar" );
196 
197         try
198         {
199             MavenArchiver archiver = new MavenArchiver();
200 
201             MavenSession session = getDummySession();
202 
203             Model model = new Model();
204             model.setArtifactId( "dummy" );
205 
206             MavenProject project = new MavenProject( model )
207             {
208                 public List<String> getRuntimeClasspathElements()
209                 {
210                     return Collections.singletonList( tempFile.getAbsolutePath() );
211                 }
212             };
213 
214             // there should be a mock or a setter for this field.
215             ManifestConfiguration manifestConfig = new ManifestConfiguration()
216             {
217                 public boolean isAddClasspath()
218                 {
219                     return true;
220                 }
221             };
222 
223             MavenArchiveConfiguration archiveConfiguration = new MavenArchiveConfiguration();
224             archiveConfiguration.setManifest( manifestConfig );
225             archiveConfiguration.addManifestEntry( "Class-Path", "help/" );
226 
227             Manifest manifest = archiver.getManifest( session, project, archiveConfiguration );
228             String classPath = manifest.getMainAttributes().getValue( "Class-Path" );
229             assertThat( classPath )
230                     .as( "User specified Class-Path entry was not prepended to manifest" )
231                     .startsWith( "help/" )
232                     .as( "Class-Path generated by addClasspath was not appended to manifest" )
233                     .endsWith( tempFile.getName() );
234         }
235         finally
236         {
237             // noinspection ResultOfMethodCallIgnored
238             tempFile.delete();
239         }
240     }
241 
242     @Test
243     public void testRecreation()
244         throws Exception
245     {
246         File jarFile = new File( "target/test/dummy.jar" );
247         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
248 
249         MavenArchiver archiver = getMavenArchiver( jarArchiver );
250 
251         MavenSession session = getDummySession();
252         MavenProject project = getDummyProject();
253 
254         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
255         config.setForced( false );
256 
257         File directory = new File( "target/maven-archiver" );
258         org.apache.commons.io.FileUtils.deleteDirectory( directory );
259         archiver.createArchive( session, project, config );
260         assertThat( jarFile ).exists();
261 
262         long history = System.currentTimeMillis() - 60000L;
263         jarFile.setLastModified( history );
264         long time = jarFile.lastModified();
265 
266         List<File> files = FileUtils.getFiles( directory, "**/**", null, true );
267         for ( File file : files )
268         {
269             assertThat( file.setLastModified( time ) ).isTrue();
270         }
271 
272         archiver.createArchive( session, project, config );
273 
274         config.setForced( true );
275         archiver.createArchive( session, project, config );
276         // I'm not sure if it could only be greater than time or if it is sufficient to be greater or equal..
277         assertThat( jarFile.lastModified() ).isGreaterThanOrEqualTo( time );
278     }
279 
280     @Test
281     public void testNotGenerateImplementationVersionForMANIFESTMF()
282         throws Exception
283     {
284         File jarFile = new File( "target/test/dummy.jar" );
285         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
286 
287         MavenArchiver archiver = getMavenArchiver( jarArchiver );
288 
289         MavenSession session = getDummySession();
290         MavenProject project = getDummyProject();
291 
292         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
293         config.setForced( true );
294         config.getManifest().setAddDefaultImplementationEntries( false );
295         archiver.createArchive( session, project, config );
296         assertThat( jarFile ).exists();
297 
298         try ( JarFile jar = new JarFile( jarFile ) )
299         {
300             assertThat( jar.getManifest().getMainAttributes() )
301                     .doesNotContainKey( Attributes.Name.IMPLEMENTATION_VERSION ); // "Implementation-Version"
302         }
303     }
304 
305     @Test
306     public void testGenerateImplementationVersionForMANIFESTMF()
307         throws Exception
308     {
309         File jarFile = new File( "target/test/dummy.jar" );
310         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
311 
312         MavenArchiver archiver = getMavenArchiver( jarArchiver );
313 
314         MavenSession session = getDummySession();
315         MavenProject project = getDummyProject();
316 
317         String ls = System.getProperty( "line.separator" );
318         project.setDescription( "foo " + ls + " bar " );
319         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
320         config.setForced( true );
321         config.getManifest().setAddDefaultImplementationEntries( true );
322         config.addManifestEntry( "Description", project.getDescription() );
323         archiver.createArchive( session, project, config );
324         assertThat( jarFile ).exists();
325 
326         try ( JarFile jar = new JarFile( jarFile ) )
327         {
328             assertThat( jar.getManifest().getMainAttributes() )
329                     .containsKey( Attributes.Name.IMPLEMENTATION_VERSION )
330                     .containsEntry( Attributes.Name.IMPLEMENTATION_VERSION, "0.1.1" );
331         }
332     }
333 
334     private MavenArchiver getMavenArchiver( JarArchiver jarArchiver )
335     {
336         MavenArchiver archiver = new MavenArchiver();
337         archiver.setArchiver( jarArchiver );
338         archiver.setOutputFile( jarArchiver.getDestFile() );
339         return archiver;
340     }
341 
342     @Test
343     public void testDashesInClassPath_MSHARED_134()
344         throws IOException, ManifestException, DependencyResolutionRequiredException
345     {
346         File jarFile = new File( "target/test/dummyWithDashes.jar" );
347         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
348 
349         MavenArchiver archiver = getMavenArchiver( jarArchiver );
350 
351         MavenSession session = getDummySession();
352         MavenProject project = getDummyProject();
353 
354         Set<Artifact> artifacts =
355             getArtifacts( getMockArtifact1(), getArtifactWithDot(), getMockArtifact2(), getMockArtifact3() );
356 
357         project.setArtifacts( artifacts );
358 
359         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
360         config.setForced( false );
361 
362         final ManifestConfiguration mftConfig = config.getManifest();
363         mftConfig.setMainClass( "org.apache.maven.Foo" );
364         mftConfig.setAddClasspath( true );
365         mftConfig.setAddExtensions( true );
366         mftConfig.setClasspathPrefix( "./lib/" );
367 
368         archiver.createArchive( session, project, config );
369         assertThat( jarFile ).exists();
370     }
371 
372     @Test
373     public void testDashesInClassPath_MSHARED_182()
374         throws IOException, ManifestException, DependencyResolutionRequiredException
375     {
376         File jarFile = new File( "target/test/dummy.jar" );
377         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
378         MavenArchiver archiver = getMavenArchiver( jarArchiver );
379 
380         MavenSession session = getDummySession();
381         MavenProject project = getDummyProject();
382 
383         Set<Artifact> artifacts =
384             getArtifacts( getMockArtifact1(), getArtifactWithDot(), getMockArtifact2(), getMockArtifact3() );
385 
386         project.setArtifacts( artifacts );
387 
388         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
389         config.setForced( false );
390 
391         final ManifestConfiguration mftConfig = config.getManifest();
392         mftConfig.setMainClass( "org.apache.maven.Foo" );
393         mftConfig.setAddClasspath( true );
394         mftConfig.setAddExtensions( true );
395         mftConfig.setClasspathPrefix( "./lib/" );
396         config.addManifestEntry( "Key1", "value1" );
397         config.addManifestEntry( "key2", "value2" );
398 
399         archiver.createArchive( session, project, config );
400         assertThat( jarFile ).exists();
401         final Attributes mainAttributes = getJarFileManifest( jarFile ).getMainAttributes();
402         assertThat( mainAttributes.getValue( "Key1" ) ).isEqualTo( "value1" );
403         assertThat( mainAttributes.getValue( "Key2" ) ).isEqualTo( "value2" );
404     }
405 
406     @Test
407     public void testCarriageReturnInManifestEntry()
408         throws Exception
409     {
410         File jarFile = new File( "target/test/dummy.jar" );
411         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
412 
413         MavenArchiver archiver = getMavenArchiver( jarArchiver );
414 
415         MavenSession session = getDummySession();
416         MavenProject project = getDummyProject();
417 
418         String ls = System.getProperty( "line.separator" );
419         project.setDescription( "foo " + ls + " bar " );
420         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
421         config.setForced( true );
422         config.getManifest().setAddDefaultImplementationEntries( true );
423         config.addManifestEntry( "Description", project.getDescription() );
424         // config.addManifestEntry( "EntryWithTab", " foo tab " + ( '\u0009' ) + ( '\u0009' ) // + " bar tab" + ( //
425         // '\u0009' // ) );
426         archiver.createArchive( session, project, config );
427         assertThat( jarFile ).exists();
428 
429         final Manifest manifest = getJarFileManifest( jarFile );
430         Attributes attributes = manifest.getMainAttributes();
431         assertThat( project.getDescription().indexOf( ls ) ).isGreaterThan( 0 );
432         Attributes.Name description = new Attributes.Name( "Description" );
433         String value = attributes.getValue( description );
434         assertThat( value ).isNotNull();
435         assertThat( value.indexOf( ls ) ).isLessThanOrEqualTo( 0 );
436     }
437 
438     @Test
439     public void testDeprecatedCreateArchiveAPI()
440         throws Exception
441     {
442         File jarFile = new File( "target/test/dummy.jar" );
443         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
444 
445         MavenArchiver archiver = getMavenArchiver( jarArchiver );
446 
447         MavenProject project = getDummyProject();
448         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
449         config.setForced( true );
450         config.getManifest().setAddDefaultImplementationEntries( true );
451         config.getManifest().setAddDefaultSpecificationEntries( true );
452 
453         MavenSession session = getDummySessionWithoutMavenVersion();
454         archiver.createArchive( session, project, config );
455         assertThat( jarFile ).exists();
456         Attributes manifest = getJarFileManifest( jarFile ).getMainAttributes();
457 
458         // no version number
459         assertThat( manifest )
460                 .containsEntry( new Attributes.Name( "Created-By" ), "Maven Archiver" )
461                 .containsEntry( Attributes.Name.SPECIFICATION_TITLE, "archiver test" )
462                 .containsEntry( Attributes.Name.SPECIFICATION_VERSION, "0.1" )
463                 .containsEntry( Attributes.Name.SPECIFICATION_VENDOR, "Apache" )
464                 .containsEntry( Attributes.Name.IMPLEMENTATION_TITLE, "archiver test" )
465                 .containsEntry( Attributes.Name.IMPLEMENTATION_VERSION, "0.1.1" )
466                 .containsEntry( Attributes.Name.IMPLEMENTATION_VENDOR, "Apache" )
467                 .containsEntry( new Attributes.Name( "Build-Jdk-Spec" ),
468                         System.getProperty( "java.specification.version" ) );
469     }
470 
471     @Test
472     public void testMinimalManifestEntries()
473         throws Exception
474     {
475         File jarFile = new File( "target/test/dummy.jar" );
476         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
477 
478         MavenArchiver archiver = getMavenArchiver( jarArchiver );
479 
480         MavenSession session = getDummySession();
481         MavenProject project = getDummyProject();
482         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
483         config.setForced( true );
484         config.getManifest().setAddDefaultEntries( false );
485 
486         archiver.createArchive( session, project, config );
487         assertThat( jarFile ).exists();
488 
489         final Manifest jarFileManifest = getJarFileManifest( jarFile );
490         Attributes manifest = jarFileManifest.getMainAttributes();
491 
492         assertThat( manifest ).hasSize( 1 ).containsOnlyKeys( new Attributes.Name( "Manifest-Version" ) );
493         assertThat( manifest.getValue( "Manifest-Version" ) ).isEqualTo( "1.0" );
494     }
495 
496 
497     @Test
498     public void testManifestEntries()
499         throws Exception
500     {
501         File jarFile = new File( "target/test/dummy.jar" );
502         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
503 
504         MavenArchiver archiver = getMavenArchiver( jarArchiver );
505 
506         MavenSession session = getDummySession();
507         MavenProject project = getDummyProject();
508         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
509         config.setForced( true );
510         config.getManifest().setAddDefaultImplementationEntries( true );
511         config.getManifest().setAddDefaultSpecificationEntries( true );
512         config.getManifest().setAddBuildEnvironmentEntries( true );
513 
514         Map<String, String> manifestEntries = new HashMap<>();
515         manifestEntries.put( "foo", "bar" );
516         manifestEntries.put( "first-name", "olivier" );
517         manifestEntries.put( "Automatic-Module-Name", "org.apache.maven.archiver" );
518         manifestEntries.put( "keyWithEmptyValue", null );
519         config.setManifestEntries( manifestEntries );
520 
521         ManifestSection manifestSection = new ManifestSection();
522         manifestSection.setName( "UserSection" );
523         manifestSection.addManifestEntry( "key", "value" );
524         List<ManifestSection> manifestSections = new ArrayList<>();
525         manifestSections.add( manifestSection );
526         config.setManifestSections( manifestSections );
527         config.getManifest().setMainClass( "org.apache.maven.Foo" );
528         archiver.createArchive( session, project, config );
529         assertThat( jarFile ).exists();
530 
531         final Manifest jarFileManifest = getJarFileManifest( jarFile );
532         Attributes manifest = jarFileManifest.getMainAttributes();
533 
534         // no version number
535         assertThat( manifest )
536                 .containsEntry( new Attributes.Name( "Created-By" ), "Maven Archiver" )
537                 .containsEntry( new Attributes.Name( "Build-Tool" ),
538                         session.getSystemProperties().get( "maven.build.version" ) )
539                 .containsEntry( new Attributes.Name( "Build-Jdk" ), String.format( "%s (%s)",
540                         System.getProperty( "java.version" ), System.getProperty( "java.vendor" ) ) )
541                 .containsEntry( new Attributes.Name( "Build-Os" ), String.format( "%s (%s; %s)",
542                         System.getProperty( "os.name" ), System.getProperty( "os.version" ),
543                         System.getProperty( "os.arch" ) ) )
544                 .containsEntry( Attributes.Name.SPECIFICATION_TITLE, "archiver test" )
545                 .containsEntry( Attributes.Name.SPECIFICATION_VERSION, "0.1" )
546                 .containsEntry( Attributes.Name.SPECIFICATION_VENDOR, "Apache" )
547                 .containsEntry( Attributes.Name.IMPLEMENTATION_TITLE, "archiver test" )
548                 .containsEntry( Attributes.Name.IMPLEMENTATION_VERSION, "0.1.1" )
549                 .containsEntry( Attributes.Name.IMPLEMENTATION_VENDOR, "Apache" )
550                 .containsEntry( Attributes.Name.MAIN_CLASS, "org.apache.maven.Foo" )
551                 .containsEntry( new Attributes.Name( "foo" ), "bar" )
552                 .containsEntry( new Attributes.Name( "first-name" ), "olivier" );
553 
554         assertThat( manifest.getValue( "Automatic-Module-Name" ) ).isEqualTo( "org.apache.maven.archiver" );
555 
556         assertThat( manifest ).containsEntry( new Attributes.Name( "Build-Jdk-Spec" ),
557                 System.getProperty( "java.specification.version" ) );
558 
559         assertThat( StringUtils.isEmpty( manifest.getValue( new Attributes.Name( "keyWithEmptyValue" ) ) ) ).isTrue();
560         assertThat( manifest ).containsKey( new Attributes.Name( "keyWithEmptyValue" ) );
561 
562         manifest = jarFileManifest.getAttributes( "UserSection" );
563 
564         assertThat( manifest ).containsEntry( new Attributes.Name( "key" ), "value" );
565     }
566 
567     @Test
568     public void testManifestWithInvalidAutomaticModuleNameThrowsOnCreateArchive()
569             throws Exception
570     {
571         File jarFile = new File( "target/test/dummy.jar" );
572         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
573 
574         MavenArchiver archiver = getMavenArchiver( jarArchiver );
575 
576         MavenSession session = getDummySession();
577         MavenProject project = getDummyProject();
578         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
579 
580         Map<String, String> manifestEntries = new HashMap<>();
581         manifestEntries.put( "Automatic-Module-Name", "123.in-valid.new.name" );
582         config.setManifestEntries( manifestEntries );
583 
584         try
585         {
586             archiver.createArchive( session, project, config );
587         }
588         catch ( ManifestException e )
589         {
590             assertThat( e.getMessage() ).isEqualTo( "Invalid automatic module name: '123.in-valid.new.name'" );
591         }
592     }
593 
594     /*
595      * Test to make sure that manifest sections are present in the manifest prior to the archive has been created.
596      */
597     @Test
598     public void testManifestSections()
599         throws Exception
600     {
601         MavenArchiver archiver = new MavenArchiver();
602 
603         MavenSession session = getDummySession();
604 
605         MavenProject project = getDummyProject();
606         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
607 
608         ManifestSection manifestSection = new ManifestSection();
609         manifestSection.setName( "SectionOne" );
610         manifestSection.addManifestEntry( "key", "value" );
611         List<ManifestSection> manifestSections = new ArrayList<>();
612         manifestSections.add( manifestSection );
613         config.setManifestSections( manifestSections );
614 
615         Manifest manifest = archiver.getManifest( session, project, config );
616 
617         Attributes section = manifest.getAttributes( "SectionOne" );
618         assertThat( section ).as( "The section is not present in the manifest as it should be." ).isNotNull();
619 
620         String attribute = section.getValue( "key" );
621         assertThat( attribute )
622                 .as( "The attribute we are looking for is not present in the section." ).isNotNull()
623                 .as( "The value of the attribute is wrong." ).isEqualTo( "value" );
624     }
625 
626     @Test
627     public void testDefaultClassPathValue()
628         throws Exception
629     {
630         MavenSession session = getDummySession();
631         MavenProject project = getDummyProject();
632         File jarFile = new File( "target/test/dummy.jar" );
633         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
634 
635         MavenArchiver archiver = getMavenArchiver( jarArchiver );
636 
637         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
638         config.setForced( true );
639         config.getManifest().setAddDefaultImplementationEntries( true );
640         config.getManifest().setAddDefaultSpecificationEntries( true );
641         config.getManifest().setMainClass( "org.apache.maven.Foo" );
642         config.getManifest().setAddClasspath( true );
643         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM );
644         config.getManifest().setCustomClasspathLayout( "${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}" );
645         archiver.createArchive( session, project, config );
646         assertThat( jarFile ).exists();
647         final Manifest manifest = getJarFileManifest( jarFile );
648         String classPath = manifest.getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
649         assertThat( classPath ).isNotNull();
650         assertThat( StringUtils.split( classPath, " " ) )
651                 .containsExactly( "dummy1-1.0.jar", "dummy2-1.5.jar", "dummy3-2.0.jar" );
652     }
653 
654     private void deleteAndAssertNotPresent( File jarFile )
655     {
656         jarFile.delete();
657         assertThat( jarFile ).doesNotExist();
658     }
659 
660     @Test
661     public void testDefaultClassPathValue_WithSnapshot()
662         throws Exception
663     {
664         MavenSession session = getDummySession();
665         MavenProject project = getDummyProjectWithSnapshot();
666         File jarFile = new File( "target/test/dummy.jar" );
667         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
668 
669         MavenArchiver archiver = getMavenArchiver( jarArchiver );
670 
671         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
672         config.setForced( true );
673         config.getManifest().setAddDefaultImplementationEntries( true );
674         config.getManifest().setAddDefaultSpecificationEntries( true );
675         config.getManifest().setMainClass( "org.apache.maven.Foo" );
676         config.getManifest().setAddClasspath( true );
677         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM );
678         config.getManifest().setCustomClasspathLayout( "${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}" );
679         archiver.createArchive( session, project, config );
680         assertThat( jarFile ).exists();
681 
682         final Manifest manifest = getJarFileManifest( jarFile );
683         String classPath = manifest.getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
684         assertThat( classPath ).isNotNull();
685         assertThat( StringUtils.split( classPath, " " ) )
686                 .containsExactly( "dummy1-1.1-20081022.112233-1.jar", "dummy2-1.5.jar", "dummy3-2.0.jar" );
687     }
688 
689     @Test
690     public void testMavenRepoClassPathValue()
691         throws Exception
692     {
693         MavenSession session = getDummySession();
694         MavenProject project = getDummyProject();
695         File jarFile = new File( "target/test/dummy.jar" );
696         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
697 
698         MavenArchiver archiver = getMavenArchiver( jarArchiver );
699 
700         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
701         config.setForced( true );
702         config.getManifest().setAddDefaultImplementationEntries( true );
703         config.getManifest().setAddDefaultSpecificationEntries( true );
704         config.getManifest().setMainClass( "org.apache.maven.Foo" );
705         config.getManifest().setAddClasspath( true );
706         config.getManifest().setUseUniqueVersions( true );
707         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_REPOSITORY );
708         archiver.createArchive( session, project, config );
709         assertThat( jarFile ).exists();
710         Manifest manifest = archiver.getManifest( session, project, config );
711         String[] classPathEntries =
712             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
713         assertThat( classPathEntries ).containsExactly(
714                 "org/apache/dummy/dummy1/1.0.1/dummy1-1.0.jar",
715                 "org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
716                 "org/apache/dummy/bar/dummy3/2.0/dummy3-2.0.jar" );
717 
718         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
719         assertThat( classPath ).isNotNull();
720         assertThat( StringUtils.split( classPath, " " ) ).containsExactly(
721                 "org/apache/dummy/dummy1/1.0.1/dummy1-1.0.jar",
722                 "org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
723                 "org/apache/dummy/bar/dummy3/2.0/dummy3-2.0.jar" );
724     }
725 
726     @Test
727     public void shouldCreateArchiveWithSimpleClassPathLayoutWhileSettingSimpleLayoutExplicit()
728         throws Exception
729     {
730         MavenSession session = getDummySession();
731         MavenProject project = getDummyProject();
732         File jarFile = new File( "target/test/dummy-explicit-simple.jar" );
733         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
734 
735         MavenArchiver archiver = getMavenArchiver( jarArchiver );
736 
737         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
738         config.setForced( true );
739         config.getManifest().setAddDefaultImplementationEntries( true );
740         config.getManifest().setAddDefaultSpecificationEntries( true );
741         config.getManifest().setMainClass( "org.apache.maven.Foo" );
742         config.getManifest().setAddClasspath( true );
743         config.getManifest().setClasspathPrefix( "lib" );
744         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_SIMPLE );
745 
746         archiver.createArchive( session, project, config );
747         assertThat( jarFile ).exists();
748         Manifest manifest = archiver.getManifest( session, project, config );
749         String[] classPathEntries =
750             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
751         assertThat( classPathEntries )
752                 .containsExactly( "lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0.jar" );
753 
754         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
755 
756         assertThat( classPath ).isNotNull();
757         assertThat( StringUtils.split( classPath, " " ) )
758                 .containsExactly( "lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0.jar" );
759     }
760 
761     @Test
762     public void shouldCreateArchiveCustomerLayoutSimple()
763         throws Exception
764     {
765         MavenSession session = getDummySession();
766         MavenProject project = getDummyProject();
767         File jarFile = new File( "target/test/dummy-custom-layout-simple.jar" );
768         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
769 
770         MavenArchiver archiver = getMavenArchiver( jarArchiver );
771 
772         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
773         config.setForced( true );
774         config.getManifest().setAddDefaultImplementationEntries( true );
775         config.getManifest().setAddDefaultSpecificationEntries( true );
776         config.getManifest().setMainClass( "org.apache.maven.Foo" );
777         config.getManifest().setAddClasspath( true );
778         config.getManifest().setClasspathPrefix( "lib" );
779         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM );
780         config.getManifest().setCustomClasspathLayout( MavenArchiver.SIMPLE_LAYOUT );
781 
782         archiver.createArchive( session, project, config );
783         assertThat( jarFile ).exists();
784         Manifest manifest = archiver.getManifest( session, project, config );
785         String[] classPathEntries =
786             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
787         assertThat( classPathEntries )
788                 .containsExactly( "lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0.jar" );
789 
790         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
791 
792         assertThat( classPath ).isNotNull();
793         assertThat( StringUtils.split( classPath, " " ) )
794                 .containsExactly( "lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0.jar" );
795     }
796 
797     @Test
798     public void shouldCreateArchiveCustomLayoutSimpleNonUnique()
799         throws Exception
800     {
801         MavenSession session = getDummySession();
802         MavenProject project = getDummyProject();
803         File jarFile = new File( "target/test/dummy-custom-layout-simple-non-unique.jar" );
804         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
805 
806         MavenArchiver archiver = getMavenArchiver( jarArchiver );
807 
808         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
809         config.setForced( true );
810         config.getManifest().setAddDefaultImplementationEntries( true );
811         config.getManifest().setAddDefaultSpecificationEntries( true );
812         config.getManifest().setMainClass( "org.apache.maven.Foo" );
813         config.getManifest().setAddClasspath( true );
814         config.getManifest().setClasspathPrefix( "lib" );
815         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM );
816         config.getManifest().setCustomClasspathLayout( MavenArchiver.SIMPLE_LAYOUT_NONUNIQUE );
817 
818         archiver.createArchive( session, project, config );
819         assertThat( jarFile ).exists();
820         Manifest manifest = archiver.getManifest( session, project, config );
821         String[] classPathEntries =
822             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
823         assertThat( classPathEntries )
824                 .containsExactly( "lib/dummy1-1.0.1.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0.jar" );
825 
826         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
827 
828         assertThat( classPath ).isNotNull();
829         assertThat( StringUtils.split( classPath, " " ) )
830                 .containsExactly( "lib/dummy1-1.0.1.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0.jar" );
831     }
832 
833     @Test
834     public void shouldCreateArchiveCustomLayoutRepository()
835         throws Exception
836     {
837         MavenSession session = getDummySession();
838         MavenProject project = getDummyProject();
839         File jarFile = new File( "target/test/dummy-custom-layout-repo.jar" );
840         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
841 
842         MavenArchiver archiver = getMavenArchiver( jarArchiver );
843 
844         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
845         config.setForced( true );
846         config.getManifest().setAddDefaultImplementationEntries( true );
847         config.getManifest().setAddDefaultSpecificationEntries( true );
848         config.getManifest().setMainClass( "org.apache.maven.Foo" );
849         config.getManifest().setAddClasspath( true );
850         config.getManifest().setClasspathPrefix( "lib" );
851         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM );
852         config.getManifest().setCustomClasspathLayout( MavenArchiver.REPOSITORY_LAYOUT );
853 
854         archiver.createArchive( session, project, config );
855         assertThat( jarFile ).exists();
856         Manifest manifest = archiver.getManifest( session, project, config );
857         String[] classPathEntries =
858             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
859         assertThat( classPathEntries ).containsExactly(
860                 "lib/org/apache/dummy/dummy1/1.0.1/dummy1-1.0.jar",
861                 "lib/org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
862                 "lib/org/apache/dummy/bar/dummy3/2.0/dummy3-2.0.jar" );
863 
864         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
865 
866         assertThat( classPath ).isNotNull();
867         assertThat( StringUtils.split( classPath, " " ) ).containsExactly(
868                 "lib/org/apache/dummy/dummy1/1.0.1/dummy1-1.0.jar",
869                 "lib/org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
870                 "lib/org/apache/dummy/bar/dummy3/2.0/dummy3-2.0.jar" );
871     }
872 
873     @Test
874     public void shouldCreateArchiveCustomLayoutRepositoryNonUnique()
875         throws Exception
876     {
877         MavenSession session = getDummySession();
878         MavenProject project = getDummyProject();
879         File jarFile = new File( "target/test/dummy-custom-layout-repo-non-unique.jar" );
880         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
881 
882         MavenArchiver archiver = getMavenArchiver( jarArchiver );
883 
884         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
885         config.setForced( true );
886         config.getManifest().setAddDefaultImplementationEntries( true );
887         config.getManifest().setAddDefaultSpecificationEntries( true );
888         config.getManifest().setMainClass( "org.apache.maven.Foo" );
889         config.getManifest().setAddClasspath( true );
890         config.getManifest().setClasspathPrefix( "lib" );
891         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM );
892         config.getManifest().setCustomClasspathLayout( MavenArchiver.REPOSITORY_LAYOUT_NONUNIQUE );
893 
894         archiver.createArchive( session, project, config );
895         assertThat( jarFile ).exists();
896         Manifest manifest = archiver.getManifest( session, project, config );
897         String[] classPathEntries =
898             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
899         assertThat( classPathEntries ).containsExactly(
900                 "lib/org/apache/dummy/dummy1/1.0.1/dummy1-1.0.1.jar",
901                 "lib/org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
902                 "lib/org/apache/dummy/bar/dummy3/2.0/dummy3-2.0.jar" );
903 
904         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
905 
906         assertThat( classPath ).isNotNull();
907         assertThat( StringUtils.split( classPath, " " ) ).containsExactly(
908                 "lib/org/apache/dummy/dummy1/1.0.1/dummy1-1.0.1.jar",
909                 "lib/org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
910                 "lib/org/apache/dummy/bar/dummy3/2.0/dummy3-2.0.jar" );
911     }
912 
913     @Test
914     public void shouldCreateArchiveWithSimpleClassPathLayoutUsingDefaults()
915         throws Exception
916     {
917         MavenSession session = getDummySession();
918         MavenProject project = getDummyProject();
919         File jarFile = new File( "target/test/dummy-defaults.jar" );
920         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
921 
922         MavenArchiver archiver = getMavenArchiver( jarArchiver );
923 
924         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
925         config.setForced( true );
926         config.getManifest().setAddDefaultImplementationEntries( true );
927         config.getManifest().setAddDefaultSpecificationEntries( true );
928         config.getManifest().setMainClass( "org.apache.maven.Foo" );
929         config.getManifest().setAddClasspath( true );
930         config.getManifest().setClasspathPrefix( "lib" );
931 
932         archiver.createArchive( session, project, config );
933         assertThat( jarFile ).exists();
934         Manifest manifest = archiver.getManifest( session, project, config );
935         String[] classPathEntries =
936             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
937         assertThat( classPathEntries )
938                 .containsExactly( "lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0.jar" );
939 
940         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
941         assertThat( classPath ).isNotNull();
942         assertThat( StringUtils.split( classPath, " " ) )
943                 .containsExactly( "lib/dummy1-1.0.jar", "lib/dummy2-1.5.jar", "lib/dummy3-2.0.jar" );
944     }
945 
946     @Test
947     public void testMavenRepoClassPathValue_WithSnapshot()
948         throws Exception
949     {
950         MavenSession session = getDummySession();
951         MavenProject project = getDummyProjectWithSnapshot();
952         File jarFile = new File( "target/test/dummy.jar" );
953         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
954 
955         MavenArchiver archiver = getMavenArchiver( jarArchiver );
956 
957         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
958         config.setForced( true );
959         config.getManifest().setAddDefaultImplementationEntries( true );
960         config.getManifest().setAddDefaultSpecificationEntries( true );
961         config.getManifest().setMainClass( "org.apache.maven.Foo" );
962         config.getManifest().setAddClasspath( true );
963         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_REPOSITORY );
964         archiver.createArchive( session, project, config );
965         assertThat( jarFile ).exists();
966 
967         Manifest manifest = archiver.getManifest( session, project, config );
968         String[] classPathEntries =
969             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
970         assertThat( classPathEntries ).containsExactly(
971                 "org/apache/dummy/dummy1/1.1-SNAPSHOT/dummy1-1.1-20081022.112233-1.jar",
972                 "org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
973                 "org/apache/dummy/bar/dummy3/2.0/dummy3-2.0.jar" );
974 
975         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
976         assertThat( classPath ).isNotNull();
977         assertThat( StringUtils.split( classPath, " " ) ).containsExactly(
978                 "org/apache/dummy/dummy1/1.1-SNAPSHOT/dummy1-1.1-20081022.112233-1.jar",
979                 "org/apache/dummy/foo/dummy2/1.5/dummy2-1.5.jar",
980                 "org/apache/dummy/bar/dummy3/2.0/dummy3-2.0.jar" );
981     }
982 
983     @Test
984     public void testCustomClassPathValue()
985         throws Exception
986     {
987         MavenSession session = getDummySession();
988         MavenProject project = getDummyProject();
989         File jarFile = new File( "target/test/dummy.jar" );
990         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
991 
992         MavenArchiver archiver = getMavenArchiver( jarArchiver );
993 
994         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
995         config.setForced( true );
996         config.getManifest().setAddDefaultImplementationEntries( true );
997         config.getManifest().setAddDefaultSpecificationEntries( true );
998         config.getManifest().setMainClass( "org.apache.maven.Foo" );
999         config.getManifest().setAddClasspath( true );
1000         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM );
1001         config.getManifest().setCustomClasspathLayout( "${artifact.groupIdPath}/${artifact.artifactId}/${artifact.version}/TEST-${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}" );
1002         archiver.createArchive( session, project, config );
1003         assertThat( jarFile ).exists();
1004         Manifest manifest = archiver.getManifest( session, project, config );
1005         String[] classPathEntries =
1006             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
1007         assertThat( classPathEntries ).containsExactly(
1008                 "org/apache/dummy/dummy1/1.0/TEST-dummy1-1.0.jar",
1009                 "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1010                 "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0.jar" );
1011 
1012         final Manifest manifest1 = getJarFileManifest( jarFile );
1013         String classPath = manifest1.getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
1014         assertThat( classPath ).isNotNull();
1015         assertThat( StringUtils.split( classPath, " " ) ).containsExactly(
1016                 "org/apache/dummy/dummy1/1.0/TEST-dummy1-1.0.jar",
1017                 "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1018                 "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0.jar" );
1019     }
1020 
1021     @Test
1022     public void testCustomClassPathValue_WithSnapshotResolvedVersion()
1023         throws Exception
1024     {
1025         MavenSession session = getDummySession();
1026         MavenProject project = getDummyProjectWithSnapshot();
1027         File jarFile = new File( "target/test/dummy.jar" );
1028         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
1029         MavenArchiver archiver = getMavenArchiver( jarArchiver );
1030 
1031         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
1032         config.setForced( true );
1033         config.getManifest().setAddDefaultImplementationEntries( true );
1034         config.getManifest().setAddDefaultSpecificationEntries( true );
1035         config.getManifest().setMainClass( "org.apache.maven.Foo" );
1036         config.getManifest().setAddClasspath( true );
1037         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM );
1038         config.getManifest().setCustomClasspathLayout( "${artifact.groupIdPath}/${artifact.artifactId}/${artifact.baseVersion}/TEST-${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}" );
1039         archiver.createArchive( session, project, config );
1040         assertThat( jarFile ).exists();
1041 
1042         Manifest manifest = archiver.getManifest( session, project, config );
1043         String[] classPathEntries =
1044             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
1045         assertThat( classPathEntries ).containsExactly(
1046                 "org/apache/dummy/dummy1/1.1-SNAPSHOT/TEST-dummy1-1.1-20081022.112233-1.jar",
1047                 "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1048                 "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0.jar" );
1049 
1050         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
1051         assertThat( classPath ).isNotNull();
1052         assertThat( StringUtils.split( classPath, " " ) ).containsExactly(
1053                 "org/apache/dummy/dummy1/1.1-SNAPSHOT/TEST-dummy1-1.1-20081022.112233-1.jar",
1054                 "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1055                 "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0.jar" );
1056     }
1057 
1058     @Test
1059     public void testCustomClassPathValue_WithSnapshotForcingBaseVersion()
1060         throws Exception
1061     {
1062         MavenSession session = getDummySession();
1063         MavenProject project = getDummyProjectWithSnapshot();
1064         File jarFile = new File( "target/test/dummy.jar" );
1065         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
1066 
1067         MavenArchiver archiver = getMavenArchiver( jarArchiver );
1068 
1069         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
1070         config.setForced( true );
1071         config.getManifest().setAddDefaultImplementationEntries( true );
1072         config.getManifest().setAddDefaultSpecificationEntries( true );
1073         config.getManifest().setMainClass( "org.apache.maven.Foo" );
1074         config.getManifest().setAddClasspath( true );
1075         config.getManifest().setClasspathLayoutType( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM );
1076         config.getManifest().setCustomClasspathLayout( "${artifact.groupIdPath}/${artifact.artifactId}/${artifact.baseVersion}/TEST-${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}" );
1077         archiver.createArchive( session, project, config );
1078         assertThat( jarFile ).exists();
1079         Manifest manifest = archiver.getManifest( session, project, config );
1080         String[] classPathEntries =
1081             StringUtils.split( new String( manifest.getMainAttributes().getValue( "Class-Path" ).getBytes() ), " " );
1082         assertThat( classPathEntries[0] ).isEqualTo(
1083                 "org/apache/dummy/dummy1/1.1-SNAPSHOT/TEST-dummy1-1.1-SNAPSHOT.jar" );
1084         assertThat( classPathEntries[1] ).isEqualTo( "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar" );
1085         assertThat( classPathEntries[2] ).isEqualTo( "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0.jar" );
1086 
1087         String classPath = getJarFileManifest( jarFile ).getMainAttributes().getValue( Attributes.Name.CLASS_PATH );
1088         assertThat( classPath ).isNotNull();
1089         assertThat( StringUtils.split( classPath, " " ) ).containsExactly(
1090                 "org/apache/dummy/dummy1/1.1-SNAPSHOT/TEST-dummy1-1.1-SNAPSHOT.jar",
1091                 "org/apache/dummy/foo/dummy2/1.5/TEST-dummy2-1.5.jar",
1092                 "org/apache/dummy/bar/dummy3/2.0/TEST-dummy3-2.0.jar" );
1093     }
1094 
1095     @Test
1096     public void testDefaultPomProperties()
1097         throws Exception
1098     {
1099         MavenSession session = getDummySession();
1100         MavenProject project = getDummyProject();
1101         File jarFile = new File( "target/test/dummy.jar" );
1102         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
1103 
1104         MavenArchiver archiver = getMavenArchiver( jarArchiver );
1105 
1106         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
1107         config.setForced( true );
1108         archiver.createArchive( session, project, config );
1109         assertThat( jarFile ).exists();
1110 
1111         final String groupId = project.getGroupId();
1112         final String artifactId = project.getArtifactId();
1113         final String version = project.getVersion();
1114 
1115         JarFile virtJarFile = new JarFile( jarFile );
1116         ZipEntry pomPropertiesEntry =
1117             virtJarFile.getEntry( "META-INF/maven/" + groupId + "/" + artifactId + "/pom.properties" );
1118         assertThat( pomPropertiesEntry ).isNotNull();
1119 
1120         try ( InputStream is = virtJarFile.getInputStream( pomPropertiesEntry ) )
1121         {
1122             Properties p = new Properties();
1123             p.load( is );
1124 
1125             assertThat( p.getProperty( "groupId" ) ).isEqualTo( groupId );
1126             assertThat( p.getProperty( "artifactId" ) ).isEqualTo( artifactId );
1127             assertThat( p.getProperty( "version" ) ).isEqualTo( version );
1128         }
1129         virtJarFile.close();
1130     }
1131 
1132     @Test
1133     public void testCustomPomProperties()
1134         throws Exception
1135     {
1136         MavenSession session = getDummySession();
1137         MavenProject project = getDummyProject();
1138         File jarFile = new File( "target/test/dummy.jar" );
1139         JarArchiver jarArchiver = getCleanJarArchiver( jarFile );
1140 
1141         MavenArchiver archiver = getMavenArchiver( jarArchiver );
1142 
1143         File customPomPropertiesFile = new File( "src/test/resources/custom-pom.properties" );
1144         MavenArchiveConfiguration config = new MavenArchiveConfiguration();
1145         config.setForced( true );
1146         config.setPomPropertiesFile( customPomPropertiesFile );
1147         archiver.createArchive( session, project, config );
1148         assertThat( jarFile ).exists();
1149 
1150         final String groupId = project.getGroupId();
1151         final String artifactId = project.getArtifactId();
1152         final String version = project.getVersion();
1153 
1154         try ( JarFile virtJarFile = new JarFile( jarFile ) )
1155         {
1156             ZipEntry pomPropertiesEntry = virtJarFile
1157                     .getEntry( "META-INF/maven/" + groupId + "/" + artifactId + "/pom.properties" );
1158             assertThat( pomPropertiesEntry ).isNotNull();
1159 
1160             try ( InputStream is = virtJarFile.getInputStream( pomPropertiesEntry ) )
1161             {
1162                 Properties p = new Properties();
1163                 p.load( is );
1164 
1165                 assertThat( p.getProperty( "groupId" ) ).isEqualTo( groupId );
1166                 assertThat( p.getProperty( "artifactId" ) ).isEqualTo( artifactId );
1167                 assertThat( p.getProperty( "version" ) ).isEqualTo( version );
1168                 assertThat( p.getProperty( "build.revision" ) ).isEqualTo( "1337" );
1169                 assertThat( p.getProperty( "build.branch" ) ).isEqualTo( "tags/0.1.1" );
1170             }
1171         }
1172     }
1173 
1174     private JarArchiver getCleanJarArchiver( File jarFile )
1175     {
1176         deleteAndAssertNotPresent( jarFile );
1177         JarArchiver jarArchiver = new JarArchiver();
1178         jarArchiver.setDestFile( jarFile );
1179         return jarArchiver;
1180     }
1181 
1182     // ----------------------------------------
1183     // common methods for testing
1184     // ----------------------------------------
1185 
1186     private MavenProject getDummyProject()
1187     {
1188         MavenProject project = getMavenProject();
1189         File pomFile = new File( "src/test/resources/pom.xml" );
1190         pomFile.setLastModified( System.currentTimeMillis() - 60000L );
1191         project.setFile( pomFile );
1192         Build build = new Build();
1193         build.setDirectory( "target" );
1194         build.setOutputDirectory( "target" );
1195         project.setBuild( build );
1196         project.setName( "archiver test" );
1197         project.setUrl( "https://maven.apache.org" );
1198         Organization organization = new Organization();
1199         organization.setName( "Apache" );
1200         project.setOrganization( organization );
1201         MockArtifact artifact = new MockArtifact();
1202         artifact.setGroupId( "org.apache.dummy" );
1203         artifact.setArtifactId( "dummy" );
1204         artifact.setVersion( "0.1.1" );
1205         artifact.setBaseVersion( "0.1.2" );
1206         artifact.setType( "jar" );
1207         artifact.setArtifactHandler( new DefaultArtifactHandler( "jar" ) );
1208         project.setArtifact( artifact );
1209 
1210         Set<Artifact> artifacts = getArtifacts( getMockArtifact1Release(), getMockArtifact2(), getMockArtifact3() );
1211         project.setArtifacts( artifacts );
1212 
1213         return project;
1214     }
1215 
1216     private MavenProject getMavenProject()
1217     {
1218         Model model = new Model();
1219         model.setGroupId( "org.apache.dummy" );
1220         model.setArtifactId( "dummy" );
1221         model.setVersion( "0.1.1" );
1222 
1223         final MavenProject project = new MavenProject( model );
1224         project.setExtensionArtifacts( Collections.<Artifact>emptySet() );
1225         project.setRemoteArtifactRepositories( Collections.<ArtifactRepository>emptyList() );
1226         project.setPluginArtifactRepositories( Collections.<ArtifactRepository>emptyList() );
1227         return project;
1228     }
1229 
1230     private MockArtifact getMockArtifact3()
1231     {
1232         MockArtifact artifact3 = new MockArtifact();
1233         artifact3.setGroupId( "org.apache.dummy.bar" );
1234         artifact3.setArtifactId( "dummy3" );
1235         artifact3.setVersion( "2.0" );
1236         artifact3.setScope( "runtime" );
1237         artifact3.setType( "jar" );
1238         artifact3.setFile( getClasspathFile( artifact3.getArtifactId() + "-" + artifact3.getVersion() + ".jar" ) );
1239         return artifact3;
1240     }
1241 
1242     private MavenProject getDummyProjectWithSnapshot()
1243     {
1244         MavenProject project = getMavenProject();
1245         File pomFile = new File( "src/test/resources/pom.xml" );
1246         pomFile.setLastModified( System.currentTimeMillis() - 60000L );
1247         project.setFile( pomFile );
1248         Build build = new Build();
1249         build.setDirectory( "target" );
1250         build.setOutputDirectory( "target" );
1251         project.setBuild( build );
1252         project.setName( "archiver test" );
1253         Organization organization = new Organization();
1254         organization.setName( "Apache" );
1255         project.setOrganization( organization );
1256 
1257         MockArtifact artifact = new MockArtifact();
1258         artifact.setGroupId( "org.apache.dummy" );
1259         artifact.setArtifactId( "dummy" );
1260         artifact.setVersion( "0.1.1" );
1261         artifact.setBaseVersion( "0.1.1" );
1262         artifact.setType( "jar" );
1263         artifact.setArtifactHandler( new DefaultArtifactHandler( "jar" ) );
1264         project.setArtifact( artifact );
1265 
1266         Set<Artifact> artifacts = getArtifacts( getMockArtifact1(), getMockArtifact2(), getMockArtifact3() );
1267 
1268         project.setArtifacts( artifacts );
1269 
1270         return project;
1271     }
1272 
1273     private ArtifactHandler getMockArtifactHandler()
1274     {
1275         return new ArtifactHandler()
1276         {
1277 
1278             public String getClassifier()
1279             {
1280                 return null;
1281             }
1282 
1283             public String getDirectory()
1284             {
1285                 return null;
1286             }
1287 
1288             public String getExtension()
1289             {
1290                 return "jar";
1291             }
1292 
1293             public String getLanguage()
1294             {
1295                 return null;
1296             }
1297 
1298             public String getPackaging()
1299             {
1300                 return null;
1301             }
1302 
1303             public boolean isAddedToClasspath()
1304             {
1305                 return true;
1306             }
1307 
1308             public boolean isIncludesDependencies()
1309             {
1310                 return false;
1311             }
1312 
1313         };
1314     }
1315 
1316     private MockArtifact getMockArtifact2()
1317     {
1318         MockArtifact artifact2 = new MockArtifact();
1319         artifact2.setGroupId( "org.apache.dummy.foo" );
1320         artifact2.setArtifactId( "dummy2" );
1321         artifact2.setVersion( "1.5" );
1322         artifact2.setType( "jar" );
1323         artifact2.setScope( "runtime" );
1324         artifact2.setFile( getClasspathFile( artifact2.getArtifactId() + "-" + artifact2.getVersion() + ".jar" ) );
1325         return artifact2;
1326     }
1327 
1328     private MockArtifact getArtifactWithDot()
1329     {
1330         MockArtifact artifact2 = new MockArtifact();
1331         artifact2.setGroupId( "org.apache.dummy.foo" );
1332         artifact2.setArtifactId( "dummy.dot" );
1333         artifact2.setVersion( "1.5" );
1334         artifact2.setType( "jar" );
1335         artifact2.setScope( "runtime" );
1336         artifact2.setFile( getClasspathFile( artifact2.getArtifactId() + "-" + artifact2.getVersion() + ".jar" ) );
1337         return artifact2;
1338     }
1339 
1340     private MockArtifact getMockArtifact1()
1341     {
1342         MockArtifact artifact1 = new MockArtifact();
1343         artifact1.setGroupId( "org.apache.dummy" );
1344         artifact1.setArtifactId( "dummy1" );
1345         artifact1.setSnapshotVersion( "1.1-20081022.112233-1", "1.1-SNAPSHOT" );
1346         artifact1.setType( "jar" );
1347         artifact1.setScope( "runtime" );
1348         artifact1.setFile( getClasspathFile( artifact1.getArtifactId() + "-" + artifact1.getVersion() + ".jar" ) );
1349         return artifact1;
1350     }
1351 
1352     private MockArtifact getMockArtifact1Release()
1353     {
1354         MockArtifact artifact1 = new MockArtifact();
1355         artifact1.setGroupId( "org.apache.dummy" );
1356         artifact1.setArtifactId( "dummy1" );
1357         artifact1.setVersion( "1.0" );
1358         artifact1.setBaseVersion( "1.0.1" );
1359         artifact1.setType( "jar" );
1360         artifact1.setScope( "runtime" );
1361         artifact1.setFile( getClasspathFile( artifact1.getArtifactId() + "-" + artifact1.getVersion() + ".jar" ) );
1362         return artifact1;
1363     }
1364 
1365     private File getClasspathFile( String file )
1366     {
1367         URL resource = Thread.currentThread().getContextClassLoader().getResource( file );
1368         if ( resource == null )
1369         {
1370             fail( "Cannot retrieve java.net.URL for file: " + file + " on the current test classpath." );
1371         }
1372 
1373         URI uri = new File( resource.getPath() ).toURI().normalize();
1374 
1375         return new File( uri.getPath().replaceAll( "%20", " " ) );
1376     }
1377 
1378     private MavenSession getDummySession()
1379     {
1380         Properties systemProperties = new Properties();
1381         systemProperties.put( "maven.version", "3.1.1" );
1382         systemProperties.put( "maven.build.version",
1383             "Apache Maven 3.1.1 (0728685237757ffbf44136acec0402957f723d9a; 2013-09-17 17:22:22+0200)" );
1384 
1385         return getDummySession( systemProperties );
1386     }
1387 
1388     private MavenSession getDummySessionWithoutMavenVersion()
1389     {
1390         return getDummySession( new Properties() );
1391     }
1392 
1393     private MavenSession getDummySession( Properties systemProperties )
1394     {
1395         File settings = null;
1396         List<String> goals = null;
1397         Date startTime = new Date();
1398 
1399         MavenExecutionRequest request = new DefaultMavenExecutionRequest();
1400         request.setSystemProperties( systemProperties );
1401         request.setGoals( goals );
1402         request.setStartTime( startTime );
1403         request.setUserSettingsFile( settings );
1404 
1405         MavenExecutionResult result = new DefaultMavenExecutionResult();
1406 
1407         RepositorySystemSession rss = new DefaultRepositorySystemSession();
1408 
1409         return new MavenSession( null, rss, request, result );
1410 
1411     }
1412 
1413     private Set<Artifact> getArtifacts( Artifact... artifacts )
1414     {
1415         final ArtifactHandler mockArtifactHandler = getMockArtifactHandler();
1416         Set<Artifact> result = new TreeSet<>( new ArtifactComparator() );
1417         for ( Artifact artifact : artifacts )
1418         {
1419             artifact.setArtifactHandler( mockArtifactHandler );
1420             result.add( artifact );
1421         }
1422         return result;
1423     }
1424 
1425     public Manifest getJarFileManifest( File jarFile )
1426         throws IOException
1427     {
1428         try ( JarFile jar = new JarFile( jarFile ) )
1429         {
1430             return jar.getManifest();
1431         }
1432     }
1433 
1434     @Test
1435     public void testParseOutputTimestamp()
1436     {
1437         MavenArchiver archiver = new MavenArchiver();
1438 
1439         assertThat( archiver.parseOutputTimestamp( null ) ).isNull();
1440         assertThat( archiver.parseOutputTimestamp( "" ) ).isNull();
1441         assertThat( archiver.parseOutputTimestamp( "." ) ).isNull();
1442         assertThat( archiver.parseOutputTimestamp( " " ) ).isNull();
1443         assertThat( archiver.parseOutputTimestamp( "_" ) ).isNull();
1444 
1445         assertThat( archiver.parseOutputTimestamp( "1570300662" ).getTime() ).isEqualTo( 1570300662000L );
1446 
1447         assertThat( archiver.parseOutputTimestamp( "2019-10-05T18:37:42Z" ).getTime() ).isEqualTo( 1570300662000L );
1448         assertThat( archiver.parseOutputTimestamp( "2019-10-05T20:37:42+02:00" ).getTime() ).isEqualTo(
1449                 1570300662000L );
1450         assertThat( archiver.parseOutputTimestamp( "2019-10-05T16:37:42-02:00" ).getTime() ).isEqualTo(
1451                 1570300662000L );
1452 
1453         // These must result in IAE because we expect extended ISO format only (ie with - separator for date and
1454         // : separator for timezone), hence the XXX SimpleDateFormat for tz offset
1455         // X SimpleDateFormat accepts timezone without separator while date has separator, which is a mix between
1456         // basic (no separators, both for date and timezone) and extended (separator for both)
1457         try
1458         {
1459             archiver.parseOutputTimestamp( "2019-10-05T20:37:42+0200" );
1460             fail();
1461         }
1462         catch ( IllegalArgumentException ignored )
1463         {
1464         }
1465         try
1466         {
1467             archiver.parseOutputTimestamp( "2019-10-05T20:37:42-0200" );
1468             fail();
1469         }
1470         catch ( IllegalArgumentException ignored )
1471         {
1472         }
1473 
1474         // These unfortunately fail although the input is valid according to ISO 8601
1475         // SDF does not allow strict telescoping parsing w/o permitting invalid input as depicted above.
1476         // One has to use the new Java Time API for this.
1477         try
1478         {
1479             archiver.parseOutputTimestamp( "2019-10-05T20:37:42+02" );
1480             fail();
1481         }
1482         catch ( IllegalArgumentException ignored )
1483         {
1484         }
1485         try
1486         {
1487             archiver.parseOutputTimestamp( "2019-10-05T20:37:42-02" );
1488             fail();
1489         }
1490         catch ( IllegalArgumentException ignored )
1491         {
1492         }
1493     }
1494 }