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