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     public void updateCatalog( ProjectBuildingRequest buildingRequest, Archetype archetype )
142         throws ArchetypeDataSourceException
143     {
144         throw new ArchetypeDataSourceException( "Not supported yet." );
145     }
146 
147     private ArchetypeCatalog downloadCatalog( ArtifactRepository repository )
148         throws WagonException, IOException, ArchetypeDataSourceException
149     {
150         getLogger().debug( "Searching for remote catalog: " + repository.getUrl() + "/" + ARCHETYPE_CATALOG_FILENAME );
151 
152         // We use wagon to take advantage of a Proxy that has already been setup in a Maven environment.
153         Repository wagonRepository = new Repository( repository.getId(), repository.getUrl() );
154         
155         AuthenticationInfo authInfo = getAuthenticationInfo( wagonRepository.getId() );
156         ProxyInfo proxyInfo = getProxy( wagonRepository.getProtocol() );
157 
158         Wagon wagon = getWagon( wagonRepository );
159 
160         File catalog = File.createTempFile( "archetype-catalog", ".xml" );
161         try
162         {
163             wagon.connect( wagonRepository, authInfo, proxyInfo );
164             wagon.get( ARCHETYPE_CATALOG_FILENAME, catalog );
165 
166             return readCatalog( ReaderFactory.newXmlReader( catalog ) );
167         }
168         finally
169         {
170             disconnectWagon( wagon );
171             catalog.delete();
172         }
173     }
174 
175     private void disconnectWagon( Wagon wagon )
176     {
177         try
178         {
179             wagon.disconnect();
180         }
181         catch ( Exception e )
182         {
183             getLogger().warn( "Problem disconnecting from wagon - ignoring: " + e.getMessage() );
184         }
185     }
186 
187     // 
188     
189     private Wagon getWagon( Repository repository )
190         throws UnsupportedProtocolException
191     {
192         return getWagon( repository.getProtocol() );
193     }
194 
195     private Wagon getWagon( String protocol )
196         throws UnsupportedProtocolException
197     {
198         if ( protocol == null )
199         {
200             throw new UnsupportedProtocolException( "Unspecified protocol" );
201         }
202 
203         String hint = protocol.toLowerCase( java.util.Locale.ENGLISH );
204 
205         Wagon wagon = wagons.get( hint );
206         if ( wagon == null )
207         {
208             throw new UnsupportedProtocolException( "Cannot find wagon which supports the requested protocol: "
209                 + protocol );
210         }
211 
212         return wagon;
213     }
214     
215     private AuthenticationInfo getAuthenticationInfo( String id )
216     {
217         MavenSession session = legacySupport.getSession();
218 
219         if ( session != null && id != null )
220         {
221             MavenExecutionRequest request = session.getRequest();
222 
223             if ( request != null )
224             {
225                 List<Server> servers = request.getServers();
226 
227                 if ( servers != null )
228                 {
229                     for ( Server server : servers )
230                     {
231                         if ( id.equalsIgnoreCase( server.getId() ) )
232                         {
233                             SettingsDecryptionResult result =
234                                 settingsDecrypter.decrypt( new DefaultSettingsDecryptionRequest( server ) );
235                             server = result.getServer();
236 
237                             AuthenticationInfo authInfo = new AuthenticationInfo();
238                             authInfo.setUserName( server.getUsername() );
239                             authInfo.setPassword( server.getPassword() );
240                             authInfo.setPrivateKey( server.getPrivateKey() );
241                             authInfo.setPassphrase( server.getPassphrase() );
242 
243                             return authInfo;
244                         }
245                     }
246                 }
247             }
248         }
249 
250         // empty one to prevent NPE
251        return new AuthenticationInfo();
252     }
253 
254     private ProxyInfo getProxy( String protocol )
255     {
256         MavenSession session = legacySupport.getSession();
257 
258         if ( session != null && protocol != null )
259         {
260             MavenExecutionRequest request = session.getRequest();
261 
262             if ( request != null )
263             {
264                 List<Proxy> proxies = request.getProxies();
265 
266                 if ( proxies != null )
267                 {
268                     for ( Proxy proxy : proxies )
269                     {
270                         if ( proxy.isActive() && protocol.equalsIgnoreCase( proxy.getProtocol() ) )
271                         {
272                             SettingsDecryptionResult result =
273                                 settingsDecrypter.decrypt( new DefaultSettingsDecryptionRequest( proxy ) );
274                             proxy = result.getProxy();
275 
276                             ProxyInfo proxyInfo = new ProxyInfo();
277                             proxyInfo.setHost( proxy.getHost() );
278                             proxyInfo.setType( proxy.getProtocol() );
279                             proxyInfo.setPort( proxy.getPort() );
280                             proxyInfo.setNonProxyHosts( proxy.getNonProxyHosts() );
281                             proxyInfo.setUserName( proxy.getUsername() );
282                             proxyInfo.setPassword( proxy.getPassword() );
283 
284                             return proxyInfo;
285                         }
286                     }
287                 }
288             }
289         }
290 
291         return null;
292     }
293     
294     private Mirror getMirror( String repoId )
295     {
296         MavenSession session = legacySupport.getSession();
297 
298         MavenExecutionRequest request = session.getRequest();
299 
300         if ( request != null )
301         {
302             return getMirror( repoId, request.getMirrors() );
303         }
304         
305         return null;
306     }
307     
308     private static final String WILDCARD = "*";
309 
310     private static final String EXTERNAL_WILDCARD = "external:*";
311 
312     private Mirror getMirror( String repoId, List<Mirror> mirrors )
313     {
314         if ( repoId != null && mirrors != null )
315         {
316             for ( Mirror mirror : mirrors )
317             {
318                 if ( repoId.equals( mirror.getMirrorOf() ) )
319                 {
320                     return mirror;
321                 }
322             }
323 
324             for ( Mirror mirror : mirrors )
325             {
326                 if ( matchPattern( repoId, mirror.getMirrorOf() ) )
327                 {
328                     return mirror;
329                 }
330             }
331         }
332 
333         return null;
334     }
335 
336     /**
337      * This method checks if the pattern matches the originalRepository. Valid patterns: * =
338      * everything external:* = everything not on the localhost and not file based. repo,repo1 = repo
339      * or repo1 *,!repo1 = everything except repo1
340      *
341      * @param originalRepository to compare for a match.
342      * @param pattern used for match. Currently only '*' is supported.
343      * @return true if the repository is a match to this pattern.
344      */
345     static boolean matchPattern( String originalId, String pattern )
346     {
347         boolean result = false;
348 
349         // simple checks first to short circuit processing below.
350         if ( WILDCARD.equals( pattern ) || pattern.equals( originalId ) )
351         {
352             result = true;
353         }
354         else
355         {
356             // process the list
357             String[] repos = pattern.split( "," );
358             for ( String repo : repos )
359             {
360                 // see if this is a negative match
361                 if ( repo.length() > 1 && repo.startsWith( "!" ) )
362                 {
363                     if ( repo.substring( 1 ).equals( originalId ) )
364                     {
365                         // explicitly exclude. Set result and stop processing.
366                         result = false;
367                         break;
368                     }
369                 }
370                 // check for exact match
371                 else if ( repo.equals( originalId ) )
372                 {
373                     result = true;
374                     break;
375                 }
376                 // check for external:*
377                 else if ( EXTERNAL_WILDCARD.equals( repo ) )
378                 {
379                     result = true;
380                     // don't stop processing in case a future segment explicitly excludes this repo
381                 }
382                 else if ( WILDCARD.equals( repo ) )
383                 {
384                     result = true;
385                     // don't stop processing in case a future segment explicitly excludes this repo
386                 }
387             }
388         }
389         return result;
390     }
391 
392 
393 
394     static boolean matchesLayout( ArtifactRepository repository, Mirror mirror )
395     {
396         return matchesLayout( repository.getLayout().getId(), mirror.getMirrorOfLayouts() );
397     }
398 
399     /**
400      * Checks whether the layouts configured for a mirror match with the layout of the repository.
401      *
402      * @param repoLayout The layout of the repository, may be {@code null}.
403      * @param mirrorLayout The layouts supported by the mirror, may be {@code null}.
404      * @return {@code true} if the layouts associated with the mirror match the layout of the original repository,
405      *         {@code false} otherwise.
406      */
407     static boolean matchesLayout( String repoLayout, String mirrorLayout )
408     {
409         boolean result = false;
410 
411         // simple checks first to short circuit processing below.
412         if ( StringUtils.isEmpty( mirrorLayout ) || WILDCARD.equals( mirrorLayout ) )
413         {
414             result = true;
415         }
416         else if ( mirrorLayout.equals( repoLayout ) )
417         {
418             result = true;
419         }
420         else
421         {
422             // process the list
423             String[] layouts = mirrorLayout.split( "," );
424             for ( String layout : layouts )
425             {
426                 // see if this is a negative match
427                 if ( layout.length() > 1 && layout.startsWith( "!" ) )
428                 {
429                     if ( layout.substring( 1 ).equals( repoLayout ) )
430                     {
431                         // explicitly exclude. Set result and stop processing.
432                         result = false;
433                         break;
434                     }
435                 }
436                 // check for exact match
437                 else if ( layout.equals( repoLayout ) )
438                 {
439                     result = true;
440                     break;
441                 }
442                 else if ( WILDCARD.equals( layout ) )
443                 {
444                     result = true;
445                     // don't stop processing in case a future segment explicitly excludes this repo
446                 }
447             }
448         }
449 
450         return result;
451     }
452 }