View Javadoc
1   package org.eclipse.aether.util.repository;
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.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  
26  import org.eclipse.aether.repository.MirrorSelector;
27  import org.eclipse.aether.repository.RemoteRepository;
28  
29  /**
30   * A simple mirror selector that selects mirrors based on repository identifiers.
31   */
32  public final class DefaultMirrorSelector
33      implements MirrorSelector
34  {
35  
36      private static final String WILDCARD = "*";
37  
38      private static final String EXTERNAL_WILDCARD = "external:*";
39  
40      private static final String EXTERNAL_HTTP_WILDCARD = "external:http:*";
41  
42      private final List<MirrorDef> mirrors = new ArrayList<>();
43  
44      @Deprecated
45      public DefaultMirrorSelector add( String id, String url, String type, boolean repositoryManager,
46                                        String mirrorOfIds, String mirrorOfTypes )
47      {
48          return add( id, url, type, repositoryManager, false, mirrorOfIds, mirrorOfTypes );
49      }
50  
51      /**
52       * Adds the specified mirror to this selector.
53       * 
54       * @param id The identifier of the mirror, must not be {@code null}.
55       * @param url The URL of the mirror, must not be {@code null}.
56       * @param type The content type of the mirror, must not be {@code null}.
57       * @param repositoryManager A flag whether the mirror is a repository manager or a simple server.
58       * @param blocked A flag whether the mirror is blocked from performing any download requests.
59       * @param mirrorOfIds The identifier(s) of remote repositories to mirror, must not be {@code null}. Multiple
60       *            identifiers can be separated by comma and additionally the wildcards "*", "external:http:*" and
61       *            "external:*" can be used to match all (external) repositories, prefixing a repo id with an
62       *            exclamation mark allows to express an exclusion. For example "external:*,!central".
63       * @param mirrorOfTypes The content type(s) of remote repositories to mirror, may be {@code null} or empty to match
64       *            any content type. Similar to the repo id specification, multiple types can be comma-separated, the
65       *            wildcard "*" and the "!" negation syntax are supported. For example "*,!p2".
66       * @return This selector for chaining, never {@code null}.
67       */
68      public DefaultMirrorSelector add( String id, String url, String type, boolean repositoryManager, boolean blocked,
69                                        String mirrorOfIds, String mirrorOfTypes )
70      {
71          mirrors.add( new MirrorDef( id, url, type, repositoryManager, blocked, mirrorOfIds, mirrorOfTypes ) );
72  
73          return this;
74      }
75  
76      public RemoteRepository getMirror( RemoteRepository repository )
77      {
78          MirrorDef mirror = findMirror( repository );
79  
80          if ( mirror == null )
81          {
82              return null;
83          }
84  
85          RemoteRepository.Builder builder =
86              new RemoteRepository.Builder( mirror.id, repository.getContentType(), mirror.url );
87  
88          builder.setRepositoryManager( mirror.repositoryManager );
89  
90          builder.setBlocked( mirror.blocked );
91  
92          if ( mirror.type != null && mirror.type.length() > 0 )
93          {
94              builder.setContentType( mirror.type );
95          }
96  
97          builder.setSnapshotPolicy( repository.getPolicy( true ) );
98          builder.setReleasePolicy( repository.getPolicy( false ) );
99  
100         builder.setMirroredRepositories( Collections.singletonList( repository ) );
101 
102         return builder.build();
103     }
104 
105     private MirrorDef findMirror( RemoteRepository repository )
106     {
107         String repoId = repository.getId();
108 
109         if ( repoId != null && !mirrors.isEmpty() )
110         {
111             for ( MirrorDef mirror : mirrors )
112             {
113                 if ( repoId.equals( mirror.mirrorOfIds ) && matchesType( repository.getContentType(),
114                                                                          mirror.mirrorOfTypes ) )
115                 {
116                     return mirror;
117                 }
118             }
119 
120             for ( MirrorDef mirror : mirrors )
121             {
122                 if ( matchPattern( repository, mirror.mirrorOfIds ) && matchesType( repository.getContentType(),
123                                                                                     mirror.mirrorOfTypes ) )
124                 {
125                     return mirror;
126                 }
127             }
128         }
129 
130         return null;
131     }
132 
133     /**
134      * This method checks if the pattern matches the originalRepository. Valid patterns:
135      * <ul>
136      * <li>{@code *} = everything,</li>
137      * <li>{@code external:*} = everything not on the localhost and not file based,</li>
138      * <li>{@code external:http:*} = any repository not on the localhost using HTTP,</li>
139      * <li>{@code repo,repo1} = {@code repo} or {@code repo1},</li>
140      * <li>{@code *,!repo1} = everything except {@code repo1}.</li>
141      * </ul>
142      * 
143      * @param repository to compare for a match.
144      * @param pattern used for match.
145      * @return true if the repository is a match to this pattern.
146      */
147     static boolean matchPattern( RemoteRepository repository, String pattern )
148     {
149         boolean result = false;
150         String originalId = repository.getId();
151 
152         // simple checks first to short circuit processing below.
153         if ( WILDCARD.equals( pattern ) || pattern.equals( originalId ) )
154         {
155             result = true;
156         }
157         else
158         {
159             // process the list
160             String[] repos = pattern.split( "," );
161             for ( String repo : repos )
162             {
163                 // see if this is a negative match
164                 if ( repo.length() > 1 && repo.startsWith( "!" ) )
165                 {
166                     if ( repo.substring( 1 ).equals( originalId ) )
167                     {
168                         // explicitly exclude. Set result and stop processing.
169                         result = false;
170                         break;
171                     }
172                 }
173                 // check for exact match
174                 else if ( repo.equals( originalId ) )
175                 {
176                     result = true;
177                     break;
178                 }
179                 // check for external:*
180                 else if ( EXTERNAL_WILDCARD.equals( repo ) && isExternalRepo( repository ) )
181                 {
182                     result = true;
183                     // don't stop processing in case a future segment explicitly excludes this repo
184                 }
185                 // check for external:http:*
186                 else if ( EXTERNAL_HTTP_WILDCARD.equals( repo ) && isExternalHttpRepo( repository ) )
187                 {
188                     result = true;
189                     // don't stop processing in case a future segment explicitly excludes this repo
190                 }
191                 else if ( WILDCARD.equals( repo ) )
192                 {
193                     result = true;
194                     // don't stop processing in case a future segment explicitly excludes this repo
195                 }
196             }
197         }
198         return result;
199     }
200 
201     /**
202      * Checks the URL to see if this repository refers to an external repository.
203      * 
204      * @param repository The repository to check, must not be {@code null}.
205      * @return {@code true} if external, {@code false} otherwise.
206      */
207     static boolean isExternalRepo( RemoteRepository repository )
208     {
209         boolean local = isLocal( repository.getHost() ) || "file".equalsIgnoreCase( repository.getProtocol() );
210         return !local;
211     }
212 
213     private static boolean isLocal( String host )
214     {
215         return "localhost".equals( host ) || "127.0.0.1".equals( host );
216     }
217 
218     /**
219      * Checks the URL to see if this repository refers to a non-localhost repository using HTTP.
220      * 
221      * @param repository The repository to check, must not be {@code null}.
222      * @return {@code true} if external, {@code false} otherwise.
223      */
224     static boolean isExternalHttpRepo( RemoteRepository repository )
225     {
226         return ( "http".equalsIgnoreCase( repository.getProtocol() )
227             || "dav".equalsIgnoreCase( repository.getProtocol() )
228             || "dav:http".equalsIgnoreCase( repository.getProtocol() )
229             || "dav+http".equalsIgnoreCase( repository.getProtocol() ) )
230             && !isLocal( repository.getHost() );
231     }
232 
233     /**
234      * Checks whether the types configured for a mirror match with the type of the repository.
235      * 
236      * @param repoType The type of the repository, may be {@code null}.
237      * @param mirrorType The types supported by the mirror, may be {@code null}.
238      * @return {@code true} if the types associated with the mirror match the type of the original repository,
239      *         {@code false} otherwise.
240      */
241     static boolean matchesType( String repoType, String mirrorType )
242     {
243         boolean result = false;
244 
245         // simple checks first to short circuit processing below.
246         if ( mirrorType == null || mirrorType.length() <= 0 || WILDCARD.equals( mirrorType ) )
247         {
248             result = true;
249         }
250         else if ( mirrorType.equals( repoType ) )
251         {
252             result = true;
253         }
254         else
255         {
256             // process the list
257             String[] layouts = mirrorType.split( "," );
258             for ( String layout : layouts )
259             {
260                 // see if this is a negative match
261                 if ( layout.length() > 1 && layout.startsWith( "!" ) )
262                 {
263                     if ( layout.substring( 1 ).equals( repoType ) )
264                     {
265                         // explicitly exclude. Set result and stop processing.
266                         result = false;
267                         break;
268                     }
269                 }
270                 // check for exact match
271                 else if ( layout.equals( repoType ) )
272                 {
273                     result = true;
274                     break;
275                 }
276                 else if ( WILDCARD.equals( layout ) )
277                 {
278                     result = true;
279                     // don't stop processing in case a future segment explicitly excludes this repo
280                 }
281             }
282         }
283 
284         return result;
285     }
286 
287     static class MirrorDef
288     {
289 
290         final String id;
291 
292         final String url;
293 
294         final String type;
295 
296         final boolean repositoryManager;
297 
298         final boolean blocked;
299 
300         final String mirrorOfIds;
301 
302         final String mirrorOfTypes;
303 
304         MirrorDef( String id, String url, String type, boolean repositoryManager, boolean blocked, String mirrorOfIds,
305                    String mirrorOfTypes )
306         {
307             this.id = id;
308             this.url = url;
309             this.type = type;
310             this.repositoryManager = repositoryManager;
311             this.blocked = blocked;
312             this.mirrorOfIds = mirrorOfIds;
313             this.mirrorOfTypes = mirrorOfTypes;
314         }
315 
316     }
317 
318 }