1 package org.apache.maven.plugins.ear;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.BufferedWriter;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.nio.charset.StandardCharsets;
27 import java.nio.file.FileSystem;
28 import java.nio.file.FileSystems;
29 import java.nio.file.FileVisitResult;
30 import java.nio.file.Files;
31 import java.nio.file.LinkOption;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.nio.file.ProviderMismatchException;
35 import java.nio.file.SimpleFileVisitor;
36 import java.nio.file.StandardCopyOption;
37 import java.nio.file.StandardOpenOption;
38 import java.nio.file.attribute.BasicFileAttributes;
39 import java.nio.file.attribute.FileTime;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collection;
43 import java.util.List;
44 import java.util.Objects;
45
46 import org.apache.maven.archiver.MavenArchiveConfiguration;
47 import org.apache.maven.archiver.MavenArchiver;
48 import org.apache.maven.artifact.Artifact;
49 import org.apache.maven.artifact.DependencyResolutionRequiredException;
50 import org.apache.maven.execution.MavenSession;
51 import org.apache.maven.plugin.MojoExecutionException;
52 import org.apache.maven.plugin.MojoFailureException;
53 import org.apache.maven.plugins.annotations.Component;
54 import org.apache.maven.plugins.annotations.LifecyclePhase;
55 import org.apache.maven.plugins.annotations.Mojo;
56 import org.apache.maven.plugins.annotations.Parameter;
57 import org.apache.maven.plugins.annotations.ResolutionScope;
58 import org.apache.maven.plugins.ear.util.EarMavenArchiver;
59 import org.apache.maven.plugins.ear.util.JavaEEVersion;
60 import org.apache.maven.project.MavenProjectHelper;
61 import org.apache.maven.shared.filtering.FilterWrapper;
62 import org.apache.maven.shared.filtering.MavenFileFilter;
63 import org.apache.maven.shared.filtering.MavenFilteringException;
64 import org.apache.maven.shared.filtering.MavenResourcesExecution;
65 import org.apache.maven.shared.filtering.MavenResourcesFiltering;
66 import org.apache.maven.shared.mapping.MappingUtils;
67 import org.apache.maven.shared.utils.io.FileUtils;
68 import org.codehaus.plexus.archiver.Archiver;
69 import org.codehaus.plexus.archiver.ArchiverException;
70 import org.codehaus.plexus.archiver.UnArchiver;
71 import org.codehaus.plexus.archiver.ear.EarArchiver;
72 import org.codehaus.plexus.archiver.jar.JarArchiver;
73 import org.codehaus.plexus.archiver.jar.Manifest;
74 import org.codehaus.plexus.archiver.jar.Manifest.Attribute;
75 import org.codehaus.plexus.archiver.jar.ManifestException;
76 import org.codehaus.plexus.archiver.manager.ArchiverManager;
77 import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
78 import org.codehaus.plexus.components.io.filemappers.FileMapper;
79 import org.codehaus.plexus.interpolation.InterpolationException;
80 import org.codehaus.plexus.util.DirectoryScanner;
81 import org.codehaus.plexus.util.StringUtils;
82
83
84
85
86
87
88 @Mojo( name = "ear",
89 defaultPhase = LifecyclePhase.PACKAGE,
90 threadSafe = true,
91 requiresDependencyResolution = ResolutionScope.TEST )
92 public class EarMojo
93 extends AbstractEarMojo
94 {
95
96
97
98 private static final String ARTIFACT_DEFAULT_FILE_NAME_MAPPING =
99 "@{artifactId}@-@{version}@@{dashClassifier?}@.@{extension}@";
100
101
102
103
104 @Parameter( defaultValue = "${basedir}/src/main/application", required = true )
105 private File earSourceDirectory;
106
107
108
109
110 @Parameter( alias = "includes", defaultValue = "**" )
111 private String earSourceIncludes;
112
113
114
115
116 @Parameter( alias = "excludes" )
117 private String earSourceExcludes;
118
119
120
121
122
123
124 @Parameter( defaultValue = "false" )
125 private boolean filtering;
126
127
128
129
130
131
132 @Parameter
133 private List<String> filters;
134
135
136
137
138
139
140 @Parameter
141 private List<String> nonFilteredFileExtensions;
142
143
144
145
146
147
148 @Parameter( defaultValue = "false" )
149 private boolean escapedBackslashesInFilePath;
150
151
152
153
154
155
156 @Parameter
157 protected String escapeString;
158
159
160
161
162
163
164
165
166 @Parameter( defaultValue = "false" )
167 private boolean skipClassPathModification;
168
169
170
171
172 @Parameter
173 private File applicationXml;
174
175
176
177
178 @Parameter( defaultValue = "${project.build.directory}", required = true )
179 private String outputDirectory;
180
181
182
183
184 @Parameter( defaultValue = "${project.build.finalName}", required = true, readonly = true )
185 private String finalName;
186
187
188
189
190 @Parameter
191 private String unpackTypes;
192
193
194
195
196 @Parameter
197 private String classifier;
198
199
200
201
202
203
204
205
206 @Parameter
207 private String packagingExcludes;
208
209
210
211
212
213
214
215
216 @Parameter
217 private String packagingIncludes;
218
219
220
221
222
223
224
225 @Parameter( defaultValue = "false" )
226 private boolean skinnyWars;
227
228
229
230
231
232
233
234
235
236
237 @Parameter( defaultValue = "false" )
238 private boolean skinnyModules;
239
240
241
242
243 @Component( role = Archiver.class, hint = "ear" )
244 private EarArchiver earArchiver;
245
246
247
248
249 @Component( role = Archiver.class, hint = "jar" )
250 private JarArchiver jarArchiver;
251
252
253
254
255
256 @Parameter
257 private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
258
259
260
261
262
263
264
265
266 @Parameter( defaultValue = "${project.build.outputTimestamp}" )
267 private String outputTimestamp;
268
269
270
271 @Component
272 private MavenProjectHelper projectHelper;
273
274
275
276
277 @Component
278 private ArchiverManager archiverManager;
279
280
281
282 @Component( role = MavenFileFilter.class, hint = "default" )
283 private MavenFileFilter mavenFileFilter;
284
285
286
287 @Component( role = MavenResourcesFiltering.class, hint = "default" )
288 private MavenResourcesFiltering mavenResourcesFiltering;
289
290
291
292
293 @Parameter( defaultValue = "${session}", readonly = true, required = true )
294 private MavenSession session;
295
296 private List<FilterWrapper> filterWrappers;
297
298
299 public void execute()
300 throws MojoExecutionException, MojoFailureException
301 {
302
303 super.execute();
304
305 File earFile = getEarFile( outputDirectory, finalName, classifier );
306 MavenArchiver archiver = new EarMavenArchiver( getModules() );
307 File ddFile = new File( getWorkDirectory(), APPLICATION_XML_URI );
308
309 JarArchiver theArchiver;
310 if ( ddFile.exists() )
311 {
312 earArchiver.setAppxml( ddFile );
313 theArchiver = earArchiver;
314 }
315 else
316 {
317
318
319 theArchiver = jarArchiver;
320 }
321 getLog().debug( "Ear archiver implementation [" + theArchiver.getClass().getName() + "]" );
322 archiver.setArchiver( theArchiver );
323 archiver.setOutputFile( earFile );
324 archiver.setCreatedBy( "Maven EAR Plugin", "org.apache.maven.plugins", "maven-ear-plugin" );
325
326
327 archiver.configureReproducibleBuild( outputTimestamp );
328
329 final JavaEEVersion javaEEVersion = JavaEEVersion.getJavaEEVersion( version );
330
331 final Collection<String> outdatedResources = initOutdatedResources();
332
333
334 List<String> unpackTypesList = createUnpackList();
335
336
337 copyModules( javaEEVersion, unpackTypesList, outdatedResources );
338
339
340 try
341 {
342 File earSourceDir = earSourceDirectory;
343
344 if ( earSourceDir.exists() )
345 {
346 getLog().info( "Copy ear sources to " + getWorkDirectory().getAbsolutePath() );
347 String[] fileNames = getEarFiles( earSourceDir );
348 for ( String fileName : fileNames )
349 {
350 copyFile( new File( earSourceDir, fileName ), new File( getWorkDirectory(), fileName ) );
351 outdatedResources.remove( Paths.get( fileName ).toString() );
352 }
353 }
354
355 if ( applicationXml != null )
356 {
357
358 getLog().info( "Including custom application.xml[" + applicationXml + "]" );
359 File metaInfDir = new File( getWorkDirectory(), META_INF );
360 copyFile( applicationXml, new File( metaInfDir, "/application.xml" ) );
361 outdatedResources.remove( Paths.get( "META-INF/application.xml" ).toString() );
362 }
363 }
364 catch ( IOException e )
365 {
366 throw new MojoExecutionException( "Error copying EAR sources", e );
367 }
368 catch ( MavenFilteringException e )
369 {
370 throw new MojoExecutionException( "Error filtering EAR sources", e );
371 }
372
373
374 if ( !ddFile.exists() && ( javaEEVersion.lt( JavaEEVersion.FIVE ) ) )
375 {
376 throw new MojoExecutionException( "Deployment descriptor: " + ddFile.getAbsolutePath()
377 + " does not exist." );
378 }
379
380 outdatedResources.remove( Paths.get( APPLICATION_XML_URI ).toString() );
381 if ( getJbossConfiguration() != null )
382 {
383 outdatedResources.remove( Paths.get( "META-INF/jboss-app.xml" ).toString() );
384 }
385
386 deleteOutdatedResources( outdatedResources );
387
388 try
389 {
390 getLog().debug( "Excluding " + Arrays.asList( getPackagingExcludes() ) + " from the generated EAR." );
391 getLog().debug( "Including " + Arrays.asList( getPackagingIncludes() ) + " in the generated EAR." );
392
393 archiver.getArchiver().addDirectory( getWorkDirectory(), getPackagingIncludes(), getPackagingExcludes() );
394
395 archiver.createArchive( session, getProject(), archive );
396 }
397 catch ( ManifestException | IOException | DependencyResolutionRequiredException e )
398 {
399 throw new MojoExecutionException( "Error assembling EAR", e );
400 }
401
402 if ( classifier != null )
403 {
404 projectHelper.attachArtifact( getProject(), "ear", classifier, earFile );
405 }
406 else
407 {
408 getProject().getArtifact().setFile( earFile );
409 }
410 }
411
412 private void copyModules( final JavaEEVersion javaEEVersion,
413 List<String> unpackTypesList,
414 Collection<String> outdatedResources )
415 throws MojoExecutionException, MojoFailureException
416 {
417 try
418 {
419 for ( EarModule module : getModules() )
420 {
421 final File sourceFile = module.getArtifact().getFile();
422 final File destinationFile = buildDestinationFile( getWorkDirectory(), module.getUri() );
423 if ( !sourceFile.isFile() )
424 {
425 throw new MojoExecutionException( "Cannot copy a directory: " + sourceFile.getAbsolutePath()
426 + "; Did you package/install " + module.getArtifact() + "?" );
427 }
428
429 if ( destinationFile.getCanonicalPath().equals( sourceFile.getCanonicalPath() ) )
430 {
431 getLog().info( "Skipping artifact [" + module + "], as it already exists at [" + module.getUri()
432 + "]" );
433
434 continue;
435 }
436
437
438
439 if ( ( unpackTypesList.contains( module.getType() )
440 && ( module.shouldUnpack() == null || module.shouldUnpack() ) )
441 || ( module.shouldUnpack() != null && module.shouldUnpack() ) )
442 {
443 getLog().info( "Copying artifact [" + module + "] to [" + module.getUri() + "] (unpacked)" );
444
445 if ( !destinationFile.isDirectory() && !destinationFile.mkdirs() )
446 {
447 throw new MojoExecutionException( "Error creating " + destinationFile );
448 }
449 unpack( sourceFile, destinationFile, outdatedResources );
450
451 if ( module.changeManifestClasspath() )
452 {
453 changeManifestClasspath( module, destinationFile, javaEEVersion, outdatedResources );
454 }
455 }
456 else
457 {
458 if ( sourceFile.lastModified() > destinationFile.lastModified() )
459 {
460 getLog().info( "Copying artifact [" + module + "] to [" + module.getUri() + "]" );
461 createParentIfNecessary( destinationFile );
462 Files.copy( sourceFile.toPath(), destinationFile.toPath(),
463 LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING );
464 if ( module.changeManifestClasspath() )
465 {
466 changeManifestClasspath( module, destinationFile, javaEEVersion, outdatedResources );
467 }
468 }
469 else
470 {
471 getLog().debug( "Skipping artifact [" + module + "], as it is already up to date at ["
472 + module.getUri() + "]" );
473 }
474 removeFromOutdatedResources( destinationFile.toPath(), outdatedResources );
475 }
476 }
477 }
478 catch ( IOException e )
479 {
480 throw new MojoExecutionException( "Error copying EAR modules", e );
481 }
482 catch ( ArchiverException e )
483 {
484 throw new MojoExecutionException( "Error unpacking EAR modules", e );
485 }
486 catch ( NoSuchArchiverException e )
487 {
488 throw new MojoExecutionException( "No Archiver found for EAR modules", e );
489 }
490 }
491
492 private List<String> createUnpackList()
493 throws MojoExecutionException
494 {
495 List<String> unpackTypesList = new ArrayList<String>();
496 if ( unpackTypes != null )
497 {
498 unpackTypesList = Arrays.asList( unpackTypes.split( "," ) );
499 for ( String type : unpackTypesList )
500 {
501 if ( !EarModuleFactory.isStandardArtifactType( type ) )
502 {
503 throw new MojoExecutionException( "Invalid type [" + type + "] supported types are "
504 + EarModuleFactory.getStandardArtifactTypes() );
505 }
506 }
507 getLog().debug( "Initialized unpack types " + unpackTypesList );
508 }
509 return unpackTypesList;
510 }
511
512
513
514
515 public File getApplicationXml()
516 {
517 return applicationXml;
518 }
519
520
521
522
523 public void setApplicationXml( File applicationXml )
524 {
525 this.applicationXml = applicationXml;
526 }
527
528
529
530
531
532
533 protected String[] getExcludes()
534 {
535 List<String> excludeList = new ArrayList<String>( FileUtils.getDefaultExcludesAsList() );
536 if ( earSourceExcludes != null && !"".equals( earSourceExcludes ) )
537 {
538 excludeList.addAll( Arrays.asList( StringUtils.split( earSourceExcludes, "," ) ) );
539 }
540
541
542 if ( getApplicationXml() != null && !"".equals( getApplicationXml() ) )
543 {
544 excludeList.add( "**/" + META_INF + "/application.xml" );
545 }
546
547 return excludeList.toArray( new String[excludeList.size()] );
548 }
549
550
551
552
553
554
555 protected String[] getIncludes()
556 {
557 return StringUtils.split( Objects.toString( earSourceIncludes, "" ), "," );
558 }
559
560
561
562
563 public String[] getPackagingExcludes()
564 {
565 if ( StringUtils.isEmpty( packagingExcludes ) )
566 {
567 return new String[0];
568 }
569 else
570 {
571 return StringUtils.split( packagingExcludes, "," );
572 }
573 }
574
575
576
577
578 public void setPackagingExcludes( String packagingExcludes )
579 {
580 this.packagingExcludes = packagingExcludes;
581 }
582
583
584
585
586 public String[] getPackagingIncludes()
587 {
588 if ( StringUtils.isEmpty( packagingIncludes ) )
589 {
590 return new String[] { "**" };
591 }
592 else
593 {
594 return StringUtils.split( packagingIncludes, "," );
595 }
596 }
597
598
599
600
601 public void setPackagingIncludes( String packagingIncludes )
602 {
603 this.packagingIncludes = packagingIncludes;
604 }
605
606 private static File buildDestinationFile( File buildDir, String uri )
607 {
608 return new File( buildDir, uri );
609 }
610
611
612
613
614
615
616
617
618
619 private static File getEarFile( String basedir, String finalName, String classifier )
620 {
621 if ( classifier == null )
622 {
623 classifier = "";
624 }
625 else if ( classifier.trim().length() > 0 && !classifier.startsWith( "-" ) )
626 {
627 classifier = "-" + classifier;
628 }
629
630 return new File( basedir, finalName + classifier + ".ear" );
631 }
632
633
634
635
636
637
638
639 private String[] getEarFiles( File sourceDir )
640 {
641 DirectoryScanner scanner = new DirectoryScanner();
642 scanner.setBasedir( sourceDir );
643 scanner.setExcludes( getExcludes() );
644 scanner.addDefaultExcludes();
645
646 scanner.setIncludes( getIncludes() );
647
648 scanner.scan();
649
650 return scanner.getIncludedFiles();
651 }
652
653
654
655
656
657
658
659
660
661
662
663 public void unpack( File source, final File destDir, final Collection<String> outdatedResources )
664 throws ArchiverException, NoSuchArchiverException, IOException
665 {
666 Path destPath = destDir.toPath();
667
668 UnArchiver unArchiver = archiverManager.getUnArchiver( "zip" );
669 unArchiver.setSourceFile( source );
670 unArchiver.setDestDirectory( destDir );
671 unArchiver.setFileMappers( new FileMapper[] {
672 pName ->
673 {
674 removeFromOutdatedResources( destPath.resolve( pName ), outdatedResources );
675 return pName;
676 }
677 } );
678
679
680 unArchiver.extract();
681 }
682
683 private void copyFile( File source, File target )
684 throws MavenFilteringException, IOException, MojoExecutionException
685 {
686 createParentIfNecessary( target );
687 if ( filtering && !isNonFilteredExtension( source.getName() ) )
688 {
689 mavenFileFilter.copyFile( source, target, true, getFilterWrappers(), encoding );
690 }
691 else
692 {
693 Files.copy( source.toPath(), target.toPath(), LinkOption.NOFOLLOW_LINKS,
694 StandardCopyOption.REPLACE_EXISTING );
695 }
696 }
697
698 private void createParentIfNecessary( File target )
699 throws IOException
700 {
701
702 File parentDirectory = target.getParentFile();
703 if ( parentDirectory != null && !parentDirectory.exists() )
704 {
705 Files.createDirectories( parentDirectory.toPath() );
706 }
707 }
708
709
710
711
712
713 public boolean isNonFilteredExtension( String fileName )
714 {
715 return !mavenResourcesFiltering.filteredFileExtension( fileName, nonFilteredFileExtensions );
716 }
717
718 private List<FilterWrapper> getFilterWrappers()
719 throws MojoExecutionException
720 {
721 if ( filterWrappers == null )
722 {
723 try
724 {
725 MavenResourcesExecution mavenResourcesExecution = new MavenResourcesExecution();
726 mavenResourcesExecution.setMavenProject( getProject() );
727 mavenResourcesExecution.setEscapedBackslashesInFilePath( escapedBackslashesInFilePath );
728 mavenResourcesExecution.setFilters( filters );
729 mavenResourcesExecution.setEscapeString( escapeString );
730
731 filterWrappers = mavenFileFilter.getDefaultFilterWrappers( mavenResourcesExecution );
732 }
733 catch ( MavenFilteringException e )
734 {
735 getLog().error( "Fail to build filtering wrappers " + e.getMessage() );
736 throw new MojoExecutionException( e.getMessage(), e );
737 }
738 }
739 return filterWrappers;
740 }
741
742 private void changeManifestClasspath( EarModule module, File original, JavaEEVersion javaEEVersion,
743 Collection<String> outdatedResources )
744 throws MojoFailureException
745 {
746 final String moduleLibDir = module.getLibDir();
747 if ( !( ( moduleLibDir == null ) || skinnyModules || ( skinnyWars && module instanceof WebModule ) ) )
748 {
749 return;
750 }
751
752
753 FileTime outputFileTime = MavenArchiver.parseBuildOutputTimestamp( outputTimestamp )
754 .map( FileTime::from )
755 .orElse( null );
756
757 FileSystem fileSystem = null;
758
759 try
760 {
761 Path workDirectory;
762
763
764
765 if ( original.isFile() )
766 {
767 fileSystem = FileSystems.newFileSystem(
768 original.toPath(), Thread.currentThread().getContextClassLoader() );
769 workDirectory = fileSystem.getRootDirectories().iterator().next();
770 }
771 else
772 {
773 workDirectory = original.toPath();
774 }
775
776
777 Path metaInfDirectory = workDirectory.resolve( "META-INF" );
778 if ( !Files.exists( metaInfDirectory ) )
779 {
780 Files.createDirectory( metaInfDirectory );
781 if ( outputFileTime != null )
782 {
783 Files.setLastModifiedTime( metaInfDirectory, outputFileTime );
784 }
785 getLog().debug(
786 "This project did not have a META-INF directory before, so a new directory was created." );
787 }
788 Path manifestFile = metaInfDirectory.resolve( "MANIFEST.MF" );
789 if ( !Files.exists( manifestFile ) )
790 {
791 Files.createFile( manifestFile );
792 if ( outputFileTime != null )
793 {
794 Files.setLastModifiedTime( manifestFile, outputFileTime );
795 }
796 getLog().debug(
797 "This project did not have a META-INF/MANIFEST.MF file before, so a new file was created." );
798 }
799
800 Manifest mf = readManifest( manifestFile );
801 Attribute classPath = mf.getMainSection().getAttribute( "Class-Path" );
802 List<String> classPathElements = new ArrayList<>();
803
804 boolean classPathExists;
805 if ( classPath != null )
806 {
807 classPathExists = true;
808 classPathElements.addAll( Arrays.asList( classPath.getValue().split( " " ) ) );
809 }
810 else
811 {
812 classPathExists = false;
813 classPath = new Attribute( "Class-Path", "" );
814 }
815
816 if ( ( moduleLibDir != null ) && ( skinnyModules || ( skinnyWars && module instanceof WebModule ) ) )
817 {
818
819 for ( EarModule otherModule : getAllEarModules() )
820 {
821 if ( module.equals( otherModule ) )
822 {
823 continue;
824 }
825
826
827
828
829 final Path workLibDir = workDirectory.resolve( moduleLibDir );
830 Path artifact = workLibDir.resolve( module.getArtifact().getFile().getName() );
831
832
833
834
835
836 if ( !Files.exists( artifact ) )
837 {
838 getLog().debug( "module does not exist with original file name." );
839 artifact = workLibDir.resolve( otherModule.getBundleFileName() );
840 getLog().debug( "Artifact with mapping: " + artifact.toAbsolutePath() );
841 }
842
843 if ( !Files.exists( artifact ) )
844 {
845 getLog().debug( "Artifact with mapping does not exist." );
846 artifact = workLibDir.resolve( otherModule.getArtifact().getFile().getName() );
847 getLog().debug( "Artifact with original file name: " + artifact.toAbsolutePath() );
848 }
849
850 if ( !Files.exists( artifact ) )
851 {
852 getLog().debug( "Artifact with original file name does not exist." );
853 final Artifact otherModuleArtifact = otherModule.getArtifact();
854 if ( otherModuleArtifact.isSnapshot() )
855 {
856 try
857 {
858 artifact = workLibDir.resolve( MappingUtils.evaluateFileNameMapping(
859 ARTIFACT_DEFAULT_FILE_NAME_MAPPING, otherModuleArtifact ) );
860 getLog()
861 .debug( "Artifact with default mapping file name: " + artifact.toAbsolutePath() );
862 }
863 catch ( InterpolationException e )
864 {
865 getLog().warn(
866 "Failed to evaluate file name for [" + otherModule + "] module using mapping: "
867 + ARTIFACT_DEFAULT_FILE_NAME_MAPPING );
868 }
869 }
870 }
871
872 if ( Files.exists( artifact ) )
873 {
874 getLog().debug( " -> Artifact to delete: " + artifact );
875 Files.delete( artifact );
876 }
877 }
878 }
879
880
881 final boolean forceClassPathModification =
882 javaEEVersion.lt( JavaEEVersion.FIVE ) || defaultLibBundleDir == null;
883 final boolean classPathExtension = !skipClassPathModification || forceClassPathModification;
884 for ( EarModule otherModule : getModules() )
885 {
886 if ( module.equals( otherModule ) )
887 {
888 continue;
889 }
890 final int moduleClassPathIndex = findModuleInClassPathElements( classPathElements, otherModule );
891 if ( moduleClassPathIndex != -1 )
892 {
893 if ( otherModule.isClassPathItem() )
894 {
895 classPathElements.set( moduleClassPathIndex, otherModule.getUri() );
896 }
897 else
898 {
899 classPathElements.remove( moduleClassPathIndex );
900 }
901 }
902 else if ( otherModule.isClassPathItem() && classPathExtension )
903 {
904 classPathElements.add( otherModule.getUri() );
905 }
906 }
907
908
909 for ( EarModule otherModule : getProvidedEarModules() )
910 {
911 final int moduleClassPathIndex = findModuleInClassPathElements( classPathElements, otherModule );
912 if ( moduleClassPathIndex != -1 )
913 {
914 classPathElements.remove( moduleClassPathIndex );
915 }
916 }
917
918 if ( !skipClassPathModification || !classPathElements.isEmpty() || classPathExists )
919 {
920 classPath.setValue( StringUtils.join( classPathElements.iterator(), " " ) );
921 mf.getMainSection().addConfiguredAttribute( classPath );
922
923
924 FileTime lastModifiedTime = Files.getLastModifiedTime( manifestFile );
925 try ( BufferedWriter writer = Files.newBufferedWriter( manifestFile, StandardCharsets.UTF_8,
926 StandardOpenOption.WRITE,
927 StandardOpenOption.CREATE,
928 StandardOpenOption.TRUNCATE_EXISTING ) )
929 {
930 mf.write( writer );
931 }
932 Files.setLastModifiedTime( manifestFile, lastModifiedTime );
933 removeFromOutdatedResources( manifestFile, outdatedResources );
934 }
935
936 if ( fileSystem != null )
937 {
938 fileSystem.close();
939 fileSystem = null;
940 }
941 }
942 catch ( ManifestException | IOException | ArchiverException e )
943 {
944 throw new MojoFailureException( e.getMessage(), e );
945 }
946 finally
947 {
948 if ( fileSystem != null )
949 {
950 try
951 {
952 fileSystem.close();
953 }
954 catch ( IOException e )
955 {
956
957 }
958 }
959 }
960 }
961
962 private static Manifest readManifest( Path manifestFile )
963 throws IOException
964 {
965
966 try ( InputStream in = Files.newInputStream( manifestFile ) )
967 {
968 return new Manifest( in );
969 }
970 }
971
972 private Collection<String> initOutdatedResources()
973 {
974 final Collection<String> outdatedResources = new ArrayList<>();
975
976 if ( getWorkDirectory().exists() )
977 {
978 try
979 {
980 Files.walkFileTree( getWorkDirectory().toPath(), new SimpleFileVisitor<Path>()
981 {
982 @Override
983 public FileVisitResult visitFile( Path file, BasicFileAttributes attrs )
984 throws IOException
985 {
986 outdatedResources.add( getWorkDirectory().toPath().relativize( file ).toString() );
987 return super.visitFile( file, attrs );
988 }
989 } );
990 }
991 catch ( IOException e )
992 {
993 getLog().warn( "Can't detect outdated resources", e );
994 }
995 }
996
997 getLog().debug( "initOutdatedResources: " + outdatedResources );
998 return outdatedResources;
999 }
1000
1001 private void deleteOutdatedResources( final Collection<String> outdatedResources )
1002 {
1003 getLog().debug( "deleteOutdatedResources: " + outdatedResources );
1004 final long startTime = session.getStartTime().getTime();
1005
1006 getLog().debug( "deleteOutdatedResources session startTime: " + startTime );
1007
1008 for ( String outdatedResource : outdatedResources )
1009 {
1010 File resourceFile = new File( getWorkDirectory(), outdatedResource );
1011 if ( resourceFile.lastModified() < startTime )
1012 {
1013 getLog().info( "deleting outdated resource " + outdatedResource );
1014 getLog().debug( outdatedResource + " last modified: " + resourceFile.lastModified() );
1015 resourceFile.delete();
1016 }
1017 }
1018 }
1019
1020 private void removeFromOutdatedResources( Path destination, Collection<String> outdatedResources )
1021 {
1022 Path relativeDestFile;
1023 try
1024 {
1025 relativeDestFile = getWorkDirectory().toPath().relativize( destination.normalize() );
1026 }
1027 catch ( ProviderMismatchException e )
1028 {
1029 relativeDestFile = destination.normalize();
1030 }
1031
1032 if ( outdatedResources.remove( relativeDestFile.toString() ) )
1033 {
1034 getLog().debug( "Remove from outdatedResources: " + relativeDestFile );
1035 }
1036 }
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047 private int findModuleInClassPathElements( final List<String> classPathElements, final EarModule module )
1048 {
1049 if ( classPathElements.isEmpty() )
1050 {
1051 return -1;
1052 }
1053 int moduleClassPathIndex = classPathElements.indexOf( module.getBundleFileName() );
1054 if ( moduleClassPathIndex != -1 )
1055 {
1056 return moduleClassPathIndex;
1057 }
1058 final Artifact artifact = module.getArtifact();
1059 moduleClassPathIndex = classPathElements.indexOf( artifact.getFile().getName() );
1060 if ( moduleClassPathIndex != -1 )
1061 {
1062 return moduleClassPathIndex;
1063 }
1064 if ( artifact.isSnapshot() )
1065 {
1066 try
1067 {
1068 moduleClassPathIndex = classPathElements
1069 .indexOf( MappingUtils.evaluateFileNameMapping( ARTIFACT_DEFAULT_FILE_NAME_MAPPING, artifact ) );
1070 if ( moduleClassPathIndex != -1 )
1071 {
1072 return moduleClassPathIndex;
1073 }
1074 }
1075 catch ( InterpolationException e )
1076 {
1077 getLog().warn( "Failed to evaluate file name for [" + module + "] module using mapping: "
1078 + ARTIFACT_DEFAULT_FILE_NAME_MAPPING );
1079 }
1080 }
1081 return classPathElements.indexOf( module.getUri() );
1082 }
1083 }