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