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.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
72
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
87
88
89 @Inject
90 private VelocityComponent velocity;
91
92 @Inject
93 private ArchetypeArtifactManager archetypeArtifactManager;
94
95
96
97
98
99
100
101
102
103 @Override
104 public void createArchetype(ArchetypeGenerationRequest request)
105 throws UnknownArchetype, ArchetypeDescriptorException, ArchetypeTemplateProcessingException,
106 InvalidPackaging {
107
108
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
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
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
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
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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
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
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
400
401
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
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
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
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
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
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
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
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
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 }