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