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.archetype.mojos;
20  
21  import javax.inject.Inject;
22  
23  import java.io.File;
24  import java.util.Arrays;
25  import java.util.Properties;
26  
27  import org.apache.commons.lang3.StringUtils;
28  import org.apache.maven.archetype.ArchetypeGenerationRequest;
29  import org.apache.maven.archetype.ArchetypeGenerationResult;
30  import org.apache.maven.archetype.ArchetypeManager;
31  import org.apache.maven.archetype.ui.generation.ArchetypeGenerationConfigurator;
32  import org.apache.maven.archetype.ui.generation.ArchetypeSelector;
33  import org.apache.maven.execution.MavenSession;
34  import org.apache.maven.plugin.AbstractMojo;
35  import org.apache.maven.plugin.ContextEnabled;
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.plugins.annotations.Execute;
38  import org.apache.maven.plugins.annotations.LifecyclePhase;
39  import org.apache.maven.plugins.annotations.Mojo;
40  import org.apache.maven.plugins.annotations.Parameter;
41  import org.apache.maven.project.MavenProject;
42  import org.apache.maven.shared.invoker.DefaultInvocationRequest;
43  import org.apache.maven.shared.invoker.InvocationRequest;
44  import org.apache.maven.shared.invoker.InvocationResult;
45  import org.apache.maven.shared.invoker.Invoker;
46  import org.apache.maven.shared.invoker.MavenInvocationException;
47  import org.eclipse.aether.RepositorySystem;
48  
49  /**
50   * Generates a new project from an archetype, or updates the actual project if using a partial archetype.
51   * If the project is fully generated, it is generated in a directory corresponding to its artifactId.
52   * If the project is updated with a partial archetype, it is done in the current directory.
53   *
54   * @author rafale
55   */
56  @Mojo(name = "generate", requiresProject = false)
57  @Execute(phase = LifecyclePhase.GENERATE_SOURCES)
58  public class CreateProjectFromArchetypeMojo extends AbstractMojo implements ContextEnabled {
59      private ArchetypeManager manager;
60  
61      private ArchetypeSelector selector;
62  
63      private ArchetypeGenerationConfigurator configurator;
64  
65      private Invoker invoker;
66  
67      private RepositorySystem repositorySystem;
68  
69      @Inject
70      public CreateProjectFromArchetypeMojo(
71              ArchetypeManager manager,
72              ArchetypeSelector selector,
73              ArchetypeGenerationConfigurator configurator,
74              Invoker invoker,
75              RepositorySystem repositorySystem) {
76          this.manager = manager;
77          this.selector = selector;
78          this.configurator = configurator;
79          this.invoker = invoker;
80          this.repositorySystem = repositorySystem;
81      }
82  
83      /**
84       * The archetype's artifactId.
85       */
86      @Parameter(property = "archetypeArtifactId")
87      private String archetypeArtifactId;
88  
89      /**
90       * The archetype's groupId.
91       */
92      @Parameter(property = "archetypeGroupId")
93      private String archetypeGroupId;
94  
95      /**
96       * The archetype's version.
97       */
98      @Parameter(property = "archetypeVersion")
99      private String archetypeVersion;
100 
101     /**
102      * The archetype catalogs to use to build a list and let the user choose from.
103      * It is a comma-separated list of one or more of the following catalog schemes
104      * <ul>
105      * <li><code>local</code> which is the shortcut to the local repository</li>
106      * <li><code>remote</code> which is the shortcut for a repository with id {@code archetype},
107      * the Maven Central repository (with id {@code central}) or the first matching mirror for either
108      * of the repository ids {@code archetype} or {@code central}</li>
109      * <li><code>internal</code> which is an <a href="https://maven.apache.org/archetype/archetype-common/internal.html">internal catalog</a></li>
110      * </ul>
111      * <p/>
112      * If you want the catalog to come from a custom repository, please add
113      * a dedicated repository with id {@code archetype} and optionally a server section with the same id to
114      * your {@code settings.xml} and use the catalog scheme <code>remote</code>.
115      * <pre>
116      *   &lt;repository&gt;
117      *     &lt;id&gt;archetype&lt;/id&gt;
118      *     &lt;url&gt;https://repository.domain.com/path/to/repo/&lt;/url&gt;
119      *   &lt;/repository&gt;
120      *
121      *   &lt;!-- in case of a repository with authentication --&gt;
122      *   &lt;server&gt;
123      *     &lt;id&gt;archetype&lt;/id&gt;
124      *     &lt;username&gt;user.name&lt;/username&gt;
125      *     &lt;password&gt;s3cr3t&lt;/password&gt;
126      *   &lt;/server&gt;
127      * </pre>
128      * In order to use that repository <em>only</em> for resolving the catalog (and not for downloading regular Maven dependencies from it) it is recommended to
129      * include the repository in a <a href="https://maven.apache.org/settings.html#active-profiles">profile which is not active by default.</a> and explicitly select it via {@code -P<profile-id>}
130      * when this goal is called on the command-line.
131      * In case of mirrors for either repository {@code archetype} or {@code central} the <a href="https://maven.apache.org/guides/mini/guide-mirror-settings.html">matching mirror's id</a> is considered
132      * for server settings (like authentication).
133      * If the repository's catalog file is empty or cannot be retrieved, <code>internal</code> catalog is transparently used as fallback.
134      */
135     @Parameter(property = "archetypeCatalog", defaultValue = "remote,local")
136     private String archetypeCatalog;
137 
138     /**
139      * If set to {@code true} will ask for values also for properties having defaults in the first place.
140      * Only has an effect if {@link #interactiveMode} is used.
141      */
142     @Parameter(property = "askForDefaultPropertyValues", defaultValue = "false", required = true)
143     private Boolean askForDefaultPropertyValues;
144 
145     /**
146      * User settings used to check the interactiveMode.
147      */
148     @Parameter(property = "interactiveMode", defaultValue = "${settings.interactiveMode}", required = true)
149     private Boolean interactiveMode;
150 
151     @Parameter(defaultValue = "${basedir}", property = "outputDirectory")
152     private File outputDirectory;
153 
154     @Parameter(defaultValue = "${session}", readonly = true, required = true)
155     private MavenSession session;
156 
157     @Parameter(defaultValue = "${project}", readonly = true, required = true)
158     private MavenProject project;
159 
160     /**
161      * Goals to immediately run on the project created from the archetype.
162      */
163     @Parameter(property = "goals")
164     private String goals;
165 
166     /**
167      * Applying some filter on displayed archetypes list: format is <code>artifactId</code> or <code>groupId:artifactId</code>.
168      * <ul>
169      * <li><code>org.apache:</code> -> displays all archetypes which contain org.apache in groupId</li>
170      * <li><code>:jee</code> or <code>jee</code> -> displays all archetypes which contain jee in artifactId</li>
171      * <li><code>org.apache:jee</code> -> displays all archetypes which contain org.apache in groupId AND jee in artifactId</li>
172      * </ul>
173      *
174      * @since 2.1
175      */
176     @Parameter(property = "filter")
177     private String filter;
178 
179     @Override
180     public void execute() throws MojoExecutionException {
181         Properties executionProperties = session.getUserProperties();
182 
183         ArchetypeGenerationRequest request = new ArchetypeGenerationRequest()
184                 .setArchetypeGroupId(archetypeGroupId)
185                 .setArchetypeArtifactId(archetypeArtifactId)
186                 .setArchetypeVersion(archetypeVersion)
187                 .setOutputDirectory(outputDirectory.getAbsolutePath())
188                 .setRemoteRepositories(project.getRemoteProjectRepositories())
189                 .setRemoteArtifactRepositories(project.getRemoteArtifactRepositories())
190                 .setMavenSession(session)
191                 .setRepositorySession(session.getRepositorySession())
192                 .setRepositorySystem(repositorySystem)
193                 .setProjectBuildingRequest(session.getProjectBuildingRequest())
194                 .setLocalRepository(session.getLocalRepository())
195                 .setOffline(session.isOffline())
196                 .setFilter(filter)
197                 .setAskForDefaultPropertyValues(askForDefaultPropertyValues);
198 
199         try {
200             if (interactiveMode.booleanValue()) {
201                 getLog().info("Generating project in Interactive mode");
202             } else {
203                 getLog().info("Generating project in Batch mode");
204             }
205 
206             selector.selectArchetype(request, interactiveMode, archetypeCatalog);
207 
208             if (StringUtils.isBlank(request.getArchetypeArtifactId())) {
209                 // no archetype found: stopping
210                 return;
211             }
212 
213             configurator.configureArchetype(request, interactiveMode, executionProperties);
214 
215             ArchetypeGenerationResult generationResult = manager.generateProjectFromArchetype(request);
216 
217             if (generationResult.getCause() != null) {
218                 throw new MojoExecutionException(generationResult.getCause().getMessage(), generationResult.getCause());
219             }
220         } catch (Exception ex) {
221             throw new MojoExecutionException(ex.getMessage(), ex);
222         }
223 
224         String artifactId = request.getArtifactId();
225 
226         String postArchetypeGenerationGoals = request.getArchetypeGoals();
227 
228         if (postArchetypeGenerationGoals == null || postArchetypeGenerationGoals.isEmpty()) {
229             postArchetypeGenerationGoals = goals;
230         }
231 
232         if (postArchetypeGenerationGoals != null && !postArchetypeGenerationGoals.isEmpty()) {
233             invokePostArchetypeGenerationGoals(postArchetypeGenerationGoals, artifactId);
234         }
235     }
236 
237     private void invokePostArchetypeGenerationGoals(String goals, String artifactId) throws MojoExecutionException {
238         getLog().info("Invoking post-archetype-generation goals: " + goals);
239 
240         File projectBasedir = new File(outputDirectory, artifactId);
241 
242         if (projectBasedir.exists()) {
243             InvocationRequest request = new DefaultInvocationRequest()
244                     .setBaseDirectory(projectBasedir)
245                     .setGoals(Arrays.asList(StringUtils.split(goals, ",")));
246 
247             try {
248                 InvocationResult result = invoker.execute(request);
249 
250                 if (result.getExitCode() != 0) {
251                     throw new MojoExecutionException("Failed to invoke goals", result.getExecutionException());
252                 }
253             } catch (MavenInvocationException e) {
254                 throw new MojoExecutionException("Cannot run additions goals.", e);
255             }
256         } else {
257             getLog().info("Post-archetype-generation goals aborted: unavailable basedir " + projectBasedir);
258         }
259     }
260 }