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