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