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