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.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 finally
309 {
310 Thread.currentThread().setContextClassLoader( old );
311 }
312
313 if ( parentModel != null )
314 {
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337 boolean added;
338 StringWriter w = new StringWriter();
339 try ( Reader fileReader = ReaderFactory.newXmlReader( parentPomFile ) )
340 {
341 added = addModuleToParentPom( artifactId, fileReader, w );
342 }
343 catch ( IOException | SAXException | ParserConfigurationException | TransformerException e )
344 {
345 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
346 }
347
348 if ( added )
349 {
350 try ( Writer out = WriterFactory.newXmlWriter( parentPomFile ) )
351 {
352 IOUtil.copy( w.toString(), out );
353 }
354 catch ( IOException e )
355 {
356 throw new ArchetypeTemplateProcessingException( "Unable to rewrite parent POM", e );
357 }
358 }
359 }
360
361
362
363
364 if ( getLogger().isInfoEnabled() )
365 {
366 getLogger().info( "project created from Old (1.x) Archetype in dir: " + outputDirectory );
367 }
368
369 }
370
371 private InputStream getDescriptorInputStream( ClassLoader archetypeJarLoader ) throws ArchetypeDescriptorException
372 {
373 InputStream is = getStream( ARCHETYPE_DESCRIPTOR, archetypeJarLoader );
374
375 if ( is == null )
376 {
377 is = getStream( ARCHETYPE_OLD_DESCRIPTOR, archetypeJarLoader );
378 }
379
380 if ( is == null )
381 {
382 throw new ArchetypeDescriptorException( "The " + ARCHETYPE_DESCRIPTOR
383 + " descriptor cannot be found." );
384 }
385
386 return is;
387 }
388
389 static boolean addModuleToParentPom( String artifactId, Reader fileReader, Writer fileWriter )
390 throws ArchetypeTemplateProcessingException, InvalidPackaging, IOException, ParserConfigurationException,
391 SAXException, TransformerException
392 {
393 return PomUtils.addNewModule( artifactId, fileReader, fileWriter );
394 }
395
396 private void processTemplates( File pomFile, String outputDirectory, Context context,
397 ArchetypeDescriptor descriptor, String packageName, Model parentModel )
398 throws ArchetypeTemplateProcessingException
399 {
400 if ( !pomFile.exists() )
401 {
402 processTemplate( outputDirectory, context, ARCHETYPE_POM, new TemplateDescriptor(), false, null );
403 }
404
405
406
407
408
409 Model generatedModel;
410
411 try ( Reader pomReader = ReaderFactory.newXmlReader( pomFile ) )
412 {
413 MavenXpp3Reader reader = new MavenXpp3Reader();
414
415 generatedModel = reader.read( pomReader );
416 }
417 catch ( IOException | XmlPullParserException e )
418 {
419 throw new ArchetypeTemplateProcessingException( "Error reading POM", e );
420 }
421
422 if ( parentModel != null )
423 {
424 Parent parent = new Parent();
425 parent.setGroupId( parentModel.getGroupId() );
426 if ( parent.getGroupId() == null )
427 {
428 parent.setGroupId( parentModel.getParent().getGroupId() );
429 }
430 parent.setArtifactId( parentModel.getArtifactId() );
431 parent.setVersion( parentModel.getVersion() );
432 if ( parent.getVersion() == null )
433 {
434 parent.setVersion( parentModel.getParent().getVersion() );
435 }
436 generatedModel.setParent( parent );
437
438 try ( Writer pomWriter = WriterFactory.newXmlWriter( pomFile ) )
439 {
440 MavenXpp3Writer writer = new MavenXpp3Writer();
441 writer.write( pomWriter, generatedModel );
442 }
443 catch ( IOException e )
444 {
445 throw new ArchetypeTemplateProcessingException( "Error rewriting POM", e );
446 }
447 }
448
449
450
451
452 Build build = generatedModel.getBuild();
453
454 boolean overrideSrcDir = false;
455
456 boolean overrideResourceDir = false;
457
458 boolean overrideTestSrcDir = false;
459
460 boolean overrideTestResourceDir = false;
461
462 boolean foundBuildElement = build != null;
463
464 if ( getLogger().isDebugEnabled() )
465 {
466 getLogger().debug(
467 "********************* Debug info for resources created from generated Model ***********************" );
468 getLogger().debug( "Was build element found in generated POM?: " + foundBuildElement );
469 }
470
471
472 if ( foundBuildElement && null != build.getSourceDirectory() )
473 {
474 getLogger().debug( "Overriding default source directory " );
475
476 overrideSrcDir = true;
477
478 String srcDirectory = build.getSourceDirectory();
479
480 srcDirectory = StringUtils.replace( srcDirectory, "\\", "/" );
481
482 FileUtils.mkdir( getOutputDirectory( outputDirectory, srcDirectory ) );
483 }
484
485
486 if ( foundBuildElement && null != build.getScriptSourceDirectory() )
487 {
488 getLogger().debug( "Overriding default script source directory " );
489
490 String scriptSourceDirectory = build.getScriptSourceDirectory();
491
492 scriptSourceDirectory = StringUtils.replace( scriptSourceDirectory, "\\", "/" );
493
494 FileUtils.mkdir( getOutputDirectory( outputDirectory, scriptSourceDirectory ) );
495 }
496
497
498 if ( foundBuildElement && build.getResources().size() > 0 )
499 {
500 getLogger().debug( "Overriding default resource directory " );
501
502 overrideResourceDir = true;
503
504 Iterator<?> resourceItr = build.getResources().iterator();
505
506 while ( resourceItr.hasNext() )
507 {
508 Resource resource = (Resource) resourceItr.next();
509
510 String resourceDirectory = resource.getDirectory();
511
512 resourceDirectory = StringUtils.replace( resourceDirectory, "\\", "/" );
513
514 FileUtils.mkdir( getOutputDirectory( outputDirectory, resourceDirectory ) );
515 }
516 }
517
518 if ( foundBuildElement && null != build.getTestSourceDirectory() )
519 {
520 getLogger().debug( "Overriding default test directory " );
521
522 overrideTestSrcDir = true;
523
524 String testDirectory = build.getTestSourceDirectory();
525
526 testDirectory = StringUtils.replace( testDirectory, "\\", "/" );
527
528 FileUtils.mkdir( getOutputDirectory( outputDirectory, testDirectory ) );
529 }
530
531
532 if ( foundBuildElement && build.getTestResources().size() > 0 )
533 {
534 getLogger().debug( "Overriding default test resource directory " );
535
536 overrideTestResourceDir = true;
537
538 Iterator<?> testResourceItr = build.getTestResources().iterator();
539
540 while ( testResourceItr.hasNext() )
541 {
542 Resource resource = (Resource) testResourceItr.next();
543
544 String testResourceDirectory = resource.getDirectory();
545
546 testResourceDirectory = StringUtils.replace( testResourceDirectory, "\\", "/" );
547
548 FileUtils.mkdir( getOutputDirectory( outputDirectory, testResourceDirectory ) );
549 }
550 }
551
552 getLogger().debug(
553 "********************* End of debug info from resources from generated POM ***********************" );
554
555
556
557
558
559 if ( descriptor.getSources().size() > 0 )
560 {
561 if ( !overrideSrcDir )
562 {
563 FileUtils.mkdir( outputDirectory + DEFAULT_SOURCE_DIR );
564 processSources( outputDirectory, context, descriptor, packageName, DEFAULT_SOURCE_DIR );
565 }
566 else
567 {
568 processSources( outputDirectory, context, descriptor, packageName, build.getSourceDirectory() );
569 }
570 }
571
572 if ( descriptor.getResources().size() > 0 )
573 {
574 if ( !overrideResourceDir )
575 {
576 FileUtils.mkdir( outputDirectory + DEFAULT_RESOURCE_DIR );
577 }
578 processResources( outputDirectory, context, descriptor, packageName );
579 }
580
581
582
583
584
585 if ( descriptor.getTestSources().size() > 0 )
586 {
587 if ( !overrideTestSrcDir )
588 {
589 FileUtils.mkdir( outputDirectory + DEFAULT_TEST_SOURCE_DIR );
590 processTestSources( outputDirectory, context, descriptor, packageName, DEFAULT_TEST_SOURCE_DIR );
591 }
592 else
593 {
594 processTestSources( outputDirectory, context, descriptor, packageName, build.getTestSourceDirectory() );
595 }
596 }
597
598 if ( descriptor.getTestResources().size() > 0 )
599 {
600 if ( !overrideTestResourceDir )
601 {
602 FileUtils.mkdir( outputDirectory + DEFAULT_TEST_RESOURCE_DIR );
603 }
604 processTestResources( outputDirectory, context, descriptor, packageName );
605 }
606
607
608
609
610
611 if ( descriptor.getSiteResources().size() > 0 )
612 {
613 processSiteResources( outputDirectory, context, descriptor, packageName );
614 }
615 }
616
617 private void processTemplate( String outputDirectory, Context context, String template,
618 TemplateDescriptor descriptor, boolean packageInFileName, String packageName )
619 throws ArchetypeTemplateProcessingException
620 {
621 processTemplate( outputDirectory, context, template, descriptor, packageInFileName, packageName, null );
622 }
623
624 private String getOutputDirectory( String outputDirectory, String testResourceDirectory )
625 {
626 return outputDirectory
627 + ( testResourceDirectory.startsWith( "/" ) ? testResourceDirectory : "/" + testResourceDirectory );
628 }
629
630
631
632
633
634 protected void processSources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
635 String packageName, String sourceDirectory )
636 throws ArchetypeTemplateProcessingException
637 {
638 for ( String template : descriptor.getSources() )
639 {
640 processTemplate( outputDirectory, context, template, descriptor.getSourceDescriptor( template ), true,
641 packageName, sourceDirectory );
642 }
643 }
644
645 protected void processTestSources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
646 String packageName, String testSourceDirectory )
647 throws ArchetypeTemplateProcessingException
648 {
649 for ( String template : descriptor.getTestSources() )
650 {
651 processTemplate( outputDirectory, context, template, descriptor.getTestSourceDescriptor( template ), true,
652 packageName, testSourceDirectory );
653 }
654 }
655
656 protected void processResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
657 String packageName )
658 throws ArchetypeTemplateProcessingException
659 {
660 for ( String template : descriptor.getResources() )
661 {
662 processTemplate( outputDirectory, context, template, descriptor.getResourceDescriptor( template ), false,
663 packageName );
664 }
665 }
666
667 protected void processTestResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
668 String packageName )
669 throws ArchetypeTemplateProcessingException
670 {
671 for ( String template : descriptor.getTestResources() )
672 {
673 processTemplate( outputDirectory, context, template, descriptor.getTestResourceDescriptor( template ),
674 false, packageName );
675 }
676 }
677
678 protected void processSiteResources( String outputDirectory, Context context, ArchetypeDescriptor descriptor,
679 String packageName )
680 throws ArchetypeTemplateProcessingException
681 {
682 for ( String template : descriptor.getSiteResources() )
683 {
684 processTemplate( outputDirectory, context, template, descriptor.getSiteResourceDescriptor( template ),
685 false, packageName );
686 }
687 }
688
689 protected void processTemplate( String outputDirectory, Context context, String template,
690 TemplateDescriptor descriptor, boolean packageInFileName, String packageName,
691 String sourceDirectory )
692 throws ArchetypeTemplateProcessingException
693 {
694 File f;
695
696 template = StringUtils.replace( template, "\\", "/" );
697
698 if ( packageInFileName && packageName != null )
699 {
700 String templateFileName = StringUtils.replace( template, "/", File.separator );
701
702 String path = packageName.replace( '.', '/' );
703
704 String filename = FileUtils.filename( templateFileName );
705
706 String dirname = FileUtils.dirname( templateFileName ).replace( '\\', '/' );
707
708 sourceDirectory = sourceDirectory.replace( '\\', '/' );
709 if ( sourceDirectory.startsWith( "/" ) )
710 {
711 sourceDirectory = sourceDirectory.substring( 1 );
712 }
713
714 if ( !dirname.startsWith( sourceDirectory ) )
715 {
716 throw new ArchetypeTemplateProcessingException(
717 "Template '" + template + "' not in directory '" + sourceDirectory + "'" );
718 }
719
720 String extraPackages = dirname.substring( sourceDirectory.length() );
721 if ( extraPackages.startsWith( "/" ) )
722 {
723 extraPackages = extraPackages.substring( 1 );
724 }
725 if ( extraPackages.length() > 0 )
726 {
727 path += "/" + extraPackages;
728 }
729
730 f = new File( new File( new File( outputDirectory, sourceDirectory ), path ), filename );
731 }
732 else
733 {
734 f = new File( outputDirectory, template );
735 }
736
737 if ( !f.getParentFile().exists() )
738 {
739 f.getParentFile().mkdirs();
740 }
741
742 if ( descriptor.isFiltered() )
743 {
744 try ( Writer writer = new OutputStreamWriter( new FileOutputStream( f ), descriptor.getEncoding() ) )
745 {
746 StringWriter stringWriter = new StringWriter();
747
748 template = ARCHETYPE_RESOURCES + "/" + template;
749
750 velocity.getEngine().mergeTemplate( template, descriptor.getEncoding(), context, stringWriter );
751
752 writer.write( StringUtils.unifyLineSeparators( stringWriter.toString() ) );
753
754 writer.flush();
755 }
756 catch ( Exception e )
757 {
758 throw new ArchetypeTemplateProcessingException( "Error merging velocity templates", e );
759 }
760 }
761 else
762 {
763 try ( InputStream is = getStream( ARCHETYPE_RESOURCES + "/" + template, null );
764 OutputStream fos = new FileOutputStream( f ) )
765 {
766 IOUtil.copy( is, fos );
767 }
768 catch ( Exception e )
769 {
770 throw new ArchetypeTemplateProcessingException( "Error copying file", e );
771 }
772 }
773 }
774
775 protected void createProjectDirectoryStructure( String outputDirectory )
776 {
777 }
778
779 private InputStream getStream( String name, ClassLoader loader )
780 {
781 if ( loader == null )
782 {
783 return Thread.currentThread().getContextClassLoader().getResourceAsStream( name );
784 }
785 return loader.getResourceAsStream( name );
786 }
787 }