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