View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.archetype.old;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  import javax.xml.parsers.ParserConfigurationException;
25  import javax.xml.transform.TransformerException;
26  
27  import java.io.File;
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.io.OutputStream;
31  import java.io.OutputStreamWriter;
32  import java.io.Reader;
33  import java.io.StringWriter;
34  import java.io.Writer;
35  import java.net.MalformedURLException;
36  import java.net.URL;
37  import java.net.URLClassLoader;
38  import java.nio.file.Files;
39  import java.util.HashMap;
40  import java.util.Iterator;
41  import java.util.Map;
42  
43  import org.apache.maven.archetype.ArchetypeGenerationRequest;
44  import org.apache.maven.archetype.common.ArchetypeArtifactManager;
45  import org.apache.maven.archetype.common.Constants;
46  import org.apache.maven.archetype.common.util.PomUtils;
47  import org.apache.maven.archetype.exception.InvalidPackaging;
48  import org.apache.maven.archetype.exception.UnknownArchetype;
49  import org.apache.maven.archetype.old.descriptor.ArchetypeDescriptor;
50  import org.apache.maven.archetype.old.descriptor.ArchetypeDescriptorBuilder;
51  import org.apache.maven.archetype.old.descriptor.TemplateDescriptor;
52  import org.apache.maven.model.Build;
53  import org.apache.maven.model.Model;
54  import org.apache.maven.model.Parent;
55  import org.apache.maven.model.Resource;
56  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
57  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
58  import org.apache.velocity.VelocityContext;
59  import org.apache.velocity.context.Context;
60  import org.codehaus.plexus.logging.AbstractLogEnabled;
61  import org.codehaus.plexus.util.FileUtils;
62  import org.codehaus.plexus.util.IOUtil;
63  import org.codehaus.plexus.util.StringUtils;
64  import org.codehaus.plexus.util.xml.XmlStreamReader;
65  import org.codehaus.plexus.util.xml.XmlStreamWriter;
66  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
67  import org.codehaus.plexus.velocity.VelocityComponent;
68  import org.xml.sax.SAXException;
69  
70  /**
71   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
72   * @version $Id$
73   */
74  @Named
75  @Singleton
76  public class DefaultOldArchetype extends AbstractLogEnabled implements OldArchetype {
77      private static final String DEFAULT_TEST_RESOURCE_DIR = "/src/test/resources";
78  
79      private static final String DEFAULT_TEST_SOURCE_DIR = "/src/test/java";
80  
81      private static final String DEFAULT_RESOURCE_DIR = "/src/main/resources";
82  
83      private static final String DEFAULT_SOURCE_DIR = "/src/main/java";
84  
85      // ----------------------------------------------------------------------
86      // Components
87      // ----------------------------------------------------------------------
88  
89      @Inject
90      private VelocityComponent velocity;
91  
92      @Inject
93      private ArchetypeArtifactManager archetypeArtifactManager;
94  
95      // ----------------------------------------------------------------------
96      // Implementation
97      // ----------------------------------------------------------------------
98  
99      // groupId = maven
100     // artifactId = maven-foo-archetype
101     // version = latest
102 
103     @Override
104     public void createArchetype(ArchetypeGenerationRequest request)
105             throws UnknownArchetype, ArchetypeDescriptorException, ArchetypeTemplateProcessingException,
106                     InvalidPackaging {
107         // ----------------------------------------------------------------------
108         // Download the archetype
109         // ----------------------------------------------------------------------
110 
111         File archetypeFile = archetypeArtifactManager.getArchetypeFile(
112                 request.getArchetypeGroupId(),
113                 request.getArchetypeArtifactId(),
114                 request.getArchetypeVersion(),
115                 request.getRemoteRepositories(),
116                 request.getRepositorySession());
117 
118         createArchetype(request, archetypeFile);
119     }
120 
121     @Override
122     @SuppressWarnings("checkstyle:MethodLength")
123     public void createArchetype(ArchetypeGenerationRequest request, File archetypeFile)
124             throws ArchetypeDescriptorException, ArchetypeTemplateProcessingException, InvalidPackaging {
125         Map<String, String> parameters = new HashMap<>();
126 
127         parameters.put("basedir", request.getOutputDirectory());
128 
129         parameters.put(Constants.PACKAGE, request.getPackage());
130 
131         parameters.put("packageName", request.getPackage());
132 
133         parameters.put(Constants.GROUP_ID, request.getGroupId());
134 
135         parameters.put(Constants.ARTIFACT_ID, request.getArtifactId());
136 
137         parameters.put(Constants.VERSION, request.getVersion());
138 
139         // ---------------------------------------------------------------------
140         // Get Logger and display all parameters used
141         // ---------------------------------------------------------------------
142         if (getLogger().isInfoEnabled()) {
143             getLogger().info("----------------------------------------------------------------------------");
144 
145             getLogger()
146                     .info("Using following parameters for creating project from Old (1.x) Archetype: "
147                             + request.getArchetypeArtifactId() + ":" + request.getArchetypeVersion());
148 
149             getLogger().info("----------------------------------------------------------------------------");
150 
151             for (Map.Entry<String, String> entry : parameters.entrySet()) {
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         URL[] urls;
171         try {
172             urls = new URL[] {archetypeFile.toURI().toURL()};
173         } catch (MalformedURLException e) {
174             throw new ArchetypeDescriptorException(e.getMessage());
175         }
176 
177         archetypeJarLoader = new URLClassLoader(urls);
178 
179         try (InputStream is = getDescriptorInputStream(archetypeJarLoader)) {
180             descriptor = builder.build(new XmlStreamReader(is));
181         } catch (IOException | XmlPullParserException e) {
182             throw new ArchetypeDescriptorException("Error reading the " + ARCHETYPE_DESCRIPTOR + " descriptor.", e);
183         }
184 
185         // ----------------------------------------------------------------------
186         //
187         // ----------------------------------------------------------------------
188 
189         String artifactId = request.getArtifactId();
190 
191         File parentPomFile = new File(request.getOutputDirectory(), ARCHETYPE_POM);
192 
193         File outputDirectoryFile;
194 
195         boolean creating;
196         File pomFile;
197         if (parentPomFile.exists() && descriptor.isAllowPartial() && artifactId == null) {
198             outputDirectoryFile = new File(request.getOutputDirectory());
199             creating = false;
200             pomFile = parentPomFile;
201         } else {
202             if (artifactId == null) {
203                 throw new ArchetypeTemplateProcessingException(
204                         "Artifact ID must be specified when creating a new project from an archetype.");
205             }
206 
207             outputDirectoryFile = new File(request.getOutputDirectory(), artifactId);
208             creating = true;
209 
210             if (outputDirectoryFile.exists()) {
211                 if (descriptor.isAllowPartial()) {
212                     creating = false;
213                 } else {
214                     throw new ArchetypeTemplateProcessingException("Directory " + outputDirectoryFile.getName()
215                             + " already exists - please run from a clean directory");
216                 }
217             }
218 
219             pomFile = new File(outputDirectoryFile, ARCHETYPE_POM);
220         }
221 
222         if (creating) {
223             if (request.getGroupId() == null) {
224                 throw new ArchetypeTemplateProcessingException(
225                         "Group ID must be specified when creating a new project from an archetype.");
226             }
227 
228             if (request.getVersion() == null) {
229                 throw new ArchetypeTemplateProcessingException(
230                         "Version must be specified when creating a new project from an archetype.");
231             }
232         }
233 
234         String outputDirectory = outputDirectoryFile.getAbsolutePath();
235 
236         String packageName = request.getPackage();
237 
238         // ----------------------------------------------------------------------
239         // Set up the Velocity context
240         // ----------------------------------------------------------------------
241 
242         Context context = new VelocityContext();
243 
244         context.put(Constants.PACKAGE, packageName);
245 
246         for (Map.Entry<String, String> entry : parameters.entrySet()) {
247             context.put(entry.getKey(), entry.getValue());
248         }
249 
250         // ----------------------------------------------------------------------
251         // Process the templates
252         // ----------------------------------------------------------------------
253 
254         ClassLoader old = Thread.currentThread().getContextClassLoader();
255 
256         Thread.currentThread().setContextClassLoader(archetypeJarLoader);
257 
258         Model parentModel = null;
259         if (creating) {
260             if (parentPomFile.exists()) {
261                 try (Reader fileReader = new XmlStreamReader(parentPomFile)) {
262                     MavenXpp3Reader reader = new MavenXpp3Reader();
263                     parentModel = reader.read(fileReader);
264                     if (!"pom".equals(parentModel.getPackaging())) {
265                         throw new ArchetypeTemplateProcessingException(
266                                 "Unable to add module to the current project as it is not of packaging type 'pom'");
267                     }
268                 } catch (IOException | XmlPullParserException e) {
269                     throw new ArchetypeTemplateProcessingException("Unable to read parent POM", e);
270                 }
271                 parentModel.getModules().add(artifactId);
272             }
273         }
274 
275         try {
276             processTemplates(pomFile, outputDirectory, context, descriptor, packageName, parentModel);
277         } catch (IOException e) {
278             throw new ArchetypeTemplateProcessingException("Unable to process template", e);
279         } finally {
280             Thread.currentThread().setContextClassLoader(old);
281         }
282 
283         if (parentModel != null) {
284             /*
285                     // TODO: would be nice to just write out with the xpp3 writer again, except that it loses a bunch of info and
286                     // reformats, so the module is just baked in as a string instead.
287                         FileWriter fileWriter = null;
288 
289                         try
290                         {
291                             fileWriter = new FileWriter( parentPomFile );
292 
293                             MavenXpp3Writer writer = new MavenXpp3Writer();
294                             writer.write( fileWriter, parentModel );
295                         }
296                         catch ( IOException e )
297                         {
298                             throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
299                         }
300                         finally
301                         {
302                             IOUtil.close( fileWriter );
303                         }
304             */
305 
306             boolean added;
307             StringWriter w = new StringWriter();
308             try (Reader fileReader = new XmlStreamReader(parentPomFile)) {
309                 added = addModuleToParentPom(artifactId, fileReader, w);
310             } catch (IOException | SAXException | ParserConfigurationException | TransformerException e) {
311                 throw new ArchetypeTemplateProcessingException("Unable to rewrite parent POM", e);
312             }
313 
314             if (added) {
315                 try (Writer out = new XmlStreamWriter(parentPomFile)) {
316                     IOUtil.copy(w.toString(), out);
317                 } catch (IOException e) {
318                     throw new ArchetypeTemplateProcessingException("Unable to rewrite parent POM", e);
319                 }
320             }
321         }
322 
323         // ----------------------------------------------------------------------
324         // Log message on OldArchetype creation
325         // ----------------------------------------------------------------------
326         if (getLogger().isInfoEnabled()) {
327             getLogger().info("project created from Old (1.x) Archetype in dir: " + outputDirectory);
328         }
329     }
330 
331     private InputStream getDescriptorInputStream(ClassLoader archetypeJarLoader) throws ArchetypeDescriptorException {
332         InputStream is = getStream(ARCHETYPE_DESCRIPTOR, archetypeJarLoader);
333 
334         if (is == null) {
335             is = getStream(ARCHETYPE_OLD_DESCRIPTOR, archetypeJarLoader);
336         }
337 
338         if (is == null) {
339             throw new ArchetypeDescriptorException("The " + ARCHETYPE_DESCRIPTOR + " descriptor cannot be found.");
340         }
341 
342         return is;
343     }
344 
345     static boolean addModuleToParentPom(String artifactId, Reader fileReader, Writer fileWriter)
346             throws ArchetypeTemplateProcessingException, InvalidPackaging, IOException, ParserConfigurationException,
347                     SAXException, TransformerException {
348         return PomUtils.addNewModule(artifactId, fileReader, fileWriter);
349     }
350 
351     @SuppressWarnings("checkstyle:MethodLength")
352     private void processTemplates(
353             File pomFile,
354             String outputDirectory,
355             Context context,
356             ArchetypeDescriptor descriptor,
357             String packageName,
358             Model parentModel)
359             throws ArchetypeTemplateProcessingException, IOException {
360         if (!pomFile.exists()) {
361             processTemplate(outputDirectory, context, ARCHETYPE_POM, new TemplateDescriptor(), false, null);
362         }
363 
364         // ---------------------------------------------------------------------
365         // Model generated for the new archetype, so process it now
366         // ---------------------------------------------------------------------
367 
368         Model generatedModel;
369 
370         try (Reader pomReader = new XmlStreamReader(pomFile)) {
371             MavenXpp3Reader reader = new MavenXpp3Reader();
372 
373             generatedModel = reader.read(pomReader);
374         } catch (IOException | XmlPullParserException e) {
375             throw new ArchetypeTemplateProcessingException("Error reading POM", e);
376         }
377 
378         if (parentModel != null) {
379             Parent parent = new Parent();
380             parent.setGroupId(parentModel.getGroupId());
381             if (parent.getGroupId() == null) {
382                 parent.setGroupId(parentModel.getParent().getGroupId());
383             }
384             parent.setArtifactId(parentModel.getArtifactId());
385             parent.setVersion(parentModel.getVersion());
386             if (parent.getVersion() == null) {
387                 parent.setVersion(parentModel.getParent().getVersion());
388             }
389             generatedModel.setParent(parent);
390 
391             try (Writer pomWriter = new XmlStreamWriter(pomFile)) {
392                 MavenXpp3Writer writer = new MavenXpp3Writer();
393                 writer.write(pomWriter, generatedModel);
394             } catch (IOException e) {
395                 throw new ArchetypeTemplateProcessingException("Error rewriting POM", e);
396             }
397         }
398 
399         // XXX: Following POM processing block may be a candidate for
400         // refactoring out into service methods or moving to
401         // createProjectDirectoryStructure(outputDirectory)
402         Build build = generatedModel.getBuild();
403 
404         boolean overrideSrcDir = false;
405 
406         boolean overrideResourceDir = false;
407 
408         boolean overrideTestSrcDir = false;
409 
410         boolean overrideTestResourceDir = false;
411 
412         boolean foundBuildElement = build != null;
413 
414         if (getLogger().isDebugEnabled()) {
415             getLogger()
416                     .debug(
417                             "********************* Debug info for resources created from generated Model ***********************");
418             getLogger().debug("Was build element found in generated POM?: " + foundBuildElement);
419         }
420 
421         // create source directory if specified in POM
422         if (foundBuildElement && null != build.getSourceDirectory()) {
423             getLogger().debug("Overriding default source directory ");
424 
425             overrideSrcDir = true;
426 
427             String srcDirectory = build.getSourceDirectory();
428 
429             srcDirectory = StringUtils.replace(srcDirectory, "\\", "/");
430 
431             FileUtils.mkdir(getOutputDirectory(outputDirectory, srcDirectory));
432         }
433 
434         // create script source directory if specified in POM
435         if (foundBuildElement && null != build.getScriptSourceDirectory()) {
436             getLogger().debug("Overriding default script source directory ");
437 
438             String scriptSourceDirectory = build.getScriptSourceDirectory();
439 
440             scriptSourceDirectory = StringUtils.replace(scriptSourceDirectory, "\\", "/");
441 
442             FileUtils.mkdir(getOutputDirectory(outputDirectory, scriptSourceDirectory));
443         }
444 
445         // create resource director(y/ies) if specified in POM
446         if (foundBuildElement && !build.getResources().isEmpty()) {
447             getLogger().debug("Overriding default resource directory ");
448 
449             overrideResourceDir = true;
450 
451             Iterator<?> resourceItr = build.getResources().iterator();
452 
453             while (resourceItr.hasNext()) {
454                 Resource resource = (Resource) resourceItr.next();
455 
456                 String resourceDirectory = resource.getDirectory();
457 
458                 resourceDirectory = StringUtils.replace(resourceDirectory, "\\", "/");
459 
460                 FileUtils.mkdir(getOutputDirectory(outputDirectory, resourceDirectory));
461             }
462         }
463         // create test source directory if specified in POM
464         if (foundBuildElement && null != build.getTestSourceDirectory()) {
465             getLogger().debug("Overriding default test directory ");
466 
467             overrideTestSrcDir = true;
468 
469             String testDirectory = build.getTestSourceDirectory();
470 
471             testDirectory = StringUtils.replace(testDirectory, "\\", "/");
472 
473             FileUtils.mkdir(getOutputDirectory(outputDirectory, testDirectory));
474         }
475 
476         // create test resource directory if specified in POM
477         if (foundBuildElement && !build.getTestResources().isEmpty()) {
478             getLogger().debug("Overriding default test resource directory ");
479 
480             overrideTestResourceDir = true;
481 
482             Iterator<?> testResourceItr = build.getTestResources().iterator();
483 
484             while (testResourceItr.hasNext()) {
485                 Resource resource = (Resource) testResourceItr.next();
486 
487                 String testResourceDirectory = resource.getDirectory();
488 
489                 testResourceDirectory = StringUtils.replace(testResourceDirectory, "\\", "/");
490 
491                 FileUtils.mkdir(getOutputDirectory(outputDirectory, testResourceDirectory));
492             }
493         }
494 
495         getLogger()
496                 .debug(
497                         "********************* End of debug info from resources from generated POM ***********************");
498 
499         // ----------------------------------------------------------------------
500         // Main
501         // ----------------------------------------------------------------------
502 
503         if (!descriptor.getSources().isEmpty()) {
504             if (!overrideSrcDir) {
505                 FileUtils.mkdir(outputDirectory + DEFAULT_SOURCE_DIR);
506                 processSources(outputDirectory, context, descriptor, packageName, DEFAULT_SOURCE_DIR);
507             } else {
508                 processSources(outputDirectory, context, descriptor, packageName, build.getSourceDirectory());
509             }
510         }
511 
512         if (!descriptor.getResources().isEmpty()) {
513             if (!overrideResourceDir) {
514                 FileUtils.mkdir(outputDirectory + DEFAULT_RESOURCE_DIR);
515             }
516             processResources(outputDirectory, context, descriptor, packageName);
517         }
518 
519         // ----------------------------------------------------------------------
520         // Test
521         // ----------------------------------------------------------------------
522 
523         if (!descriptor.getTestSources().isEmpty()) {
524             if (!overrideTestSrcDir) {
525                 FileUtils.mkdir(outputDirectory + DEFAULT_TEST_SOURCE_DIR);
526                 processTestSources(outputDirectory, context, descriptor, packageName, DEFAULT_TEST_SOURCE_DIR);
527             } else {
528                 processTestSources(outputDirectory, context, descriptor, packageName, build.getTestSourceDirectory());
529             }
530         }
531 
532         if (!descriptor.getTestResources().isEmpty()) {
533             if (!overrideTestResourceDir) {
534                 FileUtils.mkdir(outputDirectory + DEFAULT_TEST_RESOURCE_DIR);
535             }
536             processTestResources(outputDirectory, context, descriptor, packageName);
537         }
538 
539         // ----------------------------------------------------------------------
540         // Site
541         // ----------------------------------------------------------------------
542 
543         if (!descriptor.getSiteResources().isEmpty()) {
544             processSiteResources(outputDirectory, context, descriptor, packageName);
545         }
546     }
547 
548     private void processTemplate(
549             String outputDirectory,
550             Context context,
551             String template,
552             TemplateDescriptor descriptor,
553             boolean packageInFileName,
554             String packageName)
555             throws ArchetypeTemplateProcessingException, IOException {
556         processTemplate(outputDirectory, context, template, descriptor, packageInFileName, packageName, null);
557     }
558 
559     private String getOutputDirectory(String outputDirectory, String testResourceDirectory) {
560         return outputDirectory
561                 + (testResourceDirectory.startsWith("/") ? testResourceDirectory : "/" + testResourceDirectory);
562     }
563 
564     // ----------------------------------------------------------------------
565     //
566     // ----------------------------------------------------------------------
567 
568     protected void processSources(
569             String outputDirectory,
570             Context context,
571             ArchetypeDescriptor descriptor,
572             String packageName,
573             String sourceDirectory)
574             throws ArchetypeTemplateProcessingException, IOException {
575         for (String template : descriptor.getSources()) {
576             processTemplate(
577                     outputDirectory,
578                     context,
579                     template,
580                     descriptor.getSourceDescriptor(template),
581                     true,
582                     packageName,
583                     sourceDirectory);
584         }
585     }
586 
587     protected void processTestSources(
588             String outputDirectory,
589             Context context,
590             ArchetypeDescriptor descriptor,
591             String packageName,
592             String testSourceDirectory)
593             throws ArchetypeTemplateProcessingException, IOException {
594         for (String template : descriptor.getTestSources()) {
595             processTemplate(
596                     outputDirectory,
597                     context,
598                     template,
599                     descriptor.getTestSourceDescriptor(template),
600                     true,
601                     packageName,
602                     testSourceDirectory);
603         }
604     }
605 
606     protected void processResources(
607             String outputDirectory, Context context, ArchetypeDescriptor descriptor, String packageName)
608             throws IOException, ArchetypeTemplateProcessingException {
609         for (String template : descriptor.getResources()) {
610             processTemplate(
611                     outputDirectory, context, template, descriptor.getResourceDescriptor(template), false, packageName);
612         }
613     }
614 
615     protected void processTestResources(
616             String outputDirectory, Context context, ArchetypeDescriptor descriptor, String packageName)
617             throws IOException, ArchetypeTemplateProcessingException {
618         for (String template : descriptor.getTestResources()) {
619             processTemplate(
620                     outputDirectory,
621                     context,
622                     template,
623                     descriptor.getTestResourceDescriptor(template),
624                     false,
625                     packageName);
626         }
627     }
628 
629     protected void processSiteResources(
630             String outputDirectory, Context context, ArchetypeDescriptor descriptor, String packageName)
631             throws IOException, ArchetypeTemplateProcessingException {
632         for (String template : descriptor.getSiteResources()) {
633             processTemplate(
634                     outputDirectory,
635                     context,
636                     template,
637                     descriptor.getSiteResourceDescriptor(template),
638                     false,
639                     packageName);
640         }
641     }
642 
643     protected void processTemplate(
644             String outputDirectory,
645             Context context,
646             String template,
647             TemplateDescriptor descriptor,
648             boolean packageInFileName,
649             String packageName,
650             String sourceDirectory)
651             throws IOException, ArchetypeTemplateProcessingException {
652         File f;
653 
654         template = StringUtils.replace(template, "\\", "/");
655 
656         if (packageInFileName && packageName != null) {
657             String templateFileName = StringUtils.replace(template, "/", File.separator);
658 
659             String path = packageName.replace('.', '/');
660 
661             String filename = FileUtils.filename(templateFileName);
662 
663             String dirname = FileUtils.dirname(templateFileName).replace('\\', '/');
664 
665             sourceDirectory = sourceDirectory.replace('\\', '/');
666             if (sourceDirectory.startsWith("/")) {
667                 sourceDirectory = sourceDirectory.substring(1);
668             }
669 
670             if (!dirname.startsWith(sourceDirectory)) {
671                 throw new ArchetypeTemplateProcessingException(
672                         "Template '" + template + "' not in directory '" + sourceDirectory + "'");
673             }
674 
675             String extraPackages = dirname.substring(sourceDirectory.length());
676             if (extraPackages.startsWith("/")) {
677                 extraPackages = extraPackages.substring(1);
678             }
679             if (!extraPackages.isEmpty()) {
680                 path += "/" + extraPackages;
681             }
682 
683             f = new File(new File(new File(outputDirectory, sourceDirectory), path), filename);
684         } else {
685             f = new File(outputDirectory, template);
686         }
687 
688         if (!f.getParentFile().exists()) {
689             f.getParentFile().mkdirs();
690         }
691 
692         if (!f.exists() && !f.createNewFile()) {
693             getLogger().warn("Could not create new file \"" + f.getPath() + "\" or the file already exists.");
694         }
695 
696         if (descriptor.isFiltered()) {
697             try (Writer writer = new OutputStreamWriter(Files.newOutputStream(f.toPath()), descriptor.getEncoding())) {
698                 StringWriter stringWriter = new StringWriter();
699 
700                 template = ARCHETYPE_RESOURCES + "/" + template;
701 
702                 velocity.getEngine().mergeTemplate(template, descriptor.getEncoding(), context, stringWriter);
703 
704                 writer.write(StringUtils.unifyLineSeparators(stringWriter.toString()));
705             } catch (Exception e) {
706                 throw new ArchetypeTemplateProcessingException("Error merging velocity templates", e);
707             }
708         } else {
709             try (InputStream is = getStream(ARCHETYPE_RESOURCES + "/" + template, null);
710                     OutputStream fos = Files.newOutputStream(f.toPath())) {
711                 IOUtil.copy(is, fos);
712             } catch (Exception e) {
713                 throw new ArchetypeTemplateProcessingException("Error copying file", e);
714             }
715         }
716     }
717 
718     protected void createProjectDirectoryStructure(String outputDirectory) {}
719 
720     private InputStream getStream(String name, ClassLoader loader) {
721         if (loader == null) {
722             return Thread.currentThread().getContextClassLoader().getResourceAsStream(name);
723         }
724         return loader.getResourceAsStream(name);
725     }
726 }