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