View Javadoc
1   package org.apache.maven.archetype.generator;
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 groovy.lang.Binding;
23  import groovy.lang.GroovyShell;
24  import org.apache.maven.archetype.ArchetypeGenerationRequest;
25  import org.apache.maven.archetype.common.ArchetypeArtifactManager;
26  import org.apache.maven.archetype.common.ArchetypeFilesResolver;
27  import org.apache.maven.archetype.common.Constants;
28  import org.apache.maven.archetype.common.PomManager;
29  import org.apache.maven.archetype.exception.ArchetypeGenerationFailure;
30  import org.apache.maven.archetype.exception.ArchetypeNotConfigured;
31  import org.apache.maven.archetype.exception.InvalidPackaging;
32  import org.apache.maven.archetype.exception.OutputFileExists;
33  import org.apache.maven.archetype.exception.PomFileExists;
34  import org.apache.maven.archetype.exception.ProjectDirectoryExists;
35  import org.apache.maven.archetype.exception.UnknownArchetype;
36  import org.apache.maven.archetype.metadata.AbstractArchetypeDescriptor;
37  import org.apache.maven.archetype.metadata.ArchetypeDescriptor;
38  import org.apache.maven.archetype.metadata.FileSet;
39  import org.apache.maven.archetype.metadata.ModuleDescriptor;
40  import org.apache.maven.archetype.metadata.RequiredProperty;
41  import org.apache.velocity.VelocityContext;
42  import org.apache.velocity.app.Velocity;
43  import org.apache.velocity.context.Context;
44  import org.codehaus.plexus.component.annotations.Component;
45  import org.codehaus.plexus.component.annotations.Requirement;
46  import org.codehaus.plexus.logging.AbstractLogEnabled;
47  import org.codehaus.plexus.util.FileUtils;
48  import org.codehaus.plexus.util.IOUtil;
49  import org.codehaus.plexus.util.StringUtils;
50  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
51  import org.codehaus.plexus.velocity.VelocityComponent;
52  import org.dom4j.DocumentException;
53  
54  import java.io.File;
55  import java.io.FileNotFoundException;
56  import java.io.FileOutputStream;
57  import java.io.IOException;
58  import java.io.InputStream;
59  import java.io.OutputStream;
60  import java.io.OutputStreamWriter;
61  import java.io.StringWriter;
62  import java.io.Writer;
63  import java.util.ArrayList;
64  import java.util.Iterator;
65  import java.util.List;
66  import java.util.Map;
67  import java.util.Properties;
68  import java.util.regex.Pattern;
69  import java.util.zip.ZipEntry;
70  import java.util.zip.ZipFile;
71  
72  @Component( role = FilesetArchetypeGenerator.class )
73  public class DefaultFilesetArchetypeGenerator
74      extends AbstractLogEnabled
75      implements FilesetArchetypeGenerator
76  {
77      @Requirement
78      private ArchetypeArtifactManager archetypeArtifactManager;
79  
80      @Requirement
81      private ArchetypeFilesResolver archetypeFilesResolver;
82  
83      @Requirement
84      private PomManager pomManager;
85  
86      @Requirement
87      private VelocityComponent velocity;
88  
89      /**
90       * Token delimiter.
91       */
92      private static final String DELIMITER = "__";
93  
94      /**
95       * Pattern used to detect tokens in a string. Tokens are any text surrounded
96       * by the delimiter.
97       */
98      private static final Pattern TOKEN_PATTERN = Pattern.compile( ".*" + DELIMITER + ".*" + DELIMITER + ".*" );
99  
100     public void generateArchetype( ArchetypeGenerationRequest request, File archetypeFile )
101         throws UnknownArchetype, ArchetypeNotConfigured, ProjectDirectoryExists, PomFileExists, OutputFileExists,
102         ArchetypeGenerationFailure
103     {
104         ClassLoader old = Thread.currentThread().getContextClassLoader();
105 
106         try
107         {
108             ArchetypeDescriptor archetypeDescriptor =
109                 archetypeArtifactManager.getFileSetArchetypeDescriptor( archetypeFile );
110 
111             if ( !isArchetypeConfigured( archetypeDescriptor, request ) )
112             {
113                 if ( request.isInteractiveMode() )
114                 {
115                     throw new ArchetypeNotConfigured( "No archetype was chosen.", null );
116                 }
117 
118                 StringBuffer exceptionMessage = new StringBuffer(
119                     "Archetype " + request.getArchetypeGroupId() + ":" + request.getArchetypeArtifactId() + ":"
120                         + request.getArchetypeVersion() + " is not configured" );
121 
122                 List<String> missingProperties = new ArrayList<String>( 0 );
123                 for ( RequiredProperty requiredProperty : archetypeDescriptor.getRequiredProperties() )
124                 {
125                     if ( StringUtils.isEmpty( request.getProperties().getProperty( requiredProperty.getKey() ) ) )
126                     {
127                         exceptionMessage.append( "\n\tProperty " + requiredProperty.getKey() + " is missing." );
128 
129                         missingProperties.add( requiredProperty.getKey() );
130                     }
131                 }
132 
133                 throw new ArchetypeNotConfigured( exceptionMessage.toString(), missingProperties );
134             }
135 
136             Context context = prepareVelocityContext( request );
137 
138             String packageName = request.getPackage();
139             String artifactId = request.getArtifactId();
140             File outputDirectoryFile = new File( request.getOutputDirectory(), artifactId );
141             File basedirPom = new File( request.getOutputDirectory(), Constants.ARCHETYPE_POM );
142             File pom = new File( outputDirectoryFile, Constants.ARCHETYPE_POM );
143 
144             List<String> archetypeResources = archetypeArtifactManager.getFilesetArchetypeResources( archetypeFile );
145 
146             ZipFile archetypeZipFile = archetypeArtifactManager.getArchetypeZipFile( archetypeFile );
147 
148             ClassLoader archetypeJarLoader = archetypeArtifactManager.getArchetypeJarLoader( archetypeFile );
149 
150             Thread.currentThread().setContextClassLoader( archetypeJarLoader );
151 
152             if ( archetypeDescriptor.isPartial() )
153             {
154                 getLogger().debug( "Processing partial archetype " + archetypeDescriptor.getName() );
155                 if ( outputDirectoryFile.exists() )
156                 {
157                     if ( !pom.exists() )
158                     {
159                         throw new PomFileExists( "This is a partial archetype and the pom.xml file doesn't exist." );
160                     }
161 
162                     processPomWithMerge( context, pom, "" );
163 
164                     processArchetypeTemplatesWithWarning( archetypeDescriptor, archetypeResources, archetypeZipFile, "",
165                                                           context, packageName, outputDirectoryFile );
166                 }
167                 else
168                 {
169                     if ( basedirPom.exists() )
170                     {
171                         processPomWithMerge( context, basedirPom, "" );
172 
173                         processArchetypeTemplatesWithWarning( archetypeDescriptor, archetypeResources, archetypeZipFile,
174                                                               "", context, packageName,
175                                                               new File( request.getOutputDirectory() ) );
176                     }
177                     else
178                     {
179                         processPom( context, pom, "" );
180 
181                         processArchetypeTemplates( archetypeDescriptor, archetypeResources, archetypeZipFile, "",
182                                                    context, packageName, outputDirectoryFile );
183                     }
184                 }
185 
186                 if ( archetypeDescriptor.getModules().size() > 0 )
187                 {
188                     getLogger().info( "Modules ignored in partial mode" );
189                 }
190             }
191             else
192             {
193                 getLogger().debug( "Processing complete archetype " + archetypeDescriptor.getName() );
194                 if ( outputDirectoryFile.exists() && pom.exists() )
195                 {
196                     throw new ProjectDirectoryExists(
197                         "A Maven 2 project already exists in the directory " + outputDirectoryFile.getPath() );
198                 }
199 
200                 if ( outputDirectoryFile.exists() )
201                 {
202                     getLogger().warn( "The directory " + outputDirectoryFile.getPath() + " already exists." );
203                 }
204 
205                 context.put( "rootArtifactId", artifactId );
206 
207                 processFilesetModule( artifactId, artifactId, archetypeResources, pom, archetypeZipFile, "", basedirPom,
208                                       outputDirectoryFile, packageName, archetypeDescriptor, context );
209             }
210 
211             String postGenerationScript = archetypeArtifactManager.getPostGenerationScript( archetypeFile );
212             if ( postGenerationScript != null )
213             {
214                 getLogger().info( "Executing " + Constants.ARCHETYPE_POST_GENERATION_SCRIPT
215                     + " post-generation script" );
216 
217                 Binding binding = new Binding();
218 
219                 final Properties archetypeGeneratorProperties = new Properties();
220                 archetypeGeneratorProperties.putAll( System.getProperties() );
221 
222                 if ( request.getProperties() != null )
223                 {
224                     archetypeGeneratorProperties.putAll( request.getProperties() );
225                 }
226 
227                 for ( Map.Entry<Object, Object> entry : archetypeGeneratorProperties.entrySet() )
228                 {
229                     binding.setVariable( entry.getKey().toString(), entry.getValue() );
230                 }
231 
232                 binding.setVariable( "request", request );
233 
234                 GroovyShell shell = new GroovyShell( binding );
235                 shell.evaluate( postGenerationScript );
236             }
237 
238             // ----------------------------------------------------------------------
239             // Log message on OldArchetype creation
240             // ----------------------------------------------------------------------
241             if ( getLogger().isInfoEnabled() )
242             {
243                 getLogger().info( "Project created from Archetype in dir: " + outputDirectoryFile.getAbsolutePath() );
244             }
245         }
246         catch ( FileNotFoundException ex )
247         {
248             throw new ArchetypeGenerationFailure( ex );
249         }
250         catch ( IOException ex )
251         {
252             throw new ArchetypeGenerationFailure( ex );
253         }
254         catch ( XmlPullParserException ex )
255         {
256             throw new ArchetypeGenerationFailure( ex );
257         }
258         catch ( DocumentException ex )
259         {
260             throw new ArchetypeGenerationFailure( ex );
261         }
262         catch ( ArchetypeGenerationFailure ex )
263         {
264             throw new ArchetypeGenerationFailure( ex );
265         }
266         catch ( InvalidPackaging ex )
267         {
268             throw new ArchetypeGenerationFailure( ex );
269         }
270         finally
271         {
272             Thread.currentThread().setContextClassLoader( old );
273         }
274     }
275 
276     public String getPackageAsDirectory( String packageName )
277     {
278         return StringUtils.replace( packageName, ".", "/" );
279     }
280 
281     private boolean copyFile( final File outFile, final String template, final boolean failIfExists,
282                               final ZipFile archetypeZipFile )
283         throws FileNotFoundException, OutputFileExists, IOException
284     {
285         getLogger().debug( "Copying file " + template );
286 
287         if ( failIfExists && outFile.exists() )
288         {
289             throw new OutputFileExists( "Don't rewrite file " + outFile.getName() );
290         }
291         else if ( outFile.exists() )
292         {
293             getLogger().warn( "CP Don't override file " + outFile );
294 
295             return false;
296         }
297 
298         ZipEntry input = archetypeZipFile.getEntry( Constants.ARCHETYPE_RESOURCES + "/" + template );
299 
300         if ( input.isDirectory() )
301         {
302             outFile.mkdirs();
303         }
304         else
305         {
306             InputStream inputStream = null;
307             OutputStream out = null;
308             try
309             {
310                 inputStream = archetypeZipFile.getInputStream( input );
311 
312                 outFile.getParentFile().mkdirs();
313 
314                 out = new FileOutputStream( outFile );
315 
316                 IOUtil.copy( inputStream, out );
317             }
318             finally
319             {
320                 IOUtil.close( inputStream );
321                 IOUtil.close( out );
322             }
323         }
324 
325         return true;
326     }
327 
328     private int copyFiles( String directory, List<String> fileSetResources, boolean packaged, String packageName,
329                            File outputDirectoryFile, ZipFile archetypeZipFile, String moduleOffset,
330                            boolean failIfExists, Context context )
331         throws OutputFileExists, FileNotFoundException, IOException
332     {
333         int count = 0;
334 
335         for ( String template : fileSetResources )
336         {
337             File outputFile =
338                 getOutputFile( template, directory, outputDirectoryFile, packaged, packageName, moduleOffset, context );
339 
340             if ( copyFile( outputFile, template, failIfExists, archetypeZipFile ) )
341             {
342                 count++;
343             }
344         }
345 
346         return count;
347     }
348 
349     private String getEncoding( String archetypeEncoding )
350     {
351         return StringUtils.isEmpty( archetypeEncoding ) ? "UTF-8" : archetypeEncoding;
352     }
353 
354     private String getOffsetSeparator( String moduleOffset )
355     {
356         return StringUtils.isEmpty( moduleOffset ) ? "/" : ( "/" + moduleOffset + "/" );
357     }
358 
359     private File getOutputFile( String template, String directory, File outputDirectoryFile, boolean packaged,
360                                 String packageName, String moduleOffset, Context context )
361     {
362         String templateName = StringUtils.replaceOnce( template, directory, "" );
363 
364         String outputFileName =
365             directory + "/" + ( packaged ? getPackageAsDirectory( packageName ) : "" ) + "/" + templateName.substring(
366                 moduleOffset.length() );
367 
368         if ( TOKEN_PATTERN.matcher( outputFileName ).matches() )
369         {
370             outputFileName = replaceFilenameTokens( outputFileName, context );
371         }
372 
373         return new File( outputDirectoryFile, outputFileName );
374     }
375 
376     /**
377      * Replaces all tokens (text surrounded by the {@link #DELIMITER}) within
378      * the given string, using properties contained within the context. If a
379      * property does not exist in the context, the token is left unmodified
380      * and a warning is logged.
381      *
382      * @param filePath the file name and path to be interpolated
383      * @param context  contains the available properties
384      */
385     private String replaceFilenameTokens( final String filePath, final Context context )
386     {
387         String interpolatedResult = filePath;
388 
389         int start = 0;
390 
391         while ( true )
392         {
393             start = interpolatedResult.indexOf( DELIMITER, start );
394 
395             if ( start == -1 )
396             {
397                 break;
398             }
399 
400             int end = interpolatedResult.indexOf( DELIMITER, start + DELIMITER.length() );
401 
402             if ( end == -1 )
403             {
404                 break;
405             }
406 
407             String propertyToken = interpolatedResult.substring( start + DELIMITER.length(), end );
408 
409             String contextPropertyValue = (String) context.get( propertyToken );
410 
411             if ( contextPropertyValue != null && contextPropertyValue.trim().length() > 0 )
412             {
413                 String search = DELIMITER + propertyToken + DELIMITER;
414 
415                 if ( getLogger().isDebugEnabled() )
416                 {
417                     getLogger().debug(
418                         "Replacing '" + search + "' in file path '" + interpolatedResult + "' with value '"
419                             + contextPropertyValue + "'." );
420                 }
421 
422                 interpolatedResult = StringUtils.replace( interpolatedResult, search, contextPropertyValue );
423 
424                 end = end + contextPropertyValue.length() - search.length();
425             }
426             else
427             {
428                 // Need to skip the undefined property
429                 getLogger().warn(
430                     "Property '" + propertyToken + "' was not specified, so the token in '" + interpolatedResult
431                         + "' is not being replaced." );
432             }
433 
434             start = end + DELIMITER.length() + 1;
435         }
436 
437         if ( getLogger().isDebugEnabled() )
438         {
439             getLogger().debug( "Final interpolated file path: '" + interpolatedResult + "'" );
440         }
441 
442         return interpolatedResult;
443     }
444 
445     private String getPackageInPathFormat( String aPackage )
446     {
447         return StringUtils.replace( aPackage, ".", "/" );
448     }
449 
450     private boolean isArchetypeConfigured( ArchetypeDescriptor archetypeDescriptor, ArchetypeGenerationRequest request )
451     {
452         for ( RequiredProperty requiredProperty : archetypeDescriptor.getRequiredProperties() )
453         {
454             if ( StringUtils.isEmpty( request.getProperties().getProperty( requiredProperty.getKey() ) ) )
455             {
456                 return false;
457             }
458         }
459 
460         return true;
461     }
462 
463     private void setParentArtifactId( Context context, String artifactId )
464     {
465         context.put( Constants.PARENT_ARTIFACT_ID, artifactId );
466     }
467 
468     private Context prepareVelocityContext( ArchetypeGenerationRequest request )
469     {
470         Context context = new VelocityContext();
471         context.put( Constants.GROUP_ID, request.getGroupId() );
472         context.put( Constants.ARTIFACT_ID, request.getArtifactId() );
473         context.put( Constants.VERSION, request.getVersion() );
474         context.put( Constants.PACKAGE, request.getPackage() );
475         final String packageInPathFormat = getPackageInPathFormat( request.getPackage() );
476         context.put( Constants.PACKAGE_IN_PATH_FORMAT, packageInPathFormat );
477 
478         if ( getLogger().isInfoEnabled() )
479         {
480             getLogger().info( "----------------------------------------------------------------------------" );
481 
482             getLogger().info(
483                 "Using following parameters for creating project from Archetype: " + request.getArchetypeArtifactId()
484                     + ":" + request.getArchetypeVersion() );
485 
486             getLogger().info( "----------------------------------------------------------------------------" );
487             getLogger().info( "Parameter: " + Constants.GROUP_ID + ", Value: " + request.getGroupId() );
488             getLogger().info( "Parameter: " + Constants.ARTIFACT_ID + ", Value: " + request.getArtifactId() );
489             getLogger().info( "Parameter: " + Constants.VERSION + ", Value: " + request.getVersion() );
490             getLogger().info( "Parameter: " + Constants.PACKAGE + ", Value: " + request.getPackage() );
491             getLogger().info( "Parameter: " + Constants.PACKAGE_IN_PATH_FORMAT + ", Value: " + packageInPathFormat );
492         }
493 
494         for ( Iterator<?> iterator = request.getProperties().keySet().iterator(); iterator.hasNext(); )
495         {
496             String key = (String) iterator.next();
497 
498             String value = request.getProperties().getProperty( key );
499 
500             if ( maybeVelocityExpression( value ) )
501             {
502                 value = evaluateExpression( context, key, value );
503             }
504 
505             context.put( key, value );
506 
507             if ( getLogger().isInfoEnabled() )
508             {
509                 getLogger().info( "Parameter: " + key + ", Value: " + value );
510             }
511         }
512         return context;
513     }
514 
515     private boolean maybeVelocityExpression( String value )
516     {
517         return value != null && value.contains( "${" );
518     }
519 
520     private String evaluateExpression( Context context, String key, String value )
521     {
522         StringWriter stringWriter = new StringWriter();
523         try
524         {
525             Velocity.evaluate( context, stringWriter, key, value );
526             return stringWriter.toString();
527         }
528         catch ( Exception ex )
529         {
530             return value;
531         }
532         finally
533         {
534             IOUtil.close( stringWriter );
535         }
536 
537     }
538 
539     private void processArchetypeTemplates( AbstractArchetypeDescriptor archetypeDescriptor,
540                                             List<String> archetypeResources, ZipFile archetypeZipFile,
541                                             String moduleOffset, Context context, String packageName,
542                                             File outputDirectoryFile )
543         throws OutputFileExists, ArchetypeGenerationFailure, FileNotFoundException, IOException
544     {
545         processTemplates( packageName, outputDirectoryFile, context, archetypeDescriptor, archetypeResources,
546                           archetypeZipFile, moduleOffset, false );
547     }
548 
549     private void processArchetypeTemplatesWithWarning( ArchetypeDescriptor archetypeDescriptor,
550                                                        List<String> archetypeResources, ZipFile archetypeZipFile,
551                                                        String moduleOffset, Context context, String packageName,
552                                                        File outputDirectoryFile )
553         throws OutputFileExists, ArchetypeGenerationFailure, FileNotFoundException, IOException
554     {
555         processTemplates( packageName, outputDirectoryFile, context, archetypeDescriptor, archetypeResources,
556                           archetypeZipFile, moduleOffset, true );
557     }
558 
559     private int processFileSet( String directory, List<String> fileSetResources, boolean packaged, String packageName,
560                                 Context context, File outputDirectoryFile, String moduleOffset,
561                                 String archetypeEncoding, boolean failIfExists )
562         throws OutputFileExists, ArchetypeGenerationFailure
563     {
564         int count = 0;
565 
566         for ( String template : fileSetResources )
567         {
568             File outputFile =
569                 getOutputFile( template, directory, outputDirectoryFile, packaged, packageName, moduleOffset, context );
570 
571             if ( processTemplate( outputFile, context, Constants.ARCHETYPE_RESOURCES + "/" + template,
572                                   archetypeEncoding, failIfExists ) )
573             {
574                 count++;
575             }
576         }
577 
578         return count;
579     }
580 
581     private void processFilesetModule( final String rootArtifactId, final String artifactId,
582                                        final List<String> archetypeResources, File pom, final ZipFile archetypeZipFile,
583                                        String moduleOffset, File basedirPom, File outputDirectoryFile,
584                                        final String packageName, final AbstractArchetypeDescriptor archetypeDescriptor,
585                                        final Context context )
586         throws DocumentException, XmlPullParserException, ArchetypeGenerationFailure, InvalidPackaging, IOException,
587         OutputFileExists
588     {
589         outputDirectoryFile.mkdirs();
590         getLogger().debug( "Processing module " + artifactId );
591         getLogger().debug( "Processing module rootArtifactId " + rootArtifactId );
592         getLogger().debug( "Processing module pom " + pom );
593         getLogger().debug( "Processing module moduleOffset " + moduleOffset );
594         getLogger().debug( "Processing module outputDirectoryFile " + outputDirectoryFile );
595 
596         processFilesetProject( archetypeDescriptor,
597                                StringUtils.replace( artifactId, "${rootArtifactId}", rootArtifactId ),
598                                archetypeResources, pom, archetypeZipFile, moduleOffset, context, packageName,
599                                outputDirectoryFile, basedirPom );
600 
601         String parentArtifactId = (String) context.get( Constants.PARENT_ARTIFACT_ID );
602 
603         Iterator<ModuleDescriptor> subprojects = archetypeDescriptor.getModules().iterator();
604 
605         if ( subprojects.hasNext() )
606         {
607             getLogger().debug( artifactId + " has modules (" + archetypeDescriptor.getModules() + ")" );
608 
609             setParentArtifactId( context, StringUtils.replace( artifactId, "${rootArtifactId}", rootArtifactId ) );
610         }
611 
612         while ( subprojects.hasNext() )
613         {
614             ModuleDescriptor project = subprojects.next();
615 
616             String modulePath = StringUtils.replace( project.getDir(), "__rootArtifactId__", rootArtifactId );
617             modulePath = replaceFilenameTokens( modulePath, context );
618             
619             File moduleOutputDirectoryFile = new File( outputDirectoryFile, modulePath );
620 
621             context.put( Constants.ARTIFACT_ID,
622                          StringUtils.replace( project.getId(), "${rootArtifactId}", rootArtifactId ) );
623             
624             String moduleArtifactId = StringUtils.replace( project.getDir(), "__rootArtifactId__", rootArtifactId );
625             moduleArtifactId = replaceFilenameTokens( moduleArtifactId, context );
626             
627             processFilesetModule( rootArtifactId, moduleArtifactId,
628                                   archetypeResources, new File( moduleOutputDirectoryFile, Constants.ARCHETYPE_POM ),
629                                   archetypeZipFile,
630                                   ( StringUtils.isEmpty( moduleOffset ) ? "" : ( moduleOffset + "/" ) )
631                                       + StringUtils.replace( project.getDir(), "${rootArtifactId}", rootArtifactId ),
632                                   pom, moduleOutputDirectoryFile, packageName, project, context );
633         }
634 
635         restoreParentArtifactId( context, parentArtifactId );
636 
637         getLogger().debug( "Processed " + artifactId );
638     }
639 
640     private void processFilesetProject( final AbstractArchetypeDescriptor archetypeDescriptor, final String moduleId,
641                                         final List<String> archetypeResources, final File pom,
642                                         final ZipFile archetypeZipFile, String moduleOffset, final Context context,
643                                         final String packageName, final File outputDirectoryFile,
644                                         final File basedirPom )
645         throws DocumentException, XmlPullParserException, ArchetypeGenerationFailure, InvalidPackaging, IOException,
646         FileNotFoundException, OutputFileExists
647     {
648         getLogger().debug( "Processing fileset project moduleId " + moduleId );
649         getLogger().debug( "Processing fileset project pom " + pom );
650         getLogger().debug( "Processing fileset project moduleOffset " + moduleOffset );
651         getLogger().debug( "Processing fileset project outputDirectoryFile " + outputDirectoryFile );
652         getLogger().debug( "Processing fileset project basedirPom " + basedirPom );
653 
654         if ( basedirPom.exists() )
655         {
656             processPomWithParent( context, pom, moduleOffset, basedirPom, moduleId );
657         }
658         else
659         {
660             processPom( context, pom, moduleOffset );
661         }
662 
663         processArchetypeTemplates( archetypeDescriptor, archetypeResources, archetypeZipFile, moduleOffset, context,
664                                    packageName, outputDirectoryFile );
665     }
666 
667     private void processPom( Context context, File pom, String moduleOffset )
668         throws OutputFileExists, ArchetypeGenerationFailure
669     {
670         getLogger().debug( "Processing pom " + pom );
671 
672         processTemplate( pom, context,
673                          Constants.ARCHETYPE_RESOURCES + getOffsetSeparator( moduleOffset ) + Constants.ARCHETYPE_POM,
674                          getEncoding( null ), true );
675     }
676 
677     private void processPomWithMerge( Context context, File pom, String moduleOffset )
678         throws OutputFileExists, IOException, XmlPullParserException, ArchetypeGenerationFailure
679     {
680         getLogger().debug( "Processing pom " + pom + " with merge" );
681 
682         File temporaryPom = getTemporaryFile( pom );
683 
684         processTemplate( temporaryPom, context,
685                          Constants.ARCHETYPE_RESOURCES + getOffsetSeparator( moduleOffset ) + Constants.ARCHETYPE_POM,
686                          getEncoding( null ), true );
687 
688         pomManager.mergePoms( pom, temporaryPom );
689 
690         // getTemporaryFile sets deleteOnExit. Lets try to delete and then make sure deleteOnExit is
691         // still set. Windows has issues deleting files with certain JDKs.
692         try
693         {
694             FileUtils.forceDelete( temporaryPom );
695         }
696         catch ( IOException e )
697         {
698             temporaryPom.deleteOnExit();
699         }
700     }
701 
702     private void processPomWithParent( Context context, File pom, String moduleOffset, File basedirPom,
703                                        String moduleId )
704         throws OutputFileExists, XmlPullParserException, DocumentException, IOException, InvalidPackaging,
705         ArchetypeGenerationFailure
706     {
707         getLogger().debug( "Processing pom " + pom + " with parent " + basedirPom );
708 
709         processTemplate( pom, context,
710                          Constants.ARCHETYPE_RESOURCES + getOffsetSeparator( moduleOffset ) + Constants.ARCHETYPE_POM,
711                          getEncoding( null ), true );
712 
713         getLogger().debug( "Adding module " + moduleId );
714 
715         pomManager.addModule( basedirPom, moduleId );
716 
717         pomManager.addParent( pom, basedirPom );
718     }
719 
720     @SuppressWarnings( "deprecation" )
721     private boolean processTemplate( File outFile, Context context, String templateFileName, String encoding,
722                                      boolean failIfExists )
723         throws OutputFileExists, ArchetypeGenerationFailure
724     {
725         templateFileName = templateFileName.replace( File.separatorChar, '/' );
726 
727         String localTemplateFileName = templateFileName.replace( '/', File.separatorChar );
728         if ( !templateFileName.equals( localTemplateFileName ) && !velocity.getEngine().templateExists(
729             templateFileName ) && velocity.getEngine().templateExists( localTemplateFileName ) )
730         {
731             templateFileName = localTemplateFileName;
732         }
733 
734         getLogger().debug( "Processing template " + templateFileName );
735 
736         if ( outFile.exists() )
737         {
738             if ( failIfExists )
739             {
740                 throw new OutputFileExists( "Don't override file " + outFile.getAbsolutePath() );
741             }
742 
743             getLogger().warn( "Don't override file " + outFile );
744 
745             return false;
746         }
747 
748         if ( templateFileName.endsWith( "/" ) )
749         {
750             getLogger().debug( "Creating directory " + outFile );
751 
752             outFile.mkdirs();
753 
754             return true;
755         }
756 
757         if ( !outFile.getParentFile().exists() )
758         {
759             outFile.getParentFile().mkdirs();
760         }
761 
762         getLogger().debug( "Merging into " + outFile );
763 
764         Writer writer = null;
765 
766         try
767         {
768             StringWriter stringWriter = new StringWriter();
769 
770             velocity.getEngine().mergeTemplate( templateFileName, encoding, context, stringWriter );
771 
772             writer = new OutputStreamWriter( new FileOutputStream( outFile ), encoding );
773 
774             writer.write( StringUtils.unifyLineSeparators( stringWriter.toString() ) );
775 
776             writer.flush();
777         }
778         catch ( Exception e )
779         {
780             throw new ArchetypeGenerationFailure( "Error merging velocity templates: " + e.getMessage(), e );
781         }
782         finally
783         {
784             IOUtil.close( writer );
785         }
786 
787         return true;
788     }
789 
790     private void processTemplates( String packageName, File outputDirectoryFile, Context context,
791                                    AbstractArchetypeDescriptor archetypeDescriptor, List<String> archetypeResources,
792                                    ZipFile archetypeZipFile, String moduleOffset, boolean failIfExists )
793         throws OutputFileExists, ArchetypeGenerationFailure, FileNotFoundException, IOException
794     {
795         Iterator<FileSet> iterator = archetypeDescriptor.getFileSets().iterator();
796         if ( iterator.hasNext() )
797         {
798             getLogger().debug( "Processing filesets" + "\n  " + archetypeResources );
799         }
800 
801         int count = 0;
802         while ( iterator.hasNext() )
803         {
804             FileSet fileSet = iterator.next();
805             count++;
806 
807             List<String> fileSetResources =
808                 archetypeFilesResolver.filterFiles( moduleOffset, fileSet, archetypeResources );
809 
810             // This creates an empty directory, even if there is no file to process
811             // Fix for ARCHETYPE-57
812             getOutputFile( moduleOffset, fileSet.getDirectory(), outputDirectoryFile, fileSet.isPackaged(), packageName,
813                            moduleOffset, context ).mkdirs();
814 
815             if ( fileSet.isFiltered() )
816             {
817                 getLogger().debug( "    Processing fileset " + fileSet + " -> " + fileSetResources.size() + ":\n      "
818                                        + fileSetResources );
819 
820                 int processed =
821                     processFileSet( fileSet.getDirectory(), fileSetResources, fileSet.isPackaged(), packageName,
822                                     context, outputDirectoryFile, moduleOffset, getEncoding( fileSet.getEncoding() ),
823                                     failIfExists );
824 
825                 getLogger().debug( "    Processed " + processed + " files." );
826             }
827             else
828             {
829                 getLogger().debug( "    Copying fileset " + fileSet + " -> " + fileSetResources.size() + ":\n      "
830                                        + fileSetResources );
831 
832                 int copied = copyFiles( fileSet.getDirectory(), fileSetResources, fileSet.isPackaged(), packageName,
833                                         outputDirectoryFile, archetypeZipFile, moduleOffset, failIfExists, context );
834 
835                 getLogger().debug( "    Copied " + copied + " files." );
836             }
837         }
838 
839         getLogger().debug( "Processed " + count + " filesets" );
840     }
841 
842     private void restoreParentArtifactId( Context context, String parentArtifactId )
843     {
844         if ( StringUtils.isEmpty( parentArtifactId ) )
845         {
846             context.remove( Constants.PARENT_ARTIFACT_ID );
847         }
848         else
849         {
850             context.put( Constants.PARENT_ARTIFACT_ID, parentArtifactId );
851         }
852     }
853 
854     private File getTemporaryFile( File file )
855     {
856         File tmp = FileUtils.createTempFile( file.getName(), Constants.TMP, file.getParentFile() );
857 
858         tmp.deleteOnExit();
859 
860         return tmp;
861     }
862 }