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.commons.lang3.StringUtils;
23  import org.apache.maven.archetype.ArchetypeGenerationRequest;
24  import org.apache.maven.archetype.ArchetypeManager;
25  import org.apache.maven.archetype.catalog.Archetype;
26  import org.apache.maven.archetype.exception.ArchetypeNotDefined;
27  import org.apache.maven.archetype.exception.ArchetypeSelectionFailure;
28  import org.apache.maven.archetype.exception.UnknownArchetype;
29  import org.apache.maven.archetype.exception.UnknownGroup;
30  import org.apache.maven.archetype.ui.ArchetypeDefinition;
31  import org.apache.maven.project.ProjectBuildingRequest;
32  import org.codehaus.plexus.component.annotations.Component;
33  import org.codehaus.plexus.component.annotations.Requirement;
34  import org.codehaus.plexus.components.interactivity.PrompterException;
35  import org.codehaus.plexus.logging.AbstractLogEnabled;
36  
37  import java.io.IOException;
38  import java.util.HashMap;
39  import java.util.LinkedHashMap;
40  import java.util.List;
41  import java.util.Map;
42  
43  @Component( role = ArchetypeSelector.class, hint = "default" )
44  public class DefaultArchetypeSelector
45      extends AbstractLogEnabled
46      implements ArchetypeSelector
47  {
48      static final String DEFAULT_ARCHETYPE_GROUPID = "org.apache.maven.archetypes";
49  
50      static final String DEFAULT_ARCHETYPE_VERSION = "1.0";
51  
52      static final String DEFAULT_ARCHETYPE_ARTIFACTID = "maven-archetype-quickstart";
53  
54      @Requirement
55      private ArchetypeSelectionQueryer archetypeSelectionQueryer;
56  
57      @Requirement
58      private ArchetypeManager archetypeManager;
59  
60      @Override
61      public void selectArchetype( ArchetypeGenerationRequest request, Boolean interactiveMode, String catalogs )
62          throws ArchetypeNotDefined, UnknownArchetype, UnknownGroup, IOException, PrompterException,
63          ArchetypeSelectionFailure
64      {
65          ArchetypeDefinition definition = new ArchetypeDefinition( request );
66  
67          if ( definition.isDefined() && StringUtils.isNotEmpty( request.getArchetypeRepository() ) )
68          {
69              getLogger().info( "Archetype defined by properties" );
70              return;
71          }
72  
73          Map<String, List<Archetype>> archetypes = getArchetypesByCatalog( request.getProjectBuildingRequest(), catalogs );
74  
75          if ( StringUtils.isNotBlank( request.getFilter() ) )
76          {
77              // applying some filtering depending on filter parameter
78              archetypes = ArchetypeSelectorUtils.getFilteredArchetypesByCatalog( archetypes, request.getFilter() );
79              if ( archetypes.isEmpty() )
80              {
81                  getLogger().info( "Your filter doesn't match any archetype, so try again with another value." );
82                  return;
83              }
84          }
85  
86          if ( definition.isDefined() )
87          {
88              Map.Entry<String, Archetype> found =
89                  findArchetype( archetypes, request.getArchetypeGroupId(), request.getArchetypeArtifactId() );
90  
91              if ( found != null )
92              {
93                  String catalogKey = found.getKey();
94                  Archetype archetype = found.getValue();
95  
96                  updateRepository( definition, archetype );
97  
98                  getLogger().info( "Archetype repository not defined. Using the one from " + archetype + " found in catalog "
99                                        + catalogKey );
100             }
101             else
102             {
103                 getLogger().warn(
104                     "Archetype not found in any catalog. Falling back to central repository." );
105                 getLogger().warn(
106                     "Add a repository with id 'archetype' in your settings.xml if archetype's repository is elsewhere." );
107             }
108         }
109         else if ( definition.isPartiallyDefined() )
110         {
111             Map.Entry<String, Archetype> found =
112                 findArchetype( archetypes, request.getArchetypeGroupId(), request.getArchetypeArtifactId() );
113 
114             if ( found != null )
115             {
116                 String catalogKey = found.getKey();
117                 Archetype archetype = found.getValue();
118 
119                 updateDefinition( definition, archetype );
120 
121                 getLogger().info( "Archetype " + archetype + " found in catalog " + catalogKey );
122             }
123             else
124             {
125                 getLogger().warn( "Specified archetype not found." );
126                 if ( interactiveMode.booleanValue() )
127                 {
128                     definition.setVersion( null );
129                     definition.setGroupId( null );
130                     definition.setArtifactId( null );
131                 }
132             }
133         }
134 
135         // set the defaults - only group and version can be auto-defaulted
136         if ( definition.getGroupId() == null )
137         {
138             definition.setGroupId( DEFAULT_ARCHETYPE_GROUPID );
139         }
140         if ( definition.getVersion() == null )
141         {
142             definition.setVersion( DEFAULT_ARCHETYPE_VERSION );
143         }
144 
145         if ( !definition.isPartiallyDefined() )
146         {
147             // if artifact ID is set to its default, we still prompt to confirm
148             if ( definition.getArtifactId() == null )
149             {
150                 getLogger().info(
151                     "No archetype defined. Using " + DEFAULT_ARCHETYPE_ARTIFACTID + " (" + definition.getGroupId() + ":"
152                         + DEFAULT_ARCHETYPE_ARTIFACTID + ":" + definition.getVersion() + ")" );
153                 definition.setArtifactId( DEFAULT_ARCHETYPE_ARTIFACTID );
154             }
155 
156             if ( interactiveMode.booleanValue() && ( archetypes.size() > 0 ) )
157             {
158                 Archetype selectedArchetype = archetypeSelectionQueryer.selectArchetype( archetypes, definition );
159 
160                 updateDefinition( definition, selectedArchetype );
161             }
162 
163             // Make sure the groupId and artifactId are valid, the version may just default to
164             // the latest release.
165             if ( !definition.isPartiallyDefined() )
166             {
167                 throw new ArchetypeSelectionFailure( "No valid archetypes could be found to choose." );
168             }
169         }
170 
171         // finally update the request with gathered information
172         definition.updateRequest( request );
173     }
174 
175 
176     private Map<String, List<Archetype>> getArchetypesByCatalog( ProjectBuildingRequest buildingRequest, String catalogs )
177     {
178         if ( catalogs == null )
179         {
180             throw new NullPointerException( "Catalogs cannot be null" );
181         }
182 
183         Map<String, List<Archetype>> archetypes = new LinkedHashMap<>();
184 
185         for ( String catalog : StringUtils.split( catalogs, "," ) )
186         {
187             if ( "internal".equalsIgnoreCase( catalog ) )
188             {
189                 archetypes.put( "internal", archetypeManager.getInternalCatalog().getArchetypes() );
190             }
191             else if ( "local".equalsIgnoreCase( catalog ) )
192             {
193                 archetypes.put( "local", archetypeManager.getLocalCatalog( buildingRequest ).getArchetypes() );
194             }
195             else if ( "remote".equalsIgnoreCase( catalog ) )
196             {
197                 List<Archetype> archetypesFromRemote =
198                     archetypeManager.getRemoteCatalog( buildingRequest ).getArchetypes();
199                 
200                 if ( archetypesFromRemote.size() > 0 )
201                 {
202                     archetypes.put( "remote", archetypesFromRemote );
203                 }
204                 else
205                 {
206                     getLogger().warn( "No archetype found in remote catalog. Defaulting to internal catalog" );
207                     archetypes.put( "internal", archetypeManager.getInternalCatalog().getArchetypes() );
208                 }
209             }
210             else
211             {
212                 throw new IllegalArgumentException( "archetypeCatalog '" + catalog + "' is not supported anymore. "
213                     + "Please read the plugin documentation for details." );
214             }
215         }
216 
217         if ( archetypes.size() == 0 )
218         {
219             getLogger().info( "No catalog defined. Using internal catalog" );
220 
221             archetypes.put( "internal", archetypeManager.getInternalCatalog().getArchetypes() );
222         }
223         return archetypes;
224     }
225 
226     private void updateRepository( ArchetypeDefinition definition, Archetype archetype )
227     {
228         String repository = archetype.getRepository();
229         if ( StringUtils.isNotEmpty( repository ) )
230         {
231             definition.setRepository( repository );
232         }
233     }
234 
235     private void updateDefinition( ArchetypeDefinition definition, Archetype archetype )
236     {
237         definition.setGroupId( archetype.getGroupId() );
238         definition.setArtifactId( archetype.getArtifactId() );
239         definition.setVersion( archetype.getVersion() );
240         definition.setName( archetype.getArtifactId() );
241         updateRepository( definition, archetype );
242         definition.setGoals( StringUtils.join( archetype.getGoals().iterator(), "," ) );
243     }
244 
245     public void setArchetypeSelectionQueryer( ArchetypeSelectionQueryer archetypeSelectionQueryer )
246     {
247         this.archetypeSelectionQueryer = archetypeSelectionQueryer;
248     }
249 
250     private Map.Entry<String, Archetype> findArchetype( Map<String, List<Archetype>> archetypes, String groupId,
251                                                         String artifactId )
252     {
253         Archetype example = new Archetype();
254         example.setGroupId( groupId );
255         example.setArtifactId( artifactId );
256 
257         for ( Map.Entry<String, List<Archetype>> entry : archetypes.entrySet() )
258         {
259             List<Archetype> catalog = entry.getValue();
260 
261             if ( catalog.contains( example ) )
262             {
263                 Archetype archetype = catalog.get( catalog.indexOf( example ) );
264 
265                 return newMapEntry( entry.getKey(), archetype );
266             }
267         }
268 
269         return null;
270     }
271 
272     private static <K, V> Map.Entry<K, V> newMapEntry( K key, V value )
273     {
274         Map<K, V> map = new HashMap<>( 1 );
275         map.put( key, value );
276 
277         return map.entrySet().iterator().next();
278     }
279 }