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 = null;
300 
301         if ( session != null )
302         {
303             request = session.getRequest();
304         }
305 
306         if ( request != null )
307         {
308             return getMirror( repoId, request.getMirrors() );
309         }
310 
311         return null;
312     }
313     
314     private static final String WILDCARD = "*";
315 
316     private static final String EXTERNAL_WILDCARD = "external:*";
317 
318     private Mirror getMirror( String repoId, List<Mirror> mirrors )
319     {
320         if ( repoId != null && mirrors != null )
321         {
322             for ( Mirror mirror : mirrors )
323             {
324                 if ( repoId.equals( mirror.getMirrorOf() ) )
325                 {
326                     return mirror;
327                 }
328             }
329 
330             for ( Mirror mirror : mirrors )
331             {
332                 if ( matchPattern( repoId, mirror.getMirrorOf() ) )
333                 {
334                     return mirror;
335                 }
336             }
337         }
338 
339         return null;
340     }
341 
342     /**
343      * This method checks if the pattern matches the originalRepository. Valid patterns: * =
344      * everything external:* = everything not on the localhost and not file based. repo,repo1 = repo
345      * or repo1 *,!repo1 = everything except repo1
346      *
347      * @param originalId to compare for a match.
348      * @param pattern used for match. Currently only '*' is supported.
349      * @return true if the repository is a match to this pattern.
350      */
351     static boolean matchPattern( String originalId, String pattern )
352     {
353         boolean result = false;
354 
355         // simple checks first to short circuit processing below.
356         if ( WILDCARD.equals( pattern ) || pattern.equals( originalId ) )
357         {
358             result = true;
359         }
360         else
361         {
362             // process the list
363             String[] repos = pattern.split( "," );
364             for ( String repo : repos )
365             {
366                 // see if this is a negative match
367                 if ( repo.length() > 1 && repo.startsWith( "!" ) )
368                 {
369                     if ( repo.substring( 1 ).equals( originalId ) )
370                     {
371                         // explicitly exclude. Set result and stop processing.
372                         result = false;
373                         break;
374                     }
375                 }
376                 // check for exact match
377                 else if ( repo.equals( originalId ) )
378                 {
379                     result = true;
380                     break;
381                 }
382                 // check for external:*
383                 else if ( EXTERNAL_WILDCARD.equals( repo ) )
384                 {
385                     result = true;
386                     // don't stop processing in case a future segment explicitly excludes this repo
387                 }
388                 else if ( WILDCARD.equals( repo ) )
389                 {
390                     result = true;
391                     // don't stop processing in case a future segment explicitly excludes this repo
392                 }
393             }
394         }
395         return result;
396     }
397 
398 
399 
400     static boolean matchesLayout( ArtifactRepository repository, Mirror mirror )
401     {
402         return matchesLayout( repository.getLayout().getId(), mirror.getMirrorOfLayouts() );
403     }
404 
405     /**
406      * Checks whether the layouts configured for a mirror match with the layout of the repository.
407      *
408      * @param repoLayout The layout of the repository, may be {@code null}.
409      * @param mirrorLayout The layouts supported by the mirror, may be {@code null}.
410      * @return {@code true} if the layouts associated with the mirror match the layout of the original repository,
411      *         {@code false} otherwise.
412      */
413     static boolean matchesLayout( String repoLayout, String mirrorLayout )
414     {
415         boolean result = false;
416 
417         // simple checks first to short circuit processing below.
418         if ( StringUtils.isEmpty( mirrorLayout ) || WILDCARD.equals( mirrorLayout ) )
419         {
420             result = true;
421         }
422         else if ( mirrorLayout.equals( repoLayout ) )
423         {
424             result = true;
425         }
426         else
427         {
428             // process the list
429             String[] layouts = mirrorLayout.split( "," );
430             for ( String layout : layouts )
431             {
432                 // see if this is a negative match
433                 if ( layout.length() > 1 && layout.startsWith( "!" ) )
434                 {
435                     if ( layout.substring( 1 ).equals( repoLayout ) )
436                     {
437                         // explicitly exclude. Set result and stop processing.
438                         result = false;
439                         break;
440                     }
441                 }
442                 // check for exact match
443                 else if ( layout.equals( repoLayout ) )
444                 {
445                     result = true;
446                     break;
447                 }
448                 else if ( WILDCARD.equals( layout ) )
449                 {
450                     result = true;
451                     // don't stop processing in case a future segment explicitly excludes this repo
452                 }
453             }
454         }
455 
456         return result;
457     }
458 }