View Javadoc
1   package org.apache.maven.archetype.ui.generation;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.archetype.ArchetypeGenerationRequest;
23  import org.apache.maven.archetype.common.ArchetypeArtifactManager;
24  import org.apache.maven.archetype.common.Constants;
25  import org.apache.maven.archetype.exception.ArchetypeGenerationConfigurationFailure;
26  import org.apache.maven.archetype.exception.ArchetypeNotConfigured;
27  import org.apache.maven.archetype.exception.ArchetypeNotDefined;
28  import org.apache.maven.archetype.exception.UnknownArchetype;
29  import org.apache.maven.archetype.old.OldArchetype;
30  import org.apache.maven.archetype.ui.ArchetypeConfiguration;
31  import org.apache.maven.archetype.ui.ArchetypeDefinition;
32  import org.apache.maven.archetype.ui.ArchetypeFactory;
33  import org.apache.maven.artifact.repository.ArtifactRepository;
34  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
35  import org.apache.maven.artifact.repository.MavenArtifactRepository;
36  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
37  import org.apache.velocity.VelocityContext;
38  import org.apache.velocity.app.Velocity;
39  import org.apache.velocity.context.Context;
40  import org.codehaus.plexus.component.annotations.Component;
41  import org.codehaus.plexus.component.annotations.Requirement;
42  import org.codehaus.plexus.components.interactivity.PrompterException;
43  import org.codehaus.plexus.logging.AbstractLogEnabled;
44  import org.codehaus.plexus.util.IOUtil;
45  import org.codehaus.plexus.util.StringUtils;
46  
47  import java.io.IOException;
48  import java.io.StringWriter;
49  import java.util.ArrayList;
50  import java.util.Collections;
51  import java.util.Comparator;
52  import java.util.List;
53  import java.util.Properties;
54  
55  // TODO: this seems to have more responsibilities than just a configurator
56  @Component( role = ArchetypeGenerationConfigurator.class, hint = "default" )
57  public class DefaultArchetypeGenerationConfigurator
58      extends AbstractLogEnabled
59      implements ArchetypeGenerationConfigurator
60  {
61      @Requirement
62      OldArchetype oldArchetype;
63  
64      @Requirement
65      private ArchetypeArtifactManager archetypeArtifactManager;
66  
67      @Requirement
68      private ArchetypeFactory archetypeFactory;
69  
70      @Requirement
71      private ArchetypeGenerationQueryer archetypeGenerationQueryer;
72  
73      /**
74       * Determines whether the layout is legacy or not.
75       */
76      @Requirement
77      private ArtifactRepositoryLayout defaultArtifactRepositoryLayout;
78  
79      public void setArchetypeArtifactManager( ArchetypeArtifactManager archetypeArtifactManager )
80      {
81          this.archetypeArtifactManager = archetypeArtifactManager;
82      }
83  
84      public void configureArchetype( ArchetypeGenerationRequest request, Boolean interactiveMode,
85                                      Properties executionProperties )
86          throws ArchetypeNotDefined, UnknownArchetype, ArchetypeNotConfigured, IOException, PrompterException,
87          ArchetypeGenerationConfigurationFailure
88      {
89          ArtifactRepository localRepository = request.getLocalRepository();
90  
91          ArtifactRepository archetypeRepository = null;
92  
93          List<ArtifactRepository> repositories = new ArrayList<ArtifactRepository>();
94  
95          Properties properties = new Properties( executionProperties );
96  
97          ArchetypeDefinition ad = new ArchetypeDefinition( request );
98  
99          if ( !ad.isDefined() )
100         {
101             if ( !interactiveMode.booleanValue() )
102             {
103                 throw new ArchetypeNotDefined( "No archetype was chosen" );
104             }
105             else
106             {
107                 throw new ArchetypeNotDefined( "The archetype is not defined" );
108             }
109         }
110         if ( request.getArchetypeRepository() != null )
111         {
112             archetypeRepository = createRepository( request.getArchetypeRepository(),
113                                                                              ad.getArtifactId() + "-repo" );
114             repositories.add( archetypeRepository );
115         }
116         if ( request.getRemoteArtifactRepositories() != null )
117         {
118             repositories.addAll( request.getRemoteArtifactRepositories() );
119         }
120 
121         if ( !archetypeArtifactManager.exists( ad.getGroupId(), ad.getArtifactId(), ad.getVersion(),
122                                                archetypeRepository, localRepository, repositories,
123                                                request.getProjectBuildingRequest() ) )
124         {
125             throw new UnknownArchetype( "The desired archetype does not exist (" + ad.getGroupId() + ":"
126                 + ad.getArtifactId() + ":" + ad.getVersion() + ")" );
127         }
128 
129         request.setArchetypeVersion( ad.getVersion() );
130 
131         ArchetypeConfiguration archetypeConfiguration;
132 
133         if ( archetypeArtifactManager.isFileSetArchetype( ad.getGroupId(), ad.getArtifactId(), ad.getVersion(),
134                                                           archetypeRepository, localRepository, repositories,
135                                                           request.getProjectBuildingRequest() ) )
136         {
137             org.apache.maven.archetype.metadata.ArchetypeDescriptor archetypeDescriptor =
138                 archetypeArtifactManager.getFileSetArchetypeDescriptor( ad.getGroupId(), ad.getArtifactId(),
139                                                                         ad.getVersion(), archetypeRepository,
140                                                                         localRepository, repositories,
141                                                                         request.getProjectBuildingRequest() );
142 
143             archetypeConfiguration = archetypeFactory.createArchetypeConfiguration( archetypeDescriptor, properties );
144         }
145         else if ( archetypeArtifactManager.isOldArchetype( ad.getGroupId(), ad.getArtifactId(), ad.getVersion(),
146                                                            archetypeRepository, localRepository, repositories,
147                                                            request.getProjectBuildingRequest() ) )
148         {
149             org.apache.maven.archetype.old.descriptor.ArchetypeDescriptor archetypeDescriptor =
150                 archetypeArtifactManager.getOldArchetypeDescriptor( ad.getGroupId(), ad.getArtifactId(),
151                                                                     ad.getVersion(), archetypeRepository,
152                                                                     localRepository, repositories,
153                                                                     request.getProjectBuildingRequest() );
154 
155             archetypeConfiguration = archetypeFactory.createArchetypeConfiguration( archetypeDescriptor, properties );
156         }
157         else
158         {
159             throw new ArchetypeGenerationConfigurationFailure( "The defined artifact is not an archetype" );
160         }
161 
162         Context context = new VelocityContext();
163         if ( interactiveMode.booleanValue() )
164         {
165             boolean confirmed = false;
166             context.put( Constants.GROUP_ID, ad.getGroupId() );
167             context.put( Constants.ARTIFACT_ID, ad.getArtifactId() );
168             context.put( Constants.VERSION, ad.getVersion() );
169             while ( !confirmed )
170             {
171                 List<String> propertiesRequired = archetypeConfiguration.getRequiredProperties();
172                 getLogger().debug( "Required properties before content sort: " + propertiesRequired );
173                 Collections.sort( propertiesRequired, new RequiredPropertyComparator( archetypeConfiguration ) );
174                 getLogger().debug( "Required properties after content sort: " + propertiesRequired );
175 
176                 if ( !archetypeConfiguration.isConfigured() )
177                 {
178                     for ( String requiredProperty : propertiesRequired )
179                     {
180                         if ( !archetypeConfiguration.isConfigured( requiredProperty ) )
181                         {
182                             if ( "package".equals( requiredProperty ) )
183                             {
184                                 // if the asked property is 'package', then
185                                 // use its default and if not defined,
186                                 // use the 'groupId' property value.
187                                 String packageDefault = archetypeConfiguration.getDefaultValue( requiredProperty );
188                                 packageDefault = ( null == packageDefault || "".equals( packageDefault ) )
189                                     ? archetypeConfiguration.getProperty( "groupId" )
190                                     : archetypeConfiguration.getDefaultValue( requiredProperty );
191 
192                                 String value =
193                                     getTransitiveDefaultValue( packageDefault, archetypeConfiguration, requiredProperty,
194                                                                context );
195 
196                                 value = archetypeGenerationQueryer.getPropertyValue( requiredProperty, value, null );
197 
198                                 archetypeConfiguration.setProperty( requiredProperty, value );
199 
200                                 context.put( Constants.PACKAGE, value );
201                             }
202                             else
203                             {
204                                 String value = archetypeConfiguration.getDefaultValue( requiredProperty );
205 
206                                 value = getTransitiveDefaultValue( value, archetypeConfiguration, requiredProperty,
207                                                                    context );
208 
209                                 value = archetypeGenerationQueryer.getPropertyValue( requiredProperty, value,
210                                     archetypeConfiguration.getPropertyValidationRegex( requiredProperty ) );
211 
212                                 archetypeConfiguration.setProperty( requiredProperty, value );
213 
214                                 context.put( requiredProperty, value );
215                             }
216                         }
217                         else
218                         {
219                             getLogger().info(
220                                 "Using property: " + requiredProperty + " = " + archetypeConfiguration.getProperty(
221                                     requiredProperty ) );
222                             archetypeConfiguration.setProperty( requiredProperty, archetypeConfiguration.getProperty(
223                                 requiredProperty ) );
224                         }
225                     }
226                 }
227                 else
228                 {
229 
230                     for ( String requiredProperty : propertiesRequired )
231                     {
232                         getLogger().info(
233                             "Using property: " + requiredProperty + " = " + archetypeConfiguration.getProperty(
234                                 requiredProperty ) );
235                     }
236                 }
237 
238                 if ( !archetypeConfiguration.isConfigured() )
239                 {
240                     getLogger().warn( "Archetype is not fully configured" );
241                 }
242                 else if ( !archetypeGenerationQueryer.confirmConfiguration( archetypeConfiguration ) )
243                 {
244                     getLogger().debug( "Archetype generation configuration not confirmed" );
245                     archetypeConfiguration.reset();
246                     restoreCommandLineProperties( archetypeConfiguration, executionProperties );
247                 }
248                 else
249                 {
250                     getLogger().debug( "Archetype generation configuration confirmed" );
251 
252                     confirmed = true;
253                 }
254             }
255         }
256         else
257         {
258             if ( !archetypeConfiguration.isConfigured() )
259             {
260                 for ( String requiredProperty : archetypeConfiguration.getRequiredProperties() )
261                 {
262                     if ( !archetypeConfiguration.isConfigured( requiredProperty ) && (
263                         archetypeConfiguration.getDefaultValue( requiredProperty ) != null ) )
264                     {
265                         String value = archetypeConfiguration.getDefaultValue( requiredProperty );
266                         value = getTransitiveDefaultValue( value, archetypeConfiguration, requiredProperty, context );
267                         archetypeConfiguration.setProperty( requiredProperty, value );
268                         context.put( requiredProperty, value );
269                     }
270                 }
271 
272                 // in batch mode, we assume the defaults, and if still not configured fail
273                 if ( !archetypeConfiguration.isConfigured() )
274                 {
275                     StringBuffer exceptionMessage = new StringBuffer();
276                     exceptionMessage.append( "Archetype " );
277                     exceptionMessage.append( request.getArchetypeGroupId() );
278                     exceptionMessage.append( ":" );
279                     exceptionMessage.append( request.getArchetypeArtifactId() );
280                     exceptionMessage.append( ":" );
281                     exceptionMessage.append( request.getArchetypeVersion() );
282                     exceptionMessage.append( " is not configured" );
283 
284                     List<String> missingProperties = new ArrayList<String>( 0 );
285                     for ( String requiredProperty : archetypeConfiguration.getRequiredProperties() )
286                     {
287                         if ( !archetypeConfiguration.isConfigured( requiredProperty ) )
288                         {
289                             exceptionMessage.append( "\n\tProperty " );
290                             exceptionMessage.append( requiredProperty );
291                             missingProperties.add( requiredProperty );
292                             exceptionMessage.append( " is missing." );
293                             getLogger().warn( "Property " + requiredProperty + " is missing. Add -D" + requiredProperty
294                                                   + "=someValue" );
295                         }
296                     }
297 
298                     throw new ArchetypeNotConfigured( exceptionMessage.toString(), missingProperties );
299                 }
300             }
301         }
302 
303         request.setGroupId( archetypeConfiguration.getProperty( Constants.GROUP_ID ) );
304 
305         request.setArtifactId( archetypeConfiguration.getProperty( Constants.ARTIFACT_ID ) );
306 
307         request.setVersion( archetypeConfiguration.getProperty( Constants.VERSION ) );
308 
309         request.setPackage( archetypeConfiguration.getProperty( Constants.PACKAGE ) );
310 
311         properties = archetypeConfiguration.getProperties();
312 
313         request.setProperties( properties );
314     }
315 
316     private String getTransitiveDefaultValue( String defaultValue, ArchetypeConfiguration archetypeConfiguration,
317                                               String requiredProperty, Context context )
318     {
319         String result = defaultValue;
320         if ( null == result )
321         {
322             return null;
323         }
324         for ( String property : archetypeConfiguration.getRequiredProperties() )
325         {
326             if ( result.indexOf( "${" + property + "}" ) >= 0 )
327             {
328 
329                 result = StringUtils.replace( result, "${" + property + "}",
330                                               archetypeConfiguration.getProperty( property ) );
331             }
332         }
333         if ( result.contains( "${" ) )
334         {
335             result = evaluateProperty( context, requiredProperty, defaultValue );
336         }
337         return result;
338     }
339 
340 
341     private String evaluateProperty( Context context, String property, String value )
342     {
343         StringWriter stringWriter = new StringWriter();
344         try
345         {
346             Velocity.evaluate( context, stringWriter, property, value );
347             return stringWriter.toString();
348         }
349         catch ( Exception ex )
350         {
351             return value;
352         }
353         finally
354         {
355             IOUtil.close( stringWriter );
356         }
357     }
358 
359 
360     private void restoreCommandLineProperties( ArchetypeConfiguration archetypeConfiguration,
361                                                Properties executionProperties )
362     {
363         getLogger().debug( "Restoring command line properties" );
364 
365         for ( String property : archetypeConfiguration.getRequiredProperties() )
366         {
367             if ( executionProperties.containsKey( property ) )
368             {
369                 archetypeConfiguration.setProperty( property, executionProperties.getProperty( property ) );
370                 getLogger().debug( "Restored " + property + "=" + archetypeConfiguration.getProperty( property ) );
371             }
372         }
373     }
374 
375     public static class RequiredPropertyComparator
376         implements Comparator<String>
377     {
378         private final ArchetypeConfiguration archetypeConfiguration;
379 
380         public RequiredPropertyComparator( ArchetypeConfiguration archetypeConfiguration )
381         {
382             this.archetypeConfiguration = archetypeConfiguration;
383         }
384 
385         public int compare( String left, String right )
386         {
387             String leftDefault = archetypeConfiguration.getDefaultValue( left );
388 
389             if ( ( leftDefault != null ) && leftDefault.indexOf( "${" + right + "}" ) >= 0 )
390             { //left contains right
391                 return 1;
392             }
393 
394             String rightDefault = archetypeConfiguration.getDefaultValue( right );
395 
396             if ( ( rightDefault != null ) && rightDefault.indexOf( "${" + left + "}" ) >= 0 )
397             { //right contains left
398                 return -1;
399             }
400 
401             return comparePropertyName( left, right );
402         }
403 
404         private int comparePropertyName( String left, String right )
405         {
406             if ( "groupId".equals( left ) )
407             {
408                 return -1;
409             }
410             if ( "groupId".equals( right ) )
411             {
412                 return 1;
413             }
414             if ( "artifactId".equals( left ) )
415             {
416                 return -1;
417             }
418             if ( "artifactId".equals( right ) )
419             {
420                 return 1;
421             }
422             if ( "version".equals( left ) )
423             {
424                 return -1;
425             }
426             if ( "version".equals( right ) )
427             {
428                 return 1;
429             }
430             if ( "package".equals( left ) )
431             {
432                 return -1;
433             }
434             if ( "package".equals( right ) )
435             {
436                 return 1;
437             }
438             return left.compareTo( right );
439         }
440     }
441     
442     private ArtifactRepository createRepository( String url, String repositoryId )
443     {
444         
445         
446         // snapshots vs releases
447         // offline = to turning the update policy off
448 
449         // TODO: we'll need to allow finer grained creation of repositories but this will do for now
450 
451         String updatePolicyFlag = ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS;
452 
453         String checksumPolicyFlag = ArtifactRepositoryPolicy.CHECKSUM_POLICY_WARN;
454 
455         ArtifactRepositoryPolicy snapshotsPolicy =
456             new ArtifactRepositoryPolicy( true, updatePolicyFlag, checksumPolicyFlag );
457 
458         ArtifactRepositoryPolicy releasesPolicy =
459             new ArtifactRepositoryPolicy( true, updatePolicyFlag, checksumPolicyFlag );
460         
461         return new MavenArtifactRepository( repositoryId, url, defaultArtifactRepositoryLayout, snapshotsPolicy,
462                                             releasesPolicy );
463     }
464 
465 }