View Javadoc
1   package org.apache.maven.archetype.old;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.io.input.XmlStreamReader;
23  import org.apache.maven.archetype.ArchetypeGenerationRequest;
24  import org.apache.maven.archetype.old.descriptor.ArchetypeDescriptor;
25  import org.apache.maven.archetype.old.descriptor.ArchetypeDescriptorBuilder;
26  import org.apache.maven.archetype.old.descriptor.TemplateDescriptor;
27  import org.apache.maven.artifact.repository.ArtifactRepository;
28  import org.apache.maven.archetype.common.ArchetypeArtifactManager;
29  import org.apache.maven.archetype.common.Constants;
30  import org.apache.maven.archetype.exception.UnknownArchetype;
31  import org.apache.maven.model.Build;
32  import org.apache.maven.model.Model;
33  import org.apache.maven.model.Parent;
34  import org.apache.maven.model.Resource;
35  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
36  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
37  import org.apache.velocity.VelocityContext;
38  import org.apache.velocity.context.Context;
39  import org.codehaus.plexus.component.annotations.Component;
40  import org.codehaus.plexus.component.annotations.Requirement;
41  import org.codehaus.plexus.logging.AbstractLogEnabled;
42  import org.codehaus.plexus.util.FileUtils;
43  import org.codehaus.plexus.util.IOUtil;
44  import org.codehaus.plexus.util.ReaderFactory;
45  import org.codehaus.plexus.util.StringUtils;
46  import org.codehaus.plexus.util.WriterFactory;
47  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
48  import org.codehaus.plexus.velocity.VelocityComponent;
49  import org.dom4j.Document;
50  import org.dom4j.DocumentException;
51  import org.dom4j.Element;
52  import org.dom4j.Node;
53  import org.dom4j.io.SAXReader;
54  import org.dom4j.io.XMLWriter;
55  
56  import java.io.File;
57  import java.io.FileOutputStream;
58  import java.io.IOException;
59  import java.io.InputStream;
60  import java.io.OutputStream;
61  import java.io.OutputStreamWriter;
62  import java.io.Reader;
63  import java.io.StringWriter;
64  import java.io.Writer;
65  import java.net.URL;
66  import java.net.URLClassLoader;
67  import java.util.HashMap;
68  import java.util.Iterator;
69  import java.util.Map;
70  
71  /**
72   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
73   * @version $Id: DefaultOldArchetype.html 942523 2015-03-06 00:20:33Z bimargulies $
74   */
75  @Component( role = OldArchetype.class )
76  public class DefaultOldArchetype
77      extends AbstractLogEnabled
78      implements OldArchetype
79  {
80      private static final String DEFAULT_TEST_RESOURCE_DIR = "/src/test/resources";
81  
82      private static final String DEFAULT_TEST_SOURCE_DIR = "/src/test/java";
83  
84      private static final String DEFAULT_RESOURCE_DIR = "/src/main/resources";
85  
86      private static final String DEFAULT_SOURCE_DIR = "/src/main/java";
87  
88      // ----------------------------------------------------------------------
89      // Components
90      // ----------------------------------------------------------------------
91  
92      @Requirement
93      private VelocityComponent velocity;
94  
95      @Requirement
96      private ArchetypeArtifactManager archetypeArtifactManager;
97  
98      // ----------------------------------------------------------------------
99      // Implementation
100     // ----------------------------------------------------------------------
101 
102     // groupId = maven
103     // artifactId = maven-foo-archetype
104     // version = latest
105 
106     public void createArchetype( ArchetypeGenerationRequest request, ArtifactRepository archetypeRepository )
107         throws UnknownArchetype, ArchetypeNotFoundException, ArchetypeDescriptorException,
108         ArchetypeTemplateProcessingException
109     {
110         // ----------------------------------------------------------------------
111         // Download the archetype
112         // ----------------------------------------------------------------------
113 
114         File archetypeFile = archetypeArtifactManager.getArchetypeFile(
115                 request.getArchetypeGroupId(), request.getArchetypeArtifactId(), request.getArchetypeVersion(),
116                 archetypeRepository, request.getLocalRepository(), request.getRemoteArtifactRepositories() );
117 
118         createArchetype( request, archetypeFile );
119     }
120 
121     public void createArchetype( ArchetypeGenerationRequest request, File archetypeFile )
122         throws ArchetypeDescriptorException, ArchetypeTemplateProcessingException
123     {
124         Map<String, String> parameters = new HashMap<String, String>();
125 
126         parameters.put( "basedir", request.getOutputDirectory() );
127 
128         parameters.put( Constants.PACKAGE, request.getPackage() );
129 
130         parameters.put( "packageName", request.getPackage() );
131 
132         parameters.put( Constants.GROUP_ID, request.getGroupId() );
133 
134         parameters.put( Constants.ARTIFACT_ID, request.getArtifactId() );
135 
136         parameters.put( Constants.VERSION, request.getVersion() );
137 
138         // ---------------------------------------------------------------------
139         // Get Logger and display all parameters used
140         // ---------------------------------------------------------------------
141         if ( getLogger().isInfoEnabled() )
142         {
143             getLogger().info( "----------------------------------------------------------------------------" );
144 
145             getLogger().info( "Using following parameters for creating project from Old (1.x) Archetype: "
146                                   + request.getArchetypeArtifactId() + ":" + request.getArchetypeVersion() );
147 
148             getLogger().info( "----------------------------------------------------------------------------" );
149 
150             for ( Map.Entry<String, String> entry : parameters.entrySet() )
151             {
152                 String parameterName = entry.getKey();
153 
154                 String parameterValue = entry.getValue();
155 
156                 getLogger().info( "Parameter: " + parameterName + ", Value: " + parameterValue );
157             }
158         }
159 
160         // ----------------------------------------------------------------------
161         // Load the descriptor
162         // ----------------------------------------------------------------------
163 
164         ArchetypeDescriptorBuilder builder = new ArchetypeDescriptorBuilder();
165 
166         ArchetypeDescriptor descriptor;
167 
168         URLClassLoader archetypeJarLoader;
169 
170         InputStream is = null;
171 
172         try
173         {
174             URL[] urls = new URL[1];
175 
176             urls[0] = archetypeFile.toURL();
177 
178             archetypeJarLoader = new URLClassLoader( urls );
179 
180             is = getStream( ARCHETYPE_DESCRIPTOR, archetypeJarLoader );
181 
182             if ( is == null )
183             {
184                 is = getStream( ARCHETYPE_OLD_DESCRIPTOR, archetypeJarLoader );
185             }
186 
187             if ( is == null )
188             {
189                 throw new ArchetypeDescriptorException( "The " + ARCHETYPE_DESCRIPTOR
190                                                         + " descriptor cannot be found." );
191             }
192 
193             descriptor = builder.build( new XmlStreamReader( is ) );
194         }
195         catch ( IOException e )
196         {
197             throw new ArchetypeDescriptorException( "Error reading the " + ARCHETYPE_DESCRIPTOR + " descriptor.", e );
198         }
199         catch ( XmlPullParserException e )
200         {
201             throw new ArchetypeDescriptorException( "Error reading the " + ARCHETYPE_DESCRIPTOR + " descriptor.", e );
202         }
203         finally
204         {
205             IOUtil.close( is );
206         }
207 
208         // ----------------------------------------------------------------------
209         //
210         // ----------------------------------------------------------------------
211 
212         String artifactId = request.getArtifactId();
213 
214         File parentPomFile = new File( request.getOutputDirectory(), ARCHETYPE_POM );
215 
216         File outputDirectoryFile;
217 
218         boolean creating;
219         File pomFile;
220         if ( parentPomFile.exists() && descriptor.isAllowPartial() && artifactId == null )
221         {
222             outputDirectoryFile = new File( request.getOutputDirectory() );
223             creating = false;
224             pomFile = parentPomFile;
225         }
226         else
227         {
228             if ( artifactId == null )
229             {
230                 throw new ArchetypeTemplateProcessingException(
231                     "Artifact ID must be specified when creating a new project from an archetype." );
232             }
233 
234             outputDirectoryFile = new File( request.getOutputDirectory(), artifactId );
235             creating = true;
236 
237             if ( outputDirectoryFile.exists() )
238             {
239                 if ( descriptor.isAllowPartial() )
240                 {
241                     creating = false;
242                 }
243                 else
244                 {
245                     throw new ArchetypeTemplateProcessingException( "Directory "
246                         + outputDirectoryFile.getName() + " already exists - please run from a clean directory" );
247                 }
248             }
249 
250             pomFile = new File( outputDirectoryFile, ARCHETYPE_POM );
251         }
252 
253         if ( creating )
254         {
255             if ( request.getGroupId() == null )
256             {
257                 throw new ArchetypeTemplateProcessingException(
258                     "Group ID must be specified when creating a new project from an archetype." );
259             }
260 
261             if ( request.getVersion() == null )
262             {
263                 throw new ArchetypeTemplateProcessingException(
264                     "Version must be specified when creating a new project from an archetype." );
265             }
266         }
267 
268         String outputDirectory = outputDirectoryFile.getAbsolutePath();
269 
270         String packageName = request.getPackage();
271 
272         // ----------------------------------------------------------------------
273         // Set up the Velocity context
274         // ----------------------------------------------------------------------
275 
276         Context context = new VelocityContext();
277 
278         context.put( Constants.PACKAGE, packageName );
279 
280         for ( Map.Entry<String, String> entry : parameters.entrySet() )
281         {
282             context.put( entry.getKey(), entry.getValue() );
283         }
284 
285         // ----------------------------------------------------------------------
286         // Process the templates
287         // ----------------------------------------------------------------------
288 
289         ClassLoader old = Thread.currentThread().getContextClassLoader();
290 
291         Thread.currentThread().setContextClassLoader( archetypeJarLoader );
292 
293         Model parentModel = null;
294         if ( creating )
295         {
296             if ( parentPomFile.exists() )
297             {
298                 Reader fileReader = null;
299 
300                 try
301                 {
302                     fileReader = ReaderFactory.newXmlReader( parentPomFile );
303                     MavenXpp3Reader reader = new MavenXpp3Reader();
304                     parentModel = reader.read( fileReader );
305                     if ( !"pom".equals( parentModel.getPackaging() ) )
306                     {
307                         throw new ArchetypeTemplateProcessingException(
308                             "Unable to add module to the current project as it is not of packaging type 'pom'" );
309                     }
310                 }
311                 catch ( IOException e )
312                 {
313                     throw new ArchetypeTemplateProcessingException( "Unable to read parent POM", e );
314                 }
315                 catch ( XmlPullParserException e )
316                 {
317                     throw new ArchetypeTemplateProcessingException( "Unable to read parent POM", e );
318                 }
319                 finally
320                 {
321                     IOUtil.close( fileReader );
322                 }
323 
324                 parentModel.getModules().add( artifactId );
325             }
326         }
327 
328         try
329         {
330             processTemplates( pomFile, outputDirectory, context, descriptor, packageName, parentModel );
331         }
332         finally
333         {
334             Thread.currentThread().setContextClassLoader( old );
335         }
336 
337         if ( parentModel != null )
338         {
339 /*
340         // TODO: would be nice to just write out with the xpp3 writer again, except that it loses a bunch of info and
341         // reformats, so the module is just baked in as a string instead.
342             FileWriter fileWriter = null;
343 
344             try
345             {
346                 fileWriter = new FileWriter( parentPomFile );
347 
348                 MavenXpp3Writer writer = new MavenXpp3Writer();
349                 writer.write( fileWriter, parentModel );
350             }
351             catch ( IOException e )
352             {
353                 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
354             }
355             finally
356             {
357                 IOUtil.close( fileWriter );
358             }
359 */
360             Reader fileReader = null;
361             boolean added;
362             StringWriter w = new StringWriter();
363             try
364             {
365                 fileReader = ReaderFactory.newXmlReader( parentPomFile );
366                 added = addModuleToParentPom( artifactId, fileReader, w );
367             }
368             catch ( IOException e )
369             {
370                 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
371             }
372             catch ( DocumentException e )
373             {
374                 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
375             }
376             finally
377             {
378                 IOUtil.close( fileReader );
379             }
380 
381             if ( added )
382             {
383                 Writer out = null;
384                 try
385                 {
386                     out = WriterFactory.newXmlWriter( parentPomFile );
387                     IOUtil.copy( w.toString(), out );
388                 }
389                 catch ( IOException e )
390                 {
391                     throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
392                 }
393                 finally
394                 {
395                     IOUtil.close( out );
396                 }
397             }
398         }
399 
400         // ----------------------------------------------------------------------
401         // Log message on OldArchetype creation
402         // ----------------------------------------------------------------------
403         if ( getLogger().isInfoEnabled() )
404         {
405             getLogger().info( "project created from Old (1.x) Archetype in dir: " + outputDirectory );
406         }
407 
408     }
409 
410     static boolean addModuleToParentPom( String artifactId, Reader fileReader, Writer fileWriter )
411         throws DocumentException, IOException, ArchetypeTemplateProcessingException
412     {
413         SAXReader reader = new SAXReader();
414         Document document = reader.read( fileReader );
415         Element project = document.getRootElement();
416 
417         String packaging = null;
418         Element packagingElement = project.element( "packaging" );
419         if ( packagingElement != null )
420         {
421             packaging = packagingElement.getStringValue();
422         }
423         if ( !"pom".equals( packaging ) )
424         {
425             throw new ArchetypeTemplateProcessingException(
426                 "Unable to add module to the current project as it is not of packaging type 'pom'" );
427         }
428 
429         Element modules = project.element( "modules" );
430         if ( modules == null )
431         {
432             modules = project.addText( "  " ).addElement( "modules" );
433             modules.setText( "\n  " );
434             project.addText( "\n" );
435         }
436         boolean found = false;
437         for ( Iterator<?> i = modules.elementIterator( "module" ); i.hasNext() && !found; )
438         {
439             Element module = (Element) i.next();
440             if ( module.getText().equals( artifactId ) )
441             {
442                 found = true;
443             }
444         }
445         if ( !found )
446         {
447             Node lastTextNode = null;
448             for ( Iterator<?> i = modules.nodeIterator(); i.hasNext(); )
449             {
450                 Node node = (Node) i.next();
451                 if ( node.getNodeType() == Node.ELEMENT_NODE )
452                 {
453                     lastTextNode = null;
454                 }
455                 else if ( node.getNodeType() == Node.TEXT_NODE )
456                 {
457                     lastTextNode = node;
458                 }
459             }
460 
461             if ( lastTextNode != null )
462             {
463                 modules.remove( lastTextNode );
464             }
465 
466             modules.addText( "\n    " );
467             modules.addElement( "module" ).setText( artifactId );
468             modules.addText( "\n  " );
469 
470             XMLWriter writer = new XMLWriter( fileWriter );
471             writer.write( document );
472         }
473         return !found;
474     }
475 
476     private void processTemplates( File pomFile, String outputDirectory, Context context,
477                                    ArchetypeDescriptor descriptor, String packageName, Model parentModel )
478         throws ArchetypeTemplateProcessingException
479     {
480         if ( !pomFile.exists() )
481         {
482             processTemplate( outputDirectory, context, ARCHETYPE_POM, new TemplateDescriptor(), false, null );
483         }
484 
485         // ---------------------------------------------------------------------
486         // Model generated for the new archetype, so process it now
487         // ---------------------------------------------------------------------
488 
489         Model generatedModel;
490         Reader pomReader = null;
491         try
492         {
493             pomReader = ReaderFactory.newXmlReader( pomFile );
494 
495             MavenXpp3Reader reader = new MavenXpp3Reader();
496 
497             generatedModel = reader.read( pomReader );
498         }
499         catch ( IOException e )
500         {
501             throw new ArchetypeTemplateProcessingException( "Error reading POM", e );
502         }
503         catch ( XmlPullParserException e )
504         {
505             throw new ArchetypeTemplateProcessingException( "Error reading POM", e );
506         }
507         finally
508         {
509             IOUtil.close( pomReader );
510         }
511 
512         if ( parentModel != null )
513         {
514             Parent parent = new Parent();
515             parent.setGroupId( parentModel.getGroupId() );
516             if ( parent.getGroupId() == null )
517             {
518                 parent.setGroupId( parentModel.getParent().getGroupId() );
519             }
520             parent.setArtifactId( parentModel.getArtifactId() );
521             parent.setVersion( parentModel.getVersion() );
522             if ( parent.getVersion() == null )
523             {
524                 parent.setVersion( parentModel.getParent().getVersion() );
525             }
526             generatedModel.setParent( parent );
527 
528             Writer pomWriter = null;
529             try
530             {
531                 pomWriter = WriterFactory.newXmlWriter( pomFile );
532 
533                 MavenXpp3Writer writer = new MavenXpp3Writer();
534                 writer.write( pomWriter, generatedModel );
535             }
536             catch ( IOException e )
537             {
538                 throw new ArchetypeTemplateProcessingException( "Error rewriting POM", e );
539             }
540             finally
541             {
542                 IOUtil.close( pomWriter );
543             }
544         }
545 
546         // XXX: Following POM processing block may be a candidate for
547         // refactoring out into service methods or moving to
548         // createProjectDirectoryStructure(outputDirectory)
549         Build build = generatedModel.getBuild();
550 
551         boolean overrideSrcDir = false;
552 
553         boolean overrideResourceDir = false;
554 
555         boolean overrideTestSrcDir = false;
556 
557         boolean overrideTestResourceDir = false;
558 
559         boolean foundBuildElement = build != null;
560 
561         if ( getLogger().isDebugEnabled() )
562         {
563             getLogger().debug(
564                 "********************* Debug info for resources created from generated Model ***********************" );
565             getLogger().debug( "Was build element found in generated POM?: " + foundBuildElement );
566         }
567 
568         // create source directory if specified in POM
569         if ( foundBuildElement && null != build.getSourceDirectory() )
570         {
571             getLogger().debug( "Overriding default source directory " );
572 
573             overrideSrcDir = true;
574 
575             String srcDirectory = build.getSourceDirectory();
576 
577             srcDirectory = StringUtils.replace( srcDirectory, "\\", "/" );
578 
579             FileUtils.mkdir( getOutputDirectory( outputDirectory, srcDirectory ) );
580         }
581 
582         // create script source directory if specified in POM
583         if ( foundBuildElement && null != build.getScriptSourceDirectory() )
584         {
585             getLogger().debug( "Overriding default script source directory " );
586 
587             String scriptSourceDirectory = build.getScriptSourceDirectory();
588 
589             scriptSourceDirectory = StringUtils.replace( scriptSourceDirectory, "\\", "/" );
590 
591             FileUtils.mkdir( getOutputDirectory( outputDirectory, scriptSourceDirectory ) );
592         }
593 
594         // create resource director(y/ies) if specified in POM
595         if ( foundBuildElement && build.getResources().size() > 0 )
596         {
597             getLogger().debug( "Overriding default resource directory " );
598 
599             overrideResourceDir = true;
600 
601             Iterator<?> resourceItr = build.getResources().iterator();
602 
603             while ( resourceItr.hasNext() )
604             {
605                 Resource resource = (Resource) resourceItr.next();
606 
607                 String resourceDirectory = resource.getDirectory();
608 
609                 resourceDirectory = StringUtils.replace( resourceDirectory, "\\", "/" );
610 
611                 FileUtils.mkdir( getOutputDirectory( outputDirectory, resourceDirectory ) );
612             }
613         }
614         // create test source directory if specified in POM
615         if ( foundBuildElement && null != build.getTestSourceDirectory() )
616         {
617             getLogger().debug( "Overriding default test directory " );
618 
619             overrideTestSrcDir = true;
620 
621             String testDirectory = build.getTestSourceDirectory();
622 
623             testDirectory = StringUtils.replace( testDirectory, "\\", "/" );
624 
625             FileUtils.mkdir( getOutputDirectory( outputDirectory, testDirectory ) );
626         }
627 
628         // create test resource directory if specified in POM
629         if ( foundBuildElement && build.getTestResources().size() > 0 )
630         {
631             getLogger().debug( "Overriding default test resource directory " );
632 
633             overrideTestResourceDir = true;
634 
635             Iterator<?> testResourceItr = build.getTestResources().iterator();
636 
637             while ( testResourceItr.hasNext() )
638             {
639                 Resource resource = (Resource) testResourceItr.next();
640 
641                 String testResourceDirectory = resource.getDirectory();
642 
643                 testResourceDirectory = StringUtils.replace( testResourceDirectory, "\\", "/" );
644 
645                 FileUtils.mkdir( getOutputDirectory( outputDirectory, testResourceDirectory ) );
646             }
647         }
648 
649         getLogger().debug(
650             "********************* End of debug info from resources from generated POM ***********************" );
651 
652         // ----------------------------------------------------------------------
653         // Main
654         // ----------------------------------------------------------------------
655 
656         if ( descriptor.getSources().size() > 0 )
657         {
658             if ( !overrideSrcDir )
659             {
660                 FileUtils.mkdir( outputDirectory + DEFAULT_SOURCE_DIR );
661                 processSources( outputDirectory, context, descriptor, packageName, DEFAULT_SOURCE_DIR );
662             }
663             else
664             {
665                 processSources( outputDirectory, context, descriptor, packageName, build.getSourceDirectory() );
666             }
667         }
668 
669         if ( descriptor.getResources().size() > 0 )
670         {
671             if ( !overrideResourceDir )
672             {
673                 FileUtils.mkdir( outputDirectory + DEFAULT_RESOURCE_DIR );
674             }
675             processResources( outputDirectory, context, descriptor, packageName );
676         }
677 
678         // ----------------------------------------------------------------------
679         // Test
680         // ----------------------------------------------------------------------
681 
682         if ( descriptor.getTestSources().size() > 0 )
683         {
684             if ( !overrideTestSrcDir )
685             {
686                 FileUtils.mkdir( outputDirectory + DEFAULT_TEST_SOURCE_DIR );
687                 processTestSources( outputDirectory, context, descriptor, packageName, DEFAULT_TEST_SOURCE_DIR );
688             }
689             else
690             {
691                 processTestSources( outputDirectory, context, descriptor, packageName, build.getTestSourceDirectory() );
692             }
693         }
694 
695         if ( descriptor.getTestResources().size() > 0 )
696         {
697             if ( !overrideTestResourceDir )
698             {
699                 FileUtils.mkdir( outputDirectory + DEFAULT_TEST_RESOURCE_DIR );
700             }
701             processTestResources( outputDirectory, context, descriptor, packageName );
702         }
703 
704         // ----------------------------------------------------------------------
705         // Site
706         // ----------------------------------------------------------------------
707 
708         if ( descriptor.getSiteResources().size() > 0 )
709         {
710             processSiteResources( outputDirectory, context, descriptor, packageName );
711         }
712     }
713 
714     private void processTemplate( String outputDirectory, Context context, String template,
715                                   TemplateDescriptor descriptor, boolean packageInFileName, String packageName )
716         throws ArchetypeTemplateProcessingException
717     {
718         processTemplate( outputDirectory, context, template, descriptor, packageInFileName, packageName, null );
719     }
720 
721     private String getOutputDirectory( String outputDirectory, String testResourceDirectory )
722     {
723         return outputDirectory
724             + ( testResourceDirectory.startsWith( "/" ) ? testResourceDirectory : "/" + testResourceDirectory );
725     }
726 
727     // ----------------------------------------------------------------------
728     //
729     // ----------------------------------------------------------------------
730 
731     protected void processSources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
732                                    String packageName, String sourceDirectory )
733         throws ArchetypeTemplateProcessingException
734     {
735         for ( String template : descriptor.getSources() )
736         {
737             processTemplate( outputDirectory, context, template, descriptor.getSourceDescriptor( template ), true,
738                              packageName, sourceDirectory );
739         }
740     }
741 
742     protected void processTestSources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
743                                        String packageName, String testSourceDirectory )
744         throws ArchetypeTemplateProcessingException
745     {
746         for ( String template : descriptor.getTestSources() )
747         {
748             processTemplate( outputDirectory, context, template, descriptor.getTestSourceDescriptor( template ), true,
749                              packageName, testSourceDirectory );
750         }
751     }
752 
753     protected void processResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
754                                      String packageName )
755         throws ArchetypeTemplateProcessingException
756     {
757         for ( String template : descriptor.getResources() )
758         {
759             processTemplate( outputDirectory, context, template, descriptor.getResourceDescriptor( template ), false,
760                              packageName );
761         }
762     }
763 
764     protected void processTestResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
765                                          String packageName )
766         throws ArchetypeTemplateProcessingException
767     {
768         for ( String template : descriptor.getTestResources() )
769         {
770             processTemplate( outputDirectory, context, template, descriptor.getTestResourceDescriptor( template ),
771                              false, packageName );
772         }
773     }
774 
775     protected void processSiteResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
776                                          String packageName )
777         throws ArchetypeTemplateProcessingException
778     {
779         for ( String template : descriptor.getSiteResources() )
780         {
781             processTemplate( outputDirectory, context, template, descriptor.getSiteResourceDescriptor( template ),
782                              false, packageName );
783         }
784     }
785 
786     protected void processTemplate( String outputDirectory, Context context, String template,
787                                     TemplateDescriptor descriptor, boolean packageInFileName, String packageName,
788                                     String sourceDirectory )
789         throws ArchetypeTemplateProcessingException
790     {
791         File f;
792 
793         template = StringUtils.replace( template, "\\", "/" );
794 
795         if ( packageInFileName && packageName != null )
796         {
797             String templateFileName = StringUtils.replace( template, "/", File.separator );
798 
799             String path = packageName.replace( '.', '/' );
800 
801             String filename = FileUtils.filename( templateFileName );
802 
803             String dirname = FileUtils.dirname( templateFileName ).replace( '\\', '/' );
804 
805             sourceDirectory = sourceDirectory.replace( '\\', '/' );
806             if ( sourceDirectory.startsWith( "/" ) )
807             {
808                 sourceDirectory = sourceDirectory.substring( 1 );
809             }
810 
811             if ( !dirname.startsWith( sourceDirectory ) )
812             {
813                 throw new ArchetypeTemplateProcessingException(
814                     "Template '" + template + "' not in directory '" + sourceDirectory + "'" );
815             }
816 
817             String extraPackages = dirname.substring( sourceDirectory.length() );
818             if ( extraPackages.startsWith( "/" ) )
819             {
820                 extraPackages = extraPackages.substring( 1 );
821             }
822             if ( extraPackages.length() > 0 )
823             {
824                 path += "/" + extraPackages;
825             }
826 
827             f = new File( new File( new File( outputDirectory, sourceDirectory ), path ), filename );
828         }
829         else
830         {
831             f = new File( outputDirectory, template );
832         }
833 
834         if ( !f.getParentFile().exists() )
835         {
836             f.getParentFile().mkdirs();
837         }
838 
839         if ( descriptor.isFiltered() )
840         {
841             Writer writer = null;
842             try
843             {
844                 StringWriter stringWriter = new StringWriter();
845 
846                 template = ARCHETYPE_RESOURCES + "/" + template;
847 
848                 velocity.getEngine().mergeTemplate( template, descriptor.getEncoding(), context, stringWriter );
849 
850                 writer = new OutputStreamWriter( new FileOutputStream( f ), descriptor.getEncoding() );
851 
852                 writer.write( StringUtils.unifyLineSeparators( stringWriter.toString() ) );
853 
854                 writer.flush();
855             }
856             catch ( Exception e )
857             {
858                 throw new ArchetypeTemplateProcessingException( "Error merging velocity templates", e );
859             }
860             finally
861             {
862                 IOUtil.close( writer );
863             }
864         }
865         else
866         {
867             InputStream is = getStream( ARCHETYPE_RESOURCES + "/" + template, null );
868 
869             OutputStream fos = null;
870 
871             try
872             {
873                 fos = new FileOutputStream( f );
874 
875                 IOUtil.copy( is, fos );
876             }
877             catch ( Exception e )
878             {
879                 throw new ArchetypeTemplateProcessingException( "Error copying file", e );
880             }
881             finally
882             {
883                 IOUtil.close( fos );
884 
885                 IOUtil.close( is );
886             }
887         }
888     }
889 
890     protected void createProjectDirectoryStructure( String outputDirectory )
891     {
892     }
893 
894     private InputStream getStream( String name, ClassLoader loader )
895     {
896         if ( loader == null )
897         {
898             return Thread.currentThread().getContextClassLoader().getResourceAsStream( name );
899         }
900         return loader.getResourceAsStream( name );
901     }
902 }