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