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