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