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.File;
23  import java.io.IOException;
24  import java.lang.reflect.Field;
25  import java.util.List;
26  
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.artifact.repository.ArtifactRepository;
29  import org.apache.maven.execution.MavenSession;
30  import org.apache.maven.plugin.AbstractMojo;
31  import org.apache.maven.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.MojoFailureException;
33  import org.apache.maven.plugins.annotations.Component;
34  import org.apache.maven.plugins.annotations.Parameter;
35  import org.apache.maven.plugins.dependency.utils.DependencySilentLog;
36  import org.apache.maven.project.DefaultProjectBuildingRequest;
37  import org.apache.maven.project.MavenProject;
38  import org.apache.maven.project.ProjectBuildingRequest;
39  import org.codehaus.plexus.archiver.ArchiverException;
40  import org.codehaus.plexus.archiver.UnArchiver;
41  import org.codehaus.plexus.archiver.manager.ArchiverManager;
42  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
43  import org.codehaus.plexus.archiver.zip.ZipUnArchiver;
44  import org.codehaus.plexus.components.io.filemappers.FileMapper;
45  import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
46  import org.codehaus.plexus.util.FileUtils;
47  import org.codehaus.plexus.util.ReflectionUtils;
48  import org.codehaus.plexus.util.StringUtils;
49  import org.sonatype.plexus.build.incremental.BuildContext;
50  
51  /**
52   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
53   */
54  public abstract class AbstractDependencyMojo
55      extends AbstractMojo
56  {
57      /**
58       * To look up Archiver/UnArchiver implementations
59       */
60      @Component
61      private ArchiverManager archiverManager;
62  
63  
64      /**
65       * For IDE build support
66       */
67      @Component
68      private BuildContext buildContext;
69  
70      /**
71       * Skip plugin execution only during incremental builds (e.g. triggered from M2E).
72       * 
73       * @since 3.4.0
74       * @see #skip
75       */
76      @Parameter( defaultValue = "false" )
77      private boolean skipDuringIncrementalBuild;
78  
79      /**
80       * <p>
81       * will use the jvm chmod, this is available for user and all level group level will be ignored
82       * </p>
83       * <b>since 2.6 is on by default</b>
84       * 
85       * @since 2.5.1
86       */
87      @Parameter( property = "dependency.useJvmChmod", defaultValue = "true" )
88      private boolean useJvmChmod = true;
89  
90      /**
91       * ignore to set file permissions when unpacking a dependency
92       * 
93       * @since 2.7
94       */
95      @Parameter( property = "dependency.ignorePermissions", defaultValue = "false" )
96      private boolean ignorePermissions;
97  
98      /**
99       * POM
100      */
101     @Parameter( defaultValue = "${project}", readonly = true, required = true )
102     private MavenProject project;
103 
104     /**
105      * Remote repositories which will be searched for artifacts.
106      */
107     @Parameter( defaultValue = "${project.remoteArtifactRepositories}", readonly = true, required = true )
108     private List<ArtifactRepository> remoteRepositories;
109 
110     /**
111      * Remote repositories which will be searched for plugins.
112      */
113     @Parameter( defaultValue = "${project.pluginArtifactRepositories}", readonly = true, required = true )
114     private List<ArtifactRepository> remotePluginRepositories;
115 
116     /**
117      * Contains the full list of projects in the reactor.
118      */
119     @Parameter( defaultValue = "${reactorProjects}", readonly = true )
120     protected List<MavenProject> reactorProjects;
121 
122     /**
123      * The Maven session
124      */
125     @Parameter( defaultValue = "${session}", readonly = true, required = true )
126     protected MavenSession session;
127 
128     /**
129      * If the plugin should be silent.
130      *
131      * @since 2.0
132      */
133     @Parameter( property = "silent", defaultValue = "false" )
134     private boolean silent;
135 
136     /**
137      * Output absolute filename for resolved artifacts
138      *
139      * @since 2.0
140      */
141     @Parameter( property = "outputAbsoluteArtifactFilename", defaultValue = "false" )
142     protected boolean outputAbsoluteArtifactFilename;
143 
144     /**
145      * Skip plugin execution completely.
146      *
147      * @since 2.7
148      */
149     @Parameter( property = "mdep.skip", defaultValue = "false" )
150     private boolean skip;
151 
152     // Mojo methods -----------------------------------------------------------
153 
154     /*
155      * @see org.apache.maven.plugin.Mojo#execute()
156      */
157     @Override
158     public final void execute()
159         throws MojoExecutionException, MojoFailureException
160     {
161         if ( isSkip() )
162         {
163             getLog().info( "Skipping plugin execution" );
164             return;
165         }
166 
167         doExecute();
168     }
169 
170     /**
171      * @throws MojoExecutionException {@link MojoExecutionException}
172      * @throws MojoFailureException {@link MojoFailureException}
173      */
174     protected abstract void doExecute()
175         throws MojoExecutionException, MojoFailureException;
176 
177     /**
178      * @return Returns the archiverManager.
179      */
180     public ArchiverManager getArchiverManager()
181     {
182         return this.archiverManager;
183     }
184 
185     /**
186      * Does the actual copy of the file and logging.
187      *
188      * @param artifact represents the file to copy.
189      * @param destFile file name of destination file.
190      * @throws MojoExecutionException with a message if an error occurs.
191      */
192     protected void copyFile( File artifact, File destFile )
193         throws MojoExecutionException
194     {
195         try
196         {
197             getLog().info( "Copying "
198                 + ( this.outputAbsoluteArtifactFilename ? artifact.getAbsolutePath() : artifact.getName() ) + " to "
199                 + destFile );
200 
201             if ( artifact.isDirectory() )
202             {
203                 // usual case is a future jar packaging, but there are special cases: classifier and other packaging
204                 throw new MojoExecutionException( "Artifact has not been packaged yet. When used on reactor artifact, "
205                     + "copy should be executed after packaging: see MDEP-187." );
206             }
207 
208             FileUtils.copyFile( artifact, destFile );
209             buildContext.refresh( destFile );
210         }
211         catch ( IOException e )
212         {
213             throw new MojoExecutionException( "Error copying artifact from " + artifact + " to " + destFile, e );
214         }
215     }
216 
217     /**
218      * @param artifact {@link Artifact}
219      * @param location The location.
220      * @param encoding The encoding.
221      * @param fileMappers {@link FileMapper}s to be used for rewriting each target path, or {@code null} if no rewriting
222      *                    shall happen.
223      * @throws MojoExecutionException in case of an error.
224      */
225     protected void unpack( Artifact artifact, File location, String encoding, FileMapper[] fileMappers )
226         throws MojoExecutionException
227     {
228         unpack( artifact, location, null, null, encoding, fileMappers );
229     }
230 
231     /**
232      * Unpacks the archive file.
233      *
234      * @param artifact File to be unpacked.
235      * @param location Location where to put the unpacked files.
236      * @param includes Comma separated list of file patterns to include i.e. <code>**&#47;.xml,
237      *                 **&#47;*.properties</code>
238      * @param excludes Comma separated list of file patterns to exclude i.e. <code>**&#47;*.xml,
239      *                 **&#47;*.properties</code>
240      * @param encoding Encoding of artifact. Set {@code null} for default encoding.
241      * @param fileMappers {@link FileMapper}s to be used for rewriting each target path, or {@code null} if no rewriting
242      *                    shall happen.
243      * @throws MojoExecutionException In case of errors.
244      */
245     protected void unpack( Artifact artifact, File location, String includes, String excludes, String encoding,
246                            FileMapper[] fileMappers ) throws MojoExecutionException
247     {
248         unpack( artifact, artifact.getType(), location, includes, excludes, encoding, fileMappers );
249     }
250 
251     /**
252      * @param artifact {@link Artifact}
253      * @param type The type.
254      * @param location The location.
255      * @param includes includes list.
256      * @param excludes excludes list.
257      * @param encoding the encoding.
258      * @param fileMappers {@link FileMapper}s to be used for rewriting each target path, or {@code null} if no rewriting
259      *                    shall happen.
260      * @throws MojoExecutionException in case of an error.
261      */
262     protected void unpack( Artifact artifact, String type, File location, String includes, String excludes,
263                            String encoding, FileMapper[] fileMappers )
264         throws MojoExecutionException
265     {
266         File file = artifact.getFile();
267         try
268         {
269             logUnpack( file, location, includes, excludes );
270 
271             location.mkdirs();
272             if ( !location.exists() )
273             {
274                 throw new MojoExecutionException( "Location to write unpacked files to could not be created: "
275                     + location );
276             }
277 
278             if ( file.isDirectory() )
279             {
280                 // usual case is a future jar packaging, but there are special cases: classifier and other packaging
281                 throw new MojoExecutionException( "Artifact has not been packaged yet. When used on reactor artifact, "
282                     + "unpack should be executed after packaging: see MDEP-98." );
283             }
284 
285             UnArchiver unArchiver;
286 
287             try
288             {
289                 unArchiver = archiverManager.getUnArchiver( type );
290                 getLog().debug( "Found unArchiver by type: " + unArchiver );
291             }
292             catch ( NoSuchArchiverException e )
293             {
294                 unArchiver = archiverManager.getUnArchiver( file );
295                 getLog().debug( "Found unArchiver by extension: " + unArchiver );
296             }
297 
298             if ( encoding != null && unArchiver instanceof ZipUnArchiver )
299             {
300                 ( (ZipUnArchiver) unArchiver ).setEncoding( encoding );
301                 getLog().info( "Unpacks '" + type + "' with encoding '" + encoding + "'." );
302             }
303 
304             unArchiver.setIgnorePermissions( ignorePermissions );
305 
306             unArchiver.setSourceFile( file );
307 
308             unArchiver.setDestDirectory( location );
309 
310             if ( StringUtils.isNotEmpty( excludes ) || StringUtils.isNotEmpty( includes ) )
311             {
312                 // Create the selectors that will filter
313                 // based on include/exclude parameters
314                 // MDEP-47
315                 IncludeExcludeFileSelector[] selectors =
316                     new IncludeExcludeFileSelector[] { new IncludeExcludeFileSelector() };
317 
318                 if ( StringUtils.isNotEmpty( excludes ) )
319                 {
320                     selectors[0].setExcludes( excludes.split( "," ) );
321                 }
322 
323                 if ( StringUtils.isNotEmpty( includes ) )
324                 {
325                     selectors[0].setIncludes( includes.split( "," ) );
326                 }
327 
328                 unArchiver.setFileSelectors( selectors );
329             }
330             if ( this.silent )
331             {
332                 silenceUnarchiver( unArchiver );
333             }
334 
335             unArchiver.setFileMappers( fileMappers );
336 
337             unArchiver.extract();
338         }
339         catch ( NoSuchArchiverException e )
340         {
341             throw new MojoExecutionException( "Unknown archiver type", e );
342         }
343         catch ( ArchiverException e )
344         {
345             throw new MojoExecutionException( "Error unpacking file: " + file + " to: " + location, e );
346         }
347         buildContext.refresh( location );
348     }
349 
350     private void silenceUnarchiver( UnArchiver unArchiver )
351     {
352         // dangerous but handle any errors. It's the only way to silence the unArchiver.
353         try
354         {
355             Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( "logger", unArchiver.getClass() );
356 
357             field.setAccessible( true );
358 
359             field.set( unArchiver, this.getLog() );
360         }
361         catch ( Exception e )
362         {
363             // was a nice try. Don't bother logging because the log is silent.
364         }
365     }
366 
367     /**
368      * @return Returns a new ProjectBuildingRequest populated from the current session and the current project remote
369      *         repositories, used to resolve artifacts.
370      */
371     public ProjectBuildingRequest newResolveArtifactProjectBuildingRequest()
372     {
373         return newProjectBuildingRequest( remoteRepositories );
374     }
375 
376     /**
377      * @return Returns a new ProjectBuildingRequest populated from the current session and the current project remote
378      *         repositories, used to resolve plugins.
379      */
380     protected ProjectBuildingRequest newResolvePluginProjectBuildingRequest()
381     {
382         return newProjectBuildingRequest( remotePluginRepositories );
383     }
384 
385     private ProjectBuildingRequest newProjectBuildingRequest( List<ArtifactRepository> repositories )
386     {
387         ProjectBuildingRequest buildingRequest =
388             new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
389 
390         buildingRequest.setRemoteRepositories( repositories );
391 
392         return buildingRequest;
393     }
394 
395     /**
396      * @return Returns the project.
397      */
398     public MavenProject getProject()
399     {
400         return this.project;
401     }
402 
403     /**
404      * @param archiverManager The archiverManager to set.
405      */
406     public void setArchiverManager( ArchiverManager archiverManager )
407     {
408         this.archiverManager = archiverManager;
409     }
410 
411     /**
412      * @return {@link #useJvmChmod}
413      */
414     public boolean isUseJvmChmod()
415     {
416         return useJvmChmod;
417     }
418 
419     /**
420      * @param useJvmChmod {@link #useJvmChmod}
421      */
422     public void setUseJvmChmod( boolean useJvmChmod )
423     {
424         this.useJvmChmod = useJvmChmod;
425     }
426 
427     /**
428      * @return {@link #skip}
429      */
430     public boolean isSkip()
431     {
432         if ( skipDuringIncrementalBuild && buildContext.isIncremental() )
433         {
434             return true;
435         }
436         return skip;
437     }
438 
439     /**
440      * @param skip {@link #skip}
441      */
442     public void setSkip( boolean skip )
443     {
444         this.skip = skip;
445     }
446 
447     /**
448      * @return {@link #silent}
449      */
450     protected final boolean isSilent()
451     {
452         return silent;
453     }
454 
455     /**
456      * @param silent {@link #silent}
457      */
458     public void setSilent( boolean silent )
459     {
460         this.silent = silent;
461         if ( silent )
462         {
463             setLog( new DependencySilentLog() );
464         }
465     }
466 
467     private void logUnpack( File file, File location, String includes, String excludes )
468     {
469         if ( !getLog().isInfoEnabled() )
470         {
471             return;
472         }
473 
474         StringBuilder msg = new StringBuilder();
475         msg.append( "Unpacking " );
476         msg.append( file );
477         msg.append( " to " );
478         msg.append( location );
479 
480         if ( includes != null && excludes != null )
481         {
482             msg.append( " with includes \"" );
483             msg.append( includes );
484             msg.append( "\" and excludes \"" );
485             msg.append( excludes );
486             msg.append( "\"" );
487         }
488         else if ( includes != null )
489         {
490             msg.append( " with includes \"" );
491             msg.append( includes );
492             msg.append( "\"" );
493         }
494         else if ( excludes != null )
495         {
496             msg.append( " with excludes \"" );
497             msg.append( excludes );
498             msg.append( "\"" );
499         }
500 
501         getLog().info( msg.toString() );
502     }
503 }