View Javadoc
1   package org.apache.maven.archetype.creator;
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.File;
23  import java.io.FileInputStream;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.OutputStream;
29  import java.io.Reader;
30  import java.io.Writer;
31  import java.util.ArrayList;
32  import java.util.Arrays;
33  import java.util.Collections;
34  import java.util.HashMap;
35  import java.util.HashSet;
36  import java.util.Iterator;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Properties;
40  import java.util.Set;
41  
42  import org.apache.commons.collections.CollectionUtils;
43  import org.apache.maven.archetype.ArchetypeCreationRequest;
44  import org.apache.maven.archetype.ArchetypeCreationResult;
45  import org.apache.maven.archetype.common.ArchetypeFilesResolver;
46  import org.apache.maven.archetype.common.Constants;
47  import org.apache.maven.archetype.common.PomManager;
48  import org.apache.maven.archetype.common.util.FileCharsetDetector;
49  import org.apache.maven.archetype.common.util.ListScanner;
50  import org.apache.maven.archetype.common.util.PathUtils;
51  import org.apache.maven.archetype.exception.TemplateCreationException;
52  import org.apache.maven.archetype.metadata.ArchetypeDescriptor;
53  import org.apache.maven.archetype.metadata.FileSet;
54  import org.apache.maven.archetype.metadata.ModuleDescriptor;
55  import org.apache.maven.archetype.metadata.RequiredProperty;
56  import org.apache.maven.archetype.metadata.io.xpp3.ArchetypeDescriptorXpp3Writer;
57  import org.apache.maven.model.Build;
58  import org.apache.maven.model.Dependency;
59  import org.apache.maven.model.Extension;
60  import org.apache.maven.model.Model;
61  import org.apache.maven.model.Plugin;
62  import org.apache.maven.model.PluginManagement;
63  import org.apache.maven.model.Profile;
64  import org.apache.maven.model.Resource;
65  import org.apache.maven.project.MavenProject;
66  import org.apache.maven.project.ProjectBuildingRequest;
67  import org.apache.maven.shared.invoker.DefaultInvocationRequest;
68  import org.apache.maven.shared.invoker.InvocationRequest;
69  import org.apache.maven.shared.invoker.InvocationResult;
70  import org.apache.maven.shared.invoker.Invoker;
71  import org.codehaus.plexus.component.annotations.Component;
72  import org.codehaus.plexus.component.annotations.Requirement;
73  import org.codehaus.plexus.logging.AbstractLogEnabled;
74  import org.codehaus.plexus.util.DirectoryScanner;
75  import org.codehaus.plexus.util.FileUtils;
76  import org.codehaus.plexus.util.IOUtil;
77  import org.codehaus.plexus.util.ReaderFactory;
78  import org.codehaus.plexus.util.StringUtils;
79  import org.codehaus.plexus.util.WriterFactory;
80  import org.codehaus.plexus.util.xml.Xpp3Dom;
81  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
82  
83  /**
84   * Create a 2.x Archetype project from a project. Since 2.0-alpha-5, an integration-test named "basic" is created along
85   * the archetype itself to provide immediate test when building the archetype.
86   */
87  @Component( role = ArchetypeCreator.class, hint = "fileset" )
88  public class FilesetArchetypeCreator
89      extends AbstractLogEnabled
90      implements ArchetypeCreator
91  {
92      private static final String DEFAULT_OUTPUT_DIRECTORY =
93          "target" + File.separator + "generated-sources" + File.separator + "archetype";
94  
95      @Requirement
96      private ArchetypeFilesResolver archetypeFilesResolver;
97  
98      @Requirement
99      private PomManager pomManager;
100     
101     @Requirement
102     private Invoker invoker;
103 
104     public void createArchetype( ArchetypeCreationRequest request, ArchetypeCreationResult result )
105     {
106         MavenProject project = request.getProject();
107         List<String> languages = request.getLanguages();
108         List<String> filtereds = request.getFiltereds();
109         String defaultEncoding = request.getDefaultEncoding();
110         boolean preserveCData = request.isPreserveCData();
111         boolean keepParent = request.isKeepParent();
112         boolean partialArchetype = request.isPartialArchetype();
113         File outputDirectory = request.getOutputDirectory();
114         File basedir = project.getBasedir();
115 
116         Properties properties = new Properties();
117         Properties configurationProperties = new Properties();
118         if ( request.getProperties() != null )
119         {
120             properties.putAll( request.getProperties() );
121             configurationProperties.putAll( request.getProperties() );
122         }
123 
124         extractPropertiesFromProject( project, properties, configurationProperties, request.getPackageName() );
125 
126         if ( outputDirectory == null )
127         {
128             getLogger().debug( "No output directory defined, using default: " + DEFAULT_OUTPUT_DIRECTORY );
129             outputDirectory = FileUtils.resolveFile( basedir, DEFAULT_OUTPUT_DIRECTORY );
130         }
131         outputDirectory.mkdirs();
132 
133         getLogger().debug( "Creating archetype in " + outputDirectory );
134 
135         try
136         {
137             File archetypePomFile = createArchetypeProjectPom( project, request.getProjectBuildingRequest(),
138                                                                configurationProperties, outputDirectory );
139 
140             File archetypeResourcesDirectory = new File( outputDirectory, getTemplateOutputDirectory() );
141 
142             File archetypeFilesDirectory = new File( archetypeResourcesDirectory, Constants.ARCHETYPE_RESOURCES );
143             getLogger().debug( "Archetype's files output directory " + archetypeFilesDirectory );
144 
145             File archetypeDescriptorFile = new File( archetypeResourcesDirectory, Constants.ARCHETYPE_DESCRIPTOR );
146             archetypeDescriptorFile.getParentFile().mkdirs();
147 
148             File archetypePostGenerationScript =
149                 new File( archetypeResourcesDirectory, Constants.ARCHETYPE_POST_GENERATION_SCRIPT );
150             archetypePostGenerationScript.getParentFile().mkdirs();
151 
152             if ( request.getProject().getBuild() != null && CollectionUtils.isNotEmpty(
153                 request.getProject().getBuild().getResources() ) )
154             {
155                 for ( Resource resource : request.getProject().getBuild().getResources() )
156                 {
157                     File inputFile = new File(
158                         resource.getDirectory() + File.separator + Constants.ARCHETYPE_POST_GENERATION_SCRIPT );
159                     if ( inputFile.exists() )
160                     {
161                         FileUtils.copyFile( inputFile, archetypePostGenerationScript );
162                     }
163                 }
164             }
165 
166             getLogger().debug( "Starting archetype's descriptor " + project.getArtifactId() );
167             ArchetypeDescriptor archetypeDescriptor = new ArchetypeDescriptor();
168 
169             archetypeDescriptor.setName( project.getArtifactId() );
170             archetypeDescriptor.setPartial( partialArchetype );
171 
172             addRequiredProperties( archetypeDescriptor, properties );
173 
174             // TODO ensure reverseProperties contains NO dotted properties
175             Properties reverseProperties = getReversedProperties( archetypeDescriptor, properties );
176             // reverseProperties.remove( Constants.GROUP_ID );
177 
178             // TODO ensure pomReversedProperties contains NO dotted properties
179             Properties pomReversedProperties = getReversedProperties( archetypeDescriptor, properties );
180             // pomReversedProperties.remove( Constants.PACKAGE );
181 
182             String packageName = configurationProperties.getProperty( Constants.PACKAGE );
183 
184             Model pom = pomManager.readPom( FileUtils.resolveFile( basedir, Constants.ARCHETYPE_POM ) );
185 
186             List<String> excludePatterns =
187                 configurationProperties.getProperty( Constants.EXCLUDE_PATTERNS ) != null
188                     ? Arrays.asList(
189                     StringUtils.split( configurationProperties.getProperty( Constants.EXCLUDE_PATTERNS ), "," ) )
190                     : Collections.<String>emptyList();
191 
192             List<String> fileNames = resolveFileNames( pom, basedir, excludePatterns );
193             if ( getLogger().isDebugEnabled() )
194             {
195                 getLogger().debug( "Scanned for files " + fileNames.size() );
196 
197                 for ( String name : fileNames )
198                 {
199                     getLogger().debug( "- " + name );
200                 }
201             }
202 
203             List<FileSet> filesets = resolveFileSets( packageName, fileNames, languages, filtereds, defaultEncoding );
204             getLogger().debug( "Resolved filesets for " + archetypeDescriptor.getName() );
205 
206             archetypeDescriptor.setFileSets( filesets );
207 
208             createArchetypeFiles( reverseProperties, filesets, packageName, basedir, archetypeFilesDirectory,
209                                   defaultEncoding, excludePatterns );
210             getLogger().debug( "Created files for " + archetypeDescriptor.getName() );
211 
212             setParentArtifactId( reverseProperties, configurationProperties.getProperty( Constants.ARTIFACT_ID ) );
213 
214             for ( String moduleId : pom.getModules() )
215             {
216                 String rootArtifactId = configurationProperties.getProperty( Constants.ARTIFACT_ID );
217                 String moduleIdDirectory = moduleId;
218 
219                 if ( moduleId.indexOf( rootArtifactId ) >= 0 )
220                 {
221                     moduleIdDirectory = StringUtils.replace( moduleId, rootArtifactId, "__rootArtifactId__" );
222                 }
223 
224                 getLogger().debug( "Creating module " + moduleId );
225 
226                 ModuleDescriptor moduleDescriptor =
227                     createModule( reverseProperties, rootArtifactId, moduleId, packageName,
228                                   FileUtils.resolveFile( basedir, moduleId ),
229                                   new File( archetypeFilesDirectory, moduleIdDirectory ), languages, filtereds,
230                                   defaultEncoding, preserveCData, keepParent );
231 
232                 archetypeDescriptor.addModule( moduleDescriptor );
233 
234                 getLogger().debug(
235                     "Added module " + moduleDescriptor.getName() + " in " + archetypeDescriptor.getName() );
236             }
237 
238             restoreParentArtifactId( reverseProperties, null );
239             restoreArtifactId( reverseProperties, configurationProperties.getProperty( Constants.ARTIFACT_ID ) );
240 
241             createPoms( pom, configurationProperties.getProperty( Constants.ARTIFACT_ID ),
242                         configurationProperties.getProperty( Constants.ARTIFACT_ID ), archetypeFilesDirectory, basedir,
243                         pomReversedProperties, preserveCData, keepParent );
244             getLogger().debug( "Created Archetype " + archetypeDescriptor.getName() + " template pom(s)" );
245 
246             Writer out = null;
247             try
248             {
249                 out = WriterFactory.newXmlWriter( archetypeDescriptorFile );
250 
251                 ArchetypeDescriptorXpp3Writer writer = new ArchetypeDescriptorXpp3Writer();
252 
253                 writer.write( out, archetypeDescriptor );
254 
255                 getLogger().debug( "Archetype " + archetypeDescriptor.getName() + " descriptor written" );
256             }
257             finally
258             {
259                 IOUtil.close( out );
260             }
261 
262             createArchetypeBasicIt( archetypeDescriptor, outputDirectory );
263 
264             // Copy archetype integration tests.
265             File archetypeIntegrationTestInputFolder =
266                 new File( basedir, Constants.SRC + File.separator + "it" + File.separator + "projects" );
267             File archetypeIntegrationTestOutputFolder = new File( outputDirectory,
268                                                                   Constants.SRC + File.separator + Constants.TEST
269                                                                       + File.separator + Constants.RESOURCES
270                                                                       + File.separator + "projects" );
271 
272             if ( archetypeIntegrationTestInputFolder.exists() )
273             {
274                 getLogger().info( "Copying: " + archetypeIntegrationTestInputFolder.getAbsolutePath() + " into "
275                                       + archetypeIntegrationTestOutputFolder.getAbsolutePath() );
276 
277                 FileUtils.copyDirectoryStructure( archetypeIntegrationTestInputFolder,
278                                                   archetypeIntegrationTestOutputFolder );
279 
280             }
281             InvocationRequest internalRequest = new DefaultInvocationRequest();
282             internalRequest.setPomFile( archetypePomFile );
283             internalRequest.setGoals( Collections.singletonList( request.getPostPhase() ) );
284             if ( request.getLocalRepository() != null )
285             {
286                 internalRequest.setLocalRepositoryDirectory( new File( request.getLocalRepository().getBasedir() ) );
287             }
288 
289             InvocationResult invokerResult = invoker.execute( internalRequest );
290             if ( invokerResult.getExitCode() != 0 )
291             {
292                 if ( invokerResult.getExecutionException() != null )
293                 {
294                     throw invokerResult.getExecutionException();
295                 }
296                 else
297                 {
298                     throw new Exception( "Invoker process ended with result different than 0!" );
299                 }
300             }
301 
302         }
303         catch ( Exception e )
304         {
305             result.setCause( e );
306         }
307     }
308 
309     /**
310      * Create an archetype IT, ie goals.txt and archetype.properties in src/test/resources/projects/basic.
311      *
312      * @param archetypeDescriptor
313      * @param generatedSourcesDirectory
314      * @throws IOException
315      * @since 2.0-alpha-5
316      */
317     private void createArchetypeBasicIt( ArchetypeDescriptor archetypeDescriptor, File generatedSourcesDirectory )
318         throws IOException
319     {
320         String basic =
321             Constants.SRC + File.separator + Constants.TEST + File.separator + Constants.RESOURCES + File.separator
322                 + "projects" + File.separator + "basic";
323         File basicItDirectory = new File( generatedSourcesDirectory, basic );
324         basicItDirectory.mkdirs();
325 
326         InputStream in = null;
327         OutputStream out = null;
328 
329         try
330         {
331             in = FilesetArchetypeCreator.class.getResourceAsStream( "archetype.properties" );
332 
333             Properties archetypeProperties = new Properties();
334             archetypeProperties.load( in );
335 
336             for ( RequiredProperty req : archetypeDescriptor.getRequiredProperties() )
337             {
338                 archetypeProperties.put( req.getKey(), req.getDefaultValue() );
339             }
340 
341             out = new FileOutputStream( new File( basicItDirectory, "archetype.properties" ) );
342             archetypeProperties.store( out, null );
343         }
344         finally
345         {
346             IOUtil.close( in );
347             IOUtil.close( out );
348         }
349         copyResource( "goal.txt", new File( basicItDirectory, "goal.txt" ) );
350 
351         getLogger().debug( "Added basic integration test" );
352     }
353 
354     private void extractPropertiesFromProject( MavenProject project, Properties properties,
355                                                Properties configurationProperties, String packageName )
356     {
357         if ( !properties.containsKey( Constants.GROUP_ID ) )
358         {
359             properties.setProperty( Constants.GROUP_ID, project.getGroupId() );
360         }
361         configurationProperties.setProperty( Constants.GROUP_ID, properties.getProperty( Constants.GROUP_ID ) );
362 
363         if ( !properties.containsKey( Constants.ARTIFACT_ID ) )
364         {
365             properties.setProperty( Constants.ARTIFACT_ID, project.getArtifactId() );
366         }
367         configurationProperties.setProperty( Constants.ARTIFACT_ID, properties.getProperty( Constants.ARTIFACT_ID ) );
368 
369         if ( !properties.containsKey( Constants.VERSION ) )
370         {
371             properties.setProperty( Constants.VERSION, project.getVersion() );
372         }
373         configurationProperties.setProperty( Constants.VERSION, properties.getProperty( Constants.VERSION ) );
374 
375         if ( packageName != null )
376         {
377             properties.setProperty( Constants.PACKAGE, packageName );
378         }
379         else if ( !properties.containsKey( Constants.PACKAGE ) )
380         {
381             properties.setProperty( Constants.PACKAGE, project.getGroupId() );
382         }
383         configurationProperties.setProperty( Constants.PACKAGE, properties.getProperty( Constants.PACKAGE ) );
384     }
385 
386     /**
387      * Create the archetype project pom.xml file, that will be used to build the archetype.
388      */
389     private File createArchetypeProjectPom( MavenProject project, ProjectBuildingRequest buildingRequest,
390                                             Properties configurationProperties, File projectDir )
391         throws TemplateCreationException, IOException
392     {
393         Model model = new Model();
394         model.setModelVersion( "4.0.0" );
395         // these values should be retrieved from the request with sensible defaults
396         model.setGroupId( configurationProperties.getProperty( Constants.ARCHETYPE_GROUP_ID, project.getGroupId() ) );
397         model.setArtifactId(
398             configurationProperties.getProperty( Constants.ARCHETYPE_ARTIFACT_ID, project.getArtifactId() ) );
399         model.setVersion( configurationProperties.getProperty( Constants.ARCHETYPE_VERSION, project.getVersion() ) );
400         model.setPackaging( "maven-archetype" );
401         model.setName(
402             configurationProperties.getProperty( Constants.ARCHETYPE_ARTIFACT_ID, project.getArtifactId() ) );
403         model.setUrl( configurationProperties.getProperty( Constants.ARCHETYPE_URL, project.getUrl() ) );
404         model.setDescription(
405             configurationProperties.getProperty( Constants.ARCHETYPE_DESCRIPTION, project.getDescription() ) );
406         model.setLicenses( project.getLicenses() );
407         model.setDevelopers( project.getDevelopers() );
408         model.setScm( project.getScm() );
409         Build build = new Build();
410         model.setBuild( build );
411 
412         // In many cases where we are behind a firewall making Archetypes for work mates we want
413         // to simply be able to deploy the archetypes once we have created them. In order to do
414         // this we want to utilize information from the project we are creating the archetype from.
415         // This will be a fully working project that has been testing and inherits from a POM
416         // that contains deployment information, along with any extensions required for deployment.
417         // We don't want to create archetypes that cannot be deployed after we create them. People
418         // might want to edit the archetype POM but they should not have too.
419 
420         if ( project.getParent() != null )
421         {
422             MavenProject p = project.getParent();
423 
424             if ( p.getDistributionManagement() != null )
425             {
426                 model.setDistributionManagement( p.getDistributionManagement() );
427             }
428 
429             if ( p.getBuildExtensions() != null )
430             {
431                 for ( Extension be : p.getBuildExtensions() )
432                 {
433                     model.getBuild().addExtension( be );
434                 }
435             }
436         }
437 
438         Extension extension = new Extension();
439         extension.setGroupId( "org.apache.maven.archetype" );
440         extension.setArtifactId( "archetype-packaging" );
441         extension.setVersion( getArchetypeVersion() );
442         model.getBuild().addExtension( extension );
443 
444         Plugin plugin = new Plugin();
445         plugin.setGroupId( "org.apache.maven.plugins" );
446         plugin.setArtifactId( "maven-archetype-plugin" );
447         plugin.setVersion( getArchetypeVersion() );
448 
449         PluginManagement pluginManagement = new PluginManagement();
450         pluginManagement.addPlugin( plugin );
451         model.getBuild().setPluginManagement( pluginManagement );
452 
453         getLogger().debug( "Creating archetype's pom" );
454 
455         File archetypePomFile = new File( projectDir, Constants.ARCHETYPE_POM );
456 
457         archetypePomFile.getParentFile().mkdirs();
458 
459         copyResource( "pom-prototype.xml", archetypePomFile );
460 
461         pomManager.writePom( model, archetypePomFile, archetypePomFile );
462 
463         return archetypePomFile;
464     }
465 
466     private void copyResource( String name, File destination )
467         throws IOException
468     {
469         InputStream in = null;
470         OutputStream out = null;
471 
472         try
473         {
474             in = FilesetArchetypeCreator.class.getResourceAsStream( name );
475             out = new FileOutputStream( destination );
476 
477             IOUtil.copy( in, out );
478         }
479         finally
480         {
481             IOUtil.close( in );
482             IOUtil.close( out );
483         }
484     }
485 
486     private void addRequiredProperties( ArchetypeDescriptor archetypeDescriptor, Properties properties )
487     {
488         Properties requiredProperties = new Properties();
489         requiredProperties.putAll( properties );
490         requiredProperties.remove( Constants.ARCHETYPE_GROUP_ID );
491         requiredProperties.remove( Constants.ARCHETYPE_ARTIFACT_ID );
492         requiredProperties.remove( Constants.ARCHETYPE_VERSION );
493         requiredProperties.remove( Constants.GROUP_ID );
494         requiredProperties.remove( Constants.ARTIFACT_ID );
495         requiredProperties.remove( Constants.VERSION );
496         requiredProperties.remove( Constants.PACKAGE );
497         requiredProperties.remove( Constants.EXCLUDE_PATTERNS );
498 
499         for ( Iterator<?> propertiesIterator = requiredProperties.keySet().iterator(); propertiesIterator.hasNext(); )
500         {
501             String propertyKey = (String) propertiesIterator.next();
502 
503             RequiredProperty requiredProperty = new RequiredProperty();
504             requiredProperty.setKey( propertyKey );
505             requiredProperty.setDefaultValue( requiredProperties.getProperty( propertyKey ) );
506 
507             archetypeDescriptor.addRequiredProperty( requiredProperty );
508 
509             getLogger().debug(
510                 "Adding requiredProperty " + propertyKey + "=" + requiredProperties.getProperty( propertyKey )
511                     + " to archetype's descriptor" );
512         }
513     }
514 
515     private void createModulePoms( Properties pomReversedProperties, String rootArtifactId, String packageName,
516                                    File basedir, File archetypeFilesDirectory, boolean preserveCData,
517                                    boolean keepParent )
518         throws FileNotFoundException, IOException, XmlPullParserException
519     {
520         Model pom = pomManager.readPom( FileUtils.resolveFile( basedir, Constants.ARCHETYPE_POM ) );
521 
522         String parentArtifactId = pomReversedProperties.getProperty( Constants.PARENT_ARTIFACT_ID );
523         String artifactId = pom.getArtifactId();
524         setParentArtifactId( pomReversedProperties, pomReversedProperties.getProperty( Constants.ARTIFACT_ID ) );
525         setArtifactId( pomReversedProperties, pom.getArtifactId() );
526 
527         for ( String subModuleId : pom.getModules() )
528         {
529             String subModuleIdDirectory = subModuleId;
530 
531             if ( subModuleId.indexOf( rootArtifactId ) >= 0 )
532             {
533                 subModuleIdDirectory = StringUtils.replace( subModuleId, rootArtifactId, "__rootArtifactId__" );
534             }
535 
536             createModulePoms( pomReversedProperties, rootArtifactId, packageName,
537                               FileUtils.resolveFile( basedir, subModuleId ),
538                               FileUtils.resolveFile( archetypeFilesDirectory, subModuleIdDirectory ), preserveCData,
539                               keepParent );
540         }
541 
542         createModulePom( pom, rootArtifactId, archetypeFilesDirectory, pomReversedProperties,
543                          FileUtils.resolveFile( basedir, Constants.ARCHETYPE_POM ), preserveCData, keepParent );
544 
545         restoreParentArtifactId( pomReversedProperties, parentArtifactId );
546         restoreArtifactId( pomReversedProperties, artifactId );
547     }
548 
549     private void createPoms( Model pom, String rootArtifactId, String artifactId, File archetypeFilesDirectory,
550                              File basedir, Properties pomReversedProperties, boolean preserveCData, boolean keepParent )
551         throws IOException, FileNotFoundException, XmlPullParserException
552     {
553         setArtifactId( pomReversedProperties, pom.getArtifactId() );
554 
555         for ( String moduleId : pom.getModules() )
556         {
557             String moduleIdDirectory = moduleId;
558 
559             if ( moduleId.indexOf( rootArtifactId ) >= 0 )
560             {
561                 moduleIdDirectory = StringUtils.replace( moduleId, rootArtifactId, "__rootArtifactId__" );
562             }
563 
564             createModulePoms( pomReversedProperties, rootArtifactId, moduleId,
565                               FileUtils.resolveFile( basedir, moduleId ),
566                               new File( archetypeFilesDirectory, moduleIdDirectory ), preserveCData, keepParent );
567         }
568 
569         restoreParentArtifactId( pomReversedProperties, null );
570         restoreArtifactId( pomReversedProperties, artifactId );
571 
572         createArchetypePom( pom, archetypeFilesDirectory, pomReversedProperties,
573                             FileUtils.resolveFile( basedir, Constants.ARCHETYPE_POM ), preserveCData, keepParent );
574     }
575 
576     private String getPackageInPathFormat( String aPackage )
577     {
578         return StringUtils.replace( aPackage, ".", "/" );
579     }
580 
581     private void rewriteReferences( Model pom, String rootArtifactId, String groupId )
582     {
583         // rewrite Dependencies
584         if ( pom.getDependencies() != null && !pom.getDependencies().isEmpty() )
585         {
586             for ( Dependency dependency : pom.getDependencies() )
587             {
588                 rewriteDependencyReferences( dependency, rootArtifactId, groupId );
589             }
590         }
591 
592         // rewrite DependencyManagement
593         if ( pom.getDependencyManagement() != null && pom.getDependencyManagement().getDependencies() != null
594             && !pom.getDependencyManagement().getDependencies().isEmpty() )
595         {
596             for ( Dependency dependency : pom.getDependencyManagement().getDependencies() )
597             {
598                 rewriteDependencyReferences( dependency, rootArtifactId, groupId );
599             }
600         }
601 
602         // rewrite Plugins
603         if ( pom.getBuild() != null && pom.getBuild().getPlugins() != null && !pom.getBuild().getPlugins().isEmpty() )
604         {
605             for ( Plugin plugin : pom.getBuild().getPlugins() )
606             {
607                 rewritePluginReferences( plugin, rootArtifactId, groupId );
608             }
609         }
610 
611         // rewrite PluginManagement
612         if ( pom.getBuild() != null && pom.getBuild().getPluginManagement() != null
613             && pom.getBuild().getPluginManagement().getPlugins() != null
614             && !pom.getBuild().getPluginManagement().getPlugins().isEmpty() )
615         {
616             for ( Plugin plugin : pom.getBuild().getPluginManagement().getPlugins() )
617             {
618                 rewritePluginReferences( plugin, rootArtifactId, groupId );
619             }
620         }
621 
622         // rewrite Profiles
623         if ( pom.getProfiles() != null )
624         {
625             for ( Profile profile : pom.getProfiles() )
626             {
627                 // rewrite Dependencies
628                 if ( profile.getDependencies() != null && !profile.getDependencies().isEmpty() )
629                 {
630                     for ( Dependency dependency : profile.getDependencies() )
631                     {
632                         rewriteDependencyReferences( dependency, rootArtifactId, groupId );
633                     }
634                 }
635 
636                 // rewrite DependencyManagement
637                 if ( profile.getDependencyManagement() != null
638                     && profile.getDependencyManagement().getDependencies() != null
639                     && !profile.getDependencyManagement().getDependencies().isEmpty() )
640                 {
641                     for ( Dependency dependency : profile.getDependencyManagement().getDependencies() )
642                     {
643                         rewriteDependencyReferences( dependency, rootArtifactId, groupId );
644                     }
645                 }
646 
647                 // rewrite Plugins
648                 if ( profile.getBuild() != null && profile.getBuild().getPlugins() != null
649                     && !profile.getBuild().getPlugins().isEmpty() )
650                 {
651                     for ( Plugin plugin : profile.getBuild().getPlugins() )
652                     {
653                         rewritePluginReferences( plugin, rootArtifactId, groupId );
654                     }
655                 }
656 
657                 // rewrite PluginManagement
658                 if ( profile.getBuild() != null && profile.getBuild().getPluginManagement() != null
659                     && profile.getBuild().getPluginManagement().getPlugins() != null
660                     && !profile.getBuild().getPluginManagement().getPlugins().isEmpty() )
661                 {
662                     for ( Plugin plugin : profile.getBuild().getPluginManagement().getPlugins() )
663                     {
664                         rewritePluginReferences( plugin, rootArtifactId, groupId );
665                     }
666                 }
667             }
668         }
669     }
670 
671     private void rewriteDependencyReferences( Dependency dependency, String rootArtifactId, String groupId )
672     {
673         if ( dependency.getArtifactId() != null && dependency.getArtifactId().indexOf( rootArtifactId ) >= 0 )
674         {
675             if ( dependency.getGroupId() != null )
676             {
677                 dependency.setGroupId(
678                     StringUtils.replace( dependency.getGroupId(), groupId, "${" + Constants.GROUP_ID + "}" ) );
679             }
680 
681             dependency.setArtifactId(
682                 StringUtils.replace( dependency.getArtifactId(), rootArtifactId, "${rootArtifactId}" ) );
683 
684             if ( dependency.getVersion() != null )
685             {
686                 dependency.setVersion( "${" + Constants.VERSION + "}" );
687             }
688         }
689     }
690 
691     private void rewritePluginReferences( Plugin plugin, String rootArtifactId, String groupId )
692     {
693         if ( plugin.getArtifactId() != null && plugin.getArtifactId().indexOf( rootArtifactId ) >= 0 )
694         {
695             if ( plugin.getGroupId() != null )
696             {
697                 String g = StringUtils.replace( plugin.getGroupId(), groupId, "${" + Constants.GROUP_ID + "}" );
698                 plugin.setGroupId( g );
699             }
700 
701             plugin.setArtifactId( StringUtils.replace( plugin.getArtifactId(), rootArtifactId, "${rootArtifactId}" ) );
702 
703             if ( plugin.getVersion() != null )
704             {
705                 plugin.setVersion( "${" + Constants.VERSION + "}" );
706             }
707         }
708 
709         if ( plugin.getArtifactId() != null && "maven-ear-plugin".equals( plugin.getArtifactId() ) )
710         {
711             rewriteEARPluginReferences( plugin, rootArtifactId, groupId );
712         }
713     }
714 
715     private void rewriteEARPluginReferences( Plugin plugin, String rootArtifactId, String groupId )
716     {
717         Xpp3Dom configuration = (Xpp3Dom) plugin.getConfiguration();
718         if ( configuration != null )
719         {
720             Xpp3Dom[] modules = configuration.getChild( "modules" ).getChildren();
721             for ( int i = 0; i < modules.length; i++ )
722             {
723                 Xpp3Dom module = modules[i];
724                 Xpp3Dom moduleGroupId = module.getChild( "groupId" );
725                 Xpp3Dom moduleArtifactId = module.getChild( "artifactId" );
726                 Xpp3Dom moduleBundleFileName = module.getChild( "bundleFileName" );
727                 Xpp3Dom moduleModuleId = module.getChild( "moduleId" );
728                 Xpp3Dom moduleContextRoot = module.getChild( "contextRoot" );
729 
730                 if ( moduleGroupId != null )
731                 {
732                     moduleGroupId.setValue(
733                         StringUtils.replace( moduleGroupId.getValue(), groupId, "${" + Constants.GROUP_ID + "}" ) );
734                 }
735 
736                 if ( moduleArtifactId != null )
737                 {
738                     moduleArtifactId.setValue(
739                         StringUtils.replace( moduleArtifactId.getValue(), rootArtifactId, "${rootArtifactId}" ) );
740                 }
741 
742                 if ( moduleBundleFileName != null )
743                 {
744                     moduleBundleFileName.setValue(
745                         StringUtils.replace( moduleBundleFileName.getValue(), rootArtifactId, "${rootArtifactId}" ) );
746                 }
747 
748                 if ( moduleModuleId != null )
749                 {
750                     moduleModuleId.setValue(
751                         StringUtils.replace( moduleModuleId.getValue(), rootArtifactId, "${rootArtifactId}" ) );
752                 }
753 
754                 if ( moduleContextRoot != null )
755                 {
756                     moduleContextRoot.setValue(
757                         StringUtils.replace( moduleContextRoot.getValue(), rootArtifactId, "${rootArtifactId}" ) );
758                 }
759             }
760         }
761     }
762 
763     private void setArtifactId( Properties properties, String artifactId )
764     {
765         properties.setProperty( Constants.ARTIFACT_ID, artifactId );
766     }
767 
768     private List<String> concatenateToList( List<String> toConcatenate, String with )
769     {
770         List<String> result = new ArrayList<String>( toConcatenate.size() );
771 
772         for ( String concatenate : toConcatenate )
773         {
774             result.add( ( ( with.length() > 0 ) ? ( with + "/" + concatenate ) : concatenate ) );
775         }
776 
777         return result;
778     }
779 
780     private List<String> addLists( List<String> list, List<String> other )
781     {
782         List<String> result = new ArrayList<String>( list.size() + other.size() );
783         result.addAll( list );
784         result.addAll( other );
785         return result;
786     }
787 
788     private void copyFiles( File basedir, File archetypeFilesDirectory, String directory, List<String> fileSetResources,
789                             boolean packaged, String packageName, Properties reverseProperties )
790         throws IOException
791     {
792         String packageAsDirectory = StringUtils.replace( packageName, ".", File.separator );
793 
794         getLogger().debug( "Package as Directory: Package:" + packageName + "->" + packageAsDirectory );
795 
796         for ( String inputFileName : fileSetResources )
797         {
798             String outputFileName = packaged
799                 ? StringUtils.replace( inputFileName, packageAsDirectory + File.separator, "" )
800                 : inputFileName;
801             getLogger().debug( "InputFileName:" + inputFileName );
802             getLogger().debug( "OutputFileName:" + outputFileName );
803 
804             reverseProperties.remove( "archetype.languages" );
805 
806             String reversedOutputFilename = getReversedFilename( outputFileName, reverseProperties );
807 
808             File outputFile = new File( archetypeFilesDirectory, reversedOutputFilename );
809 
810             File inputFile = new File( basedir, inputFileName );
811 
812             outputFile.getParentFile().mkdirs();
813 
814             FileUtils.copyFile( inputFile, outputFile );
815         }
816     }
817 
818     private void createArchetypeFiles( Properties reverseProperties, List<FileSet> fileSets, String packageName,
819                                        File basedir, File archetypeFilesDirectory, String defaultEncoding,
820                                        List<String> excludePatterns )
821         throws IOException
822     {
823         getLogger().debug( "Creating Archetype/Module files from " + basedir + " to " + archetypeFilesDirectory );
824 
825         for ( FileSet fileSet : fileSets )
826         {
827             DirectoryScanner scanner = new DirectoryScanner();
828             scanner.setBasedir( basedir );
829             scanner.setIncludes( (String[]) concatenateToList( fileSet.getIncludes(), fileSet.getDirectory() ).toArray(
830                 new String[fileSet.getIncludes().size()] ) );
831             scanner.setExcludes( (String[]) addLists( fileSet.getExcludes(), excludePatterns ).toArray(
832                 new String[fileSet.getExcludes().size()] ) );
833             scanner.addDefaultExcludes();
834             getLogger().debug( "Using fileset " + fileSet );
835             scanner.scan();
836 
837             List<String> fileSetResources = Arrays.asList( scanner.getIncludedFiles() );
838             getLogger().debug( "Scanned " + fileSetResources.size() + " resources" );
839 
840             if ( fileSet.isFiltered() )
841             {
842                 processFileSet( basedir, archetypeFilesDirectory, fileSet.getDirectory(), fileSetResources,
843                                 fileSet.isPackaged(), packageName, reverseProperties, defaultEncoding );
844                 getLogger().debug( "Processed " + fileSet.getDirectory() + " files" );
845             }
846             else
847             {
848                 copyFiles( basedir, archetypeFilesDirectory, fileSet.getDirectory(), fileSetResources,
849                            fileSet.isPackaged(), packageName, reverseProperties );
850                 getLogger().debug( "Copied " + fileSet.getDirectory() + " files" );
851             }
852         }
853     }
854 
855     private void createArchetypePom( Model pom, File archetypeFilesDirectory, Properties pomReversedProperties,
856                                      File initialPomFile, boolean preserveCData, boolean keepParent )
857         throws IOException
858     {
859         File outputFile = FileUtils.resolveFile( archetypeFilesDirectory, Constants.ARCHETYPE_POM );
860 
861         if ( preserveCData )
862         {
863             getLogger().debug( "Preserving CDATA parts of pom" );
864             File inputFile = FileUtils.resolveFile( archetypeFilesDirectory, Constants.ARCHETYPE_POM + ".tmp" );
865 
866             FileUtils.copyFile( initialPomFile, inputFile );
867 
868             Reader in = null;
869             Writer out = null;
870             try
871             {
872                 in = ReaderFactory.newXmlReader( inputFile );
873 
874                 String initialcontent = IOUtil.toString( in );
875 
876                 String content = getReversedContent( initialcontent, pomReversedProperties );
877 
878                 outputFile.getParentFile().mkdirs();
879 
880                 out = WriterFactory.newXmlWriter( outputFile );
881 
882                 IOUtil.copy( content, out );
883             }
884             finally
885             {
886                 IOUtil.close( in );
887                 IOUtil.close( out );
888             }
889 
890             inputFile.delete();
891         }
892         else
893         {
894             if ( !keepParent )
895             {
896                 pom.setParent( null );
897             }
898 
899             pom.setModules( null );
900             pom.setGroupId( "${" + Constants.GROUP_ID + "}" );
901             pom.setArtifactId( "${" + Constants.ARTIFACT_ID + "}" );
902             pom.setVersion( "${" + Constants.VERSION + "}" );
903             pom.setName( getReversedPlainContent( pom.getName(), pomReversedProperties ) );
904             pom.setDescription( getReversedPlainContent( pom.getDescription(), pomReversedProperties ) );
905             pom.setUrl( getReversedPlainContent( pom.getUrl(), pomReversedProperties ) );
906 
907             rewriteReferences( pom, pomReversedProperties.getProperty( Constants.ARTIFACT_ID ),
908                                pomReversedProperties.getProperty( Constants.GROUP_ID ) );
909 
910             pomManager.writePom( pom, outputFile, initialPomFile );
911         }
912 
913         Reader in = null;
914         try
915         {
916             in = ReaderFactory.newXmlReader( initialPomFile );
917             String initialcontent = IOUtil.toString( in );
918 
919             Iterator<?> properties = pomReversedProperties.keySet().iterator();
920             while ( properties.hasNext() )
921             {
922                 String property = (String) properties.next();
923 
924                 if ( initialcontent.indexOf( "${" + property + "}" ) > 0 )
925                 {
926                     getLogger().warn(
927                         "Archetype uses ${" + property + "} for internal processing, but file " + initialPomFile
928                             + " contains this property already" );
929                 }
930             }
931         }
932         finally
933         {
934             IOUtil.close( in );
935         }
936     }
937 
938     private FileSet createFileSet( final List<String> excludes, final boolean packaged, final boolean filtered,
939                                    final String group, final List<String> includes, String defaultEncoding )
940     {
941         FileSet fileSet = new FileSet();
942 
943         fileSet.setDirectory( group );
944         fileSet.setPackaged( packaged );
945         fileSet.setFiltered( filtered );
946         fileSet.setIncludes( includes );
947         fileSet.setExcludes( excludes );
948         fileSet.setEncoding( defaultEncoding );
949 
950         getLogger().debug( "Created Fileset " + fileSet );
951 
952         return fileSet;
953     }
954 
955     private List<FileSet> createFileSets( List<String> files, int level, boolean packaged, String packageName,
956                                           boolean filtered, String defaultEncoding )
957     {
958         List<FileSet> fileSets = new ArrayList<FileSet>();
959 
960         if ( !files.isEmpty() )
961         {
962             getLogger().debug( "Creating filesets" + ( packaged ? ( " packaged (" + packageName + ")" ) : "" ) + (
963                 filtered
964                     ? " filtered"
965                     : "" ) + " at level " + level );
966             if ( level == 0 )
967             {
968                 List<String> includes = new ArrayList<String>( files );
969                 List<String> excludes = new ArrayList<String>();
970 
971                 if ( !includes.isEmpty() )
972                 {
973                     fileSets.add( createFileSet( excludes, packaged, filtered, "", includes, defaultEncoding ) );
974                 }
975             }
976             else
977             {
978                 Map<String, List<String>> groups = getGroupsMap( files, level );
979 
980                 for ( String group : groups.keySet() )
981                 {
982                     getLogger().debug( "Creating filesets for group " + group );
983 
984                     if ( !packaged )
985                     {
986                         fileSets.add( getUnpackagedFileSet( filtered, group, groups.get( group ), defaultEncoding ) );
987                     }
988                     else
989                     {
990                         fileSets.addAll(
991                             getPackagedFileSets( filtered, group, groups.get( group ), packageName, defaultEncoding ) );
992                     }
993                 }
994             } // end if
995 
996             getLogger().debug( "Resolved fileSets " + fileSets );
997         } // end if
998 
999         return fileSets;
1000     }
1001 
1002     private ModuleDescriptor createModule( Properties reverseProperties, String rootArtifactId, String moduleId,
1003                                            String packageName, File basedir, File archetypeFilesDirectory,
1004                                            List<String> languages, List<String> filtereds, String defaultEncoding,
1005                                            boolean preserveCData, boolean keepParent )
1006         throws IOException, XmlPullParserException
1007     {
1008         ModuleDescriptor archetypeDescriptor = new ModuleDescriptor();
1009         getLogger().debug( "Starting module's descriptor " + moduleId );
1010 
1011         archetypeFilesDirectory.mkdirs();
1012         getLogger().debug( "Module's files output directory " + archetypeFilesDirectory );
1013 
1014         Model pom = pomManager.readPom( FileUtils.resolveFile( basedir, Constants.ARCHETYPE_POM ) );
1015         String replacementId = pom.getArtifactId();
1016         String moduleDirectory = pom.getArtifactId();
1017 
1018         if ( replacementId.indexOf( rootArtifactId ) >= 0 )
1019         {
1020             replacementId = StringUtils.replace( replacementId, rootArtifactId, "${rootArtifactId}" );
1021             moduleDirectory = StringUtils.replace( moduleId, rootArtifactId, "__rootArtifactId__" );
1022         }
1023 
1024         if ( moduleId.indexOf( rootArtifactId ) >= 0 )
1025         {
1026             moduleDirectory = StringUtils.replace( moduleId, rootArtifactId, "__rootArtifactId__" );
1027         }
1028 
1029         archetypeDescriptor.setName( replacementId );
1030         archetypeDescriptor.setId( replacementId );
1031         archetypeDescriptor.setDir( moduleDirectory );
1032 
1033         setArtifactId( reverseProperties, pom.getArtifactId() );
1034 
1035         List<String> excludePatterns =
1036             reverseProperties.getProperty( Constants.EXCLUDE_PATTERNS ) != null
1037                 ? Arrays.asList( StringUtils.split( reverseProperties.getProperty( Constants.EXCLUDE_PATTERNS ), "," ) )
1038                 : Collections.<String>emptyList();
1039 
1040         List<String> fileNames = resolveFileNames( pom, basedir, excludePatterns );
1041 
1042         List<FileSet> filesets = resolveFileSets( packageName, fileNames, languages, filtereds, defaultEncoding );
1043         getLogger().debug( "Resolved filesets for module " + archetypeDescriptor.getName() );
1044 
1045         archetypeDescriptor.setFileSets( filesets );
1046 
1047         createArchetypeFiles( reverseProperties, filesets, packageName, basedir, archetypeFilesDirectory,
1048                               defaultEncoding, excludePatterns );
1049         getLogger().debug( "Created files for module " + archetypeDescriptor.getName() );
1050 
1051         String parentArtifactId = reverseProperties.getProperty( Constants.PARENT_ARTIFACT_ID );
1052         setParentArtifactId( reverseProperties, pom.getArtifactId() );
1053 
1054         for ( String subModuleId : pom.getModules() )
1055         {
1056             String subModuleIdDirectory = subModuleId;
1057             if ( subModuleId.indexOf( rootArtifactId ) >= 0 )
1058             {
1059                 subModuleIdDirectory = StringUtils.replace( subModuleId, rootArtifactId, "__rootArtifactId__" );
1060             }
1061 
1062             getLogger().debug( "Creating module " + subModuleId );
1063 
1064             ModuleDescriptor moduleDescriptor =
1065                 createModule( reverseProperties, rootArtifactId, subModuleId, packageName,
1066                               FileUtils.resolveFile( basedir, subModuleId ),
1067                               FileUtils.resolveFile( archetypeFilesDirectory, subModuleIdDirectory ), languages,
1068                               filtereds, defaultEncoding, preserveCData, keepParent );
1069 
1070             archetypeDescriptor.addModule( moduleDescriptor );
1071 
1072             getLogger().debug( "Added module " + moduleDescriptor.getName() + " in " + archetypeDescriptor.getName() );
1073         }
1074 
1075         restoreParentArtifactId( reverseProperties, parentArtifactId );
1076         restoreArtifactId( reverseProperties, pom.getArtifactId() );
1077 
1078         getLogger().debug( "Created Module " + archetypeDescriptor.getName() + " pom" );
1079 
1080         return archetypeDescriptor;
1081     }
1082 
1083     private void createModulePom( Model pom, String rootArtifactId, File archetypeFilesDirectory,
1084                                   Properties pomReversedProperties, File initialPomFile, boolean preserveCData,
1085                                   boolean keepParent )
1086         throws IOException
1087     {
1088         File outputFile = FileUtils.resolveFile( archetypeFilesDirectory, Constants.ARCHETYPE_POM );
1089 
1090         if ( preserveCData )
1091         {
1092             getLogger().debug( "Preserving CDATA parts of pom" );
1093             File inputFile = FileUtils.resolveFile( archetypeFilesDirectory, Constants.ARCHETYPE_POM + ".tmp" );
1094 
1095             FileUtils.copyFile( initialPomFile, inputFile );
1096 
1097             Reader in = null;
1098             Writer out = null;
1099             try
1100             {
1101                 in = ReaderFactory.newXmlReader( inputFile );
1102 
1103                 String initialcontent = IOUtil.toString( in );
1104 
1105                 String content = getReversedContent( initialcontent, pomReversedProperties );
1106 
1107                 outputFile.getParentFile().mkdirs();
1108 
1109                 out = WriterFactory.newXmlWriter( outputFile );
1110 
1111                 IOUtil.copy( content, out );
1112             }
1113             finally
1114             {
1115                 IOUtil.close( in );
1116                 IOUtil.close( out );
1117             }
1118 
1119             inputFile.delete();
1120         }
1121         else
1122         {
1123             if ( pom.getParent() != null )
1124             {
1125                 pom.getParent().setGroupId( StringUtils.replace( pom.getParent().getGroupId(),
1126                                                                  pomReversedProperties.getProperty(
1127                                                                      Constants.GROUP_ID ),
1128                                                                  "${" + Constants.GROUP_ID + "}" ) );
1129                 if ( pom.getParent().getArtifactId() != null
1130                     && pom.getParent().getArtifactId().indexOf( rootArtifactId ) >= 0 )
1131                 {
1132                     pom.getParent().setArtifactId(
1133                         StringUtils.replace( pom.getParent().getArtifactId(), rootArtifactId, "${rootArtifactId}" ) );
1134                 }
1135                 if ( pom.getParent().getVersion() != null )
1136                 {
1137                     pom.getParent().setVersion( "${" + Constants.VERSION + "}" );
1138                 }
1139             }
1140             pom.setModules( null );
1141 
1142             if ( pom.getGroupId() != null )
1143             {
1144                 pom.setGroupId(
1145                     StringUtils.replace( pom.getGroupId(), pomReversedProperties.getProperty( Constants.GROUP_ID ),
1146                                          "${" + Constants.GROUP_ID + "}" ) );
1147             }
1148 
1149             pom.setArtifactId( "${" + Constants.ARTIFACT_ID + "}" );
1150 
1151             if ( pom.getVersion() != null )
1152             {
1153                 pom.setVersion( "${" + Constants.VERSION + "}" );
1154             }
1155 
1156             pom.setName( getReversedPlainContent( pom.getName(), pomReversedProperties ) );
1157             pom.setDescription( getReversedPlainContent( pom.getDescription(), pomReversedProperties ) );
1158             pom.setUrl( getReversedPlainContent( pom.getUrl(), pomReversedProperties ) );
1159 
1160             rewriteReferences( pom, rootArtifactId, pomReversedProperties.getProperty( Constants.GROUP_ID ) );
1161 
1162             pomManager.writePom( pom, outputFile, initialPomFile );
1163         }
1164 
1165         Reader in = null;
1166         try
1167         {
1168             in = ReaderFactory.newXmlReader( initialPomFile );
1169             String initialcontent = IOUtil.toString( in );
1170 
1171             for ( Iterator<?> properties = pomReversedProperties.keySet().iterator(); properties.hasNext(); )
1172             {
1173                 String property = (String) properties.next();
1174 
1175                 if ( initialcontent.indexOf( "${" + property + "}" ) > 0 )
1176                 {
1177                     getLogger().warn(
1178                         "OldArchetype uses ${" + property + "} for internal processing, but file " + initialPomFile
1179                             + " contains this property already" );
1180                 }
1181             }
1182         }
1183         finally
1184         {
1185             IOUtil.close( in );
1186         }
1187     }
1188 
1189     private Set<String> getExtensions( List<String> files )
1190     {
1191         Set<String> extensions = new HashSet<String>();
1192 
1193         for ( String file : files )
1194         {
1195             extensions.add( FileUtils.extension( file ) );
1196         }
1197 
1198         return extensions;
1199     }
1200 
1201     private Map<String, List<String>> getGroupsMap( final List<String> files, final int level )
1202     {
1203         Map<String, List<String>> groups = new HashMap<String, List<String>>();
1204 
1205         for ( String file : files )
1206         {
1207             String directory = PathUtils.getDirectory( file, level );
1208             // make all groups have unix style
1209             directory = StringUtils.replace( directory, File.separator, "/" );
1210 
1211             if ( !groups.containsKey( directory ) )
1212             {
1213                 groups.put( directory, new ArrayList<String>() );
1214             }
1215 
1216             List<String> group = groups.get( directory );
1217 
1218             String innerPath = file.substring( directory.length() + 1 );
1219             // make all groups have unix style
1220             innerPath = StringUtils.replace( innerPath, File.separator, "/" );
1221 
1222             group.add( innerPath );
1223         }
1224 
1225         getLogger().debug( "Sorted " + groups.size() + " groups in " + files.size() + " files" );
1226         getLogger().debug( "Sorted Files: " + files );
1227 
1228         return groups;
1229     }
1230 
1231     private FileSet getPackagedFileSet( final boolean filtered, final Set<String> packagedExtensions,
1232                                         final String group, final Set<String> unpackagedExtensions,
1233                                         final List<String> unpackagedFiles, String defaultEncoding )
1234     {
1235         List<String> includes = new ArrayList<String>();
1236         List<String> excludes = new ArrayList<String>();
1237 
1238         for ( String extension : packagedExtensions )
1239         {
1240             includes.add( "**/*." + extension );
1241 
1242             if ( unpackagedExtensions.contains( extension ) )
1243             {
1244                 excludes.addAll( archetypeFilesResolver.getFilesWithExtension( unpackagedFiles, extension ) );
1245             }
1246         }
1247 
1248         return createFileSet( excludes, true, filtered, group, includes, defaultEncoding );
1249     }
1250 
1251     private List<FileSet> getPackagedFileSets( final boolean filtered, final String group,
1252                                                final List<String> groupFiles, final String packageName,
1253                                                String defaultEncoding )
1254     {
1255         String packageAsDir = StringUtils.replace( packageName, ".", "/" );
1256 
1257         List<FileSet> packagedFileSets = new ArrayList<FileSet>();
1258         List<String> packagedFiles = archetypeFilesResolver.getPackagedFiles( groupFiles, packageAsDir );
1259         getLogger().debug( "Found packaged Files:" + packagedFiles );
1260 
1261         List<String> unpackagedFiles = archetypeFilesResolver.getUnpackagedFiles( groupFiles, packageAsDir );
1262         getLogger().debug( "Found unpackaged Files:" + unpackagedFiles );
1263 
1264         Set<String> packagedExtensions = getExtensions( packagedFiles );
1265         getLogger().debug( "Found packaged extensions " + packagedExtensions );
1266 
1267         Set<String> unpackagedExtensions = getExtensions( unpackagedFiles );
1268 
1269         if ( !packagedExtensions.isEmpty() )
1270         {
1271             packagedFileSets.add(
1272                 getPackagedFileSet( filtered, packagedExtensions, group, unpackagedExtensions, unpackagedFiles,
1273                                     defaultEncoding ) );
1274         }
1275 
1276         if ( !unpackagedExtensions.isEmpty() )
1277         {
1278             getLogger().debug( "Found unpackaged extensions " + unpackagedExtensions );
1279 
1280             packagedFileSets.add(
1281                 getUnpackagedFileSet( filtered, unpackagedExtensions, unpackagedFiles, group, packagedExtensions,
1282                                       defaultEncoding ) );
1283         }
1284 
1285         return packagedFileSets;
1286     }
1287 
1288     private void setParentArtifactId( Properties properties, String parentArtifactId )
1289     {
1290         properties.setProperty( Constants.PARENT_ARTIFACT_ID, parentArtifactId );
1291     }
1292 
1293     private void processFileSet( File basedir, File archetypeFilesDirectory, String directory,
1294                                  List<String> fileSetResources, boolean packaged, String packageName,
1295                                  Properties reverseProperties, String defaultEncoding )
1296         throws IOException
1297     {
1298         String packageAsDirectory = StringUtils.replace( packageName, ".", File.separator );
1299 
1300         getLogger().debug( "Package as Directory: Package:" + packageName + "->" + packageAsDirectory );
1301 
1302         for ( String inputFileName : fileSetResources )
1303         {
1304             String initialFilename = packaged
1305                 ? StringUtils.replace( inputFileName, packageAsDirectory + File.separator, "" )
1306                 : inputFileName;
1307 
1308             getLogger().debug( "InputFileName:" + inputFileName );
1309 
1310             File inputFile = new File( basedir, inputFileName );
1311 
1312             FileCharsetDetector detector = new FileCharsetDetector( inputFile );
1313 
1314             String fileEncoding = detector.isFound() ? detector.getCharset() : defaultEncoding;
1315 
1316             String initialcontent = IOUtil.toString( new FileInputStream( inputFile ), fileEncoding );
1317 
1318             for ( Iterator<?> properties = reverseProperties.keySet().iterator(); properties.hasNext(); )
1319             {
1320                 String property = (String) properties.next();
1321 
1322                 if ( initialcontent.indexOf( "${" + property + "}" ) > 0 )
1323                 {
1324                     getLogger().warn(
1325                         "Archetype uses ${" + property + "} for internal processing, but file " + inputFile
1326                             + " contains this property already" );
1327                 }
1328             }
1329 
1330             String content = getReversedContent( initialcontent, reverseProperties );
1331             String outputFilename = getReversedFilename( initialFilename, reverseProperties );
1332 
1333             getLogger().debug( "OutputFileName:" + outputFilename );
1334 
1335             File outputFile = new File( archetypeFilesDirectory, outputFilename );
1336             outputFile.getParentFile().mkdirs();
1337 
1338             org.apache.commons.io.IOUtils.write( content, new FileOutputStream( outputFile ), fileEncoding );
1339         }
1340     }
1341 
1342     private Properties getReversedProperties( ArchetypeDescriptor archetypeDescriptor, Properties properties )
1343     {
1344         Properties reversedProperties = new Properties();
1345 
1346         reversedProperties.putAll( properties );
1347         reversedProperties.remove( Constants.ARCHETYPE_GROUP_ID );
1348         reversedProperties.remove( Constants.ARCHETYPE_ARTIFACT_ID );
1349         reversedProperties.remove( Constants.ARCHETYPE_VERSION );
1350 
1351         String packageName = properties.getProperty( Constants.PACKAGE );
1352         String packageInPathFormat = getPackageInPathFormat( packageName );
1353         if ( !packageInPathFormat.equals( packageName ) )
1354         {
1355             reversedProperties.setProperty( Constants.PACKAGE_IN_PATH_FORMAT, packageInPathFormat );
1356         }
1357 
1358         // TODO check that reversed properties are all different and no one is a substring of another?
1359         // to avoid wrong variable replacements
1360 
1361         return reversedProperties;
1362     }
1363 
1364     private List<String> resolveFileNames( final Model pom, final File basedir, List<String> excludePatterns )
1365         throws IOException
1366     {
1367         getLogger().debug( "Resolving files for " + pom.getId() + " in " + basedir );
1368 
1369         StringBuffer buff = new StringBuffer( "pom.xml*,archetype.properties*,target/**," );
1370         for ( String module : pom.getModules() )
1371         {
1372             buff.append( ',' ).append( module ).append( "/**" );
1373         }
1374 
1375         for ( String defaultExclude : ListScanner.DEFAULTEXCLUDES )
1376         {
1377             buff.append( ',' ).append( defaultExclude ).append( "/**" );
1378         }
1379 
1380         for ( String excludePattern : excludePatterns )
1381         {
1382             buff.append( ',' ).append( excludePattern );
1383         }
1384 
1385         String excludes = PathUtils.convertPathForOS( buff.toString() );
1386 
1387         List<String> fileNames = FileUtils.getFileNames( basedir, "**,.*,**/.*", excludes, false );
1388 
1389         getLogger().debug( "Resolved " + fileNames.size() + " files" );
1390         getLogger().debug( "Resolved Files:" + fileNames );
1391 
1392         return fileNames;
1393     }
1394 
1395     private List<FileSet> resolveFileSets( String packageName, List<String> fileNames, List<String> languages,
1396                                            List<String> filtereds, String defaultEncoding )
1397     {
1398         List<FileSet> resolvedFileSets = new ArrayList<FileSet>();
1399         getLogger().debug(
1400             "Resolving filesets with package=" + packageName + ", languages=" + languages + " and extentions="
1401                 + filtereds );
1402 
1403         List<String> files = new ArrayList<String>( fileNames );
1404 
1405         StringBuilder languageIncludes = new StringBuilder();
1406 
1407         for ( String language : languages )
1408         {
1409             languageIncludes.append( ( ( languageIncludes.length() == 0 ) ? "" : "," ) + language + "/**" );
1410         }
1411 
1412         getLogger().debug( "Using languages includes " + languageIncludes );
1413 
1414         StringBuilder filteredIncludes = new StringBuilder();
1415         for ( String filtered : filtereds )
1416         {
1417             filteredIncludes.append(
1418                 ( ( filteredIncludes.length() == 0 ) ? "" : "," ) + "**/" + ( filtered.startsWith( "." ) ? "" : "*." )
1419                     + filtered );
1420         }
1421 
1422         getLogger().debug( "Using filtered includes " + filteredIncludes );
1423 
1424         /* sourcesMainFiles */
1425         List<String> sourcesMainFiles =
1426             archetypeFilesResolver.findSourcesMainFiles( files, languageIncludes.toString() );
1427         if ( !sourcesMainFiles.isEmpty() )
1428         {
1429             files.removeAll( sourcesMainFiles );
1430 
1431             List<String> filteredFiles =
1432                 archetypeFilesResolver.getFilteredFiles( sourcesMainFiles, filteredIncludes.toString() );
1433             sourcesMainFiles.removeAll( filteredFiles );
1434 
1435             List<String> unfilteredFiles = sourcesMainFiles;
1436             if ( !filteredFiles.isEmpty() )
1437             {
1438                 resolvedFileSets.addAll( createFileSets( filteredFiles, 3, true, packageName, true, defaultEncoding ) );
1439             }
1440 
1441             if ( !unfilteredFiles.isEmpty() )
1442             {
1443                 resolvedFileSets.addAll(
1444                     createFileSets( unfilteredFiles, 3, true, packageName, false, defaultEncoding ) );
1445             }
1446         }
1447 
1448         /* resourcesMainFiles */
1449         List<String> resourcesMainFiles =
1450             archetypeFilesResolver.findResourcesMainFiles( files, languageIncludes.toString() );
1451         if ( !resourcesMainFiles.isEmpty() )
1452         {
1453             files.removeAll( resourcesMainFiles );
1454 
1455             List<String> filteredFiles =
1456                 archetypeFilesResolver.getFilteredFiles( resourcesMainFiles, filteredIncludes.toString() );
1457             resourcesMainFiles.removeAll( filteredFiles );
1458 
1459             List<String> unfilteredFiles = resourcesMainFiles;
1460             if ( !filteredFiles.isEmpty() )
1461             {
1462                 resolvedFileSets.addAll(
1463                     createFileSets( filteredFiles, 3, false, packageName, true, defaultEncoding ) );
1464             }
1465             if ( !unfilteredFiles.isEmpty() )
1466             {
1467                 resolvedFileSets.addAll(
1468                     createFileSets( unfilteredFiles, 3, false, packageName, false, defaultEncoding ) );
1469             }
1470         }
1471 
1472         /* sourcesTestFiles */
1473         List<String> sourcesTestFiles =
1474             archetypeFilesResolver.findSourcesTestFiles( files, languageIncludes.toString() );
1475         if ( !sourcesTestFiles.isEmpty() )
1476         {
1477             files.removeAll( sourcesTestFiles );
1478 
1479             List<String> filteredFiles =
1480                 archetypeFilesResolver.getFilteredFiles( sourcesTestFiles, filteredIncludes.toString() );
1481             sourcesTestFiles.removeAll( filteredFiles );
1482 
1483             List<String> unfilteredFiles = sourcesTestFiles;
1484             if ( !filteredFiles.isEmpty() )
1485             {
1486                 resolvedFileSets.addAll( createFileSets( filteredFiles, 3, true, packageName, true, defaultEncoding ) );
1487             }
1488             if ( !unfilteredFiles.isEmpty() )
1489             {
1490                 resolvedFileSets.addAll(
1491                     createFileSets( unfilteredFiles, 3, true, packageName, false, defaultEncoding ) );
1492             }
1493         }
1494 
1495         /* ressourcesTestFiles */
1496         List<String> resourcesTestFiles =
1497             archetypeFilesResolver.findResourcesTestFiles( files, languageIncludes.toString() );
1498         if ( !resourcesTestFiles.isEmpty() )
1499         {
1500             files.removeAll( resourcesTestFiles );
1501 
1502             List<String> filteredFiles =
1503                 archetypeFilesResolver.getFilteredFiles( resourcesTestFiles, filteredIncludes.toString() );
1504             resourcesTestFiles.removeAll( filteredFiles );
1505 
1506             List<String> unfilteredFiles = resourcesTestFiles;
1507             if ( !filteredFiles.isEmpty() )
1508             {
1509                 resolvedFileSets.addAll(
1510                     createFileSets( filteredFiles, 3, false, packageName, true, defaultEncoding ) );
1511             }
1512             if ( !unfilteredFiles.isEmpty() )
1513             {
1514                 resolvedFileSets.addAll(
1515                     createFileSets( unfilteredFiles, 3, false, packageName, false, defaultEncoding ) );
1516             }
1517         }
1518 
1519         /* siteFiles */
1520         List<String> siteFiles = archetypeFilesResolver.findSiteFiles( files, languageIncludes.toString() );
1521         if ( !siteFiles.isEmpty() )
1522         {
1523             files.removeAll( siteFiles );
1524 
1525             List<String> filteredFiles =
1526                 archetypeFilesResolver.getFilteredFiles( siteFiles, filteredIncludes.toString() );
1527             siteFiles.removeAll( filteredFiles );
1528 
1529             List<String> unfilteredFiles = siteFiles;
1530             if ( !filteredFiles.isEmpty() )
1531             {
1532                 resolvedFileSets.addAll(
1533                     createFileSets( filteredFiles, 2, false, packageName, true, defaultEncoding ) );
1534             }
1535             if ( !unfilteredFiles.isEmpty() )
1536             {
1537                 resolvedFileSets.addAll(
1538                     createFileSets( unfilteredFiles, 2, false, packageName, false, defaultEncoding ) );
1539             }
1540         }
1541 
1542         /* thirdLevelSourcesfiles */
1543         List<String> thirdLevelSourcesfiles =
1544             archetypeFilesResolver.findOtherSources( 3, files, languageIncludes.toString() );
1545         if ( !thirdLevelSourcesfiles.isEmpty() )
1546         {
1547             files.removeAll( thirdLevelSourcesfiles );
1548 
1549             List<String> filteredFiles =
1550                 archetypeFilesResolver.getFilteredFiles( thirdLevelSourcesfiles, filteredIncludes.toString() );
1551             thirdLevelSourcesfiles.removeAll( filteredFiles );
1552 
1553             List<String> unfilteredFiles = thirdLevelSourcesfiles;
1554             if ( !filteredFiles.isEmpty() )
1555             {
1556                 resolvedFileSets.addAll( createFileSets( filteredFiles, 3, true, packageName, true, defaultEncoding ) );
1557             }
1558             if ( !unfilteredFiles.isEmpty() )
1559             {
1560                 resolvedFileSets.addAll(
1561                     createFileSets( unfilteredFiles, 3, true, packageName, false, defaultEncoding ) );
1562             }
1563 
1564             /* thirdLevelResourcesfiles */
1565             List<String> thirdLevelResourcesfiles =
1566                 archetypeFilesResolver.findOtherResources( 3, files, thirdLevelSourcesfiles,
1567                                                            languageIncludes.toString() );
1568             if ( !thirdLevelResourcesfiles.isEmpty() )
1569             {
1570                 files.removeAll( thirdLevelResourcesfiles );
1571                 filteredFiles =
1572                     archetypeFilesResolver.getFilteredFiles( thirdLevelResourcesfiles, filteredIncludes.toString() );
1573                 thirdLevelResourcesfiles.removeAll( filteredFiles );
1574                 unfilteredFiles = thirdLevelResourcesfiles;
1575                 if ( !filteredFiles.isEmpty() )
1576                 {
1577                     resolvedFileSets.addAll(
1578                         createFileSets( filteredFiles, 3, false, packageName, true, defaultEncoding ) );
1579                 }
1580                 if ( !unfilteredFiles.isEmpty() )
1581                 {
1582                     resolvedFileSets.addAll(
1583                         createFileSets( unfilteredFiles, 3, false, packageName, false, defaultEncoding ) );
1584                 }
1585             }
1586         } // end if
1587 
1588         /* secondLevelSourcesfiles */
1589         List<String> secondLevelSourcesfiles =
1590             archetypeFilesResolver.findOtherSources( 2, files, languageIncludes.toString() );
1591         if ( !secondLevelSourcesfiles.isEmpty() )
1592         {
1593             files.removeAll( secondLevelSourcesfiles );
1594 
1595             List<String> filteredFiles =
1596                 archetypeFilesResolver.getFilteredFiles( secondLevelSourcesfiles, filteredIncludes.toString() );
1597             secondLevelSourcesfiles.removeAll( filteredFiles );
1598 
1599             List<String> unfilteredFiles = secondLevelSourcesfiles;
1600             if ( !filteredFiles.isEmpty() )
1601             {
1602                 resolvedFileSets.addAll( createFileSets( filteredFiles, 2, true, packageName, true, defaultEncoding ) );
1603             }
1604             if ( !unfilteredFiles.isEmpty() )
1605             {
1606                 resolvedFileSets.addAll(
1607                     createFileSets( unfilteredFiles, 2, true, packageName, false, defaultEncoding ) );
1608             }
1609         }
1610 
1611         /* secondLevelResourcesfiles */
1612         List<String> secondLevelResourcesfiles =
1613             archetypeFilesResolver.findOtherResources( 2, files, languageIncludes.toString() );
1614         if ( !secondLevelResourcesfiles.isEmpty() )
1615         {
1616             files.removeAll( secondLevelResourcesfiles );
1617 
1618             List<String> filteredFiles =
1619                 archetypeFilesResolver.getFilteredFiles( secondLevelResourcesfiles, filteredIncludes.toString() );
1620             secondLevelResourcesfiles.removeAll( filteredFiles );
1621 
1622             List<String> unfilteredFiles = secondLevelResourcesfiles;
1623             if ( !filteredFiles.isEmpty() )
1624             {
1625                 resolvedFileSets.addAll(
1626                     createFileSets( filteredFiles, 2, false, packageName, true, defaultEncoding ) );
1627             }
1628             if ( !unfilteredFiles.isEmpty() )
1629             {
1630                 resolvedFileSets.addAll(
1631                     createFileSets( unfilteredFiles, 2, false, packageName, false, defaultEncoding ) );
1632             }
1633         }
1634 
1635         /* rootResourcesfiles */
1636         List<String> rootResourcesfiles =
1637             archetypeFilesResolver.findOtherResources( 0, files, languageIncludes.toString() );
1638         if ( !rootResourcesfiles.isEmpty() )
1639         {
1640             files.removeAll( rootResourcesfiles );
1641 
1642             List<String> filteredFiles =
1643                 archetypeFilesResolver.getFilteredFiles( rootResourcesfiles, filteredIncludes.toString() );
1644             rootResourcesfiles.removeAll( filteredFiles );
1645 
1646             List<String> unfilteredFiles = rootResourcesfiles;
1647             if ( !filteredFiles.isEmpty() )
1648             {
1649                 resolvedFileSets.addAll(
1650                     createFileSets( filteredFiles, 0, false, packageName, true, defaultEncoding ) );
1651             }
1652             if ( !unfilteredFiles.isEmpty() )
1653             {
1654                 resolvedFileSets.addAll(
1655                     createFileSets( unfilteredFiles, 0, false, packageName, false, defaultEncoding ) );
1656             }
1657         }
1658 
1659         /**/
1660         if ( !files.isEmpty() )
1661         {
1662             getLogger().info( "Ignored files: " + files );
1663         }
1664 
1665         return resolvedFileSets;
1666     }
1667 
1668     private void restoreArtifactId( Properties properties, String artifactId )
1669     {
1670         if ( StringUtils.isEmpty( artifactId ) )
1671         {
1672             properties.remove( Constants.ARTIFACT_ID );
1673         }
1674         else
1675         {
1676             properties.setProperty( Constants.ARTIFACT_ID, artifactId );
1677         }
1678     }
1679 
1680     private void restoreParentArtifactId( Properties properties, String parentArtifactId )
1681     {
1682         if ( StringUtils.isEmpty( parentArtifactId ) )
1683         {
1684             properties.remove( Constants.PARENT_ARTIFACT_ID );
1685         }
1686         else
1687         {
1688             properties.setProperty( Constants.PARENT_ARTIFACT_ID, parentArtifactId );
1689         }
1690     }
1691 
1692     private String getReversedContent( String content, Properties properties )
1693     {
1694         String result =
1695             StringUtils.replace( StringUtils.replace( content, "$", "${symbol_dollar}" ), "\\", "${symbol_escape}" );
1696         result = getReversedPlainContent( result, properties );
1697 
1698         // TODO: Replace velocity to a better engine...
1699         return "#set( $symbol_pound = '#' )\n" + "#set( $symbol_dollar = '$' )\n" + "#set( $symbol_escape = '\\' )\n"
1700             + StringUtils.replace( result, "#", "${symbol_pound}" );
1701     }
1702 
1703     private String getReversedPlainContent( String content, Properties properties )
1704     {
1705         String result = content;
1706 
1707         for ( Iterator<?> propertyIterator = properties.keySet().iterator(); propertyIterator.hasNext(); )
1708         {
1709             String propertyKey = (String) propertyIterator.next();
1710 
1711             result = StringUtils.replace( result, properties.getProperty( propertyKey ), "${" + propertyKey + "}" );
1712         }
1713         return result;
1714     }
1715 
1716     private String getReversedFilename( String filename, Properties properties )
1717     {
1718         String result = filename;
1719 
1720         for ( Iterator<?> propertyIterator = properties.keySet().iterator(); propertyIterator.hasNext(); )
1721         {
1722             String propertyKey = (String) propertyIterator.next();
1723 
1724             result = StringUtils.replace( result, properties.getProperty( propertyKey ), "__" + propertyKey + "__" );
1725         }
1726 
1727         return result;
1728     }
1729 
1730     private String getTemplateOutputDirectory()
1731     {
1732         return Constants.SRC + File.separator + Constants.MAIN + File.separator + Constants.RESOURCES;
1733     }
1734 
1735     private FileSet getUnpackagedFileSet( final boolean filtered, final String group, final List<String> groupFiles,
1736                                           String defaultEncoding )
1737     {
1738         Set<String> extensions = getExtensions( groupFiles );
1739 
1740         List<String> includes = new ArrayList<String>();
1741         List<String> excludes = new ArrayList<String>();
1742 
1743         for ( String extension : extensions )
1744         {
1745             includes.add( "**/*." + extension );
1746         }
1747 
1748         return createFileSet( excludes, false, filtered, group, includes, defaultEncoding );
1749     }
1750 
1751     private FileSet getUnpackagedFileSet( final boolean filtered, final Set<String> unpackagedExtensions,
1752                                           final List<String> unpackagedFiles, final String group,
1753                                           final Set<String> packagedExtensions, String defaultEncoding )
1754     {
1755         List<String> includes = new ArrayList<String>();
1756         List<String> excludes = new ArrayList<String>();
1757 
1758         for ( String extension : unpackagedExtensions )
1759         {
1760             if ( packagedExtensions.contains( extension ) )
1761             {
1762                 includes.addAll( archetypeFilesResolver.getFilesWithExtension( unpackagedFiles, extension ) );
1763             }
1764             else
1765             {
1766                 if ( StringUtils.isEmpty( extension ) )
1767                 {
1768                     includes.add( "**/*" );
1769                 }
1770                 else
1771                 {
1772                     includes.add( "**/*." + extension );
1773                 }
1774             }
1775         }
1776 
1777         return createFileSet( excludes, false, filtered, group, includes, defaultEncoding );
1778     }
1779 
1780     private static final String MAVEN_PROPERTIES =
1781         "META-INF/maven/org.apache.maven.archetype/archetype-common/pom.properties";
1782 
1783     public String getArchetypeVersion()
1784     {
1785         InputStream is = null;
1786 
1787         // This should actually come from the pom.properties at testing but it's not generated and put into the JAR, it
1788         // happens as part of the JAR plugin which is crap as it makes testing inconsistent.
1789         String version = "version";
1790 
1791         try
1792         {
1793             Properties properties = new Properties();
1794 
1795             is = getClass().getClassLoader().getResourceAsStream( MAVEN_PROPERTIES );
1796 
1797             if ( is != null )
1798             {
1799                 properties.load( is );
1800 
1801                 String property = properties.getProperty( "version" );
1802 
1803                 if ( property != null )
1804                 {
1805                     return property;
1806                 }
1807             }
1808 
1809             return version;
1810         }
1811         catch ( IOException e )
1812         {
1813             return version;
1814         }
1815         finally
1816         {
1817             IOUtil.close( is );
1818         }
1819     }
1820 }