View Javadoc
1   package org.apache.maven.plugins.dependency;
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 java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Enumeration;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.jar.JarEntry;
28  import java.util.jar.JarFile;
29  import java.util.regex.Matcher;
30  import java.util.regex.Pattern;
31  
32  import org.apache.maven.artifact.handler.ArtifactHandler;
33  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
34  import org.apache.maven.artifact.repository.ArtifactRepository;
35  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
36  import org.apache.maven.artifact.repository.MavenArtifactRepository;
37  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
38  import org.apache.maven.execution.MavenSession;
39  import org.apache.maven.plugin.AbstractMojo;
40  import org.apache.maven.plugin.MojoExecutionException;
41  import org.apache.maven.plugin.MojoFailureException;
42  import org.apache.maven.plugins.annotations.Component;
43  import org.apache.maven.plugins.annotations.Mojo;
44  import org.apache.maven.plugins.annotations.Parameter;
45  import org.apache.maven.project.DefaultProjectBuildingRequest;
46  import org.apache.maven.project.ProjectBuildingRequest;
47  import org.apache.maven.repository.RepositorySystem;
48  import org.apache.maven.settings.Settings;
49  import org.apache.maven.shared.transfer.artifact.ArtifactCoordinate;
50  import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate;
51  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
52  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
53  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
54  import org.apache.maven.shared.transfer.dependencies.DefaultDependableCoordinate;
55  import org.apache.maven.shared.transfer.dependencies.DependableCoordinate;
56  import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
57  import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolverException;
58  
59  
60  /**
61   * Retrieves and lists all classes contained in the specified artifact from the specified remote repositories.
62   */
63  @Mojo( name = "list-classes", requiresProject = false, threadSafe = true )
64  public class ListClassesMojo
65      extends AbstractMojo
66  {
67      private static final Pattern ALT_REPO_SYNTAX_PATTERN = Pattern.compile( "(.+)::(.*)::(.+)" );
68  
69      @Parameter( defaultValue = "${session}", required = true, readonly = true )
70      private MavenSession session;
71  
72      @Component
73      private ArtifactResolver artifactResolver;
74  
75      @Component
76      private DependencyResolver dependencyResolver;
77  
78      @Component
79      private ArtifactHandlerManager artifactHandlerManager;
80  
81      /**
82       * Map that contains the layouts.
83       */
84      @Component( role = ArtifactRepositoryLayout.class )
85      private Map<String, ArtifactRepositoryLayout> repositoryLayouts;
86  
87      /**
88       * The repository system.
89       */
90      @Component
91      private RepositorySystem repositorySystem;
92  
93      private DefaultDependableCoordinate coordinate = new DefaultDependableCoordinate();
94  
95      /**
96       * The group ID of the artifact to download. Ignored if {@link #artifact} is used.
97       */
98      @Parameter( property = "groupId" )
99      private String groupId;
100 
101     /**
102      * The artifact ID of the artifact to download. Ignored if {@link #artifact} is used.
103      */
104     @Parameter( property = "artifactId" )
105     private String artifactId;
106 
107     /**
108      * The version of the artifact to download. Ignored if {@link #artifact} is used.
109      */
110     @Parameter( property = "version" )
111     private String version;
112 
113     /**
114      * The classifier of the artifact to download. Ignored if {@link #artifact} is used.
115      *
116      * @since 2.3
117      */
118     @Parameter( property = "classifier" )
119     private String classifier;
120 
121     /**
122      * The packaging of the artifact to download. Ignored if {@link #artifact} is used.
123      */
124     @Parameter( property = "packaging", defaultValue = "jar" )
125     private String packaging = "jar";
126 
127     /**
128      * Repositories in the format id::[layout]::url or just URLs, separated by comma. That is,
129      * central::default::https://repo.maven.apache.org/maven2,myrepo::::https://repo.acme.com,https://repo.acme2.com
130      */
131     @Parameter( property = "remoteRepositories" )
132     private String remoteRepositories;
133 
134     /**
135      * A string of the form groupId:artifactId:version[:packaging[:classifier]].
136      */
137     @Parameter( property = "artifact" )
138     private String artifact;
139 
140     @Parameter( defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true )
141     private List<ArtifactRepository> pomRemoteRepositories;
142 
143     /**
144      * Download transitively, retrieving the specified artifact and all of its dependencies.
145      */
146     @Parameter( property = "transitive", defaultValue = "false" )
147     private boolean transitive = false;
148 
149     /**
150      * Skip plugin execution completely.
151      */
152     @Parameter( property = "mdep.skip", defaultValue = "false" )
153     private boolean skip;
154 
155     @Override
156     public void execute() throws MojoExecutionException, MojoFailureException
157     {
158         ProjectBuildingRequest buildingRequest = makeBuildingRequest();
159 
160         try
161         {
162             if ( transitive )
163             {
164                 Iterable<ArtifactResult> artifacts = dependencyResolver
165                         .resolveDependencies( buildingRequest, coordinate, null );
166 
167                 for ( ArtifactResult result : artifacts )
168                 {
169                     printClassesFromArtifactResult( result );
170                 }
171             }
172             else
173             {
174                 ArtifactResult result = artifactResolver
175                         .resolveArtifact( buildingRequest, toArtifactCoordinate( coordinate ) );
176 
177                 printClassesFromArtifactResult( result );
178             }
179         }
180         catch ( ArtifactResolverException | DependencyResolverException | IOException e )
181         {
182             throw new MojoExecutionException( "Couldn't download artifact: " + e.getMessage(), e );
183         }
184     }
185 
186     private void printClassesFromArtifactResult( ArtifactResult result )
187             throws IOException
188     {
189         // open jar file in try-with-resources statement to guarantee the file closes after use regardless of errors
190         try ( JarFile jarFile = new JarFile( result.getArtifact().getFile() ) )
191         {
192             Enumeration<JarEntry> entries = jarFile.entries();
193 
194             while ( entries.hasMoreElements() )
195             {
196                 JarEntry entry = entries.nextElement();
197                 String entryName = entry.getName();
198 
199                 // filter out files that do not end in .class
200                 if ( !entryName.endsWith( ".class" ) )
201                 {
202                     continue;
203                 }
204 
205                 // remove .class from the end and change format to use periods instead of forward slashes
206                 String className = entryName.substring( 0, entryName.length() - 6 ).replace( '/', '.' );
207                 getLog().info( className );
208             }
209         }
210     }
211 
212     private ProjectBuildingRequest makeBuildingRequest()
213             throws MojoExecutionException, MojoFailureException
214     {
215         if ( artifact == null )
216         {
217             throw new MojoFailureException( "You must specify an artifact, "
218                     + "e.g. -Dartifact=org.apache.maven.plugins:maven-downloader-plugin:1.0" );
219         }
220         if ( artifact != null )
221         {
222             String[] tokens = artifact.split( ":" );
223             if ( tokens.length < 3 || tokens.length > 5 )
224             {
225                 throw new MojoFailureException( "Invalid artifact, you must specify "
226                         + "groupId:artifactId:version[:packaging[:classifier]] " + artifact );
227             }
228             coordinate.setGroupId( tokens[0] );
229             coordinate.setArtifactId( tokens[1] );
230             coordinate.setVersion( tokens[2] );
231             if ( tokens.length >= 4 )
232             {
233                 coordinate.setType( tokens[3] );
234             }
235             if ( tokens.length == 5 )
236             {
237                 coordinate.setClassifier( tokens[4] );
238             }
239         }
240 
241         ArtifactRepositoryPolicy always =
242                 new ArtifactRepositoryPolicy( true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS,
243                         ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN );
244 
245         List<ArtifactRepository> repoList = new ArrayList<>();
246 
247         if ( pomRemoteRepositories != null )
248         {
249             repoList.addAll( pomRemoteRepositories );
250         }
251 
252         if ( remoteRepositories != null )
253         {
254             // Use the same format as in the deploy plugin id::layout::url
255             String[] repos = remoteRepositories.split( "," );
256             for ( String repo : repos )
257             {
258                 repoList.add( parseRepository( repo, always ) );
259             }
260         }
261 
262         ProjectBuildingRequest buildingRequest =
263                 new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
264 
265         Settings settings = session.getSettings();
266         repositorySystem.injectMirror( repoList, settings.getMirrors() );
267         repositorySystem.injectProxy( repoList, settings.getProxies() );
268         repositorySystem.injectAuthentication( repoList, settings.getServers() );
269 
270         buildingRequest.setRemoteRepositories( repoList );
271 
272         return buildingRequest;
273     }
274 
275     private ArtifactCoordinate toArtifactCoordinate( DependableCoordinate dependableCoordinate )
276     {
277         ArtifactHandler artifactHandler = artifactHandlerManager.getArtifactHandler( dependableCoordinate.getType() );
278         DefaultArtifactCoordinate artifactCoordinate = new DefaultArtifactCoordinate();
279         artifactCoordinate.setGroupId( dependableCoordinate.getGroupId() );
280         artifactCoordinate.setArtifactId( dependableCoordinate.getArtifactId() );
281         artifactCoordinate.setVersion( dependableCoordinate.getVersion() );
282         artifactCoordinate.setClassifier( dependableCoordinate.getClassifier() );
283         artifactCoordinate.setExtension( artifactHandler.getExtension() );
284         return artifactCoordinate;
285     }
286 
287     protected ArtifactRepository parseRepository( String repo, ArtifactRepositoryPolicy policy )
288             throws MojoFailureException
289     {
290         // if it's a simple url
291         String id = "temp";
292         ArtifactRepositoryLayout layout = getLayout( "default" );
293 
294         // if it's an extended repo URL of the form id::layout::url
295         if ( repo.contains( "::" ) )
296         {
297             Matcher matcher = ALT_REPO_SYNTAX_PATTERN.matcher( repo );
298             if ( !matcher.matches() )
299             {
300                 throw new MojoFailureException( repo, "Invalid syntax for repository: " + repo,
301                         "Invalid syntax for repository. Use \"id::layout::url\" or \"URL\"." );
302             }
303 
304             id = matcher.group( 1 ).trim();
305             if ( !( matcher.group( 2 ) == null || matcher.group( 2 ).trim().isEmpty() ) )
306             {
307                 layout = getLayout( matcher.group( 2 ).trim() );
308             }
309             repo = matcher.group( 3 ).trim();
310         }
311         return new MavenArtifactRepository( id, repo, layout, policy, policy );
312     }
313 
314     private ArtifactRepositoryLayout getLayout( String id )
315             throws MojoFailureException
316     {
317         ArtifactRepositoryLayout layout = repositoryLayouts.get( id );
318 
319         if ( layout == null )
320         {
321             throw new MojoFailureException( id, "Invalid repository layout", "Invalid repository layout: " + id );
322         }
323 
324         return layout;
325     }
326 }