View Javadoc
1   package org.apache.maven.plugins.shade.mojo;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.Writer;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collections;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.LinkedHashSet;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Set;
36  
37  import org.apache.maven.artifact.Artifact;
38  import org.apache.maven.artifact.factory.ArtifactFactory;
39  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
40  import org.apache.maven.artifact.repository.ArtifactRepository;
41  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
42  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
43  import org.apache.maven.artifact.resolver.ArtifactResolver;
44  import org.apache.maven.execution.MavenSession;
45  import org.apache.maven.model.Dependency;
46  import org.apache.maven.model.Exclusion;
47  import org.apache.maven.model.Model;
48  import org.apache.maven.plugin.AbstractMojo;
49  import org.apache.maven.plugin.MojoExecutionException;
50  import org.apache.maven.plugins.annotations.Component;
51  import org.apache.maven.plugins.annotations.LifecyclePhase;
52  import org.apache.maven.plugins.annotations.Mojo;
53  import org.apache.maven.plugins.annotations.Parameter;
54  import org.apache.maven.plugins.annotations.ResolutionScope;
55  import org.apache.maven.plugins.shade.ShadeRequest;
56  import org.apache.maven.plugins.shade.Shader;
57  import org.apache.maven.plugins.shade.filter.Filter;
58  import org.apache.maven.plugins.shade.filter.MinijarFilter;
59  import org.apache.maven.plugins.shade.filter.SimpleFilter;
60  import org.apache.maven.plugins.shade.pom.PomWriter;
61  import org.apache.maven.plugins.shade.relocation.Relocator;
62  import org.apache.maven.plugins.shade.relocation.SimpleRelocator;
63  import org.apache.maven.plugins.shade.resource.ResourceTransformer;
64  import org.apache.maven.project.DefaultProjectBuildingRequest;
65  import org.apache.maven.project.MavenProject;
66  import org.apache.maven.project.MavenProjectHelper;
67  import org.apache.maven.project.ProjectBuilder;
68  import org.apache.maven.project.ProjectBuildingException;
69  import org.apache.maven.project.ProjectBuildingRequest;
70  import org.apache.maven.project.ProjectBuildingResult;
71  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
72  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
73  import org.apache.maven.shared.dependency.graph.DependencyNode;
74  import org.codehaus.plexus.PlexusConstants;
75  import org.codehaus.plexus.PlexusContainer;
76  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
77  import org.codehaus.plexus.context.Context;
78  import org.codehaus.plexus.context.ContextException;
79  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
80  import org.codehaus.plexus.util.IOUtil;
81  import org.codehaus.plexus.util.WriterFactory;
82  
83  /**
84   * Mojo that performs shading delegating to the Shader component.
85   *
86   * @author Jason van Zyl
87   * @author Mauro Talevi
88   * @author David Blevins
89   * @author Hiram Chirino
90   */
91  // CHECKSTYLE_OFF: LineLength
92  @Mojo( name = "shade", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true, requiresDependencyResolution = ResolutionScope.RUNTIME )
93  // CHECKSTYLE_ON: LineLength
94  public class ShadeMojo
95      extends AbstractMojo
96      implements Contextualizable
97  {
98      /**
99       * The current Maven session.
100      */
101     @Parameter( defaultValue = "${session}", readonly = true, required = true )
102     private MavenSession session;
103 
104     /**
105      * The current Maven project.
106      */
107     @Parameter( defaultValue = "${project}", readonly = true, required = true )
108     private MavenProject project;
109 
110     @Component
111     private MavenProjectHelper projectHelper;
112 
113     @Component( hint = "default", role = org.apache.maven.plugins.shade.Shader.class )
114     private Shader shader;
115 
116     /**
117      * The dependency graph builder to use.
118      */
119     @Component
120     private DependencyGraphBuilder dependencyGraphBuilder;
121 
122     /**
123      * ProjectBuilder, needed to create projects from the artifacts.
124      */
125     @Component
126     private ProjectBuilder projectBuilder;
127 
128     /**
129      * The artifact metadata source to use.
130      */
131     @Component
132     private ArtifactMetadataSource artifactMetadataSource;
133 
134     /**
135      * Remote repositories which will be searched for source attachments.
136      */
137     @Parameter( readonly = true, required = true, defaultValue = "${project.remoteArtifactRepositories}" )
138     protected List<ArtifactRepository> remoteArtifactRepositories;
139 
140     /**
141      * Local maven repository.
142      */
143     @Parameter( readonly = true, required = true, defaultValue = "${localRepository}" )
144     protected ArtifactRepository localRepository;
145 
146     /**
147      * Artifact factory, needed to download source jars for inclusion in classpath.
148      */
149     @Component
150     protected ArtifactFactory artifactFactory;
151 
152     /**
153      * Artifact resolver, needed to download source jars for inclusion in classpath.
154      */
155     @Component
156     protected ArtifactResolver artifactResolver;
157 
158     /**
159      * Artifacts to include/exclude from the final artifact. Artifacts are denoted by composite identifiers of the
160      * general form <code>groupId:artifactId:type:classifier</code>. Since version 1.3, the wildcard characters '*' and
161      * '?' can be used within the sub parts of those composite identifiers to do pattern matching. For convenience, the
162      * syntax <code>groupId</code> is equivalent to <code>groupId:*:*:*</code>, <code>groupId:artifactId</code> is
163      * equivalent to <code>groupId:artifactId:*:*</code> and <code>groupId:artifactId:classifier</code> is equivalent to
164      * <code>groupId:artifactId:*:classifier</code>. For example:
165      * 
166      * <pre>
167      * &lt;artifactSet&gt;
168      *   &lt;includes&gt;
169      *     &lt;include&gt;org.apache.maven:*&lt;/include&gt;
170      *   &lt;/includes&gt;
171      *   &lt;excludes&gt;
172      *     &lt;exclude&gt;*:maven-core&lt;/exclude&gt;
173      *   &lt;/excludes&gt;
174      * &lt;/artifactSet&gt;
175      * </pre>
176      */
177     @Parameter
178     private ArtifactSet artifactSet;
179 
180     /**
181      * Packages to be relocated. For example:
182      * 
183      * <pre>
184      * &lt;relocations&gt;
185      *   &lt;relocation&gt;
186      *     &lt;pattern&gt;org.apache&lt;/pattern&gt;
187      *     &lt;shadedPattern&gt;hidden.org.apache&lt;/shadedPattern&gt;
188      *     &lt;includes&gt;
189      *       &lt;include&gt;org.apache.maven.*&lt;/include&gt;
190      *     &lt;/includes&gt;
191      *     &lt;excludes&gt;
192      *       &lt;exclude&gt;org.apache.maven.Public*&lt;/exclude&gt;
193      *     &lt;/excludes&gt;
194      *   &lt;/relocation&gt;
195      * &lt;/relocations&gt;
196      * </pre>
197      * 
198      * <em>Note:</em> Support for includes exists only since version 1.4.
199      */
200     @SuppressWarnings( "MismatchedReadAndWriteOfArray" )
201     @Parameter
202     private PackageRelocation[] relocations;
203 
204     /**
205      * Resource transformers to be used. Please see the "Examples" section for more information on available
206      * transformers and their configuration.
207      */
208     @Parameter
209     private ResourceTransformer[] transformers;
210 
211     /**
212      * Archive Filters to be used. Allows you to specify an artifact in the form of a composite identifier as used by
213      * {@link #artifactSet} and a set of include/exclude file patterns for filtering which contents of the archive are
214      * added to the shaded jar. From a logical perspective, includes are processed before excludes, thus it's possible
215      * to use an include to collect a set of files from the archive then use excludes to further reduce the set. By
216      * default, all files are included and no files are excluded. If multiple filters apply to an artifact, the
217      * intersection of the matched files will be included in the final JAR. For example:
218      * 
219      * <pre>
220      * &lt;filters&gt;
221      *   &lt;filter&gt;
222      *     &lt;artifact&gt;junit:junit&lt;/artifact&gt;
223      *     &lt;includes&gt;
224      *       &lt;include&gt;org/junit/**&lt;/include&gt;
225      *     &lt;/includes&gt;
226      *     &lt;excludes&gt;
227      *       &lt;exclude&gt;org/junit/experimental/**&lt;/exclude&gt;
228      *     &lt;/excludes&gt;
229      *   &lt;/filter&gt;
230      * &lt;/filters&gt;
231      * </pre>
232      */
233     @SuppressWarnings( "MismatchedReadAndWriteOfArray" )
234     @Parameter
235     private ArchiveFilter[] filters;
236 
237     /**
238      * The destination directory for the shaded artifact.
239      */
240     @Parameter( defaultValue = "${project.build.directory}" )
241     private File outputDirectory;
242 
243     /**
244      * The name of the shaded artifactId.
245      * <p/>
246      * If you like to change the name of the native artifact, you may use the &lt;build>&lt;finalName> setting. If this
247      * is set to something different than &lt;build>&lt;finalName>, no file replacement will be performed, even if
248      * shadedArtifactAttached is being used.
249      */
250     @Parameter
251     private String finalName;
252 
253     /**
254      * The name of the shaded artifactId. So you may want to use a different artifactId and keep
255      * the standard version. If the original artifactId was "foo" then the final artifact would
256      * be something like foo-1.0.jar. So if you change the artifactId you might have something
257      * like foo-special-1.0.jar.
258      */
259     @Parameter( defaultValue = "${project.artifactId}" )
260     private String shadedArtifactId;
261 
262     /**
263      * If specified, this will include only artifacts which have groupIds which
264      * start with this.
265      */
266     @Parameter
267     private String shadedGroupFilter;
268 
269     /**
270      * Defines whether the shaded artifact should be attached as classifier to
271      * the original artifact. If false, the shaded jar will be the main artifact
272      * of the project
273      */
274     @Parameter
275     private boolean shadedArtifactAttached;
276 
277     /**
278      * Flag whether to generate a simplified POM for the shaded artifact. If set to <code>true</code>, dependencies that
279      * have been included into the uber JAR will be removed from the <code>&lt;dependencies&gt;</code> section of the
280      * generated POM. The reduced POM will be named <code>dependency-reduced-pom.xml</code> and is stored into the same
281      * directory as the shaded artifact. Unless you also specify dependencyReducedPomLocation, the plugin will
282      * create a temporary file named <code>dependency-reduced-pom.xml</code> in the project basedir.
283      */
284     @Parameter( defaultValue = "true" )
285     private boolean createDependencyReducedPom;
286 
287     /**
288      * Where to put the dependency reduced pom.
289      * Note: setting a value for this parameter with a directory other than ${basedir} will change the value of
290      * ${basedir} for all executions that come after the shade execution. This is often not what you want.
291      * This is considered an open issue with this plugin.
292      *
293      * @since 1.7
294      */
295     @Parameter( defaultValue = "${basedir}/dependency-reduced-pom.xml" )
296     private File dependencyReducedPomLocation;
297 
298     /**
299      * Create a dependency-reduced POM in ${basedir}/drp-UNIQUE.pom. This avoids build collisions
300      * of parallel builds without moving the dependency-reduced POM to a different directory.
301      * The property maven.shade.dependency-reduced-pom is set to the generated filename.
302      *
303      * @since 1.7.2
304      */
305     @Parameter( defaultValue = "false" )
306     private boolean generateUniqueDependencyReducedPom;
307 
308     /**
309      * When true, dependencies are kept in the pom but with scope 'provided'; when false,
310      * the dependency is removed.
311      */
312     @Parameter
313     private boolean keepDependenciesWithProvidedScope;
314 
315     /**
316      * When true, transitive deps of removed dependencies are promoted to direct dependencies.
317      * This should allow the drop in replacement of the removed deps with the new shaded
318      * jar and everything should still work.
319      */
320     @Parameter
321     private boolean promoteTransitiveDependencies;
322 
323     /**
324      * The name of the classifier used in case the shaded artifact is attached.
325      */
326     @Parameter( defaultValue = "shaded" )
327     private String shadedClassifierName;
328 
329     /**
330      * When true, it will attempt to create a sources jar as well
331      */
332     @Parameter
333     private boolean createSourcesJar;
334 
335     /**
336      * When true, it will attempt to shade the contents of the java source files when creating the sources jar.
337      * When false, it will just relocate the java source files to the shaded paths, but will not modify the
338      * actual contents of the java source files.
339      */
340     @Parameter( property = "shadeSourcesContent", defaultValue = "false" )
341     private boolean shadeSourcesContent;
342 
343     /**
344      * When true, dependencies will be stripped down on the class level to only the transitive hull required for the
345      * artifact. <em>Note:</em> Usage of this feature requires Java 1.5 or higher.
346      *
347      * @since 1.4
348      */
349     @Parameter
350     private boolean minimizeJar;
351 
352     /**
353      * The path to the output file for the shaded artifact. When this parameter is set, the created archive will neither
354      * replace the project's main artifact nor will it be attached. Hence, this parameter causes the parameters
355      * {@link #finalName}, {@link #shadedArtifactAttached}, {@link #shadedClassifierName} and
356      * {@link #createDependencyReducedPom} to be ignored when used.
357      *
358      * @since 1.3
359      */
360     @Parameter
361     private File outputFile;
362 
363     /**
364      * You can pass here the roleHint about your own Shader implementation plexus component.
365      *
366      * @since 1.6
367      */
368     @Parameter
369     private String shaderHint;
370 
371     /**
372      * When true, the version of each dependency of the reduced pom will be based on the baseVersion of the original
373      * dependency instead of its resolved version.
374      * For example, if the original pom (transitively) depends on a:a:2.7-SNAPSHOT, if useBaseVersion is set to false,
375      * the reduced pom will depend on a:a:2.7-20130312.222222-12 whereas if useBaseVersion is set to true, the reduced
376      * pom will depend on a:a:2.7-SNAPSHOT
377      *
378      * @since 3.0
379      */
380     @Parameter( defaultValue = "false" )
381     private boolean useBaseVersion;
382 
383     @Parameter( defaultValue = "false" )
384     private boolean shadeTestJar;
385 
386     /**
387      * @since 1.6
388      */
389     private PlexusContainer plexusContainer;
390 
391     public void contextualize( Context context )
392         throws ContextException
393     {
394         plexusContainer = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
395     }
396 
397     /**
398      * @throws MojoExecutionException
399      */
400     public void execute()
401         throws MojoExecutionException
402     {
403 
404         setupHintedShader();
405 
406         Set<File> artifacts = new LinkedHashSet<File>();
407         Set<String> artifactIds = new LinkedHashSet<String>();
408         Set<File> sourceArtifacts = new LinkedHashSet<File>();
409         Set<File> testArtifacts = new LinkedHashSet<File>();
410 
411         ArtifactSelector artifactSelector =
412             new ArtifactSelector( project.getArtifact(), artifactSet, shadedGroupFilter );
413 
414         if ( artifactSelector.isSelected( project.getArtifact() ) && !"pom".equals( project.getArtifact().getType() ) )
415         {
416             if ( invalidMainArtifact() )
417             {
418                 createErrorOutput();
419                 throw new MojoExecutionException( "Failed to create shaded artifact, "
420                     + "project main artifact does not exist." );
421             }
422 
423             artifacts.add( project.getArtifact().getFile() );
424 
425             if ( createSourcesJar )
426             {
427                 File file = shadedSourcesArtifactFile();
428                 if ( file.isFile() )
429                 {
430                     sourceArtifacts.add( file );
431                 }
432             }
433 
434             if ( shadeTestJar )
435             {
436                 File file = shadedTestArtifactFile();
437                 if ( file.isFile() )
438                 {
439                     testArtifacts.add( file );
440                 }
441             }
442         }
443 
444         processArtifactSelectors( artifacts, artifactIds, sourceArtifacts, artifactSelector );
445 
446         File outputJar = ( outputFile != null ) ? outputFile : shadedArtifactFileWithClassifier();
447         File sourcesJar = shadedSourceArtifactFileWithClassifier();
448         File testJar = shadedTestArtifactFileWithClassifier();
449 
450         // Now add our extra resources
451         try
452         {
453             List<Filter> filters = getFilters();
454 
455             List<Relocator> relocators = getRelocators();
456 
457             List<ResourceTransformer> resourceTransformers = getResourceTransformers();
458 
459             ShadeRequest shadeRequest = shadeRequest( artifacts, outputJar, filters, relocators, resourceTransformers );
460 
461             shader.shade( shadeRequest );
462 
463             if ( createSourcesJar )
464             {
465                 ShadeRequest shadeSourcesRequest =
466                     createShadeSourcesRequest( sourceArtifacts, sourcesJar, filters, relocators, resourceTransformers );
467 
468                 shader.shade( shadeSourcesRequest );
469             }
470 
471             if ( shadeTestJar )
472             {
473 
474                 ShadeRequest shadeSourcesRequest =
475                     createShadeSourcesRequest( testArtifacts, testJar, filters, relocators, resourceTransformers );
476 
477                 shader.shade( shadeSourcesRequest );
478             }
479 
480             if ( outputFile == null )
481             {
482                 boolean renamed = false;
483 
484                 // rename the output file if a specific finalName is set
485                 // but don't rename if the finalName is the <build><finalName>
486                 // because this will be handled implicitly later
487                 if ( finalName != null && finalName.length() > 0 //
488                     && !finalName.equals( project.getBuild().getFinalName() ) )
489                 {
490                     String finalFileName = finalName + "." + project.getArtifact().getArtifactHandler().getExtension();
491                     File finalFile = new File( outputDirectory, finalFileName );
492                     replaceFile( finalFile, outputJar );
493                     outputJar = finalFile;
494 
495                     renamed = true;
496                 }
497 
498                 if ( shadedArtifactAttached )
499                 {
500                     getLog().info( "Attaching shaded artifact." );
501                     projectHelper.attachArtifact( project, project.getArtifact().getType(), shadedClassifierName,
502                                                   outputJar );
503                     if ( createSourcesJar )
504                     {
505                         projectHelper.attachArtifact( project, "jar", shadedClassifierName + "-sources", sourcesJar );
506                     }
507                 }
508                 else if ( !renamed )
509                 {
510                     getLog().info( "Replacing original artifact with shaded artifact." );
511                     File originalArtifact = project.getArtifact().getFile();
512                     if ( originalArtifact != null )
513                     {
514                         replaceFile( originalArtifact, outputJar );
515 
516                         if ( createSourcesJar )
517                         {
518                             getLog().info( "Replacing original source artifact with shaded source artifact." );
519                             File shadedSources = shadedSourcesArtifactFile();
520 
521                             replaceFile( shadedSources, sourcesJar );
522 
523                             projectHelper.attachArtifact( project, "jar", "sources", shadedSources );
524                         }
525 
526                         if ( shadeTestJar )
527                         {
528                             getLog().info( "Replacing original test artifact with shaded test artifact." );
529                             File shadedTests = shadedTestArtifactFile();
530 
531                             replaceFile( shadedTests, testJar );
532 
533                             projectHelper.attachArtifact( project, "jar", "tests", shadedTests );
534                         }
535 
536                         if ( createDependencyReducedPom )
537                         {
538                             createDependencyReducedPom( artifactIds );
539                         }
540                     }
541                 }
542             }
543         }
544         catch ( Exception e )
545         {
546             throw new MojoExecutionException( "Error creating shaded jar: " + e.getMessage(), e );
547         }
548     }
549 
550     private void createErrorOutput()
551     {
552         getLog().error( "The project main artifact does not exist. This could have the following" );
553         getLog().error( "reasons:" );
554         getLog().error( "- You have invoked the goal directly from the command line. This is not" );
555         getLog().error( "  supported. Please add the goal to the default lifecycle via an" );
556         getLog().error( "  <execution> element in your POM and use \"mvn package\" to have it run." );
557         getLog().error( "- You have bound the goal to a lifecycle phase before \"package\". Please" );
558         getLog().error( "  remove this binding from your POM such that the goal will be run in" );
559         getLog().error( "  the proper phase." );
560         getLog().error( "- You removed the configuration of the maven-jar-plugin that produces the main artifact." );
561     }
562 
563     private ShadeRequest shadeRequest( Set<File> artifacts, File outputJar, List<Filter> filters,
564                                        List<Relocator> relocators, List<ResourceTransformer> resourceTransformers )
565     {
566         ShadeRequest shadeRequest = new ShadeRequest();
567         shadeRequest.setJars( artifacts );
568         shadeRequest.setUberJar( outputJar );
569         shadeRequest.setFilters( filters );
570         shadeRequest.setRelocators( relocators );
571         shadeRequest.setResourceTransformers( resourceTransformers );
572         return shadeRequest;
573     }
574 
575     private ShadeRequest createShadeSourcesRequest( Set<File> testArtifacts, File testJar, List<Filter> filters,
576                                                     List<Relocator> relocators,
577                                                     List<ResourceTransformer> resourceTransformers )
578     {
579         ShadeRequest shadeSourcesRequest =
580             shadeRequest( testArtifacts, testJar, filters, relocators, resourceTransformers );
581         shadeSourcesRequest.setShadeSourcesContent( shadeSourcesContent );
582         return shadeSourcesRequest;
583     }
584 
585     private void setupHintedShader()
586         throws MojoExecutionException
587     {
588         if ( shaderHint != null )
589         {
590             try
591             {
592                 shader = (Shader) plexusContainer.lookup( Shader.ROLE, shaderHint );
593             }
594             catch ( ComponentLookupException e )
595             {
596                 throw new MojoExecutionException( "unable to lookup own Shader implementation with hint:'" + shaderHint
597                     + "'", e );
598             }
599         }
600     }
601 
602     private void processArtifactSelectors( Set<File> artifacts, Set<String> artifactIds, Set<File> sourceArtifacts,
603                                            ArtifactSelector artifactSelector )
604     {
605         for ( Artifact artifact : project.getArtifacts() )
606         {
607             if ( !artifactSelector.isSelected( artifact ) )
608             {
609                 getLog().info( "Excluding " + artifact.getId() + " from the shaded jar." );
610 
611                 continue;
612             }
613 
614             if ( "pom".equals( artifact.getType() ) )
615             {
616                 getLog().info( "Skipping pom dependency " + artifact.getId() + " in the shaded jar." );
617                 continue;
618             }
619 
620             getLog().info( "Including " + artifact.getId() + " in the shaded jar." );
621 
622             artifacts.add( artifact.getFile() );
623             artifactIds.add( getId( artifact ) );
624 
625             if ( createSourcesJar )
626             {
627                 File file = resolveArtifactSources( artifact );
628                 if ( file != null )
629                 {
630                     sourceArtifacts.add( file );
631                 }
632             }
633         }
634     }
635 
636     private boolean invalidMainArtifact()
637     {
638         return project.getArtifact().getFile() == null || !project.getArtifact().getFile().isFile();
639     }
640 
641     private void replaceFile( File oldFile, File newFile )
642         throws MojoExecutionException
643     {
644         getLog().info( "Replacing " + oldFile + " with " + newFile );
645 
646         File origFile = new File( outputDirectory, "original-" + oldFile.getName() );
647         if ( oldFile.exists() && !oldFile.renameTo( origFile ) )
648         {
649             // try a gc to see if an unclosed stream needs garbage collecting
650             System.gc();
651             System.gc();
652 
653             if ( !oldFile.renameTo( origFile ) )
654             {
655                 // Still didn't work. We'll do a copy
656                 try
657                 {
658                     copyFiles( oldFile, origFile );
659                 }
660                 catch ( IOException ex )
661                 {
662                     // kind of ignorable here. We're just trying to save the original
663                     getLog().warn( ex );
664                 }
665             }
666         }
667         if ( !newFile.renameTo( oldFile ) )
668         {
669             // try a gc to see if an unclosed stream needs garbage collecting
670             System.gc();
671             System.gc();
672 
673             if ( !newFile.renameTo( oldFile ) )
674             {
675                 // Still didn't work. We'll do a copy
676                 try
677                 {
678                     copyFiles( newFile, oldFile );
679                 }
680                 catch ( IOException ex )
681                 {
682                     throw new MojoExecutionException( "Could not replace original artifact with shaded artifact!", ex );
683                 }
684             }
685         }
686     }
687 
688     private void copyFiles( File source, File target )
689         throws IOException
690     {
691         FileOutputStream fout = new FileOutputStream( target );
692         FileInputStream fin = new FileInputStream( source );
693         try
694         {
695             IOUtil.copy( fin, fout );
696         }
697         finally
698         {
699             IOUtil.close( fin );
700             IOUtil.close( fout );
701         }
702     }
703 
704     private File resolveArtifactSources( Artifact artifact )
705     {
706 
707         Artifact resolvedArtifact =
708             artifactFactory.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(),
709                                                           artifact.getVersion(), "java-source", "sources" );
710 
711         try
712         {
713             artifactResolver.resolve( resolvedArtifact, remoteArtifactRepositories, localRepository );
714         }
715         catch ( ArtifactNotFoundException e )
716         {
717             // ignore, the jar has not been found
718         }
719         catch ( ArtifactResolutionException e )
720         {
721             getLog().warn( "Could not get sources for " + artifact );
722         }
723 
724         if ( resolvedArtifact.isResolved() )
725         {
726             return resolvedArtifact.getFile();
727         }
728         return null;
729     }
730 
731     private List<Relocator> getRelocators()
732     {
733         List<Relocator> relocators = new ArrayList<Relocator>();
734 
735         if ( relocations == null )
736         {
737             return relocators;
738         }
739 
740         for ( PackageRelocation r : relocations )
741         {
742             relocators.add( new SimpleRelocator( r.getPattern(), r.getShadedPattern(), r.getIncludes(),
743                                                  r.getExcludes(), r.isRawString() ) );
744         }
745 
746         return relocators;
747     }
748 
749     private List<ResourceTransformer> getResourceTransformers()
750     {
751         if ( transformers == null )
752         {
753             return Collections.emptyList();
754         }
755 
756         return Arrays.asList( transformers );
757     }
758 
759     private List<Filter> getFilters()
760         throws MojoExecutionException
761     {
762         List<Filter> filters = new ArrayList<Filter>();
763         List<SimpleFilter> simpleFilters = new ArrayList<SimpleFilter>();
764 
765         if ( this.filters != null && this.filters.length > 0 )
766         {
767             Map<Artifact, ArtifactId> artifacts = new HashMap<Artifact, ArtifactId>();
768 
769             artifacts.put( project.getArtifact(), new ArtifactId( project.getArtifact() ) );
770 
771             for ( Artifact artifact : project.getArtifacts() )
772             {
773                 artifacts.put( artifact, new ArtifactId( artifact ) );
774             }
775 
776             for ( ArchiveFilter filter : this.filters )
777             {
778                 ArtifactId pattern = new ArtifactId( filter.getArtifact() );
779 
780                 Set<File> jars = new HashSet<File>();
781 
782                 for ( Map.Entry<Artifact, ArtifactId> entry : artifacts.entrySet() )
783                 {
784                     if ( entry.getValue().matches( pattern ) )
785                     {
786                         Artifact artifact = entry.getKey();
787 
788                         jars.add( artifact.getFile() );
789 
790                         if ( createSourcesJar )
791                         {
792                             File file = resolveArtifactSources( artifact );
793                             if ( file != null )
794                             {
795                                 jars.add( file );
796                             }
797                         }
798                     }
799                 }
800 
801                 if ( jars.isEmpty() )
802                 {
803                     getLog().info( "No artifact matching filter " + filter.getArtifact() );
804 
805                     continue;
806                 }
807 
808                 simpleFilters.add( new SimpleFilter( jars, filter.getIncludes(), filter.getExcludes() ) );
809             }
810         }
811 
812         filters.addAll( simpleFilters );
813 
814         if ( minimizeJar )
815         {
816             getLog().info( "Minimizing jar " + project.getArtifact() );
817 
818             try
819             {
820                 filters.add( new MinijarFilter( project, getLog(), simpleFilters ) );
821             }
822             catch ( IOException e )
823             {
824                 throw new MojoExecutionException( "Failed to analyze class dependencies", e );
825             }
826         }
827 
828         return filters;
829     }
830 
831     private File shadedArtifactFileWithClassifier()
832     {
833         Artifact artifact = project.getArtifact();
834         final String shadedName =
835             shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "."
836                 + artifact.getArtifactHandler().getExtension();
837         return new File( outputDirectory, shadedName );
838     }
839 
840     private File shadedSourceArtifactFileWithClassifier()
841     {
842         Artifact artifact = project.getArtifact();
843         final String shadedName =
844             shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "-sources."
845                 + artifact.getArtifactHandler().getExtension();
846         return new File( outputDirectory, shadedName );
847     }
848 
849     private File shadedTestArtifactFileWithClassifier()
850     {
851         Artifact artifact = project.getArtifact();
852         final String shadedName =
853             shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "-tests."
854                 + artifact.getArtifactHandler().getExtension();
855         return new File( outputDirectory, shadedName );
856     }
857 
858     private File shadedSourcesArtifactFile()
859     {
860         Artifact artifact = project.getArtifact();
861 
862         String shadedName;
863 
864         if ( project.getBuild().getFinalName() != null )
865         {
866             shadedName = project.getBuild().getFinalName() + "-sources." + artifact.getArtifactHandler().getExtension();
867         }
868         else
869         {
870             shadedName =
871                 shadedArtifactId + "-" + artifact.getVersion() + "-sources."
872                     + artifact.getArtifactHandler().getExtension();
873         }
874 
875         return new File( outputDirectory, shadedName );
876     }
877 
878     private File shadedTestArtifactFile()
879     {
880         Artifact artifact = project.getArtifact();
881 
882         String shadedName;
883 
884         if ( project.getBuild().getFinalName() != null )
885         {
886             shadedName = project.getBuild().getFinalName() + "-tests." + artifact.getArtifactHandler().getExtension();
887         }
888         else
889         {
890             shadedName =
891                 shadedArtifactId + "-" + artifact.getVersion() + "-tests."
892                     + artifact.getArtifactHandler().getExtension();
893         }
894 
895         return new File( outputDirectory, shadedName );
896     }
897 
898     // We need to find the direct dependencies that have been included in the uber JAR so that we can modify the
899     // POM accordingly.
900     private void createDependencyReducedPom( Set<String> artifactsToRemove )
901         throws IOException, DependencyGraphBuilderException, ProjectBuildingException
902     {
903         List<Dependency> dependencies = new ArrayList<Dependency>();
904 
905         boolean modified = false;
906 
907         List<Dependency> transitiveDeps = new ArrayList<Dependency>();
908 
909         // NOTE: By using the getArtifacts() we get the completely evaluated artifacts
910         // including the system scoped artifacts with expanded values of properties used.
911         for ( Artifact artifact : project.getArtifacts() )
912         {
913             if ( "pom".equals( artifact.getType() ) )
914             {
915                 // don't include pom type dependencies in dependency reduced pom
916                 continue;
917             }
918 
919             // promote
920             Dependency dep = createDependency( artifact );
921 
922             // we'll figure out the exclusions in a bit.
923             transitiveDeps.add( dep );
924         }
925         List<Dependency> origDeps = project.getDependencies();
926 
927         if ( promoteTransitiveDependencies )
928         {
929             origDeps = transitiveDeps;
930         }
931 
932         Model model = project.getOriginalModel();
933         // MSHADE-185: We will remove all system scoped dependencies which usually
934         // have some kind of property usage. At this time the properties within
935         // such things are already evaluated.
936         List<Dependency> originalDependencies = model.getDependencies();
937         removeSystemScopedDependencies( artifactsToRemove, originalDependencies );
938 
939         for ( Dependency d : origDeps )
940         {
941             dependencies.add( d );
942 
943             String id = getId( d );
944 
945             if ( artifactsToRemove.contains( id ) )
946             {
947                 modified = true;
948 
949                 if ( keepDependenciesWithProvidedScope )
950                 {
951                     d.setScope( "provided" );
952                 }
953                 else
954                 {
955                     dependencies.remove( d );
956                 }
957             }
958         }
959 
960         // MSHADE-155
961         model.setArtifactId( shadedArtifactId );
962 
963         // MSHADE-185: We will add those system scoped dependencies
964         // from the non interpolated original pom file. So we keep
965         // things like this: <systemPath>${tools.jar}</systemPath> intact.
966         addSystemScopedDependencyFromNonInterpolatedPom( dependencies, originalDependencies );
967 
968         // Check to see if we have a reduction and if so rewrite the POM.
969         rewriteDependencyReducedPomIfWeHaveReduction( dependencies, modified, transitiveDeps, model );
970     }
971 
972     private void rewriteDependencyReducedPomIfWeHaveReduction( List<Dependency> dependencies, boolean modified,
973                                                                List<Dependency> transitiveDeps, Model model )
974         throws IOException, ProjectBuildingException, DependencyGraphBuilderException
975     {
976         if ( modified )
977         {
978             while ( modified )
979             {
980 
981                 model.setDependencies( dependencies );
982 
983                 if ( generateUniqueDependencyReducedPom )
984                 {
985                     dependencyReducedPomLocation =
986                         File.createTempFile( "dependency-reduced-pom-", ".xml", project.getBasedir() );
987                     project.getProperties().setProperty( "maven.shade.dependency-reduced-pom",
988                                                          dependencyReducedPomLocation.getAbsolutePath() );
989                 }
990                 else
991                 {
992                     if ( dependencyReducedPomLocation == null )
993                     {
994                         // MSHADE-123: We can't default to 'target' because it messes up uses of ${project.basedir}
995                         dependencyReducedPomLocation = new File( project.getBasedir(), "dependency-reduced-pom.xml" );
996                     }
997                 }
998 
999                 File f = dependencyReducedPomLocation;
1000                 getLog().info( "Dependency-reduced POM written at: " + f.getAbsolutePath() );
1001 
1002                 if ( f.exists() )
1003                 {
1004                     // noinspection ResultOfMethodCallIgnored
1005                     f.delete();
1006                 }
1007 
1008                 Writer w = WriterFactory.newXmlWriter( f );
1009 
1010                 String replaceRelativePath = null;
1011                 if ( model.getParent() != null )
1012                 {
1013                     replaceRelativePath = model.getParent().getRelativePath();
1014 
1015                 }
1016 
1017                 if ( model.getParent() != null )
1018                 {
1019                     File parentFile =
1020                         new File( project.getBasedir(), model.getParent().getRelativePath() ).getCanonicalFile();
1021                     if ( !parentFile.isFile() )
1022                     {
1023                         parentFile = new File( parentFile, "pom.xml" );
1024                     }
1025 
1026                     parentFile = parentFile.getCanonicalFile();
1027 
1028                     String relPath = RelativizePath.convertToRelativePath( parentFile, f );
1029                     model.getParent().setRelativePath( relPath );
1030                 }
1031 
1032                 try
1033                 {
1034                     PomWriter.write( w, model, true );
1035                 }
1036                 finally
1037                 {
1038                     if ( model.getParent() != null )
1039                     {
1040                         model.getParent().setRelativePath( replaceRelativePath );
1041                     }
1042                     w.close();
1043                 }
1044 
1045                 ProjectBuildingRequest projectBuildingRequest =
1046                     new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
1047                 projectBuildingRequest.setLocalRepository( localRepository );
1048                 projectBuildingRequest.setRemoteRepositories( remoteArtifactRepositories );
1049 
1050                 ProjectBuildingResult result = projectBuilder.build( f, projectBuildingRequest );
1051 
1052                 getLog().debug( "updateExcludesInDeps()" );
1053                 modified = updateExcludesInDeps( result.getProject(), dependencies, transitiveDeps );
1054             }
1055 
1056             project.setFile( dependencyReducedPomLocation );
1057         }
1058     }
1059 
1060     private void removeSystemScopedDependencies( Set<String> artifactsToRemove, List<Dependency> originalDependencies )
1061     {
1062         for ( Dependency dependency : originalDependencies )
1063         {
1064             if ( dependency.getScope() != null && dependency.getScope().equalsIgnoreCase( "system" ) )
1065             {
1066                 artifactsToRemove.add( getId( dependency ) );
1067             }
1068         }
1069     }
1070 
1071     private void addSystemScopedDependencyFromNonInterpolatedPom( List<Dependency> dependencies,
1072                                                                   List<Dependency> originalDependencies )
1073     {
1074         for ( Dependency dependency : originalDependencies )
1075         {
1076             if ( dependency.getScope() != null && dependency.getScope().equalsIgnoreCase( "system" ) )
1077             {
1078                 dependencies.add( dependency );
1079             }
1080         }
1081     }
1082 
1083     private Dependency createDependency( Artifact artifact )
1084     {
1085         Dependency dep = new Dependency();
1086         dep.setArtifactId( artifact.getArtifactId() );
1087         if ( artifact.hasClassifier() )
1088         {
1089             dep.setClassifier( artifact.getClassifier() );
1090         }
1091         dep.setGroupId( artifact.getGroupId() );
1092         dep.setOptional( artifact.isOptional() );
1093         dep.setScope( artifact.getScope() );
1094         dep.setType( artifact.getType() );
1095         if ( useBaseVersion )
1096         {
1097             dep.setVersion( artifact.getBaseVersion() );
1098         }
1099         else
1100         {
1101             dep.setVersion( artifact.getVersion() );
1102         }
1103         return dep;
1104     }
1105 
1106     private String getId( Artifact artifact )
1107     {
1108         return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier() );
1109     }
1110 
1111     private String getId( Dependency dependency )
1112     {
1113         return getId( dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(),
1114                       dependency.getClassifier() );
1115     }
1116 
1117     private String getId( String groupId, String artifactId, String type, String classifier )
1118     {
1119         return groupId + ":" + artifactId + ":" + type + ":" + ( ( classifier != null ) ? classifier : "" );
1120     }
1121 
1122     public boolean updateExcludesInDeps( MavenProject project, List<Dependency> dependencies,
1123                                          List<Dependency> transitiveDeps )
1124         throws DependencyGraphBuilderException
1125     {
1126         DependencyNode node = dependencyGraphBuilder.buildDependencyGraph( project, null );
1127         boolean modified = false;
1128         for ( DependencyNode n2 : node.getChildren() )
1129         {
1130             for ( DependencyNode n3 : n2.getChildren() )
1131             {
1132                 // check if it really isn't in the list of original dependencies. Maven
1133                 // prior to 2.0.8 may grab versions from transients instead of
1134                 // from the direct deps in which case they would be marked included
1135                 // instead of OMITTED_FOR_DUPLICATE
1136 
1137                 // also, if not promoting the transitives, level 2's would be included
1138                 boolean found = false;
1139                 for ( Dependency dep : transitiveDeps )
1140                 {
1141                     if ( dep.getArtifactId().equals( n3.getArtifact().getArtifactId() )
1142                         && dep.getGroupId().equals( n3.getArtifact().getGroupId() ) )
1143                     {
1144                         found = true;
1145                         break;
1146                     }
1147                 }
1148 
1149                 if ( !found )
1150                 {
1151                     for ( Dependency dep : dependencies )
1152                     {
1153                         if ( dep.getArtifactId().equals( n2.getArtifact().getArtifactId() )
1154                             && dep.getGroupId().equals( n2.getArtifact().getGroupId() ) )
1155                         {
1156                             Exclusion exclusion = new Exclusion();
1157                             exclusion.setArtifactId( n3.getArtifact().getArtifactId() );
1158                             exclusion.setGroupId( n3.getArtifact().getGroupId() );
1159                             dep.addExclusion( exclusion );
1160                             modified = true;
1161                             break;
1162                         }
1163                     }
1164                 }
1165             }
1166         }
1167         return modified;
1168     }
1169 }