View Javadoc
1   package org.apache.maven.plugins.dependency.resolvers;
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  
24  import org.apache.maven.plugin.MojoExecutionException;
25  import org.apache.maven.plugins.dependency.utils.DependencyStatusSets;
26  import org.apache.maven.plugins.dependency.utils.DependencyUtil;
27  import org.apache.maven.plugins.dependency.utils.filters.ResolveFileFilter;
28  import org.apache.maven.plugins.dependency.utils.markers.SourcesFileMarkerHandler;
29  import org.apache.maven.plugins.annotations.LifecyclePhase;
30  import org.apache.maven.plugins.annotations.Mojo;
31  import org.apache.maven.plugins.annotations.Parameter;
32  import org.apache.maven.plugins.annotations.ResolutionScope;
33  import org.apache.maven.shared.artifact.filter.collection.ArtifactsFilter;
34  import org.apache.maven.shared.utils.logging.MessageBuilder;
35  import org.apache.maven.shared.utils.logging.MessageUtils;
36  
37  import java.io.File;
38  import java.io.IOException;
39  import java.lang.reflect.InvocationTargetException;
40  import java.lang.reflect.Method;
41  import java.util.ArrayList;
42  import java.util.Collections;
43  import java.util.LinkedHashSet;
44  import java.util.List;
45  import java.util.Set;
46  import java.util.jar.JarFile;
47  import java.util.jar.Manifest;
48  
49  /**
50   * Goal that resolves the project dependencies from the repository. When using this goal while running on Java 9 the
51   * module names will be visible as well.
52   *
53   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
54   * @version $Id: ResolveDependenciesMojo.java 1807877 2017-09-09 10:35:59Z khmarbaise $
55   * @since 2.0
56   */
57  //CHECKSTYLE_OFF: LineLength
58  @Mojo( name = "resolve", requiresDependencyResolution = ResolutionScope.TEST, defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true )
59  //CHECKSTYLE_ON: LineLength
60  public class ResolveDependenciesMojo
61      extends AbstractResolveMojo
62  {
63  
64      /**
65       * If we should display the scope when resolving
66       *
67       * @since 2.0-alpha-2
68       */
69      @Parameter( property = "mdep.outputScope", defaultValue = "true" )
70      protected boolean outputScope;
71  
72      /**
73       * Only used to store results for integration test validation
74       */
75      DependencyStatusSets results;
76  
77      /**
78       * Sort the output list of resolved artifacts alphabetically. The default ordering matches the classpath order.
79       * 
80       * @since 2.8
81       */
82      @Parameter( property = "sort", defaultValue = "false" )
83      boolean sort;
84  
85      /**
86       * Include parent poms in the dependency resolution list.
87       * 
88       * @since 2.8
89       */
90      @Parameter( property = "includeParents", defaultValue = "false" )
91      boolean includeParents;
92  
93      /**
94       * Main entry into mojo. Gets the list of dependencies and iterates through displaying the resolved version.
95       *
96       * @throws MojoExecutionException with a message if an error occurs.
97       */
98      @Override
99      protected void doExecute()
100         throws MojoExecutionException
101     {
102         // get sets of dependencies
103         results = this.getDependencySets( false, includeParents );
104 
105         String output = getOutput( outputAbsoluteArtifactFilename, outputScope, sort );
106         try
107         {
108             if ( outputFile == null )
109             {
110                 DependencyUtil.log( output, getLog() );
111             }
112             else
113             {
114                 DependencyUtil.write( output, outputFile, appendOutput, getLog() );
115             }
116         }
117         catch ( IOException e )
118         {
119             throw new MojoExecutionException( e.getMessage(), e );
120         }
121     }
122 
123     /**
124      * @return Returns the results.
125      */
126     public DependencyStatusSets getResults()
127     {
128         return this.results;
129     }
130 
131     @Override
132     protected ArtifactsFilter getMarkedArtifactFilter()
133     {
134         return new ResolveFileFilter( new SourcesFileMarkerHandler( this.markersDirectory ) );
135     }
136 
137     public String getOutput( boolean outputAbsoluteArtifactFilename, boolean outputScope, boolean sort )
138     {
139         StringBuilder sb = new StringBuilder();
140         sb.append( "\n" );
141         sb.append( "The following files have been resolved:\n" );
142         if ( results.getResolvedDependencies() == null || results.getResolvedDependencies().isEmpty() )
143         {
144             sb.append( "   none\n" );
145         }
146         else
147         {
148             sb.append( buildArtifactListOutput( results.getResolvedDependencies(), outputAbsoluteArtifactFilename,
149                                                 outputScope, sort ) );
150         }
151 
152         if ( results.getSkippedDependencies() != null && !results.getSkippedDependencies().isEmpty() )
153         {
154             sb.append( "\n" );
155             sb.append( "The following files were skipped:\n" );
156             Set<Artifact> skippedDependencies = new LinkedHashSet<Artifact>();
157             skippedDependencies.addAll( results.getSkippedDependencies() );
158             sb.append( buildArtifactListOutput( skippedDependencies, outputAbsoluteArtifactFilename, outputScope,
159                                                 sort ) );
160         }
161 
162         if ( results.getUnResolvedDependencies() != null && !results.getUnResolvedDependencies().isEmpty() )
163         {
164             sb.append( "\n" );
165             sb.append( "The following files have NOT been resolved:\n" );
166             Set<Artifact> unResolvedDependencies = new LinkedHashSet<Artifact>();
167             unResolvedDependencies.addAll( results.getUnResolvedDependencies() );
168             sb.append( buildArtifactListOutput( unResolvedDependencies, outputAbsoluteArtifactFilename, outputScope,
169                                                 sort ) );
170         }
171         sb.append( "\n" );
172 
173         return sb.toString();
174     }
175 
176     private StringBuilder buildArtifactListOutput( Set<Artifact> artifacts, boolean outputAbsoluteArtifactFilename,
177                                                    boolean outputScope, boolean sort )
178     {
179         StringBuilder sb = new StringBuilder();
180         List<String> artifactStringList = new ArrayList<String>();
181         for ( Artifact artifact : artifacts )
182         {
183             MessageBuilder messageBuilder = MessageUtils.buffer();
184 
185             messageBuilder.a( "   " );
186 
187             if ( outputScope )
188             {
189                 messageBuilder.a( artifact.toString() );
190             }
191             else
192             {
193                 messageBuilder.a( artifact.getId() );
194             }
195 
196             if ( outputAbsoluteArtifactFilename )
197             {
198                 try
199                 {
200                     // we want to print the absolute file name here
201                     String artifactFilename = artifact.getFile().getAbsoluteFile().getPath();
202 
203                     messageBuilder.a( ':' ).a( artifactFilename );
204                 }
205                 catch ( NullPointerException e )
206                 {
207                     // ignore the null pointer, we'll output a null string
208                 }
209             }
210 
211             if ( outputScope && artifact.isOptional() )
212             {
213                 messageBuilder.a( " (optional) " );
214             }
215 
216             // dependencies:collect won't download jars
217             if ( artifact.getFile() != null )
218             {
219                 ModuleDescriptor moduleDescriptor = getModuleDescriptor( artifact.getFile() );
220                 if ( moduleDescriptor != null )
221                 {
222                     messageBuilder.project( " -- module " + moduleDescriptor.name );
223 
224                     if ( moduleDescriptor.automatic )
225                     {
226                         if ( "MANIFEST".equals( moduleDescriptor.moduleNameSource ) )
227                         {
228                             messageBuilder.strong( " [auto]" );
229                         }
230                         else
231                         {
232                             messageBuilder.warning( " (auto)" );
233                         }
234                     }
235                 }
236             }
237             artifactStringList.add( messageBuilder.toString() + "\n" );
238         }
239         if ( sort )
240         {
241             Collections.sort( artifactStringList );
242         }
243         for ( String artifactString : artifactStringList )
244         {
245             sb.append( artifactString );
246         }
247         return sb;
248     }
249 
250     private ModuleDescriptor getModuleDescriptor( File artifactFile )
251     {
252         ModuleDescriptor moduleDescriptor = null;
253         try
254         {
255             // Use Java9 code to get moduleName, don't try to do it better with own implementation
256             Class<?> moduleFinderClass = Class.forName( "java.lang.module.ModuleFinder" );
257 
258             java.nio.file.Path path = artifactFile.toPath();
259 
260             Method ofMethod = moduleFinderClass.getMethod( "of", java.nio.file.Path[].class );
261             Object moduleFinderInstance = ofMethod.invoke( null, new Object[] { new java.nio.file.Path[] { path } } );
262 
263             Method findAllMethod = moduleFinderClass.getMethod( "findAll" );
264             @SuppressWarnings( "unchecked" )
265             Set<Object> moduleReferences = (Set<Object>) findAllMethod.invoke( moduleFinderInstance );
266 
267             // moduleReferences can be empty when referring to target/classes without module-info.class
268             if ( !moduleReferences.isEmpty() )
269             {
270                 Object moduleReference = moduleReferences.iterator().next();
271                 Method descriptorMethod = moduleReference.getClass().getMethod( "descriptor" );
272                 Object moduleDescriptorInstance = descriptorMethod.invoke( moduleReference );
273 
274                 Method nameMethod = moduleDescriptorInstance.getClass().getMethod( "name" );
275                 String name = (String) nameMethod.invoke( moduleDescriptorInstance );
276 
277                 moduleDescriptor = new ModuleDescriptor();
278                 moduleDescriptor.name = name;
279 
280                 Method isAutomaticMethod = moduleDescriptorInstance.getClass().getMethod( "isAutomatic" );
281                 moduleDescriptor.automatic = (Boolean) isAutomaticMethod.invoke( moduleDescriptorInstance );
282 
283                 if ( moduleDescriptor.automatic )
284                 {
285                     if ( artifactFile.isFile() )
286                     {
287                         JarFile jarFile = null;
288                         try
289                         {
290                             jarFile = new JarFile( artifactFile );
291 
292                             Manifest manifest = jarFile.getManifest();
293 
294                             if ( manifest != null
295                                 && manifest.getMainAttributes().getValue( "Automatic-Module-Name" ) != null )
296                             {
297                                 moduleDescriptor.moduleNameSource = "MANIFEST";
298                             }
299                             else
300                             {
301                                 moduleDescriptor.moduleNameSource = "FILENAME";
302                             }
303                         }
304                         catch ( IOException e )
305                         {
306                             // noop
307                         }
308                         finally
309                         {
310                             if ( jarFile != null )
311                             {
312                                 try
313                                 {
314                                     jarFile.close();
315                                 }
316                                 catch ( IOException e )
317                                 {
318                                     // noop
319                                 }
320                             }
321                         }
322                     }
323                 }
324             }
325         }
326         catch ( ClassNotFoundException e )
327         {
328             // do nothing
329         }
330         catch ( NoSuchMethodException e )
331         {
332             e.printStackTrace();
333         }
334         catch ( SecurityException e )
335         {
336             // do nothing
337         }
338         catch ( IllegalAccessException e )
339         {
340             // do nothing
341         }
342         catch ( IllegalArgumentException e )
343         {
344             // do nothing
345         }
346         catch ( InvocationTargetException e )
347         {
348             Throwable cause = e.getCause();
349             while ( cause.getCause() != null )
350             {
351                 cause = cause.getCause();
352             }
353             getLog().info( "Can't extract module name from " + artifactFile.getName() + ": " + cause.getMessage() );
354         }
355         return moduleDescriptor;
356     }
357 
358     private class ModuleDescriptor
359     {
360         String name;
361 
362         boolean automatic = true;
363 
364         String moduleNameSource;
365     }
366 }