1 package org.apache.maven.archiver;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.Artifact;
23 import org.apache.maven.artifact.DependencyResolutionRequiredException;
24 import org.apache.maven.artifact.versioning.ArtifactVersion;
25 import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
26 import org.apache.maven.execution.MavenSession;
27 import org.apache.maven.project.MavenProject;
28 import org.codehaus.plexus.archiver.jar.JarArchiver;
29 import org.codehaus.plexus.archiver.jar.Manifest;
30 import org.codehaus.plexus.archiver.jar.ManifestException;
31 import org.codehaus.plexus.interpolation.InterpolationException;
32 import org.codehaus.plexus.interpolation.Interpolator;
33 import org.codehaus.plexus.interpolation.PrefixAwareRecursionInterceptor;
34 import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
35 import org.codehaus.plexus.interpolation.PrefixedPropertiesValueSource;
36 import org.codehaus.plexus.interpolation.RecursionInterceptor;
37 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
38 import org.codehaus.plexus.interpolation.ValueSource;
39 import org.apache.maven.shared.utils.PropertyUtils;
40 import org.apache.maven.shared.utils.StringUtils;
41
42 import javax.lang.model.SourceVersion;
43 import java.io.File;
44 import java.io.IOException;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Properties;
50 import java.util.Set;
51 import java.util.jar.Attributes;
52
53
54
55
56
57
58 public class MavenArchiver
59 {
60
61 private static final String CREATED_BY = "Maven Archiver";
62
63
64
65
66 public static final String SIMPLE_LAYOUT =
67 "${artifact.artifactId}-${artifact.version}${dashClassifier?}.${artifact.extension}";
68
69
70
71
72 public static final String REPOSITORY_LAYOUT =
73 "${artifact.groupIdPath}/${artifact.artifactId}/" + "${artifact.baseVersion}/${artifact.artifactId}-"
74 + "${artifact.version}${dashClassifier?}.${artifact.extension}";
75
76
77
78
79 public static final String SIMPLE_LAYOUT_NONUNIQUE =
80 "${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}";
81
82
83
84
85 public static final String REPOSITORY_LAYOUT_NONUNIQUE =
86 "${artifact.groupIdPath}/${artifact.artifactId}/" + "${artifact.baseVersion}/${artifact.artifactId}-"
87 + "${artifact.baseVersion}${dashClassifier?}.${artifact.extension}";
88
89 private static final List<String> ARTIFACT_EXPRESSION_PREFIXES;
90
91 static
92 {
93 List<String> artifactExpressionPrefixes = new ArrayList<String>();
94 artifactExpressionPrefixes.add( "artifact." );
95
96 ARTIFACT_EXPRESSION_PREFIXES = artifactExpressionPrefixes;
97 }
98
99 static boolean isValidModuleName( String name )
100 {
101 return SourceVersion.isName( name );
102 }
103
104 private JarArchiver archiver;
105
106 private File archiveFile;
107
108
109
110
111
112
113
114
115
116 public Manifest getManifest( MavenSession session, MavenProject project, MavenArchiveConfiguration config )
117 throws ManifestException, DependencyResolutionRequiredException
118 {
119 boolean hasManifestEntries = !config.isManifestEntriesEmpty();
120 Map<String, String> entries =
121 hasManifestEntries ? config.getManifestEntries() : Collections.<String, String>emptyMap();
122
123 Manifest manifest = getManifest( session, project, config.getManifest(), entries );
124
125
126 if ( hasManifestEntries )
127 {
128
129 for ( Map.Entry<String, String> entry : entries.entrySet() )
130 {
131 String key = entry.getKey();
132 String value = entry.getValue();
133 Manifest.Attribute attr = manifest.getMainSection().getAttribute( key );
134 if ( key.equals( Attributes.Name.CLASS_PATH.toString() ) && attr != null )
135 {
136
137
138
139 attr.setValue( value + " " + attr.getValue() );
140 }
141 else
142 {
143 addManifestAttribute( manifest, key, value );
144 }
145 }
146 }
147
148
149 if ( !config.isManifestSectionsEmpty() )
150 {
151 for ( ManifestSection section : config.getManifestSections() )
152 {
153 Manifest.Section theSection = new Manifest.Section();
154 theSection.setName( section.getName() );
155
156 if ( !section.isManifestEntriesEmpty() )
157 {
158 Map<String, String> sectionEntries = section.getManifestEntries();
159
160 for ( Map.Entry<String, String> entry : sectionEntries.entrySet() )
161 {
162 String key = entry.getKey();
163 String value = entry.getValue();
164 Manifest.Attribute attr = new Manifest.Attribute( key, value );
165 theSection.addConfiguredAttribute( attr );
166 }
167 }
168
169 manifest.addConfiguredSection( theSection );
170 }
171 }
172
173 return manifest;
174 }
175
176
177
178
179
180
181
182
183
184
185
186 public Manifest getManifest( MavenProject project, ManifestConfiguration config )
187 throws ManifestException, DependencyResolutionRequiredException
188 {
189 return getManifest( null, project, config, Collections.<String, String>emptyMap() );
190 }
191
192
193
194
195
196
197
198
199
200 public Manifest getManifest( MavenSession mavenSession, MavenProject project, ManifestConfiguration config )
201 throws ManifestException, DependencyResolutionRequiredException
202 {
203 return getManifest( mavenSession, project, config, Collections.<String, String>emptyMap() );
204 }
205
206 private void addManifestAttribute( Manifest manifest, Map<String, String> map, String key, String value )
207 throws ManifestException
208 {
209 if ( map.containsKey( key ) )
210 {
211 return;
212 }
213 addManifestAttribute( manifest, key, value );
214 }
215
216 private void addManifestAttribute( Manifest manifest, String key, String value )
217 throws ManifestException
218 {
219 if ( !StringUtils.isEmpty( value ) )
220 {
221 Manifest.Attribute attr = new Manifest.Attribute( key, value );
222 manifest.addConfiguredAttribute( attr );
223 }
224 else
225 {
226
227
228 Manifest.Attribute attr = new Manifest.Attribute( key, "" );
229 manifest.addConfiguredAttribute( attr );
230 }
231 }
232
233
234
235
236
237
238
239
240
241
242 protected Manifest getManifest( MavenSession session, MavenProject project, ManifestConfiguration config,
243 Map<String, String> entries )
244 throws ManifestException, DependencyResolutionRequiredException
245 {
246
247
248 Manifest m = new Manifest();
249
250 if ( config.isAddDefaultEntries() )
251 {
252 handleDefaultEntries( m, entries );
253 }
254
255
256 if ( config.isAddBuildEnvironmentEntries() )
257 {
258 handleBuildEnvironmentEntries( session, m, entries );
259 }
260
261 if ( config.isAddClasspath() )
262 {
263 StringBuilder classpath = new StringBuilder();
264
265 List<String> artifacts = project.getRuntimeClasspathElements();
266 String classpathPrefix = config.getClasspathPrefix();
267 String layoutType = config.getClasspathLayoutType();
268 String layout = config.getCustomClasspathLayout();
269
270 Interpolator interpolator = new StringSearchInterpolator();
271
272 for ( String artifactFile : artifacts )
273 {
274 File f = new File( artifactFile );
275 if ( f.getAbsoluteFile().isFile() )
276 {
277 Artifact artifact = findArtifactWithFile( project.getArtifacts(), f );
278
279 if ( classpath.length() > 0 )
280 {
281 classpath.append( " " );
282 }
283 classpath.append( classpathPrefix );
284
285
286
287 if ( artifact == null || layoutType == null )
288 {
289 classpath.append( f.getName() );
290 }
291 else
292 {
293 List<ValueSource> valueSources = new ArrayList<ValueSource>();
294
295 handleExtraExpression( artifact, valueSources );
296
297 for ( ValueSource vs : valueSources )
298 {
299 interpolator.addValueSource( vs );
300 }
301
302 RecursionInterceptor recursionInterceptor =
303 new PrefixAwareRecursionInterceptor( ARTIFACT_EXPRESSION_PREFIXES );
304
305 try
306 {
307 if ( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_SIMPLE.equals( layoutType ) )
308 {
309 if ( config.isUseUniqueVersions() )
310 {
311 classpath.append( interpolator.interpolate( SIMPLE_LAYOUT, recursionInterceptor ) );
312 }
313 else
314 {
315 classpath.append( interpolator.interpolate( SIMPLE_LAYOUT_NONUNIQUE,
316 recursionInterceptor ) );
317 }
318 }
319 else if ( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_REPOSITORY.equals( layoutType ) )
320 {
321
322
323 if ( config.isUseUniqueVersions() )
324 {
325 classpath.append( interpolator.interpolate( REPOSITORY_LAYOUT,
326 recursionInterceptor ) );
327 }
328 else
329 {
330 classpath.append( interpolator.interpolate( REPOSITORY_LAYOUT_NONUNIQUE,
331 recursionInterceptor ) );
332 }
333 }
334 else if ( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM.equals( layoutType ) )
335 {
336 if ( layout == null )
337 {
338 throw new ManifestException( ManifestConfiguration.CLASSPATH_LAYOUT_TYPE_CUSTOM
339 + " layout type was declared, but custom layout expression was not"
340 + " specified. Check your <archive><manifest><customLayout/> element." );
341 }
342
343 classpath.append( interpolator.interpolate( layout, recursionInterceptor ) );
344 }
345 else
346 {
347 throw new ManifestException( "Unknown classpath layout type: '" + layoutType
348 + "'. Check your <archive><manifest><layoutType/> element." );
349 }
350 }
351 catch ( InterpolationException e )
352 {
353 ManifestException error =
354 new ManifestException( "Error interpolating artifact path for classpath entry: "
355 + e.getMessage() );
356
357 error.initCause( e );
358 throw error;
359 }
360 finally
361 {
362 for ( ValueSource vs : valueSources )
363 {
364 interpolator.removeValuesSource( vs );
365 }
366 }
367 }
368 }
369 }
370
371 if ( classpath.length() > 0 )
372 {
373
374
375 addManifestAttribute( m, "Class-Path", classpath.toString() );
376 }
377 }
378
379 if ( config.isAddDefaultSpecificationEntries() )
380 {
381 handleSpecificationEntries( project, entries, m );
382 }
383
384 if ( config.isAddDefaultImplementationEntries() )
385 {
386 handleImplementationEntries( project, entries, m );
387 }
388
389 String mainClass = config.getMainClass();
390 if ( mainClass != null && !"".equals( mainClass ) )
391 {
392 addManifestAttribute( m, entries, "Main-Class", mainClass );
393 }
394
395 if ( config.isAddExtensions() )
396 {
397 handleExtensions( project, entries, m );
398 }
399
400 addCustomEntries( m, entries, config );
401
402 return m;
403 }
404
405 private void handleExtraExpression( Artifact artifact, List<ValueSource> valueSources )
406 {
407 valueSources.add( new PrefixedObjectValueSource( ARTIFACT_EXPRESSION_PREFIXES, artifact,
408 true ) );
409 valueSources.add( new PrefixedObjectValueSource( ARTIFACT_EXPRESSION_PREFIXES,
410 artifact.getArtifactHandler(), true ) );
411
412 Properties extraExpressions = new Properties();
413
414
415 if ( !artifact.isSnapshot() )
416 {
417 extraExpressions.setProperty( "baseVersion", artifact.getVersion() );
418 }
419
420 extraExpressions.setProperty( "groupIdPath", artifact.getGroupId().replace( '.', '/' ) );
421 if ( StringUtils.isNotEmpty( artifact.getClassifier() ) )
422 {
423 extraExpressions.setProperty( "dashClassifier", "-" + artifact.getClassifier() );
424 extraExpressions.setProperty( "dashClassifier?", "-" + artifact.getClassifier() );
425 }
426 else
427 {
428 extraExpressions.setProperty( "dashClassifier", "" );
429 extraExpressions.setProperty( "dashClassifier?", "" );
430 }
431 valueSources.add( new PrefixedPropertiesValueSource( ARTIFACT_EXPRESSION_PREFIXES,
432 extraExpressions, true ) );
433 }
434
435 private void handleExtensions( MavenProject project, Map<String, String> entries, Manifest m )
436 throws ManifestException
437 {
438
439 StringBuilder extensionsList = new StringBuilder();
440 Set<Artifact> artifacts = (Set<Artifact>) project.getArtifacts();
441
442 for ( Artifact artifact : artifacts )
443 {
444 if ( !Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
445 {
446 if ( "jar".equals( artifact.getType() ) )
447 {
448 if ( extensionsList.length() > 0 )
449 {
450 extensionsList.append( " " );
451 }
452 extensionsList.append( artifact.getArtifactId() );
453 }
454 }
455 }
456
457 if ( extensionsList.length() > 0 )
458 {
459 addManifestAttribute( m, entries, "Extension-List", extensionsList.toString() );
460 }
461
462 for ( Object artifact1 : artifacts )
463 {
464
465
466 Artifact artifact = (Artifact) artifact1;
467 if ( "jar".equals( artifact.getType() ) )
468 {
469 String artifactId = artifact.getArtifactId().replace( '.', '_' );
470 String ename = artifactId + "-Extension-Name";
471 addManifestAttribute( m, entries, ename, artifact.getArtifactId() );
472 String iname = artifactId + "-Implementation-Version";
473 addManifestAttribute( m, entries, iname, artifact.getVersion() );
474
475 if ( artifact.getRepository() != null )
476 {
477 iname = artifactId + "-Implementation-URL";
478 String url = artifact.getRepository().getUrl() + "/" + artifact.toString();
479 addManifestAttribute( m, entries, iname, url );
480 }
481 }
482 }
483 }
484
485 private void handleImplementationEntries( MavenProject project, Map<String, String> entries, Manifest m )
486 throws ManifestException
487 {
488 addManifestAttribute( m, entries, "Implementation-Title", project.getName() );
489 addManifestAttribute( m, entries, "Implementation-Version", project.getVersion() );
490
491 if ( project.getOrganization() != null )
492 {
493 addManifestAttribute( m, entries, "Implementation-Vendor", project.getOrganization().getName() );
494 }
495 }
496
497 private void handleSpecificationEntries( MavenProject project, Map<String, String> entries, Manifest m )
498 throws ManifestException
499 {
500 addManifestAttribute( m, entries, "Specification-Title", project.getName() );
501
502 try
503 {
504 ArtifactVersion version = project.getArtifact().getSelectedVersion();
505 String specVersion = String.format( "%s.%s", version.getMajorVersion(), version.getMinorVersion() );
506 addManifestAttribute( m, entries, "Specification-Version", specVersion );
507 }
508 catch ( OverConstrainedVersionException e )
509 {
510 throw new ManifestException( "Failed to get selected artifact version to calculate"
511 + " the specification version: " + e.getMessage() );
512 }
513
514 if ( project.getOrganization() != null )
515 {
516 addManifestAttribute( m, entries, "Specification-Vendor", project.getOrganization().getName() );
517 }
518 }
519
520 private void addCustomEntries( Manifest m, Map<String, String> entries, ManifestConfiguration config )
521 throws ManifestException
522 {
523
524
525
526
527
528 if ( config.getPackageName() != null )
529 {
530 addManifestAttribute( m, entries, "Package", config.getPackageName() );
531 }
532 }
533
534
535
536
537 public JarArchiver getArchiver()
538 {
539 return archiver;
540 }
541
542
543
544
545 public void setArchiver( JarArchiver archiver )
546 {
547 this.archiver = archiver;
548 }
549
550
551
552
553 public void setOutputFile( File outputFile )
554 {
555 archiveFile = outputFile;
556 }
557
558
559
560
561
562
563
564
565
566
567 public void createArchive( MavenSession session, MavenProject project,
568 MavenArchiveConfiguration archiveConfiguration )
569 throws ManifestException, IOException,
570 DependencyResolutionRequiredException
571 {
572
573
574 MavenProject workingProject = null;
575 workingProject = (MavenProject) project.clone();
576
577 boolean forced = archiveConfiguration.isForced();
578 if ( archiveConfiguration.isAddMavenDescriptor() )
579 {
580
581
582
583
584
585
586
587
588
589
590
591 if ( workingProject.getArtifact().isSnapshot() )
592 {
593 workingProject.setVersion( workingProject.getArtifact().getVersion() );
594 }
595
596 String groupId = workingProject.getGroupId();
597
598 String artifactId = workingProject.getArtifactId();
599
600 archiver.addFile( project.getFile(), "META-INF/maven/" + groupId + "/" + artifactId + "/pom.xml" );
601
602
603
604
605
606 File customPomPropertiesFile = archiveConfiguration.getPomPropertiesFile();
607 File dir = new File( workingProject.getBuild().getDirectory(), "maven-archiver" );
608 File pomPropertiesFile = new File( dir, "pom.properties" );
609
610 new PomPropertiesUtil().createPomProperties( session, workingProject, archiver,
611 customPomPropertiesFile, pomPropertiesFile, forced );
612 }
613
614
615
616
617
618 archiver.setMinimalDefaultManifest( true );
619
620 File manifestFile = archiveConfiguration.getManifestFile();
621
622 if ( manifestFile != null )
623 {
624 archiver.setManifest( manifestFile );
625 }
626
627 Manifest manifest = getManifest( session, workingProject, archiveConfiguration );
628
629
630 archiver.addConfiguredManifest( manifest );
631
632 archiver.setCompress( archiveConfiguration.isCompress() );
633
634 archiver.setRecompressAddedZips( archiveConfiguration.isRecompressAddedZips() );
635
636 archiver.setIndex( archiveConfiguration.isIndex() );
637
638 archiver.setDestFile( archiveFile );
639
640
641 if ( archiveConfiguration.getManifest().isAddClasspath() )
642 {
643 List<String> artifacts = project.getRuntimeClasspathElements();
644 for ( String artifact : artifacts )
645 {
646 File f = new File( artifact );
647 archiver.addConfiguredIndexJars( f );
648 }
649 }
650
651 archiver.setForced( forced );
652 if ( !archiveConfiguration.isForced() && archiver.isSupportingForced() )
653 {
654
655
656
657 }
658
659 String automaticModuleName = manifest.getMainSection().getAttributeValue( "Automatic-Module-Name" );
660 if ( automaticModuleName != null )
661 {
662 if ( !isValidModuleName( automaticModuleName ) )
663 {
664 throw new ManifestException( "Invalid automatic module name: '" + automaticModuleName + "'" );
665 }
666 }
667
668
669 archiver.createArchive();
670 }
671
672 private void handleDefaultEntries( Manifest m, Map<String, String> entries )
673 throws ManifestException
674 {
675 String createdBy = CREATED_BY;
676 String archiverVersion = getArchiverVersion();
677 if ( archiverVersion != null )
678 {
679 createdBy += " " + archiverVersion;
680 }
681 addManifestAttribute( m, entries, "Created-By", createdBy );
682 addManifestAttribute( m, entries, "Build-Jdk-Spec", System.getProperty( "java.specification.version" ) );
683 }
684
685 private void handleBuildEnvironmentEntries( MavenSession session, Manifest m, Map<String, String> entries )
686 throws ManifestException
687 {
688 addManifestAttribute( m, entries, "Build-Tool",
689 session != null ? session.getSystemProperties().getProperty( "maven.build.version" ) : "Apache Maven" );
690 addManifestAttribute( m, entries, "Build-Jdk", String.format( "%s (%s)", System.getProperty( "java.version" ),
691 System.getProperty( "java.vendor" ) ) );
692 addManifestAttribute( m, entries, "Build-Os", String.format( "%s (%s; %s)", System.getProperty( "os.name" ),
693 System.getProperty( "os.version" ), System.getProperty( "os.arch" ) ) );
694 }
695
696 private Artifact findArtifactWithFile( Set<Artifact> artifacts, File file )
697 {
698 for ( Artifact artifact : artifacts )
699 {
700
701 if ( artifact.getFile() != null )
702 {
703 if ( artifact.getFile().equals( file ) )
704 {
705 return artifact;
706 }
707 }
708 }
709 return null;
710 }
711
712 private static String getArchiverVersion()
713 {
714 final Properties properties = PropertyUtils.loadOptionalProperties( MavenArchiver.class.getResourceAsStream(
715 "/META-INF/maven/org.apache.maven/maven-archiver/pom.properties" ) );
716
717 return properties.getProperty( "version" );
718 }
719
720 }