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