View Javadoc
1   package org.apache.maven.plugins.wrapper;
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.BufferedWriter;
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.util.Map;
30  import java.util.Properties;
31  
32  import javax.inject.Inject;
33  
34  import org.apache.maven.Maven;
35  import org.apache.maven.artifact.Artifact;
36  import org.apache.maven.execution.MavenSession;
37  import org.apache.maven.plugin.AbstractMojo;
38  import org.apache.maven.plugin.MojoExecutionException;
39  import org.apache.maven.plugin.MojoFailureException;
40  import org.apache.maven.plugins.annotations.Mojo;
41  import org.apache.maven.plugins.annotations.Parameter;
42  import org.apache.maven.project.DefaultProjectBuildingRequest;
43  import org.apache.maven.project.ProjectBuildingRequest;
44  import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate;
45  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
46  import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
47  import org.codehaus.plexus.archiver.UnArchiver;
48  import org.codehaus.plexus.components.io.fileselectors.FileSelector;
49  
50  /**
51   * Adds the maven-wrapper files to this project 
52   * 
53   * @author Robert Scholte
54   * @since 3.0.0
55   */
56  @Mojo( name = "wrapper", aggregator = true, requiresDirectInvocation = true )
57  public class WrapperMojo extends AbstractMojo
58  {
59      // CONFIGURATION PARAMETERS
60      
61      /**
62       * The version of Maven to require, default value is the Runtime version of Maven. 
63       * Can be any valid release above 2.0.9
64       */
65      @Parameter( property = "mavenVersion" )
66      private String mavenVersion;
67  
68      /**
69       * The version of the wrapper, default value is the Runtime version of Maven, should be at least 4
70       */
71      @Parameter( property = "wrapperVersion" )
72      private String wrapperVersion;
73  
74      /**
75       * Options are:
76       * <dl>
77       *   <dt>script (default)</dt>
78       *   <dd>only mvnw scripts</dd>
79       *   <dt>bin</dt>
80       *   <dd>precompiled and packaged code</dd>
81       *   <dt>source</dt>
82       *   <dd>sourcecode, will be compiled on the fly</dd>
83       * </dl> 
84       * 
85       * Value will be used as classifier of the downloaded file
86       */
87      @Parameter( defaultValue = "script", property = "type" )
88      private String distributionType;
89  
90      @Parameter( defaultValue = "true", property = "includeDebug" )
91      private boolean includeDebugScript;
92      
93      // READONLY PARAMETERS 
94      
95      @Parameter( defaultValue = "${session}", readonly = true, required = true )
96      private MavenSession session;
97      
98      // Waiting for org.codehaus.plexus.component.configurator.converters.basic.PathConverter
99      @Parameter( defaultValue = "${project.basedir}", readonly = true, required = true )
100     private File basedir;
101 
102     // CONSTANTS
103 
104     private static final String WRAPPER_GROUP_ID = "org.apache.maven";
105 
106     private static final String WRAPPER_ARTIFACT_ID = "apache-maven-wrapper";
107 
108     private static final String WRAPPER_EXTENSION = "zip";
109     
110     // COMPONENTS 
111     
112     @Inject
113     private ArtifactResolver artifactResolver;
114     
115     @Inject
116     private Map<String, UnArchiver> unarchivers;
117     
118     @Override
119     public void execute() throws MojoExecutionException, MojoFailureException
120     {
121         String wrapperVersion = getVersion( this.wrapperVersion );
122         if ( wrapperVersion.startsWith( "3." ) )
123         {
124             throw new MojoFailureException( "wrapperVersion not supported for Maven " + wrapperVersion + ","
125                 + " it must be at least 4." );
126         }
127         
128         Artifact artifact;
129         try
130         {
131             artifact = downloadWrapper( wrapperVersion );
132         }
133         catch ( ArtifactResolverException e )
134         {
135             throw new MojoExecutionException( e.getMessage(), e );
136         }
137         
138         try
139         {
140             unpack( artifact, basedir.toPath() );
141             getLog().info( "Unpacked " + artifact );
142         }
143         catch ( IOException e )
144         {
145             throw new MojoExecutionException( e.getMessage(), e );
146         }
147         
148         if ( mavenVersion != null )
149         {
150             try
151             {
152                 replaceProperties( Files.createDirectories( basedir.toPath().resolve( ".mvn/wrapper" ) ) );
153             }
154             catch ( IOException e )
155             {
156                 throw new MojoExecutionException( "can't create wrapper.properties", e );
157             }
158         }
159     }
160     
161     private Artifact downloadWrapper( String wrapperVersion )
162         throws ArtifactResolverException
163     {
164         ProjectBuildingRequest buildingRequest =
165             new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
166 
167         DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
168         coordinate.setGroupId( WRAPPER_GROUP_ID );
169         coordinate.setArtifactId( WRAPPER_ARTIFACT_ID );
170         coordinate.setVersion( wrapperVersion ); 
171         coordinate.setClassifier( distributionType );
172         coordinate.setExtension( WRAPPER_EXTENSION );
173 
174         return artifactResolver.resolveArtifact( buildingRequest, coordinate ).getArtifact();
175     }
176     
177     private void unpack( Artifact artifact, Path targetFolder ) throws IOException 
178     {
179         targetFolder = Files.createDirectories( targetFolder );
180         UnArchiver unarchiver = unarchivers.get( WRAPPER_EXTENSION );
181         unarchiver.setDestDirectory( targetFolder.toFile() );
182         unarchiver.setSourceFile( artifact.getFile() );
183         if ( !includeDebugScript )
184         {
185             unarchiver.setFileSelectors( new FileSelector[] 
186                             {
187                                 f -> !f.getName().contains( "Debug" ) 
188                             } );
189         }
190         unarchiver.extract();
191     }
192     
193     /**
194      * As long as the content only contains the license and the distributionUrl, we can simply replace it.
195      * No need to look for other properties, restore them, respecting comments, etc.
196      * 
197      * @param targetFolder the folder containing the wrapper.properties
198      * @throws IOException if writing fails
199      */
200     private void replaceProperties( Path targetFolder ) throws IOException
201     {
202         Path wrapperPropertiesFile = targetFolder.resolve( "maven-wrapper.properties" );
203         
204         final String license = "# Licensed to the Apache Software Foundation (ASF) under one\n"
205             + "# or more contributor license agreements.  See the NOTICE file\n"
206             + "# distributed with this work for additional information\n"
207             + "# regarding copyright ownership.  The ASF licenses this file\n"
208             + "# to you under the Apache License, Version 2.0 (the\n"
209             + "# \"License\"); you may not use this file except in compliance\n"
210             + "# with the License.  You may obtain a copy of the License at\n"
211             + "# \n"
212             + "#   http://www.apache.org/licenses/LICENSE-2.0\n"
213             + "# \n"
214             + "# Unless required by applicable law or agreed to in writing,\n"
215             + "# software distributed under the License is distributed on an\n"
216             + "# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n"
217             + "# KIND, either express or implied.  See the License for the\n"
218             + "# specific language governing permissions and limitations\n"
219             + "# under the License.\n";
220         
221         try ( BufferedWriter out = Files.newBufferedWriter( wrapperPropertiesFile ) )
222         {
223             out.append( license );
224             out.append( "distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/"
225                 + mavenVersion
226                 + "/apache-maven-"
227                 + mavenVersion
228                 + "-bin.zip" );
229         }
230     }
231     
232     private String getVersion( String defaultVersion )
233     {
234         String version = defaultVersion;
235         if ( version == null )
236         {
237             Properties props = new Properties();
238             try ( InputStream is =
239                 Maven.class.getResourceAsStream( "/META-INF/maven/org.apache.maven/maven-core/pom.properties" ) )
240             {
241                 props.load( is );
242                 version = props.getProperty( "version" );
243             }
244             catch ( IOException e )
245             {
246                 // noop
247             }
248         }
249         return version;
250     }
251 }