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 }