Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
CreateArchetypeFromProjectMojo |
|
| 7.666666666666667;7.667 |
1 | package org.apache.maven.archetype.mojos; | |
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 org.apache.maven.archetype.ArchetypeCreationRequest; | |
23 | import org.apache.maven.archetype.ArchetypeCreationResult; | |
24 | import org.apache.maven.archetype.ArchetypeManager; | |
25 | import org.apache.maven.archetype.common.Constants; | |
26 | import org.apache.maven.archetype.ui.creation.ArchetypeCreationConfigurator; | |
27 | import org.apache.maven.artifact.repository.ArtifactRepository; | |
28 | import org.apache.maven.execution.MavenSession; | |
29 | import org.apache.maven.plugin.AbstractMojo; | |
30 | import org.apache.maven.plugin.MojoExecutionException; | |
31 | import org.apache.maven.plugin.MojoFailureException; | |
32 | import org.apache.maven.project.MavenProject; | |
33 | import org.codehaus.plexus.util.PropertyUtils; | |
34 | import org.codehaus.plexus.util.StringUtils; | |
35 | ||
36 | import java.io.File; | |
37 | import java.util.ArrayList; | |
38 | import java.util.Arrays; | |
39 | import java.util.List; | |
40 | import java.util.Properties; | |
41 | ||
42 | /** | |
43 | * <p> | |
44 | * Creates an archetype project from the current project. | |
45 | * </p> | |
46 | * <p> | |
47 | * This goal reads your source and resource files, the values of its parameters, | |
48 | * and properties you specify in a <code>.property</code> file, and uses them to | |
49 | * create a Maven archetype project using the maven-archetype packaging. | |
50 | * If you build the resulting project, it will create the archetype. You can then | |
51 | * use this archetype to create new projects that resemble the original. | |
52 | * </p> | |
53 | * <p> | |
54 | * The maven-archetype-plugin uses Velocity to expand template files, and this documentation | |
55 | * talks about 'Velocity Properties', which are values substituted into Velocity templates. | |
56 | * See <a href="http://velocity.apache.org/engine/devel/user-guide.html">The Velocity User's Guide</a> | |
57 | * for more information. | |
58 | * </p> | |
59 | * <p> | |
60 | * This goal modifies the text of the files of the current project to form the Velocity template files | |
61 | * that make up the archetype. | |
62 | * </p> | |
63 | * <dl> | |
64 | * <dt>GAV</dt><dd>The GAV values for the current project are replaced by properties: groupId, artifactId, and version. | |
65 | * The user chooses new values for these when generating a project from the archetype.</dd> | |
66 | * <dt>package</dt><dd>All the files under one specified Java (or cognate) package are relocated to a project | |
67 | * that the user chooses when generating a project. References to the class name are replaced by a property reference. For | |
68 | * example, if the current project's sources are in the package <code>org.apache.saltedpeanuts</code>, then | |
69 | * any example of the string <code>org.apache.saltedpeanuts</code> is replaced with the Velocity property | |
70 | * reference <code>${packageName}</code>. When the user generates a project, this is in turn replaced by | |
71 | * his or her choice of a package. | |
72 | * </dd> | |
73 | * <dt>custom properties</dt><dd>You may identify additional strings that should be replaced by parameters. | |
74 | * To add custom properties, you must use the <code>propertyFile</code> parameter to specify a property file. | |
75 | * See the documentation for <code>propertyFile</code> for the details. | |
76 | * </dl> | |
77 | * <p> | |
78 | * Note that you may need to edit the results of this goal. This goal has no way to exclude unwanted files, | |
79 | * or add copyright notices to the Velocity templates, or add more complex elements to the archetype metadata file. | |
80 | * </p> | |
81 | * <p> | |
82 | * This goal also generates a simple integration-test that exercises the generated archetype. | |
83 | * </p> | |
84 | * | |
85 | * @author rafale | |
86 | * @requiresProject true | |
87 | * @goal create-from-project | |
88 | * @execute phase="generate-sources" | |
89 | * @aggregator | |
90 | */ | |
91 | 0 | public class CreateArchetypeFromProjectMojo |
92 | extends AbstractMojo | |
93 | { | |
94 | /** @component */ | |
95 | private ArchetypeCreationConfigurator configurator; | |
96 | ||
97 | /** | |
98 | * Enable the interactive mode to define the archetype from the project. | |
99 | * | |
100 | * @parameter expression="${interactive}" default-value="false" | |
101 | */ | |
102 | private boolean interactive; | |
103 | ||
104 | /** @component */ | |
105 | private ArchetypeManager manager; | |
106 | ||
107 | /** | |
108 | * File extensions which are checked for project's text files (vs binary files). | |
109 | * | |
110 | * @parameter expression="${archetype.filteredExtentions}" | |
111 | */ | |
112 | private String archetypeFilteredExtentions; | |
113 | ||
114 | /** | |
115 | * Directory names which are checked for project's sources main package. | |
116 | * | |
117 | * @parameter expression="${archetype.languages}" | |
118 | */ | |
119 | private String archetypeLanguages; | |
120 | ||
121 | /** | |
122 | * The location of the registry file. | |
123 | * | |
124 | * @parameter expression="${user.home}/.m2/archetype.xml" | |
125 | */ | |
126 | private File archetypeRegistryFile; | |
127 | ||
128 | /** | |
129 | * Velocity templates encoding. | |
130 | * | |
131 | * @parameter default-value="UTF-8" expression="${archetype.encoding}" | |
132 | */ | |
133 | private String defaultEncoding; | |
134 | ||
135 | /** | |
136 | * Create a partial archetype. | |
137 | * | |
138 | * @parameter expression="${archetype.partialArchetype}" | |
139 | */ | |
140 | 0 | private boolean partialArchetype = false; |
141 | ||
142 | /** | |
143 | * Create pom's velocity templates with CDATA preservation. This uses the <code>String.replaceAll()</code> | |
144 | * method and risks to have some overly replacement capabilities (beware of '1.0' value). | |
145 | * | |
146 | * @parameter expression="${archetype.preserveCData}" | |
147 | */ | |
148 | 0 | private boolean preserveCData = false; |
149 | ||
150 | /** @parameter expression="${localRepository}" | |
151 | * @readonly | |
152 | **/ | |
153 | private ArtifactRepository localRepository; | |
154 | ||
155 | /** | |
156 | * POMs in archetype are created with their initial parent. | |
157 | * This property is ignored when preserveCData is true. | |
158 | * | |
159 | * @parameter expression="${archetype.keepParent}" | |
160 | */ | |
161 | 0 | private boolean keepParent = true; |
162 | ||
163 | /** | |
164 | * The Maven project to create an archetype from. | |
165 | * | |
166 | * @parameter expression="${project}" | |
167 | * @required | |
168 | * @readonly | |
169 | */ | |
170 | private MavenProject project; | |
171 | ||
172 | /** | |
173 | * The property file that holds the plugin configuration. If this is provided, then | |
174 | * the plugin reads properties from here. The properties in here can be standard | |
175 | * properties listed below or custom properties for this archetype. The standard properties | |
176 | * are below. Several of them overlap parameters of this goal; it's better to just | |
177 | * set the parameter. | |
178 | * | |
179 | * <dl><dt>package</dt><dd>See the packageName parameter.</dd> | |
180 | * <dt>archetype.languages</dt><dd>See the archetypeLanguages parameter.</dd> | |
181 | * <dt>groupId</dt><dd>The default groupId of the generated project.</dd> | |
182 | * <dt>artifactId</dt><dd>The default artifactId of the generated project.</dd> | |
183 | * <dt>version</dt><dd>The default version of the generated project.</dd> | |
184 | * <dt>archetype.filteredExtensions</dt><dd>See the filteredExensions parameter.</dd> | |
185 | * </dl> | |
186 | * <strong>Custom Properties</strong> | |
187 | * <p> | |
188 | * Custom properties allow you to replace some constant values in the project's files | |
189 | * with Velocity macro references. When a user generates a project from your archetype | |
190 | * he or she gets the opportunity to replace the value from the source project. | |
191 | * </p> | |
192 | * <p> | |
193 | * Custom property names <strong>may not contain the '.' character</strong>. | |
194 | * </p> | |
195 | * <p> | |
196 | * For example, if you include a line like the following in your property file: | |
197 | * <pre> | |
198 | * cxf-version=2.5.1-SNAPSHOT | |
199 | * </pre> | |
200 | * the plugin will search your files for the string <pre>2.5.1-SNAPSHOT</pre> and | |
201 | * replace them with references to a velocity macro <pre>cxf-version</pre>. It will | |
202 | * then list <pre>cxf-version</pre> as a <pre>requiredProperty</pre> in the | |
203 | * archetype-metadata.xml, with <pre>2.5.1-SNAPSHOT</pre> as the default value. | |
204 | * </p> | |
205 | * | |
206 | * | |
207 | * @parameter expression="${archetype.properties}" | |
208 | */ | |
209 | private File propertyFile; | |
210 | ||
211 | /** | |
212 | * The property telling which phase to call on the generated archetype. | |
213 | * Interesting values are: <code>package</code>, <code>integration-test</code>, <code>install</code> and <code>deploy</code>. | |
214 | * | |
215 | * @parameter expression="${archetype.postPhase}" default-value="package" | |
216 | */ | |
217 | private String archetypePostPhase; | |
218 | ||
219 | /** | |
220 | * The directory where the archetype should be created. | |
221 | * | |
222 | * @parameter expression="${project.build.directory}/generated-sources/archetype" | |
223 | */ | |
224 | private File outputDirectory; | |
225 | ||
226 | /** @parameter expression="${testMode}" */ | |
227 | private boolean testMode; | |
228 | ||
229 | /** | |
230 | * The package name for Java source files to be incorporated in the archetype and | |
231 | * and relocated to the package that the user selects. | |
232 | * | |
233 | * @parameter expression="${packageName}" */ | |
234 | private String packageName; //Find a better way to resolve the package!!! enforce usage of the configurator | |
235 | ||
236 | /** | |
237 | * @parameter expression="${session}" | |
238 | * @readonly | |
239 | */ | |
240 | private MavenSession session; | |
241 | ||
242 | public void execute() | |
243 | throws MojoExecutionException, MojoFailureException | |
244 | { | |
245 | 0 | Properties executionProperties = session.getExecutionProperties(); |
246 | try | |
247 | { | |
248 | 0 | if ( propertyFile != null ) |
249 | { | |
250 | 0 | propertyFile.getParentFile().mkdirs(); |
251 | } | |
252 | ||
253 | 0 | List<String> languages = getLanguages( archetypeLanguages, propertyFile ); |
254 | ||
255 | 0 | Properties properties = |
256 | configurator.configureArchetypeCreation( project, Boolean.valueOf( interactive ), executionProperties, | |
257 | propertyFile, languages ); | |
258 | ||
259 | 0 | List<String> filtereds = getFilteredExtensions( archetypeFilteredExtentions, propertyFile ); |
260 | ||
261 | 0 | ArchetypeCreationRequest request = new ArchetypeCreationRequest() |
262 | .setDefaultEncoding( defaultEncoding ) | |
263 | .setProject( project ) | |
264 | /* Used when in interactive mode */ | |
265 | .setProperties( properties ) | |
266 | .setLanguages( languages ) | |
267 | /* Should be refactored to use some ant patterns */ | |
268 | .setFiltereds( filtereds ) | |
269 | /* This should be correctly handled */ | |
270 | .setPreserveCData( preserveCData ) | |
271 | .setKeepParent( keepParent ) | |
272 | .setPartialArchetype( partialArchetype ) | |
273 | /* This should be used before there and use only languages and filtereds */ | |
274 | .setArchetypeRegistryFile( archetypeRegistryFile ) | |
275 | .setLocalRepository( localRepository ) | |
276 | /* this should be resolved and asked for user to verify */ | |
277 | .setPackageName( packageName ) | |
278 | .setPostPhase( archetypePostPhase ) | |
279 | .setOutputDirectory( outputDirectory ); | |
280 | ||
281 | 0 | ArchetypeCreationResult result = manager.createArchetypeFromProject( request ); |
282 | ||
283 | 0 | if ( result.getCause() != null ) |
284 | { | |
285 | 0 | throw new MojoFailureException( result.getCause(), result.getCause().getMessage(), |
286 | result.getCause().getMessage() ); | |
287 | } | |
288 | ||
289 | 0 | getLog().info( "Archetype created in " + outputDirectory ); |
290 | ||
291 | 0 | if ( testMode ) |
292 | { | |
293 | // Now here a properties file would be useful to write so that we could automate | |
294 | // some functional tests where we string together an: | |
295 | // | |
296 | // archetype create from project -> deploy it into a test repo | |
297 | // project create from archetype -> use the repository we deployed to archetype to | |
298 | // generate | |
299 | // test the output | |
300 | // | |
301 | // This of course would be strung together from the outside. | |
302 | } | |
303 | ||
304 | } | |
305 | 0 | catch ( MojoFailureException ex ) |
306 | { | |
307 | 0 | throw ex; |
308 | } | |
309 | 0 | catch ( Exception ex ) |
310 | { | |
311 | 0 | throw new MojoFailureException( ex, ex.getMessage(), ex.getMessage() ); |
312 | 0 | } |
313 | 0 | } |
314 | ||
315 | private List<String> getFilteredExtensions( String archetypeFilteredExtentions, File propertyFile ) | |
316 | { | |
317 | 0 | List<String> filteredExtensions = new ArrayList<String>(); |
318 | ||
319 | 0 | if ( StringUtils.isNotEmpty( archetypeFilteredExtentions ) ) |
320 | { | |
321 | 0 | filteredExtensions.addAll( Arrays.asList( StringUtils.split( archetypeFilteredExtentions, "," ) ) ); |
322 | ||
323 | 0 | getLog().debug( "Found in command line extensions = " + filteredExtensions ); |
324 | } | |
325 | ||
326 | 0 | if ( filteredExtensions.isEmpty() && propertyFile != null && propertyFile.exists() ) |
327 | { | |
328 | 0 | Properties properties = PropertyUtils.loadProperties( propertyFile ); |
329 | ||
330 | 0 | String extensions = properties.getProperty( Constants.ARCHETYPE_FILTERED_EXTENSIONS ); |
331 | 0 | if ( StringUtils.isNotEmpty( extensions ) ) |
332 | { | |
333 | 0 | filteredExtensions.addAll( Arrays.asList( StringUtils.split( extensions, "," ) ) ); |
334 | } | |
335 | ||
336 | 0 | getLog().debug( "Found in propertyFile " + propertyFile.getName() + " extensions = " + filteredExtensions ); |
337 | } | |
338 | ||
339 | 0 | if ( filteredExtensions.isEmpty() ) |
340 | { | |
341 | 0 | filteredExtensions.addAll( Constants.DEFAULT_FILTERED_EXTENSIONS ); |
342 | ||
343 | 0 | getLog().debug( "Using default extensions = " + filteredExtensions ); |
344 | } | |
345 | ||
346 | 0 | return filteredExtensions; |
347 | } | |
348 | ||
349 | private List<String> getLanguages( String archetypeLanguages, File propertyFile ) | |
350 | { | |
351 | 0 | List<String> resultingLanguages = new ArrayList<String>(); |
352 | ||
353 | 0 | if ( StringUtils.isNotEmpty( archetypeLanguages ) ) |
354 | { | |
355 | 0 | resultingLanguages.addAll( Arrays.asList( StringUtils.split( archetypeLanguages, "," ) ) ); |
356 | ||
357 | 0 | getLog().debug( "Found in command line languages = " + resultingLanguages ); |
358 | } | |
359 | ||
360 | 0 | if ( resultingLanguages.isEmpty() && propertyFile != null && propertyFile.exists() ) |
361 | { | |
362 | 0 | Properties properties = PropertyUtils.loadProperties( propertyFile ); |
363 | ||
364 | 0 | String languages = properties.getProperty( Constants.ARCHETYPE_LANGUAGES ); |
365 | 0 | if ( StringUtils.isNotEmpty( languages ) ) |
366 | { | |
367 | 0 | resultingLanguages.addAll( Arrays.asList( StringUtils.split( languages, "," ) ) ); |
368 | } | |
369 | ||
370 | 0 | getLog().debug( "Found in propertyFile " + propertyFile.getName() + " languages = " + resultingLanguages ); |
371 | } | |
372 | ||
373 | 0 | if ( resultingLanguages.isEmpty() ) |
374 | { | |
375 | 0 | resultingLanguages.addAll( Constants.DEFAULT_LANGUAGES ); |
376 | ||
377 | 0 | getLog().debug( "Using default languages = " + resultingLanguages ); |
378 | } | |
379 | ||
380 | 0 | return resultingLanguages; |
381 | } | |
382 | } |