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