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 029/** 030 * A simple mirror selector that selects mirrors based on repository identifiers. 031 */ 032public final class DefaultMirrorSelector 033 implements MirrorSelector 034{ 035 036 private static final String WILDCARD = "*"; 037 038 private static final String EXTERNAL_WILDCARD = "external:*"; 039 040 private static final String EXTERNAL_HTTP_WILDCARD = "external:http:*"; 041 042 private final List<MirrorDef> mirrors = new ArrayList<>(); 043 044 @Deprecated 045 public DefaultMirrorSelector add( String id, String url, String type, boolean repositoryManager, 046 String mirrorOfIds, String mirrorOfTypes ) 047 { 048 return add( id, url, type, repositoryManager, false, mirrorOfIds, mirrorOfTypes ); 049 } 050 051 /** 052 * Adds the specified mirror to this selector. 053 * 054 * @param id The identifier of the mirror, must not be {@code null}. 055 * @param url The URL of the mirror, must not be {@code null}. 056 * @param type The content type of the mirror, must not be {@code null}. 057 * @param repositoryManager A flag whether the mirror is a repository manager or a simple server. 058 * @param blocked A flag whether the mirror is blocked from performing any download requests. 059 * @param mirrorOfIds The identifier(s) of remote repositories to mirror, must not be {@code null}. Multiple 060 * identifiers can be separated by comma and additionally the wildcards "*", "external:http:*" and 061 * "external:*" can be used to match all (external) repositories, prefixing a repo id with an 062 * exclamation mark allows to express an exclusion. For example "external:*,!central". 063 * @param mirrorOfTypes The content type(s) of remote repositories to mirror, may be {@code null} or empty to match 064 * any content type. Similar to the repo id specification, multiple types can be comma-separated, the 065 * wildcard "*" and the "!" negation syntax are supported. For example "*,!p2". 066 * @return This selector for chaining, never {@code null}. 067 */ 068 public DefaultMirrorSelector add( String id, String url, String type, boolean repositoryManager, boolean blocked, 069 String mirrorOfIds, String mirrorOfTypes ) 070 { 071 mirrors.add( new MirrorDef( id, url, type, repositoryManager, blocked, mirrorOfIds, mirrorOfTypes ) ); 072 073 return this; 074 } 075 076 public RemoteRepository getMirror( RemoteRepository repository ) 077 { 078 MirrorDef mirror = findMirror( repository ); 079 080 if ( mirror == null ) 081 { 082 return null; 083 } 084 085 RemoteRepository.Builder builder = 086 new RemoteRepository.Builder( mirror.id, repository.getContentType(), mirror.url ); 087 088 builder.setRepositoryManager( mirror.repositoryManager ); 089 090 builder.setBlocked( mirror.blocked ); 091 092 if ( mirror.type != null && mirror.type.length() > 0 ) 093 { 094 builder.setContentType( mirror.type ); 095 } 096 097 builder.setSnapshotPolicy( repository.getPolicy( true ) ); 098 builder.setReleasePolicy( repository.getPolicy( false ) ); 099 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}