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