View Javadoc
1   package org.apache.maven.archetype.source;
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 java.io.File;
23  import java.io.IOException;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.maven.archetype.catalog.Archetype;
28  import org.apache.maven.archetype.catalog.ArchetypeCatalog;
29  import org.apache.maven.artifact.repository.ArtifactRepository;
30  import org.apache.maven.execution.MavenExecutionRequest;
31  import org.apache.maven.execution.MavenSession;
32  import org.apache.maven.plugin.LegacySupport;
33  import org.apache.maven.project.ProjectBuildingRequest;
34  import org.apache.maven.settings.Mirror;
35  import org.apache.maven.settings.Proxy;
36  import org.apache.maven.settings.Server;
37  import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest;
38  import org.apache.maven.settings.crypto.SettingsDecrypter;
39  import org.apache.maven.settings.crypto.SettingsDecryptionResult;
40  import org.apache.maven.wagon.UnsupportedProtocolException;
41  import org.apache.maven.wagon.Wagon;
42  import org.apache.maven.wagon.WagonException;
43  import org.apache.maven.wagon.authentication.AuthenticationInfo;
44  import org.apache.maven.wagon.proxy.ProxyInfo;
45  import org.apache.maven.wagon.repository.Repository;
46  import org.codehaus.plexus.component.annotations.Component;
47  import org.codehaus.plexus.component.annotations.Requirement;
48  import org.codehaus.plexus.util.ReaderFactory;
49  import org.codehaus.plexus.util.StringUtils;
50  
51  /**
52   * @author Jason van Zyl
53   */
54  @Component( role = ArchetypeDataSource.class, hint = "remote-catalog" )
55  public class RemoteCatalogArchetypeDataSource extends CatalogArchetypeDataSource implements ArchetypeDataSource
56  {
57      @Requirement
58      private Map<String, Wagon> wagons;
59      
60      @Requirement
61      private LegacySupport legacySupport;
62  
63      @Requirement
64      private SettingsDecrypter settingsDecrypter;
65  
66  //    Should be used for mirror/proxy/authentication
67  //    available since M3.2.3
68  //    @Requirement
69  //    private MavenRepositorySystem;
70  
71      /**
72       * Id of the repository used to download catalog file. Proxy or authentication info can
73       * be setup in settings.xml.
74       */
75      public static final String ARCHETYPE_REPOSITORY_ID = "archetype";
76  
77      public static final String CENTRAL_REPOSITORY_ID = "central";
78  
79      @Override
80      public ArchetypeCatalog getArchetypeCatalog( ProjectBuildingRequest buildingRequest )
81          throws ArchetypeDataSourceException
82      {
83          // With M3 artifactRepositories are already injected with their mirror, including the new id
84          // First look for mirrorId of both 'central' and 'archetype'
85          final String archetypeRepoId;
86          Mirror archetypeMirror = getMirror( ARCHETYPE_REPOSITORY_ID );
87          if ( archetypeMirror != null )
88          {
89              archetypeRepoId = archetypeMirror.getId();
90          }
91          else
92          {
93              archetypeRepoId = ARCHETYPE_REPOSITORY_ID;
94          }
95          
96          final String centralRepoId;
97          Mirror centralMirror = getMirror( CENTRAL_REPOSITORY_ID );
98          if ( centralMirror != null )
99          {
100             centralRepoId = centralMirror.getId();
101         }
102         else
103         {
104             centralRepoId = CENTRAL_REPOSITORY_ID;
105         }
106         
107         ArtifactRepository centralRepository = null;
108         ArtifactRepository archetypeRepository = null;
109         for ( ArtifactRepository remoteRepository : buildingRequest.getRemoteRepositories() )
110         {
111             if ( archetypeRepoId.equals( remoteRepository.getId() ) )
112             {
113                 archetypeRepository = remoteRepository;
114                 break;
115             }
116             else if ( centralRepoId.equals( remoteRepository.getId() ) )
117             {
118                 centralRepository = remoteRepository;
119             }
120         }
121 
122         if ( archetypeRepository == null )
123         {
124             archetypeRepository = centralRepository;
125         }
126 
127         try
128         {
129             return downloadCatalog( archetypeRepository );
130         }
131         catch ( IOException e )
132         {
133             throw new ArchetypeDataSourceException( e );
134         }
135         catch ( WagonException e )
136         {
137             throw new ArchetypeDataSourceException( e );
138         }
139     }
140 
141     @Override
142     public void updateCatalog( ProjectBuildingRequest buildingRequest, Archetype archetype )
143         throws ArchetypeDataSourceException
144     {
145         throw new ArchetypeDataSourceException( "Not supported yet." );
146     }
147 
148     private ArchetypeCatalog downloadCatalog( ArtifactRepository repository )
149         throws WagonException, IOException, ArchetypeDataSourceException
150     {
151         getLogger().debug( "Searching for remote catalog: " + repository.getUrl() + "/" + ARCHETYPE_CATALOG_FILENAME );
152 
153         // We use wagon to take advantage of a Proxy that has already been setup in a Maven environment.
154         Repository wagonRepository = new Repository( repository.getId(), repository.getUrl() );
155         
156         AuthenticationInfo authInfo = getAuthenticationInfo( wagonRepository.getId() );
157         ProxyInfo proxyInfo = getProxy( wagonRepository.getProtocol() );
158 
159         Wagon wagon = getWagon( wagonRepository );
160 
161         File catalog = File.createTempFile( "archetype-catalog", ".xml" );
162         try
163         {
164             wagon.connect( wagonRepository, authInfo, proxyInfo );
165             wagon.get( ARCHETYPE_CATALOG_FILENAME, catalog );
166 
167             return readCatalog( ReaderFactory.newXmlReader( catalog ) );
168         }
169         finally
170         {
171             disconnectWagon( wagon );
172             catalog.delete();
173         }
174     }
175 
176     private void disconnectWagon( Wagon wagon )
177     {
178         try
179         {
180             wagon.disconnect();
181         }
182         catch ( Exception e )
183         {
184             getLogger().warn( "Problem disconnecting from wagon - ignoring: " + e.getMessage() );
185         }
186     }
187 
188     // 
189     
190     private Wagon getWagon( Repository repository )
191         throws UnsupportedProtocolException
192     {
193         return getWagon( repository.getProtocol() );
194     }
195 
196     private Wagon getWagon( String protocol )
197         throws UnsupportedProtocolException
198     {
199         if ( protocol == null )
200         {
201             throw new UnsupportedProtocolException( "Unspecified protocol" );
202         }
203 
204         String hint = protocol.toLowerCase( java.util.Locale.ENGLISH );
205 
206         Wagon wagon = wagons.get( hint );
207         if ( wagon == null )
208         {
209             throw new UnsupportedProtocolException( "Cannot find wagon which supports the requested protocol: "
210                 + protocol );
211         }
212 
213         return wagon;
214     }
215     
216     private AuthenticationInfo getAuthenticationInfo( String id )
217     {
218         MavenSession session = legacySupport.getSession();
219 
220         if ( session != null && id != null )
221         {
222             MavenExecutionRequest request = session.getRequest();
223 
224             if ( request != null )
225             {
226                 List<Server> servers = request.getServers();
227 
228                 if ( servers != null )
229                 {
230                     for ( Server server : servers )
231                     {
232                         if ( id.equalsIgnoreCase( server.getId() ) )
233                         {
234                             SettingsDecryptionResult result =
235                                 settingsDecrypter.decrypt( new DefaultSettingsDecryptionRequest( server ) );
236                             server = result.getServer();
237 
238                             AuthenticationInfo authInfo = new AuthenticationInfo();
239                             authInfo.setUserName( server.getUsername() );
240                             authInfo.setPassword( server.getPassword() );
241                             authInfo.setPrivateKey( server.getPrivateKey() );
242                             authInfo.setPassphrase( server.getPassphrase() );
243 
244                             return authInfo;
245                         }
246                     }
247                 }
248             }
249         }
250 
251         // empty one to prevent NPE
252        return new AuthenticationInfo();
253     }
254 
255     private ProxyInfo getProxy( String protocol )
256     {
257         MavenSession session = legacySupport.getSession();
258 
259         if ( session != null && protocol != null )
260         {
261             MavenExecutionRequest request = session.getRequest();
262 
263             if ( request != null )
264             {
265                 List<Proxy> proxies = request.getProxies();
266 
267                 if ( proxies != null )
268                 {
269                     for ( Proxy proxy : proxies )
270                     {
271                         if ( proxy.isActive() && protocol.equalsIgnoreCase( proxy.getProtocol() ) )
272                         {
273                             SettingsDecryptionResult result =
274                                 settingsDecrypter.decrypt( new DefaultSettingsDecryptionRequest( proxy ) );
275                             proxy = result.getProxy();
276 
277                             ProxyInfo proxyInfo = new ProxyInfo();
278                             proxyInfo.setHost( proxy.getHost() );
279                             proxyInfo.setType( proxy.getProtocol() );
280                             proxyInfo.setPort( proxy.getPort() );
281                             proxyInfo.setNonProxyHosts( proxy.getNonProxyHosts() );
282                             proxyInfo.setUserName( proxy.getUsername() );
283                             proxyInfo.setPassword( proxy.getPassword() );
284 
285                             return proxyInfo;
286                         }
287                     }
288                 }
289             }
290         }
291 
292         return null;
293     }
294     
295     private Mirror getMirror( String repoId )
296     {
297         MavenSession session = legacySupport.getSession();
298 
299         MavenExecutionRequest request = session.getRequest();
300 
301         if ( request != null )
302         {
303             return getMirror( repoId, request.getMirrors() );
304         }
305         
306         return null;
307     }
308     
309     private static final String WILDCARD = "*";
310 
311     private static final String EXTERNAL_WILDCARD = "external:*";
312 
313     private Mirror getMirror( String repoId, List<Mirror> mirrors )
314     {
315         if ( repoId != null && mirrors != null )
316         {
317             for ( Mirror mirror : mirrors )
318             {
319                 if ( repoId.equals( mirror.getMirrorOf() ) )
320                 {
321                     return mirror;
322                 }
323             }
324 
325             for ( Mirror mirror : mirrors )
326             {
327                 if ( matchPattern( repoId, mirror.getMirrorOf() ) )
328                 {
329                     return mirror;
330                 }
331             }
332         }
333 
334         return null;
335     }
336 
337     /**
338      * This method checks if the pattern matches the originalRepository. Valid patterns: * =
339      * everything external:* = everything not on the localhost and not file based. repo,repo1 = repo
340      * or repo1 *,!repo1 = everything except repo1
341      *
342      * @param originalRepository to compare for a match.
343      * @param pattern used for match. Currently only '*' is supported.
344      * @return true if the repository is a match to this pattern.
345      */
346     static boolean matchPattern( String originalId, String pattern )
347     {
348         boolean result = false;
349 
350         // simple checks first to short circuit processing below.
351         if ( WILDCARD.equals( pattern ) || pattern.equals( originalId ) )
352         {
353             result = true;
354         }
355         else
356         {
357             // process the list
358             String[] repos = pattern.split( "," );
359             for ( String repo : repos )
360             {
361                 // see if this is a negative match
362                 if ( repo.length() > 1 && repo.startsWith( "!" ) )
363                 {
364                     if ( repo.substring( 1 ).equals( originalId ) )
365                     {
366                         // explicitly exclude. Set result and stop processing.
367                         result = false;
368                         break;
369                     }
370                 }
371                 // check for exact match
372                 else if ( repo.equals( originalId ) )
373                 {
374                     result = true;
375                     break;
376                 }
377                 // check for external:*
378                 else if ( EXTERNAL_WILDCARD.equals( repo ) )
379                 {
380                     result = true;
381                     // don't stop processing in case a future segment explicitly excludes this repo
382                 }
383                 else if ( WILDCARD.equals( repo ) )
384                 {
385                     result = true;
386                     // don't stop processing in case a future segment explicitly excludes this repo
387                 }
388             }
389         }
390         return result;
391     }
392 
393 
394 
395     static boolean matchesLayout( ArtifactRepository repository, Mirror mirror )
396     {
397         return matchesLayout( repository.getLayout().getId(), mirror.getMirrorOfLayouts() );
398     }
399 
400     /**
401      * Checks whether the layouts configured for a mirror match with the layout of the repository.
402      *
403      * @param repoLayout The layout of the repository, may be {@code null}.
404      * @param mirrorLayout The layouts supported by the mirror, may be {@code null}.
405      * @return {@code true} if the layouts associated with the mirror match the layout of the original repository,
406      *         {@code false} otherwise.
407      */
408     static boolean matchesLayout( String repoLayout, String mirrorLayout )
409     {
410         boolean result = false;
411 
412         // simple checks first to short circuit processing below.
413         if ( StringUtils.isEmpty( mirrorLayout ) || WILDCARD.equals( mirrorLayout ) )
414         {
415             result = true;
416         }
417         else if ( mirrorLayout.equals( repoLayout ) )
418         {
419             result = true;
420         }
421         else
422         {
423             // process the list
424             String[] layouts = mirrorLayout.split( "," );
425             for ( String layout : layouts )
426             {
427                 // see if this is a negative match
428                 if ( layout.length() > 1 && layout.startsWith( "!" ) )
429                 {
430                     if ( layout.substring( 1 ).equals( repoLayout ) )
431                     {
432                         // explicitly exclude. Set result and stop processing.
433                         result = false;
434                         break;
435                     }
436                 }
437                 // check for exact match
438                 else if ( layout.equals( repoLayout ) )
439                 {
440                     result = true;
441                     break;
442                 }
443                 else if ( WILDCARD.equals( layout ) )
444                 {
445                     result = true;
446                     // don't stop processing in case a future segment explicitly excludes this repo
447                 }
448             }
449         }
450 
451         return result;
452     }
453 }