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