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