001package org.eclipse.aether.util.repository; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.List; 025 026import org.eclipse.aether.repository.MirrorSelector; 027import org.eclipse.aether.repository.RemoteRepository; 028 029import static java.util.Objects.requireNonNull; 030 031/** 032 * A simple mirror selector that selects mirrors based on repository identifiers. 033 */ 034public final class DefaultMirrorSelector 035 implements MirrorSelector 036{ 037 038 private static final String WILDCARD = "*"; 039 040 private static final String EXTERNAL_WILDCARD = "external:*"; 041 042 private static final String EXTERNAL_HTTP_WILDCARD = "external:http:*"; 043 044 private final List<MirrorDef> mirrors = new ArrayList<>(); 045 046 @Deprecated 047 public DefaultMirrorSelector add( String id, String url, String type, boolean repositoryManager, 048 String mirrorOfIds, String mirrorOfTypes ) 049 { 050 return add( id, url, type, repositoryManager, false, mirrorOfIds, mirrorOfTypes ); 051 } 052 053 /** 054 * Adds the specified mirror to this selector. 055 * 056 * @param id The identifier of the mirror, must not be {@code null}. 057 * @param url The URL of the mirror, must not be {@code null}. 058 * @param type The content type of the mirror, must not be {@code null}. 059 * @param repositoryManager A flag whether the mirror is a repository manager or a simple server. 060 * @param blocked A flag whether the mirror is blocked from performing any download requests. 061 * @param mirrorOfIds The identifier(s) of remote repositories to mirror, must not be {@code null}. Multiple 062 * identifiers can be separated by comma and additionally the wildcards "*", "external:http:*" and 063 * "external:*" can be used to match all (external) repositories, prefixing a repo id with an 064 * exclamation mark allows to express an exclusion. For example "external:*,!central". 065 * @param mirrorOfTypes The content type(s) of remote repositories to mirror, may be {@code null} or empty to match 066 * any content type. Similar to the repo id specification, multiple types can be comma-separated, the 067 * wildcard "*" and the "!" negation syntax are supported. For example "*,!p2". 068 * @return This selector for chaining, never {@code null}. 069 */ 070 public DefaultMirrorSelector add( String id, String url, String type, boolean repositoryManager, boolean blocked, 071 String mirrorOfIds, String mirrorOfTypes ) 072 { 073 mirrors.add( new MirrorDef( id, url, type, repositoryManager, blocked, mirrorOfIds, mirrorOfTypes ) ); 074 075 return this; 076 } 077 078 public RemoteRepository getMirror( RemoteRepository repository ) 079 { 080 requireNonNull( repository, "repository cannot be null" ); 081 MirrorDef mirror = findMirror( repository ); 082 083 if ( mirror == null ) 084 { 085 return null; 086 } 087 088 RemoteRepository.Builder builder = 089 new RemoteRepository.Builder( mirror.id, repository.getContentType(), mirror.url ); 090 091 builder.setRepositoryManager( mirror.repositoryManager ); 092 093 builder.setBlocked( mirror.blocked ); 094 095 if ( mirror.type != null && mirror.type.length() > 0 ) 096 { 097 builder.setContentType( mirror.type ); 098 } 099 100 builder.setSnapshotPolicy( repository.getPolicy( true ) ); 101 builder.setReleasePolicy( repository.getPolicy( false ) ); 102 103 builder.setMirroredRepositories( Collections.singletonList( repository ) ); 104 105 return builder.build(); 106 } 107 108 private MirrorDef findMirror( RemoteRepository repository ) 109 { 110 String repoId = repository.getId(); 111 112 if ( repoId != null && !mirrors.isEmpty() ) 113 { 114 for ( MirrorDef mirror : mirrors ) 115 { 116 if ( repoId.equals( mirror.mirrorOfIds ) && matchesType( repository.getContentType(), 117 mirror.mirrorOfTypes ) ) 118 { 119 return mirror; 120 } 121 } 122 123 for ( MirrorDef mirror : mirrors ) 124 { 125 if ( matchPattern( repository, mirror.mirrorOfIds ) && matchesType( repository.getContentType(), 126 mirror.mirrorOfTypes ) ) 127 { 128 return mirror; 129 } 130 } 131 } 132 133 return null; 134 } 135 136 /** 137 * This method checks if the pattern matches the originalRepository. Valid patterns: 138 * <ul> 139 * <li>{@code *} = everything,</li> 140 * <li>{@code external:*} = everything not on the localhost and not file based,</li> 141 * <li>{@code external:http:*} = any repository not on the localhost using HTTP,</li> 142 * <li>{@code repo,repo1} = {@code repo} or {@code repo1},</li> 143 * <li>{@code *,!repo1} = everything except {@code repo1}.</li> 144 * </ul> 145 * 146 * @param repository to compare for a match. 147 * @param pattern used for match. 148 * @return true if the repository is a match to this pattern. 149 */ 150 static boolean matchPattern( RemoteRepository repository, String pattern ) 151 { 152 boolean result = false; 153 String originalId = repository.getId(); 154 155 // simple checks first to short circuit processing below. 156 if ( WILDCARD.equals( pattern ) || pattern.equals( originalId ) ) 157 { 158 result = true; 159 } 160 else 161 { 162 // process the list 163 String[] repos = pattern.split( "," ); 164 for ( String repo : repos ) 165 { 166 // see if this is a negative match 167 if ( repo.length() > 1 && repo.startsWith( "!" ) ) 168 { 169 if ( repo.substring( 1 ).equals( originalId ) ) 170 { 171 // explicitly exclude. Set result and stop processing. 172 result = false; 173 break; 174 } 175 } 176 // check for exact match 177 else if ( repo.equals( originalId ) ) 178 { 179 result = true; 180 break; 181 } 182 // check for external:* 183 else if ( EXTERNAL_WILDCARD.equals( repo ) && isExternalRepo( repository ) ) 184 { 185 result = true; 186 // don't stop processing in case a future segment explicitly excludes this repo 187 } 188 // check for external:http:* 189 else if ( EXTERNAL_HTTP_WILDCARD.equals( repo ) && isExternalHttpRepo( repository ) ) 190 { 191 result = true; 192 // don't stop processing in case a future segment explicitly excludes this repo 193 } 194 else if ( WILDCARD.equals( repo ) ) 195 { 196 result = true; 197 // don't stop processing in case a future segment explicitly excludes this repo 198 } 199 } 200 } 201 return result; 202 } 203 204 /** 205 * Checks the URL to see if this repository refers to an external repository. 206 * 207 * @param repository The repository to check, must not be {@code null}. 208 * @return {@code true} if external, {@code false} otherwise. 209 */ 210 static boolean isExternalRepo( RemoteRepository repository ) 211 { 212 boolean local = isLocal( repository.getHost() ) || "file".equalsIgnoreCase( repository.getProtocol() ); 213 return !local; 214 } 215 216 private static boolean isLocal( String host ) 217 { 218 return "localhost".equals( host ) || "127.0.0.1".equals( host ); 219 } 220 221 /** 222 * Checks the URL to see if this repository refers to a non-localhost repository using HTTP. 223 * 224 * @param repository The repository to check, must not be {@code null}. 225 * @return {@code true} if external, {@code false} otherwise. 226 */ 227 static boolean isExternalHttpRepo( RemoteRepository repository ) 228 { 229 return ( "http".equalsIgnoreCase( repository.getProtocol() ) 230 || "dav".equalsIgnoreCase( repository.getProtocol() ) 231 || "dav:http".equalsIgnoreCase( repository.getProtocol() ) 232 || "dav+http".equalsIgnoreCase( repository.getProtocol() ) ) 233 && !isLocal( repository.getHost() ); 234 } 235 236 /** 237 * Checks whether the types configured for a mirror match with the type of the repository. 238 * 239 * @param repoType The type of the repository, may be {@code null}. 240 * @param mirrorType The types supported by the mirror, may be {@code null}. 241 * @return {@code true} if the types associated with the mirror match the type of the original repository, 242 * {@code false} otherwise. 243 */ 244 static boolean matchesType( String repoType, String mirrorType ) 245 { 246 boolean result = false; 247 248 // simple checks first to short circuit processing below. 249 if ( mirrorType == null || mirrorType.isEmpty() || WILDCARD.equals( mirrorType ) ) 250 { 251 result = true; 252 } 253 else if ( mirrorType.equals( repoType ) ) 254 { 255 result = true; 256 } 257 else 258 { 259 // process the list 260 String[] layouts = mirrorType.split( "," ); 261 for ( String layout : layouts ) 262 { 263 // see if this is a negative match 264 if ( layout.length() > 1 && layout.startsWith( "!" ) ) 265 { 266 if ( layout.substring( 1 ).equals( repoType ) ) 267 { 268 // explicitly exclude. Set result and stop processing. 269 result = false; 270 break; 271 } 272 } 273 // check for exact match 274 else if ( layout.equals( repoType ) ) 275 { 276 result = true; 277 break; 278 } 279 else if ( WILDCARD.equals( layout ) ) 280 { 281 result = true; 282 // don't stop processing in case a future segment explicitly excludes this repo 283 } 284 } 285 } 286 287 return result; 288 } 289 290 static class MirrorDef 291 { 292 293 final String id; 294 295 final String url; 296 297 final String type; 298 299 final boolean repositoryManager; 300 301 final boolean blocked; 302 303 final String mirrorOfIds; 304 305 final String mirrorOfTypes; 306 307 MirrorDef( String id, String url, String type, boolean repositoryManager, boolean blocked, String mirrorOfIds, 308 String mirrorOfTypes ) 309 { 310 this.id = id; 311 this.url = url; 312 this.type = type; 313 this.repositoryManager = repositoryManager; 314 this.blocked = blocked; 315 this.mirrorOfIds = mirrorOfIds; 316 this.mirrorOfTypes = mirrorOfTypes; 317 } 318 319 } 320 321}