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