001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.repository; 020 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collections; 024import java.util.List; 025import java.util.Objects; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028 029import static java.util.Objects.requireNonNull; 030 031/** 032 * A repository on a remote server. 033 */ 034public final class RemoteRepository implements ArtifactRepository { 035 036 private static final Pattern URL_PATTERN = 037 Pattern.compile("([^:/]+(:[^:/]{2,}+(?=://))?):(//([^@/]*@)?([^/:]+))?.*"); 038 039 private final String id; 040 041 private final String type; 042 043 private final String url; 044 045 private final String host; 046 047 private final String protocol; 048 049 private final RepositoryPolicy releasePolicy; 050 051 private final RepositoryPolicy snapshotPolicy; 052 053 private final Proxy proxy; 054 055 private final Authentication authentication; 056 057 private final List<RemoteRepository> mirroredRepositories; 058 059 private final boolean repositoryManager; 060 061 private final boolean blocked; 062 063 RemoteRepository(Builder builder) { 064 if (builder.prototype != null) { 065 id = (builder.delta & Builder.ID) != 0 ? builder.id : builder.prototype.id; 066 type = (builder.delta & Builder.TYPE) != 0 ? builder.type : builder.prototype.type; 067 url = (builder.delta & Builder.URL) != 0 ? builder.url : builder.prototype.url; 068 releasePolicy = 069 (builder.delta & Builder.RELEASES) != 0 ? builder.releasePolicy : builder.prototype.releasePolicy; 070 snapshotPolicy = (builder.delta & Builder.SNAPSHOTS) != 0 071 ? builder.snapshotPolicy 072 : builder.prototype.snapshotPolicy; 073 proxy = (builder.delta & Builder.PROXY) != 0 ? builder.proxy : builder.prototype.proxy; 074 authentication = 075 (builder.delta & Builder.AUTH) != 0 ? builder.authentication : builder.prototype.authentication; 076 repositoryManager = (builder.delta & Builder.REPOMAN) != 0 077 ? builder.repositoryManager 078 : builder.prototype.repositoryManager; 079 blocked = (builder.delta & Builder.BLOCKED) != 0 ? builder.blocked : builder.prototype.blocked; 080 mirroredRepositories = (builder.delta & Builder.MIRRORED) != 0 081 ? copy(builder.mirroredRepositories) 082 : builder.prototype.mirroredRepositories; 083 } else { 084 id = builder.id; 085 type = builder.type; 086 url = builder.url; 087 releasePolicy = builder.releasePolicy; 088 snapshotPolicy = builder.snapshotPolicy; 089 proxy = builder.proxy; 090 authentication = builder.authentication; 091 repositoryManager = builder.repositoryManager; 092 blocked = builder.blocked; 093 mirroredRepositories = copy(builder.mirroredRepositories); 094 } 095 096 Matcher m = URL_PATTERN.matcher(url); 097 if (m.matches()) { 098 protocol = m.group(1); 099 String host = m.group(5); 100 this.host = (host != null) ? host : ""; 101 } else { 102 protocol = ""; 103 host = ""; 104 } 105 } 106 107 private static List<RemoteRepository> copy(List<RemoteRepository> repos) { 108 if (repos == null || repos.isEmpty()) { 109 return Collections.emptyList(); 110 } 111 return Collections.unmodifiableList(Arrays.asList(repos.toArray(new RemoteRepository[0]))); 112 } 113 114 public String getId() { 115 return id; 116 } 117 118 public String getContentType() { 119 return type; 120 } 121 122 /** 123 * Gets the (base) URL of this repository. 124 * 125 * @return The (base) URL of this repository, never {@code null}. 126 */ 127 public String getUrl() { 128 return url; 129 } 130 131 /** 132 * Gets the protocol part from the repository's URL, for example {@code file} or {@code http}. As suggested by RFC 133 * 2396, section 3.1 "Scheme Component", the protocol name should be treated case-insensitively. 134 * 135 * @return The protocol or an empty string if none, never {@code null}. 136 */ 137 public String getProtocol() { 138 return protocol; 139 } 140 141 /** 142 * Gets the host part from the repository's URL. 143 * 144 * @return The host or an empty string if none, never {@code null}. 145 */ 146 public String getHost() { 147 return host; 148 } 149 150 /** 151 * Gets the policy to apply for snapshot/release artifacts. 152 * 153 * @param snapshot {@code true} to retrieve the snapshot policy, {@code false} to retrieve the release policy. 154 * @return The requested repository policy, never {@code null}. 155 */ 156 public RepositoryPolicy getPolicy(boolean snapshot) { 157 return snapshot ? snapshotPolicy : releasePolicy; 158 } 159 160 /** 161 * Gets the proxy that has been selected for this repository. 162 * 163 * @return The selected proxy or {@code null} if none. 164 */ 165 public Proxy getProxy() { 166 return proxy; 167 } 168 169 /** 170 * Gets the authentication that has been selected for this repository. 171 * 172 * @return The selected authentication or {@code null} if none. 173 */ 174 public Authentication getAuthentication() { 175 return authentication; 176 } 177 178 /** 179 * Gets the repositories that this repository serves as a mirror for. 180 * 181 * @return The (read-only) repositories being mirrored by this repository, never {@code null}. 182 */ 183 public List<RemoteRepository> getMirroredRepositories() { 184 return mirroredRepositories; 185 } 186 187 /** 188 * Indicates whether this repository refers to a repository manager or not. 189 * 190 * @return {@code true} if this repository is a repository manager, {@code false} otherwise. 191 */ 192 public boolean isRepositoryManager() { 193 return repositoryManager; 194 } 195 196 /** 197 * Indicates whether this repository is blocked from performing any download requests. 198 * 199 * @return {@code true} if this repository is blocked from performing any download requests, 200 * {@code false} otherwise. 201 */ 202 public boolean isBlocked() { 203 return blocked; 204 } 205 206 @Override 207 public String toString() { 208 StringBuilder buffer = new StringBuilder(256); 209 buffer.append(getId()); 210 buffer.append(" (").append(getUrl()); 211 buffer.append(", ").append(getContentType()); 212 boolean r = getPolicy(false).isEnabled(), s = getPolicy(true).isEnabled(); 213 if (r && s) { 214 buffer.append(", releases+snapshots"); 215 } else if (r) { 216 buffer.append(", releases"); 217 } else if (s) { 218 buffer.append(", snapshots"); 219 } else { 220 buffer.append(", disabled"); 221 } 222 if (isRepositoryManager()) { 223 buffer.append(", managed"); 224 } 225 if (isBlocked()) { 226 buffer.append(", blocked"); 227 } 228 buffer.append(")"); 229 return buffer.toString(); 230 } 231 232 @Override 233 public boolean equals(Object obj) { 234 if (this == obj) { 235 return true; 236 } 237 if (obj == null || !getClass().equals(obj.getClass())) { 238 return false; 239 } 240 241 RemoteRepository that = (RemoteRepository) obj; 242 243 return Objects.equals(url, that.url) 244 && Objects.equals(type, that.type) 245 && Objects.equals(id, that.id) 246 && Objects.equals(releasePolicy, that.releasePolicy) 247 && Objects.equals(snapshotPolicy, that.snapshotPolicy) 248 && Objects.equals(proxy, that.proxy) 249 && Objects.equals(authentication, that.authentication) 250 && Objects.equals(mirroredRepositories, that.mirroredRepositories) 251 && repositoryManager == that.repositoryManager; 252 } 253 254 @Override 255 public int hashCode() { 256 int hash = 17; 257 hash = hash * 31 + hash(url); 258 hash = hash * 31 + hash(type); 259 hash = hash * 31 + hash(id); 260 hash = hash * 31 + hash(releasePolicy); 261 hash = hash * 31 + hash(snapshotPolicy); 262 hash = hash * 31 + hash(proxy); 263 hash = hash * 31 + hash(authentication); 264 hash = hash * 31 + hash(mirroredRepositories); 265 hash = hash * 31 + (repositoryManager ? 1 : 0); 266 return hash; 267 } 268 269 private static int hash(Object obj) { 270 return obj != null ? obj.hashCode() : 0; 271 } 272 273 /** 274 * A builder to create remote repositories. 275 */ 276 public static final class Builder { 277 278 private static final RepositoryPolicy DEFAULT_POLICY = new RepositoryPolicy(); 279 280 static final int ID = 0x0001, 281 TYPE = 0x0002, 282 URL = 0x0004, 283 RELEASES = 0x0008, 284 SNAPSHOTS = 0x0010, 285 PROXY = 0x0020, 286 AUTH = 0x0040, 287 MIRRORED = 0x0080, 288 REPOMAN = 0x0100, 289 BLOCKED = 0x0200; 290 291 int delta; 292 293 RemoteRepository prototype; 294 295 String id; 296 297 String type; 298 299 String url; 300 301 RepositoryPolicy releasePolicy = DEFAULT_POLICY; 302 303 RepositoryPolicy snapshotPolicy = DEFAULT_POLICY; 304 305 Proxy proxy; 306 307 Authentication authentication; 308 309 List<RemoteRepository> mirroredRepositories; 310 311 boolean repositoryManager; 312 313 boolean blocked; 314 315 /** 316 * Creates a new repository builder. 317 * 318 * @param id The identifier of the repository, may be {@code null}. 319 * @param type The type of the repository, may be {@code null}. 320 * @param url The (base) URL of the repository, may be {@code null}. 321 */ 322 public Builder(String id, String type, String url) { 323 this.id = (id != null) ? id : ""; 324 this.type = (type != null) ? type : ""; 325 this.url = (url != null) ? url : ""; 326 } 327 328 /** 329 * Creates a new repository builder which uses the specified remote repository as a prototype for the new one. 330 * All properties which have not been set on the builder will be copied from the prototype when building the 331 * repository. 332 * 333 * @param prototype The remote repository to use as prototype, must not be {@code null}. 334 */ 335 public Builder(RemoteRepository prototype) { 336 this.prototype = requireNonNull(prototype, "remote repository prototype cannot be null"); 337 } 338 339 /** 340 * Builds a new remote repository from the current values of this builder. The state of the builder itself 341 * remains unchanged. 342 * 343 * @return The remote repository, never {@code null}. 344 */ 345 public RemoteRepository build() { 346 if (prototype != null && delta == 0) { 347 return prototype; 348 } 349 return new RemoteRepository(this); 350 } 351 352 private <T> void delta(int flag, T builder, T prototype) { 353 boolean equal = Objects.equals(builder, prototype); 354 if (equal) { 355 delta &= ~flag; 356 } else { 357 delta |= flag; 358 } 359 } 360 361 /** 362 * Sets the identifier of the repository. 363 * 364 * @param id The identifier of the repository, may be {@code null}. 365 * @return This builder for chaining, never {@code null}. 366 */ 367 public Builder setId(String id) { 368 this.id = (id != null) ? id : ""; 369 if (prototype != null) { 370 delta(ID, this.id, prototype.getId()); 371 } 372 return this; 373 } 374 375 /** 376 * Sets the type of the repository, e.g. "default". 377 * 378 * @param type The type of the repository, may be {@code null}. 379 * @return This builder for chaining, never {@code null}. 380 */ 381 public Builder setContentType(String type) { 382 this.type = (type != null) ? type : ""; 383 if (prototype != null) { 384 delta(TYPE, this.type, prototype.getContentType()); 385 } 386 return this; 387 } 388 389 /** 390 * Sets the (base) URL of the repository. 391 * 392 * @param url The URL of the repository, may be {@code null}. 393 * @return This builder for chaining, never {@code null}. 394 */ 395 public Builder setUrl(String url) { 396 this.url = (url != null) ? url : ""; 397 if (prototype != null) { 398 delta(URL, this.url, prototype.getUrl()); 399 } 400 return this; 401 } 402 403 /** 404 * Sets the policy to apply for snapshot and release artifacts. 405 * 406 * @param policy The repository policy to set, may be {@code null} to use a default policy. 407 * @return This builder for chaining, never {@code null}. 408 */ 409 public Builder setPolicy(RepositoryPolicy policy) { 410 this.releasePolicy = (policy != null) ? policy : DEFAULT_POLICY; 411 this.snapshotPolicy = (policy != null) ? policy : DEFAULT_POLICY; 412 if (prototype != null) { 413 delta(RELEASES, this.releasePolicy, prototype.getPolicy(false)); 414 delta(SNAPSHOTS, this.snapshotPolicy, prototype.getPolicy(true)); 415 } 416 return this; 417 } 418 419 /** 420 * Sets the policy to apply for release artifacts. 421 * 422 * @param releasePolicy The repository policy to set, may be {@code null} to use a default policy. 423 * @return This builder for chaining, never {@code null}. 424 */ 425 public Builder setReleasePolicy(RepositoryPolicy releasePolicy) { 426 this.releasePolicy = (releasePolicy != null) ? releasePolicy : DEFAULT_POLICY; 427 if (prototype != null) { 428 delta(RELEASES, this.releasePolicy, prototype.getPolicy(false)); 429 } 430 return this; 431 } 432 433 /** 434 * Sets the policy to apply for snapshot artifacts. 435 * 436 * @param snapshotPolicy The repository policy to set, may be {@code null} to use a default policy. 437 * @return This builder for chaining, never {@code null}. 438 */ 439 public Builder setSnapshotPolicy(RepositoryPolicy snapshotPolicy) { 440 this.snapshotPolicy = (snapshotPolicy != null) ? snapshotPolicy : DEFAULT_POLICY; 441 if (prototype != null) { 442 delta(SNAPSHOTS, this.snapshotPolicy, prototype.getPolicy(true)); 443 } 444 return this; 445 } 446 447 /** 448 * Sets the proxy to use in order to access the repository. 449 * 450 * @param proxy The proxy to use, may be {@code null}. 451 * @return This builder for chaining, never {@code null}. 452 */ 453 public Builder setProxy(Proxy proxy) { 454 this.proxy = proxy; 455 if (prototype != null) { 456 delta(PROXY, this.proxy, prototype.getProxy()); 457 } 458 return this; 459 } 460 461 /** 462 * Sets the authentication to use in order to access the repository. 463 * 464 * @param authentication The authentication to use, may be {@code null}. 465 * @return This builder for chaining, never {@code null}. 466 */ 467 public Builder setAuthentication(Authentication authentication) { 468 this.authentication = authentication; 469 if (prototype != null) { 470 delta(AUTH, this.authentication, prototype.getAuthentication()); 471 } 472 return this; 473 } 474 475 /** 476 * Sets the repositories being mirrored by the repository. 477 * 478 * @param mirroredRepositories The repositories being mirrored by the repository, may be {@code null}. 479 * @return This builder for chaining, never {@code null}. 480 */ 481 public Builder setMirroredRepositories(List<RemoteRepository> mirroredRepositories) { 482 if (this.mirroredRepositories == null) { 483 this.mirroredRepositories = new ArrayList<>(); 484 } else { 485 this.mirroredRepositories.clear(); 486 } 487 if (mirroredRepositories != null) { 488 this.mirroredRepositories.addAll(mirroredRepositories); 489 } 490 if (prototype != null) { 491 delta(MIRRORED, this.mirroredRepositories, prototype.getMirroredRepositories()); 492 } 493 return this; 494 } 495 496 /** 497 * Adds the specified repository to the list of repositories being mirrored by the repository. If this builder 498 * was {@link Builder constructed from a prototype}, the given repository 499 * will be added to the list of mirrored repositories from the prototype. 500 * 501 * @param mirroredRepository The repository being mirrored by the repository, may be {@code null}. 502 * @return This builder for chaining, never {@code null}. 503 */ 504 public Builder addMirroredRepository(RemoteRepository mirroredRepository) { 505 if (mirroredRepository != null) { 506 if (this.mirroredRepositories == null) { 507 this.mirroredRepositories = new ArrayList<>(); 508 if (prototype != null) { 509 mirroredRepositories.addAll(prototype.getMirroredRepositories()); 510 } 511 } 512 mirroredRepositories.add(mirroredRepository); 513 if (prototype != null) { 514 delta |= MIRRORED; 515 } 516 } 517 return this; 518 } 519 520 /** 521 * Marks the repository as a repository manager or not. 522 * 523 * @param repositoryManager {@code true} if the repository points at a repository manager, {@code false} if the 524 * repository is just serving static contents. 525 * @return This builder for chaining, never {@code null}. 526 */ 527 public Builder setRepositoryManager(boolean repositoryManager) { 528 this.repositoryManager = repositoryManager; 529 if (prototype != null) { 530 delta(REPOMAN, this.repositoryManager, prototype.isRepositoryManager()); 531 } 532 return this; 533 } 534 535 /** 536 * Marks the repository as blocked or not. 537 * 538 * @param blocked {@code true} if the repository should not be allowed to perform any requests. 539 * @return This builder for chaining, never {@code null}. 540 */ 541 public Builder setBlocked(boolean blocked) { 542 this.blocked = blocked; 543 if (prototype != null) { 544 delta(BLOCKED, this.blocked, prototype.isBlocked()); 545 } 546 return this; 547 } 548 } 549}