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