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