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.ArtifactCollector;
27  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
28  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
29  import org.apache.maven.artifact.resolver.ArtifactResolver;
30  import org.apache.maven.execution.MavenSession;
31  import org.apache.maven.model.Dependency;
32  import org.apache.maven.model.Exclusion;
33  import org.apache.maven.model.Model;
34  import org.apache.maven.plugin.AbstractMojo;
35  import org.apache.maven.plugin.MojoExecutionException;
36  import org.apache.maven.plugins.annotations.Component;
37  import org.apache.maven.plugins.annotations.LifecyclePhase;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.plugins.annotations.ResolutionScope;
41  import org.apache.maven.plugins.shade.ShadeRequest;
42  import org.apache.maven.plugins.shade.Shader;
43  import org.apache.maven.plugins.shade.filter.Filter;
44  import org.apache.maven.plugins.shade.filter.MinijarFilter;
45  import org.apache.maven.plugins.shade.filter.SimpleFilter;
46  import org.apache.maven.plugins.shade.pom.PomWriter;
47  import org.apache.maven.plugins.shade.relocation.Relocator;
48  import org.apache.maven.plugins.shade.relocation.SimpleRelocator;
49  import org.apache.maven.plugins.shade.resource.ResourceTransformer;
50  import org.apache.maven.project.DefaultProjectBuildingRequest;
51  import org.apache.maven.project.MavenProject;
52  import org.apache.maven.project.MavenProjectHelper;
53  import org.apache.maven.project.ProjectBuilder;
54  import org.apache.maven.project.ProjectBuildingException;
55  import org.apache.maven.project.ProjectBuildingRequest;
56  import org.apache.maven.project.ProjectBuildingResult;
57  import org.apache.maven.shared.dependency.graph.DependencyNode;
58  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
59  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
60  import org.codehaus.plexus.PlexusConstants;
61  import org.codehaus.plexus.PlexusContainer;
62  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
63  import org.codehaus.plexus.context.Context;
64  import org.codehaus.plexus.context.ContextException;
65  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
66  import org.codehaus.plexus.util.IOUtil;
67  import org.codehaus.plexus.util.WriterFactory;
68  
69  import java.io.File;
70  import java.io.FileInputStream;
71  import java.io.FileOutputStream;
72  import java.io.IOException;
73  import java.io.Writer;
74  import java.util.ArrayList;
75  import java.util.Arrays;
76  import java.util.Collections;
77  import java.util.HashMap;
78  import java.util.HashSet;
79  import java.util.LinkedHashSet;
80  import java.util.List;
81  import java.util.Map;
82  import java.util.Set;
83  
84  /**
85   * Mojo that performs shading delegating to the Shader component.
86   *
87   * @author Jason van Zyl
88   * @author Mauro Talevi
89   * @author David Blevins
90   * @author Hiram Chirino
91   */
92  @Mojo( name = "shade", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true,
93         requiresDependencyResolution = ResolutionScope.RUNTIME )
94  public class ShadeMojo
95      extends AbstractMojo
96      implements Contextualizable
97  {
98      /**
99       * The current Maven session.
100      */
101     @Component
102     private MavenSession session;
103 
104     /**
105      * The current Maven project.
106      */
107     @Component
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      * <pre>
166      * &lt;artifactSet&gt;
167      *   &lt;includes&gt;
168      *     &lt;include&gt;org.apache.maven:*&lt;/include&gt;
169      *   &lt;/includes&gt;
170      *   &lt;excludes&gt;
171      *     &lt;exclude&gt;*:maven-core&lt;/exclude&gt;
172      *   &lt;/excludes&gt;
173      * &lt;/artifactSet&gt;
174      * </pre>
175      */
176     @Parameter
177     private ArtifactSet artifactSet;
178 
179     /**
180      * Packages to be relocated. For example:
181      * <pre>
182      * &lt;relocations&gt;
183      *   &lt;relocation&gt;
184      *     &lt;pattern&gt;org.apache&lt;/pattern&gt;
185      *     &lt;shadedPattern&gt;hidden.org.apache&lt;/shadedPattern&gt;
186      *     &lt;includes&gt;
187      *       &lt;include&gt;org.apache.maven.*&lt;/include&gt;
188      *     &lt;/includes&gt;
189      *     &lt;excludes&gt;
190      *       &lt;exclude&gt;org.apache.maven.Public*&lt;/exclude&gt;
191      *     &lt;/excludes&gt;
192      *   &lt;/relocation&gt;
193      * &lt;/relocations&gt;
194      * </pre>
195      * <em>Note:</em> Support for includes exists only since version 1.4.
196      */
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     @Parameter
229     private ArchiveFilter[] filters;
230 
231     /**
232      * The destination directory for the shaded artifact.
233      */
234     @Parameter( defaultValue = "${project.build.directory}" )
235     private File outputDirectory;
236 
237     /**
238      * The name of the shaded artifactId.
239      * <p/>
240      * If you like to change the name of the native artifact, you may use the &lt;build>&lt;finalName> setting.
241      * If this is set to something different than &lt;build>&lt;finalName>, no file replacement
242      * will be performed, even if shadedArtifactAttached is being used.
243      */
244     @Parameter
245     private String finalName;
246 
247     /**
248      * The name of the shaded artifactId. So you may want to use a different artifactId and keep
249      * the standard version. If the original artifactId was "foo" then the final artifact would
250      * be something like foo-1.0.jar. So if you change the artifactId you might have something
251      * like foo-special-1.0.jar.
252      */
253     @Parameter( defaultValue = "${project.artifactId}" )
254     private String shadedArtifactId;
255 
256     /**
257      * If specified, this will include only artifacts which have groupIds which
258      * start with this.
259      */
260     @Parameter
261     private String shadedGroupFilter;
262 
263     /**
264      * Defines whether the shaded artifact should be attached as classifier to
265      * the original artifact.  If false, the shaded jar will be the main artifact
266      * of the project
267      */
268     @Parameter
269     private boolean shadedArtifactAttached;
270 
271     /**
272      * Flag whether to generate a simplified POM for the shaded artifact. If set to <code>true</code>, dependencies that
273      * have been included into the uber JAR will be removed from the <code>&lt;dependencies&gt;</code> section of the
274      * generated POM. The reduced POM will be named <code>dependency-reduced-pom.xml</code> and is stored into the same
275      * directory as the shaded artifact. Unless you also specify dependencyReducedPomLocation, the plugin will
276      * create a temporary file named <code>dependency-reduced-pom.xml</code> in the project basedir.
277      */
278     @Parameter( defaultValue = "true" )
279     private boolean createDependencyReducedPom;
280 
281 
282     /**
283      * Where to put the dependency reduced pom.
284      * Note: setting a value for this parameter with a directory other than ${basedir} will change the value of ${basedir}
285      * for all executions that come after the shade execution. This is often not what you want. This is considered
286      * an open issue with this plugin.
287      *
288      * @since 1.7
289      */
290     @Parameter( defaultValue = "${basedir}/dependency-reduced-pom.xml" )
291     private File dependencyReducedPomLocation;
292 
293     /**
294      * Create a dependency-reduced POM in ${basedir}/drp-UNIQUE.pom. This avoids build collisions
295      * of parallel builds without moving the dependency-reduced POM to a different directory.
296      * The property maven.shade.dependency-reduced-pom is set to the generated filename.
297      *
298      * @since 1.7.2
299      */
300     @Parameter( defaultValue = "false" )
301     private boolean generateUniqueDependencyReducedPom;
302 
303     /**
304      * When true, dependencies are kept in the pom but with scope 'provided'; when false,
305      * the dependency is removed.
306      */
307     @Parameter
308     private boolean keepDependenciesWithProvidedScope;
309 
310     /**
311      * When true, transitive deps of removed dependencies are promoted to direct dependencies.
312      * This should allow the drop in replacement of the removed deps with the new shaded
313      * jar and everything should still work.
314      */
315     @Parameter
316     private boolean promoteTransitiveDependencies;
317 
318     /**
319      * The name of the classifier used in case the shaded artifact is attached.
320      */
321     @Parameter( defaultValue = "shaded" )
322     private String shadedClassifierName;
323 
324     /**
325      * When true, it will attempt to create a sources jar as well
326      */
327     @Parameter
328     private boolean createSourcesJar;
329 
330     /**
331      * When true, it will attempt to shade the contents of the java source files when creating the sources jar.
332      * When false, it will just relocate the java source files to the shaded paths, but will not modify the
333      * actual contents of the java source files.
334      *
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      * @since 1.6
369      */
370     private PlexusContainer plexusContainer;
371 
372     public void contextualize( Context context )
373         throws ContextException
374     {
375         plexusContainer = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
376     }
377 
378     /**
379      * @throws MojoExecutionException
380      */
381     public void execute()
382         throws MojoExecutionException
383     {
384 
385         if ( shaderHint != null )
386         {
387             try
388             {
389                 shader = (Shader) plexusContainer.lookup( Shader.ROLE, shaderHint );
390             }
391             catch ( ComponentLookupException e )
392             {
393                 throw new MojoExecutionException(
394                     "unable to lookup own Shader implementation with hint:'" + shaderHint + "'", e );
395             }
396         }
397 
398         Set<File> artifacts = new LinkedHashSet<File>();
399         Set<String> artifactIds = new LinkedHashSet<String>();
400         Set<File> sourceArtifacts = new LinkedHashSet<File>();
401 
402         ArtifactSelector artifactSelector =
403             new ArtifactSelector( project.getArtifact(), artifactSet, shadedGroupFilter );
404 
405         if ( artifactSelector.isSelected( project.getArtifact() ) && !"pom".equals( project.getArtifact().getType() ) )
406         {
407             if ( invalidMainArtifact() )
408             {
409                 getLog().error( "The project main artifact does not exist. This could have the following" );
410                 getLog().error( "reasons:" );
411                 getLog().error( "- You have invoked the goal directly from the command line. This is not" );
412                 getLog().error( "  supported. Please add the goal to the default lifecycle via an" );
413                 getLog().error( "  <execution> element in your POM and use \"mvn package\" to have it run." );
414                 getLog().error( "- You have bound the goal to a lifecycle phase before \"package\". Please" );
415                 getLog().error( "  remove this binding from your POM such that the goal will be run in" );
416                 getLog().error( "  the proper phase." );
417                 getLog().error(
418                     "- You removed the configuration of the maven-jar-plugin that produces the main artifact." );
419                 throw new MojoExecutionException(
420                     "Failed to create shaded artifact, " + "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 
435         for ( Artifact artifact : project.getArtifacts() )
436         {
437             if ( !artifactSelector.isSelected( artifact ) )
438             {
439                 getLog().info( "Excluding " + artifact.getId() + " from the shaded jar." );
440 
441                 continue;
442             }
443 
444             if ( "pom".equals( artifact.getType() ) )
445             {
446                 getLog().info( "Skipping pom dependency " + artifact.getId() + " in the shaded jar." );
447                 continue;
448             }
449 
450             getLog().info( "Including " + artifact.getId() + " in the shaded jar." );
451 
452             artifacts.add( artifact.getFile() );
453             artifactIds.add( getId( artifact ) );
454 
455             if ( createSourcesJar )
456             {
457                 File file = resolveArtifactSources( artifact );
458                 if ( file != null )
459                 {
460                     sourceArtifacts.add( file );
461                 }
462             }
463         }
464 
465         File outputJar = ( outputFile != null ) ? outputFile : shadedArtifactFileWithClassifier();
466         File sourcesJar = shadedSourceArtifactFileWithClassifier();
467 
468         // Now add our extra resources
469         try
470         {
471             List<Filter> filters = getFilters();
472 
473             List<Relocator> relocators = getRelocators();
474 
475             List<ResourceTransformer> resourceTransformers = getResourceTransformers();
476 
477             ShadeRequest shadeRequest = new ShadeRequest();
478             shadeRequest.setJars( artifacts );
479             shadeRequest.setUberJar( outputJar );
480             shadeRequest.setFilters( filters );
481             shadeRequest.setRelocators( relocators );
482             shadeRequest.setResourceTransformers( resourceTransformers );
483 
484             shader.shade( shadeRequest );
485 
486             if ( createSourcesJar )
487             {
488                 ShadeRequest shadeSourcesRequest = new ShadeRequest();
489                 shadeSourcesRequest.setJars( sourceArtifacts );
490                 shadeSourcesRequest.setUberJar( sourcesJar );
491                 shadeSourcesRequest.setFilters( filters );
492                 shadeSourcesRequest.setRelocators( relocators );
493                 shadeSourcesRequest.setResourceTransformers( resourceTransformers );
494                 shadeSourcesRequest.setShadeSourcesContent( shadeSourcesContent );
495 
496                 shader.shade( shadeSourcesRequest );
497             }
498 
499             if ( outputFile == null )
500             {
501                 boolean renamed = false;
502 
503                 // rename the output file if a specific finalName is set
504                 // but don't rename if the finalName is the <build><finalName>
505                 // because this will be handled implicitly later
506                 if ( finalName != null && finalName.length() > 0 && !finalName.equals(
507                     project.getBuild().getFinalName() ) )
508                 {
509                     String finalFileName = finalName + "." + project.getArtifact().getArtifactHandler().getExtension();
510                     File finalFile = new File( outputDirectory, finalFileName );
511                     replaceFile( finalFile, outputJar );
512                     outputJar = finalFile;
513 
514                     renamed = true;
515                 }
516 
517                 if ( shadedArtifactAttached )
518                 {
519                     getLog().info( "Attaching shaded artifact." );
520                     projectHelper.attachArtifact( project, project.getArtifact().getType(), shadedClassifierName,
521                                                   outputJar );
522                     if ( createSourcesJar )
523                     {
524                         projectHelper.attachArtifact( project, "jar", shadedClassifierName + "-sources", sourcesJar );
525                     }
526                 }
527                 else if ( !renamed )
528                 {
529                     getLog().info( "Replacing original artifact with shaded artifact." );
530                     File originalArtifact = project.getArtifact().getFile();
531                     replaceFile( originalArtifact, outputJar );
532 
533                     if ( createSourcesJar )
534                     {
535                         File shadedSources = shadedSourcesArtifactFile();
536 
537                         replaceFile( shadedSources, sourcesJar );
538 
539                         projectHelper.attachArtifact( project, "jar", "sources", shadedSources );
540                     }
541 
542                     if ( createDependencyReducedPom )
543                     {
544                         createDependencyReducedPom( artifactIds );
545                     }
546                 }
547             }
548         }
549         catch ( Exception e )
550         {
551             throw new MojoExecutionException( "Error creating shaded jar: " + e.getMessage(), e );
552         }
553     }
554 
555     private boolean invalidMainArtifact()
556     {
557         return project.getArtifact().getFile() == null || !project.getArtifact().getFile().isFile();
558     }
559 
560     private void replaceFile( File oldFile, File newFile )
561         throws MojoExecutionException
562     {
563         getLog().info( "Replacing " + oldFile + " with " + newFile );
564 
565         File origFile = new File( outputDirectory, "original-" + oldFile.getName() );
566         if ( oldFile.exists() && !oldFile.renameTo( origFile ) )
567         {
568             //try a gc to see if an unclosed stream needs garbage collecting
569             System.gc();
570             System.gc();
571 
572             if ( !oldFile.renameTo( origFile ) )
573             {
574                 // Still didn't work.   We'll do a copy
575                 try
576                 {
577                     FileOutputStream fout = new FileOutputStream( origFile );
578                     FileInputStream fin = new FileInputStream( oldFile );
579                     try
580                     {
581                         IOUtil.copy( fin, fout );
582                     }
583                     finally
584                     {
585                         IOUtil.close( fin );
586                         IOUtil.close( fout );
587                     }
588                 }
589                 catch ( IOException ex )
590                 {
591                     //kind of ignorable here.   We're just trying to save the original
592                     getLog().warn( ex );
593                 }
594             }
595         }
596         if ( !newFile.renameTo( oldFile ) )
597         {
598             //try a gc to see if an unclosed stream needs garbage collecting
599             System.gc();
600             System.gc();
601 
602             if ( !newFile.renameTo( oldFile ) )
603             {
604                 // Still didn't work.   We'll do a copy
605                 try
606                 {
607                     FileOutputStream fout = new FileOutputStream( oldFile );
608                     FileInputStream fin = new FileInputStream( newFile );
609                     try
610                     {
611                         IOUtil.copy( fin, fout );
612                     }
613                     finally
614                     {
615                         IOUtil.close( fin );
616                         IOUtil.close( fout );
617                     }
618                 }
619                 catch ( IOException ex )
620                 {
621                     throw new MojoExecutionException( "Could not replace original artifact with shaded artifact!", ex );
622                 }
623             }
624         }
625     }
626 
627     private File resolveArtifactSources( Artifact artifact )
628     {
629 
630         Artifact resolvedArtifact =
631             artifactFactory.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(),
632                                                           artifact.getVersion(), "java-source", "sources" );
633 
634         try
635         {
636             artifactResolver.resolve( resolvedArtifact, remoteArtifactRepositories, localRepository );
637         }
638         catch ( ArtifactNotFoundException e )
639         {
640             // ignore, the jar has not been found
641         }
642         catch ( ArtifactResolutionException e )
643         {
644             getLog().warn( "Could not get sources for " + artifact );
645         }
646 
647         if ( resolvedArtifact.isResolved() )
648         {
649             return resolvedArtifact.getFile();
650         }
651         return null;
652     }
653 
654     private List<Relocator> getRelocators()
655     {
656         List<Relocator> relocators = new ArrayList<Relocator>();
657 
658         if ( relocations == null )
659         {
660             return relocators;
661         }
662 
663         for ( int i = 0; i < relocations.length; i++ )
664         {
665             PackageRelocation r = relocations[i];
666 
667             relocators.add( new SimpleRelocator( r.getPattern(), r.getShadedPattern(), r.getIncludes(), r.getExcludes(),
668                                                  r.isRawString() ) );
669         }
670 
671         return relocators;
672     }
673 
674     private List<ResourceTransformer> getResourceTransformers()
675     {
676         if ( transformers == null )
677         {
678             return Collections.emptyList();
679         }
680 
681         return Arrays.asList( transformers );
682     }
683 
684     private List<Filter> getFilters()
685         throws MojoExecutionException
686     {
687         List<Filter> filters = new ArrayList<Filter>();
688         List<SimpleFilter> simpleFilters = new ArrayList<SimpleFilter>();
689 
690         if ( this.filters != null && this.filters.length > 0 )
691         {
692             Map<Artifact, ArtifactId> artifacts = new HashMap<Artifact, ArtifactId>();
693 
694             artifacts.put( project.getArtifact(), new ArtifactId( project.getArtifact() ) );
695 
696             for ( Artifact artifact : project.getArtifacts() )
697             {
698                 artifacts.put( artifact, new ArtifactId( artifact ) );
699             }
700 
701             for ( ArchiveFilter filter : this.filters )
702             {
703                 ArtifactId pattern = new ArtifactId( filter.getArtifact() );
704 
705                 Set<File> jars = new HashSet<File>();
706 
707                 for ( Map.Entry<Artifact, ArtifactId> entry : artifacts.entrySet() )
708                 {
709                     if ( entry.getValue().matches( pattern ) )
710                     {
711                         Artifact artifact = entry.getKey();
712 
713                         jars.add( artifact.getFile() );
714 
715                         if ( createSourcesJar )
716                         {
717                             File file = resolveArtifactSources( artifact );
718                             if ( file != null )
719                             {
720                                 jars.add( file );
721                             }
722                         }
723                     }
724                 }
725 
726                 if ( jars.isEmpty() )
727                 {
728                     getLog().info( "No artifact matching filter " + filter.getArtifact() );
729 
730                     continue;
731                 }
732 
733                 simpleFilters.add( new SimpleFilter( jars, filter.getIncludes(), filter.getExcludes() ) );
734             }
735         }
736 
737         filters.addAll( simpleFilters );
738 
739         if ( minimizeJar )
740         {
741             getLog().info( "Minimizing jar " + project.getArtifact() );
742 
743             try
744             {
745                 filters.add( new MinijarFilter( project, getLog(), simpleFilters ) );
746             }
747             catch ( IOException e )
748             {
749                 throw new MojoExecutionException( "Failed to analyze class dependencies", e );
750             }
751         }
752 
753         return filters;
754     }
755 
756     private File shadedArtifactFileWithClassifier()
757     {
758         Artifact artifact = project.getArtifact();
759         final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "."
760             + artifact.getArtifactHandler().getExtension();
761         return new File( outputDirectory, shadedName );
762     }
763 
764     private File shadedSourceArtifactFileWithClassifier()
765     {
766         Artifact artifact = project.getArtifact();
767         final String shadedName =
768             shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "-sources."
769                 + artifact.getArtifactHandler().getExtension();
770         return new File( outputDirectory, shadedName );
771     }
772 
773     private File shadedSourcesArtifactFile()
774     {
775         Artifact artifact = project.getArtifact();
776 
777         String shadedName;
778 
779         if ( project.getBuild().getFinalName() != null )
780         {
781             shadedName = project.getBuild().getFinalName() + "-sources." + artifact.getArtifactHandler().getExtension();
782         }
783         else
784         {
785             shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-sources."
786                 + artifact.getArtifactHandler().getExtension();
787         }
788 
789         return new File( outputDirectory, shadedName );
790     }
791 
792     // We need to find the direct dependencies that have been included in the uber JAR so that we can modify the
793     // POM accordingly.
794     private void createDependencyReducedPom( Set<String> artifactsToRemove )
795         throws IOException, DependencyGraphBuilderException, ProjectBuildingException
796     {
797         Model model = project.getOriginalModel();
798         List<Dependency> dependencies = new ArrayList<Dependency>();
799 
800         boolean modified = false;
801 
802         List<Dependency> transitiveDeps = new ArrayList<Dependency>();
803 
804         for ( Artifact artifact : project.getArtifacts() )
805         {
806             if ( "pom".equals( artifact.getType() ) )
807             {
808                 // don't include pom type dependencies in dependency reduced pom
809                 continue;
810             }
811 
812             //promote
813             Dependency dep = new Dependency();
814             dep.setArtifactId( artifact.getArtifactId() );
815             if ( artifact.hasClassifier() )
816             {
817                 dep.setClassifier( artifact.getClassifier() );
818             }
819             dep.setGroupId( artifact.getGroupId() );
820             dep.setOptional( artifact.isOptional() );
821             dep.setScope( artifact.getScope() );
822             dep.setType( artifact.getType() );
823             dep.setVersion( artifact.getVersion() );
824 
825             //we'll figure out the exclusions in a bit.
826 
827             transitiveDeps.add( dep );
828         }
829         List<Dependency> origDeps = project.getDependencies();
830 
831         if ( promoteTransitiveDependencies )
832         {
833             origDeps = transitiveDeps;
834         }
835 
836         for ( Dependency d : origDeps )
837         {
838             dependencies.add( d );
839 
840             String id = getId( d );
841 
842             if ( artifactsToRemove.contains( id ) )
843             {
844                 modified = true;
845 
846                 if ( keepDependenciesWithProvidedScope )
847                 {
848                     d.setScope( "provided" );
849                 }
850                 else
851                 {
852                     dependencies.remove( d );
853                 }
854             }
855         }
856 
857         // Check to see if we have a reduction and if so rewrite the POM.
858         if ( modified )
859         {
860             while ( modified )
861             {
862 
863                 model.setDependencies( dependencies );
864 
865                 if ( generateUniqueDependencyReducedPom )
866                 {
867                     dependencyReducedPomLocation = File.createTempFile( "dependency-reduced-pom-", ".xml", project.getBasedir() );
868                     project.getProperties().setProperty( "maven.shade.dependency-reduced-pom", dependencyReducedPomLocation.getAbsolutePath() );
869                 }
870                 else
871                 {
872                     if ( dependencyReducedPomLocation == null )
873                     {
874                         // MSHADE-123: We can't default to 'target' because it messes up uses of ${project.basedir}
875                         dependencyReducedPomLocation = new File( project.getBasedir(), "dependency-reduced-pom.xml" );
876                     }
877                 }
878 
879                 File f = dependencyReducedPomLocation;
880                 getLog().info( "Dependency-reduced POM written at: " + f.getAbsolutePath() );
881 
882                 if ( f.exists() )
883                 {
884                     f.delete();
885                 }
886 
887                 Writer w = WriterFactory.newXmlWriter( f );
888 
889                 String origRelativePath = null;
890                 String replaceRelativePath = null;
891                 if ( model.getParent() != null )
892                 {
893                     origRelativePath = model.getParent().getRelativePath();
894 
895                 }
896                 replaceRelativePath = origRelativePath;
897 
898                 if ( origRelativePath == null )
899                 {
900                     origRelativePath = "../pom.xml";
901                 }
902 
903                 if ( model.getParent() != null )
904                 {
905                     File parentFile =
906                         new File( project.getBasedir(), model.getParent().getRelativePath() ).getCanonicalFile();
907                     if ( !parentFile.isFile() )
908                     {
909                         parentFile = new File( parentFile, "pom.xml" );
910                     }
911 
912                     parentFile = parentFile.getCanonicalFile();
913 
914                     String relPath = RelativizePath.convertToRelativePath( parentFile, f );
915                     model.getParent().setRelativePath( relPath );
916                 }
917 
918                 try
919                 {
920                     PomWriter.write( w, model, true );
921                 }
922                 finally
923                 {
924                     if ( model.getParent() != null )
925                     {
926                         model.getParent().setRelativePath( replaceRelativePath );
927                     }
928                     w.close();
929                 }
930 
931                 ProjectBuildingRequest projectBuildingRequest =
932                     new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
933                 projectBuildingRequest.setLocalRepository( localRepository );
934                 projectBuildingRequest.setRemoteRepositories( remoteArtifactRepositories );
935 
936                 ProjectBuildingResult result = projectBuilder.build( f, projectBuildingRequest );
937 
938                 modified = updateExcludesInDeps( result.getProject(), dependencies, transitiveDeps );
939             }
940 
941             project.setFile( dependencyReducedPomLocation );
942         }
943     }
944 
945     private String getId( Artifact artifact )
946     {
947         return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier() );
948     }
949 
950     private String getId( Dependency dependency )
951     {
952         return getId( dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(),
953                       dependency.getClassifier() );
954     }
955 
956     private String getId( String groupId, String artifactId, String type, String classifier )
957     {
958         return groupId + ":" + artifactId + ":" + type + ":" + ( ( classifier != null ) ? classifier : "" );
959     }
960 
961     public boolean updateExcludesInDeps( MavenProject project, List<Dependency> dependencies,
962                                          List<Dependency> transitiveDeps )
963         throws DependencyGraphBuilderException
964     {
965         DependencyNode node = dependencyGraphBuilder.buildDependencyGraph( project, null );
966         boolean modified = false;
967         for ( DependencyNode n2 : node.getChildren() )
968         {
969             for ( DependencyNode n3 : n2.getChildren() )
970             {
971                 //check if it really isn't in the list of original dependencies.  Maven
972                 //prior to 2.0.8 may grab versions from transients instead of
973                 //from the direct deps in which case they would be marked included
974                 //instead of OMITTED_FOR_DUPLICATE
975 
976                 //also, if not promoting the transitives, level 2's would be included
977                 boolean found = false;
978                 for ( Dependency dep : transitiveDeps )
979                 {
980                     if ( dep.getArtifactId().equals( n3.getArtifact().getArtifactId() )
981                         && dep.getGroupId().equals( n3.getArtifact().getGroupId() ) )
982                     {
983                         found = true;
984                         break;
985                     }
986                 }
987 
988                 if ( !found )
989                 {
990                     for ( Dependency dep : dependencies )
991                     {
992                         if ( dep.getArtifactId().equals( n2.getArtifact().getArtifactId() )
993                             && dep.getGroupId().equals( n2.getArtifact().getGroupId() ) )
994                         {
995                             Exclusion exclusion = new Exclusion();
996                             exclusion.setArtifactId( n3.getArtifact().getArtifactId() );
997                             exclusion.setGroupId( n3.getArtifact().getGroupId() );
998                             dep.addExclusion( exclusion );
999                             modified = true;
1000                             break;
1001                         }
1002                     }
1003                 }
1004             }
1005         }
1006         return modified;
1007     }
1008 }