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