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 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 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 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 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 Properties executionProperties = session.getExecutionProperties(); 246 try 247 { 248 if ( propertyFile != null ) 249 { 250 propertyFile.getParentFile().mkdirs(); 251 } 252 253 List<String> languages = getLanguages( archetypeLanguages, propertyFile ); 254 255 Properties properties = 256 configurator.configureArchetypeCreation( project, Boolean.valueOf( interactive ), executionProperties, 257 propertyFile, languages ); 258 259 List<String> filtereds = getFilteredExtensions( archetypeFilteredExtentions, propertyFile ); 260 261 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 ArchetypeCreationResult result = manager.createArchetypeFromProject( request ); 282 283 if ( result.getCause() != null ) 284 { 285 throw new MojoFailureException( result.getCause(), result.getCause().getMessage(), 286 result.getCause().getMessage() ); 287 } 288 289 getLog().info( "Archetype created in " + outputDirectory ); 290 291 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 catch ( MojoFailureException ex ) 306 { 307 throw ex; 308 } 309 catch ( Exception ex ) 310 { 311 throw new MojoFailureException( ex, ex.getMessage(), ex.getMessage() ); 312 } 313 } 314 315 private List<String> getFilteredExtensions( String archetypeFilteredExtentions, File propertyFile ) 316 { 317 List<String> filteredExtensions = new ArrayList<String>(); 318 319 if ( StringUtils.isNotEmpty( archetypeFilteredExtentions ) ) 320 { 321 filteredExtensions.addAll( Arrays.asList( StringUtils.split( archetypeFilteredExtentions, "," ) ) ); 322 323 getLog().debug( "Found in command line extensions = " + filteredExtensions ); 324 } 325 326 if ( filteredExtensions.isEmpty() && propertyFile != null && propertyFile.exists() ) 327 { 328 Properties properties = PropertyUtils.loadProperties( propertyFile ); 329 330 String extensions = properties.getProperty( Constants.ARCHETYPE_FILTERED_EXTENSIONS ); 331 if ( StringUtils.isNotEmpty( extensions ) ) 332 { 333 filteredExtensions.addAll( Arrays.asList( StringUtils.split( extensions, "," ) ) ); 334 } 335 336 getLog().debug( "Found in propertyFile " + propertyFile.getName() + " extensions = " + filteredExtensions ); 337 } 338 339 if ( filteredExtensions.isEmpty() ) 340 { 341 filteredExtensions.addAll( Constants.DEFAULT_FILTERED_EXTENSIONS ); 342 343 getLog().debug( "Using default extensions = " + filteredExtensions ); 344 } 345 346 return filteredExtensions; 347 } 348 349 private List<String> getLanguages( String archetypeLanguages, File propertyFile ) 350 { 351 List<String> resultingLanguages = new ArrayList<String>(); 352 353 if ( StringUtils.isNotEmpty( archetypeLanguages ) ) 354 { 355 resultingLanguages.addAll( Arrays.asList( StringUtils.split( archetypeLanguages, "," ) ) ); 356 357 getLog().debug( "Found in command line languages = " + resultingLanguages ); 358 } 359 360 if ( resultingLanguages.isEmpty() && propertyFile != null && propertyFile.exists() ) 361 { 362 Properties properties = PropertyUtils.loadProperties( propertyFile ); 363 364 String languages = properties.getProperty( Constants.ARCHETYPE_LANGUAGES ); 365 if ( StringUtils.isNotEmpty( languages ) ) 366 { 367 resultingLanguages.addAll( Arrays.asList( StringUtils.split( languages, "," ) ) ); 368 } 369 370 getLog().debug( "Found in propertyFile " + propertyFile.getName() + " languages = " + resultingLanguages ); 371 } 372 373 if ( resultingLanguages.isEmpty() ) 374 { 375 resultingLanguages.addAll( Constants.DEFAULT_LANGUAGES ); 376 377 getLog().debug( "Using default languages = " + resultingLanguages ); 378 } 379 380 return resultingLanguages; 381 } 382 }