1 /* 2 * Copyright 2015 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 22 import com.google.common.base.MoreObjects; 23 import com.google.common.base.Objects; 24 import com.google.errorprone.annotations.InlineMe; 25 import java.lang.annotation.Documented; 26 import java.lang.annotation.Retention; 27 import java.lang.annotation.RetentionPolicy; 28 import java.net.URI; 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.concurrent.Executor; 34 import java.util.concurrent.ScheduledExecutorService; 35 import javax.annotation.Nullable; 36 import javax.annotation.concurrent.ThreadSafe; 37 38 /** 39 * A pluggable component that resolves a target {@link URI} and return addresses to the caller. 40 * 41 * <p>A {@code NameResolver} uses the URI's scheme to determine whether it can resolve it, and uses 42 * the components after the scheme for actual resolution. 43 * 44 * <p>The addresses and attributes of a target may be changed over time, thus the caller registers a 45 * {@link Listener} to receive continuous updates. 46 * 47 * <p>A {@code NameResolver} does not need to automatically re-resolve on failure. Instead, the 48 * {@link Listener} is responsible for eventually (after an appropriate backoff period) invoking 49 * {@link #refresh()}. 50 * 51 * <p>Implementations <strong>don't need to be thread-safe</strong>. All methods are guaranteed to 52 * be called sequentially. Additionally, all methods that have side-effects, i.e., 53 * {@link #start(Listener2)}, {@link #shutdown} and {@link #refresh} are called from the same 54 * {@link SynchronizationContext} as returned by {@link Args#getSynchronizationContext}. <strong>Do 55 * not block</strong> within the synchronization context; blocking I/O and time-consuming tasks 56 * should be offloaded to a separate thread, generally {@link Args#getOffloadExecutor}. 57 * 58 * @since 1.0.0 59 */ 60 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770") 61 public abstract class NameResolver { 62 /** 63 * Returns the authority used to authenticate connections to servers. It <strong>must</strong> be 64 * from a trusted source, because if the authority is tampered with, RPCs may be sent to the 65 * attackers which may leak sensitive user data. 66 * 67 * <p>An implementation must generate it without blocking, typically in line, and 68 * <strong>must</strong> keep it unchanged. {@code NameResolver}s created from the same factory 69 * with the same argument must return the same authority. 70 * 71 * @since 1.0.0 72 */ getServiceAuthority()73 public abstract String getServiceAuthority(); 74 75 /** 76 * Starts the resolution. The method is not supposed to throw any exceptions. That might cause the 77 * Channel that the name resolver is serving to crash. Errors should be propagated 78 * through {@link Listener#onError}. 79 * 80 * @param listener used to receive updates on the target 81 * @since 1.0.0 82 */ start(final Listener listener)83 public void start(final Listener listener) { 84 if (listener instanceof Listener2) { 85 start((Listener2) listener); 86 } else { 87 start(new Listener2() { 88 @Override 89 public void onError(Status error) { 90 listener.onError(error); 91 } 92 93 @Override 94 public void onResult(ResolutionResult resolutionResult) { 95 listener.onAddresses(resolutionResult.getAddresses(), resolutionResult.getAttributes()); 96 } 97 }); 98 } 99 } 100 101 /** 102 * Starts the resolution. The method is not supposed to throw any exceptions. That might cause the 103 * Channel that the name resolver is serving to crash. Errors should be propagated 104 * through {@link Listener2#onError}. 105 * 106 * @param listener used to receive updates on the target 107 * @since 1.21.0 108 */ start(Listener2 listener)109 public void start(Listener2 listener) { 110 start((Listener) listener); 111 } 112 113 /** 114 * Stops the resolution. Updates to the Listener will stop. 115 * 116 * @since 1.0.0 117 */ shutdown()118 public abstract void shutdown(); 119 120 /** 121 * Re-resolve the name. 122 * 123 * <p>Can only be called after {@link #start} has been called. 124 * 125 * <p>This is only a hint. Implementation takes it as a signal but may not start resolution 126 * immediately. It should never throw. 127 * 128 * <p>The default implementation is no-op. 129 * 130 * @since 1.0.0 131 */ refresh()132 public void refresh() {} 133 134 /** 135 * Factory that creates {@link NameResolver} instances. 136 * 137 * @since 1.0.0 138 */ 139 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770") 140 public abstract static class Factory { 141 /** 142 * Creates a {@link NameResolver} for the given target URI, or {@code null} if the given URI 143 * cannot be resolved by this factory. The decision should be solely based on the scheme of the 144 * URI. 145 * 146 * @param targetUri the target URI to be resolved, whose scheme must not be {@code null} 147 * @param args other information that may be useful 148 * 149 * @since 1.21.0 150 */ newNameResolver(URI targetUri, final Args args)151 public abstract NameResolver newNameResolver(URI targetUri, final Args args); 152 153 /** 154 * Returns the default scheme, which will be used to construct a URI when {@link 155 * ManagedChannelBuilder#forTarget(String)} is given an authority string instead of a compliant 156 * URI. 157 * 158 * @since 1.0.0 159 */ getDefaultScheme()160 public abstract String getDefaultScheme(); 161 } 162 163 /** 164 * Receives address updates. 165 * 166 * <p>All methods are expected to return quickly. 167 * 168 * @since 1.0.0 169 */ 170 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770") 171 @ThreadSafe 172 public interface Listener { 173 /** 174 * Handles updates on resolved addresses and attributes. 175 * 176 * <p>Implementations will not modify the given {@code servers}. 177 * 178 * @param servers the resolved server addresses. An empty list will trigger {@link #onError} 179 * @param attributes extra information from naming system. 180 * @since 1.3.0 181 */ onAddresses( List<EquivalentAddressGroup> servers, @ResolutionResultAttr Attributes attributes)182 void onAddresses( 183 List<EquivalentAddressGroup> servers, @ResolutionResultAttr Attributes attributes); 184 185 /** 186 * Handles an error from the resolver. The listener is responsible for eventually invoking 187 * {@link #refresh()} to re-attempt resolution. 188 * 189 * @param error a non-OK status 190 * @since 1.0.0 191 */ onError(Status error)192 void onError(Status error); 193 } 194 195 /** 196 * Receives address updates. 197 * 198 * <p>All methods are expected to return quickly. 199 * 200 * <p>This is a replacement API of {@code Listener}. However, we think this new API may change 201 * again, so we aren't yet encouraging mass-migration to it. It is fine to use and works. 202 * 203 * @since 1.21.0 204 */ 205 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770") 206 public abstract static class Listener2 implements Listener { 207 /** 208 * Handles updates on resolved addresses and attributes. 209 * 210 * @deprecated This will be removed in 1.22.0 211 */ 212 @Override 213 @Deprecated 214 @InlineMe( 215 replacement = "this.onResult(ResolutionResult.newBuilder().setAddresses(servers)" 216 + ".setAttributes(attributes).build())", 217 imports = "io.grpc.NameResolver.ResolutionResult") onAddresses( List<EquivalentAddressGroup> servers, @ResolutionResultAttr Attributes attributes)218 public final void onAddresses( 219 List<EquivalentAddressGroup> servers, @ResolutionResultAttr Attributes attributes) { 220 // TODO(jihuncho) need to promote Listener2 if we want to use ConfigOrError 221 onResult( 222 ResolutionResult.newBuilder().setAddresses(servers).setAttributes(attributes).build()); 223 } 224 225 /** 226 * Handles updates on resolved addresses and attributes. If 227 * {@link ResolutionResult#getAddresses()} is empty, {@link #onError(Status)} will be called. 228 * 229 * @param resolutionResult the resolved server addresses, attributes, and Service Config. 230 * @since 1.21.0 231 */ onResult(ResolutionResult resolutionResult)232 public abstract void onResult(ResolutionResult resolutionResult); 233 234 /** 235 * Handles a name resolving error from the resolver. The listener is responsible for eventually 236 * invoking {@link NameResolver#refresh()} to re-attempt resolution. 237 * 238 * @param error a non-OK status 239 * @since 1.21.0 240 */ 241 @Override onError(Status error)242 public abstract void onError(Status error); 243 } 244 245 /** 246 * Annotation for name resolution result attributes. It follows the annotation semantics defined 247 * by {@link Attributes}. 248 */ 249 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4972") 250 @Retention(RetentionPolicy.SOURCE) 251 @Documented 252 public @interface ResolutionResultAttr {} 253 254 /** 255 * Information that a {@link Factory} uses to create a {@link NameResolver}. 256 * 257 * <p>Note this class doesn't override neither {@code equals()} nor {@code hashCode()}. 258 * 259 * @since 1.21.0 260 */ 261 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770") 262 public static final class Args { 263 private final int defaultPort; 264 private final ProxyDetector proxyDetector; 265 private final SynchronizationContext syncContext; 266 private final ServiceConfigParser serviceConfigParser; 267 @Nullable private final ScheduledExecutorService scheduledExecutorService; 268 @Nullable private final ChannelLogger channelLogger; 269 @Nullable private final Executor executor; 270 @Nullable private final String overrideAuthority; 271 Args( Integer defaultPort, ProxyDetector proxyDetector, SynchronizationContext syncContext, ServiceConfigParser serviceConfigParser, @Nullable ScheduledExecutorService scheduledExecutorService, @Nullable ChannelLogger channelLogger, @Nullable Executor executor, @Nullable String overrideAuthority)272 private Args( 273 Integer defaultPort, 274 ProxyDetector proxyDetector, 275 SynchronizationContext syncContext, 276 ServiceConfigParser serviceConfigParser, 277 @Nullable ScheduledExecutorService scheduledExecutorService, 278 @Nullable ChannelLogger channelLogger, 279 @Nullable Executor executor, 280 @Nullable String overrideAuthority) { 281 this.defaultPort = checkNotNull(defaultPort, "defaultPort not set"); 282 this.proxyDetector = checkNotNull(proxyDetector, "proxyDetector not set"); 283 this.syncContext = checkNotNull(syncContext, "syncContext not set"); 284 this.serviceConfigParser = checkNotNull(serviceConfigParser, "serviceConfigParser not set"); 285 this.scheduledExecutorService = scheduledExecutorService; 286 this.channelLogger = channelLogger; 287 this.executor = executor; 288 this.overrideAuthority = overrideAuthority; 289 } 290 291 /** 292 * The port number used in case the target or the underlying naming system doesn't provide a 293 * port number. 294 * 295 * @since 1.21.0 296 */ getDefaultPort()297 public int getDefaultPort() { 298 return defaultPort; 299 } 300 301 /** 302 * If the NameResolver wants to support proxy, it should inquire this {@link ProxyDetector}. 303 * See documentation on {@link ProxyDetector} about how proxies work in gRPC. 304 * 305 * @since 1.21.0 306 */ getProxyDetector()307 public ProxyDetector getProxyDetector() { 308 return proxyDetector; 309 } 310 311 /** 312 * Returns the {@link SynchronizationContext} where {@link #start(Listener2)}, {@link #shutdown} 313 * and {@link #refresh} are run from. 314 * 315 * @since 1.21.0 316 */ getSynchronizationContext()317 public SynchronizationContext getSynchronizationContext() { 318 return syncContext; 319 } 320 321 /** 322 * Returns a {@link ScheduledExecutorService} for scheduling delayed tasks. 323 * 324 * <p>This service is a shared resource and is only meant for quick tasks. DO NOT block or run 325 * time-consuming tasks. 326 * 327 * <p>The returned service doesn't support {@link ScheduledExecutorService#shutdown shutdown()} 328 * and {@link ScheduledExecutorService#shutdownNow shutdownNow()}. They will throw if called. 329 * 330 * @since 1.26.0 331 */ 332 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454") getScheduledExecutorService()333 public ScheduledExecutorService getScheduledExecutorService() { 334 if (scheduledExecutorService == null) { 335 throw new IllegalStateException("ScheduledExecutorService not set in Builder"); 336 } 337 return scheduledExecutorService; 338 } 339 340 /** 341 * Returns the {@link ServiceConfigParser}. 342 * 343 * @since 1.21.0 344 */ getServiceConfigParser()345 public ServiceConfigParser getServiceConfigParser() { 346 return serviceConfigParser; 347 } 348 349 /** 350 * Returns the {@link ChannelLogger} for the Channel served by this NameResolver. 351 * 352 * @since 1.26.0 353 */ 354 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438") getChannelLogger()355 public ChannelLogger getChannelLogger() { 356 if (channelLogger == null) { 357 throw new IllegalStateException("ChannelLogger is not set in Builder"); 358 } 359 return channelLogger; 360 } 361 362 /** 363 * Returns the Executor on which this resolver should execute long-running or I/O bound work. 364 * Null if no Executor was set. 365 * 366 * @since 1.25.0 367 */ 368 @Nullable getOffloadExecutor()369 public Executor getOffloadExecutor() { 370 return executor; 371 } 372 373 /** 374 * Returns the overrideAuthority from channel {@link ManagedChannelBuilder#overrideAuthority}. 375 * Overrides the host name for L7 HTTP virtual host matching. Almost all name resolvers should 376 * not use this. 377 * 378 * @since 1.49.0 379 */ 380 @Nullable 381 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406") getOverrideAuthority()382 public String getOverrideAuthority() { 383 return overrideAuthority; 384 } 385 386 387 @Override toString()388 public String toString() { 389 return MoreObjects.toStringHelper(this) 390 .add("defaultPort", defaultPort) 391 .add("proxyDetector", proxyDetector) 392 .add("syncContext", syncContext) 393 .add("serviceConfigParser", serviceConfigParser) 394 .add("scheduledExecutorService", scheduledExecutorService) 395 .add("channelLogger", channelLogger) 396 .add("executor", executor) 397 .add("overrideAuthority", overrideAuthority) 398 .toString(); 399 } 400 401 /** 402 * Returns a builder with the same initial values as this object. 403 * 404 * @since 1.21.0 405 */ toBuilder()406 public Builder toBuilder() { 407 Builder builder = new Builder(); 408 builder.setDefaultPort(defaultPort); 409 builder.setProxyDetector(proxyDetector); 410 builder.setSynchronizationContext(syncContext); 411 builder.setServiceConfigParser(serviceConfigParser); 412 builder.setScheduledExecutorService(scheduledExecutorService); 413 builder.setChannelLogger(channelLogger); 414 builder.setOffloadExecutor(executor); 415 builder.setOverrideAuthority(overrideAuthority); 416 return builder; 417 } 418 419 /** 420 * Creates a new builder. 421 * 422 * @since 1.21.0 423 */ newBuilder()424 public static Builder newBuilder() { 425 return new Builder(); 426 } 427 428 /** 429 * Builder for {@link Args}. 430 * 431 * @since 1.21.0 432 */ 433 public static final class Builder { 434 private Integer defaultPort; 435 private ProxyDetector proxyDetector; 436 private SynchronizationContext syncContext; 437 private ServiceConfigParser serviceConfigParser; 438 private ScheduledExecutorService scheduledExecutorService; 439 private ChannelLogger channelLogger; 440 private Executor executor; 441 private String overrideAuthority; 442 Builder()443 Builder() { 444 } 445 446 /** 447 * See {@link Args#getDefaultPort}. This is a required field. 448 * 449 * @since 1.21.0 450 */ setDefaultPort(int defaultPort)451 public Builder setDefaultPort(int defaultPort) { 452 this.defaultPort = defaultPort; 453 return this; 454 } 455 456 /** 457 * See {@link Args#getProxyDetector}. This is required field. 458 * 459 * @since 1.21.0 460 */ setProxyDetector(ProxyDetector proxyDetector)461 public Builder setProxyDetector(ProxyDetector proxyDetector) { 462 this.proxyDetector = checkNotNull(proxyDetector); 463 return this; 464 } 465 466 /** 467 * See {@link Args#getSynchronizationContext}. This is a required field. 468 * 469 * @since 1.21.0 470 */ setSynchronizationContext(SynchronizationContext syncContext)471 public Builder setSynchronizationContext(SynchronizationContext syncContext) { 472 this.syncContext = checkNotNull(syncContext); 473 return this; 474 } 475 476 /** 477 * See {@link Args#getScheduledExecutorService}. 478 */ 479 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454") setScheduledExecutorService( ScheduledExecutorService scheduledExecutorService)480 public Builder setScheduledExecutorService( 481 ScheduledExecutorService scheduledExecutorService) { 482 this.scheduledExecutorService = checkNotNull(scheduledExecutorService); 483 return this; 484 } 485 486 /** 487 * See {@link Args#getServiceConfigParser}. This is a required field. 488 * 489 * @since 1.21.0 490 */ setServiceConfigParser(ServiceConfigParser parser)491 public Builder setServiceConfigParser(ServiceConfigParser parser) { 492 this.serviceConfigParser = checkNotNull(parser); 493 return this; 494 } 495 496 /** 497 * See {@link Args#getChannelLogger}. 498 * 499 * @since 1.26.0 500 */ 501 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438") setChannelLogger(ChannelLogger channelLogger)502 public Builder setChannelLogger(ChannelLogger channelLogger) { 503 this.channelLogger = checkNotNull(channelLogger); 504 return this; 505 } 506 507 /** 508 * See {@link Args#getOffloadExecutor}. This is an optional field. 509 * 510 * @since 1.25.0 511 */ setOffloadExecutor(Executor executor)512 public Builder setOffloadExecutor(Executor executor) { 513 this.executor = executor; 514 return this; 515 } 516 517 /** 518 * See {@link Args#getOverrideAuthority()}. This is an optional field. 519 * 520 * @since 1.49.0 521 */ 522 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406") setOverrideAuthority(String authority)523 public Builder setOverrideAuthority(String authority) { 524 this.overrideAuthority = authority; 525 return this; 526 } 527 528 /** 529 * Builds an {@link Args}. 530 * 531 * @since 1.21.0 532 */ build()533 public Args build() { 534 return 535 new Args( 536 defaultPort, proxyDetector, syncContext, serviceConfigParser, 537 scheduledExecutorService, channelLogger, executor, overrideAuthority); 538 } 539 } 540 } 541 542 /** 543 * Parses and validates service configuration. 544 * 545 * @since 1.21.0 546 */ 547 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770") 548 public abstract static class ServiceConfigParser { 549 /** 550 * Parses and validates the service configuration chosen by the name resolver. This will 551 * return a {@link ConfigOrError} which contains either the successfully parsed config, or the 552 * {@link Status} representing the failure to parse. Implementations are expected to not throw 553 * exceptions but return a Status representing the failure. The value inside the 554 * {@link ConfigOrError} should implement {@code equals()} and {@code hashCode()}. 555 * 556 * @param rawServiceConfig The {@link Map} representation of the service config 557 * @return a tuple of the fully parsed and validated channel configuration, else the Status. 558 * @since 1.21.0 559 */ parseServiceConfig(Map<String, ?> rawServiceConfig)560 public abstract ConfigOrError parseServiceConfig(Map<String, ?> rawServiceConfig); 561 } 562 563 /** 564 * Represents the results from a Name Resolver. 565 * 566 * @since 1.21.0 567 */ 568 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770") 569 public static final class ResolutionResult { 570 private final List<EquivalentAddressGroup> addresses; 571 @ResolutionResultAttr 572 private final Attributes attributes; 573 @Nullable 574 private final ConfigOrError serviceConfig; 575 ResolutionResult( List<EquivalentAddressGroup> addresses, @ResolutionResultAttr Attributes attributes, ConfigOrError serviceConfig)576 ResolutionResult( 577 List<EquivalentAddressGroup> addresses, 578 @ResolutionResultAttr Attributes attributes, 579 ConfigOrError serviceConfig) { 580 this.addresses = Collections.unmodifiableList(new ArrayList<>(addresses)); 581 this.attributes = checkNotNull(attributes, "attributes"); 582 this.serviceConfig = serviceConfig; 583 } 584 585 /** 586 * Constructs a new builder of a name resolution result. 587 * 588 * @since 1.21.0 589 */ newBuilder()590 public static Builder newBuilder() { 591 return new Builder(); 592 } 593 594 /** 595 * Converts these results back to a builder. 596 * 597 * @since 1.21.0 598 */ toBuilder()599 public Builder toBuilder() { 600 return newBuilder() 601 .setAddresses(addresses) 602 .setAttributes(attributes) 603 .setServiceConfig(serviceConfig); 604 } 605 606 /** 607 * Gets the addresses resolved by name resolution. 608 * 609 * @since 1.21.0 610 */ getAddresses()611 public List<EquivalentAddressGroup> getAddresses() { 612 return addresses; 613 } 614 615 /** 616 * Gets the attributes associated with the addresses resolved by name resolution. If there are 617 * no attributes, {@link Attributes#EMPTY} will be returned. 618 * 619 * @since 1.21.0 620 */ 621 @ResolutionResultAttr getAttributes()622 public Attributes getAttributes() { 623 return attributes; 624 } 625 626 /** 627 * Gets the Service Config parsed by {@link Args#getServiceConfigParser}. 628 * 629 * @since 1.21.0 630 */ 631 @Nullable getServiceConfig()632 public ConfigOrError getServiceConfig() { 633 return serviceConfig; 634 } 635 636 @Override toString()637 public String toString() { 638 return MoreObjects.toStringHelper(this) 639 .add("addresses", addresses) 640 .add("attributes", attributes) 641 .add("serviceConfig", serviceConfig) 642 .toString(); 643 } 644 645 /** 646 * Useful for testing. May be slow to calculate. 647 */ 648 @Override equals(Object obj)649 public boolean equals(Object obj) { 650 if (!(obj instanceof ResolutionResult)) { 651 return false; 652 } 653 ResolutionResult that = (ResolutionResult) obj; 654 return Objects.equal(this.addresses, that.addresses) 655 && Objects.equal(this.attributes, that.attributes) 656 && Objects.equal(this.serviceConfig, that.serviceConfig); 657 } 658 659 /** 660 * Useful for testing. May be slow to calculate. 661 */ 662 @Override hashCode()663 public int hashCode() { 664 return Objects.hashCode(addresses, attributes, serviceConfig); 665 } 666 667 /** 668 * A builder for {@link ResolutionResult}. 669 * 670 * @since 1.21.0 671 */ 672 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770") 673 public static final class Builder { 674 private List<EquivalentAddressGroup> addresses = Collections.emptyList(); 675 private Attributes attributes = Attributes.EMPTY; 676 @Nullable 677 private ConfigOrError serviceConfig; 678 // Make sure to update #toBuilder above! 679 Builder()680 Builder() {} 681 682 /** 683 * Sets the addresses resolved by name resolution. This field is required. 684 * 685 * @since 1.21.0 686 */ setAddresses(List<EquivalentAddressGroup> addresses)687 public Builder setAddresses(List<EquivalentAddressGroup> addresses) { 688 this.addresses = addresses; 689 return this; 690 } 691 692 /** 693 * Sets the attributes for the addresses resolved by name resolution. If unset, 694 * {@link Attributes#EMPTY} will be used as a default. 695 * 696 * @since 1.21.0 697 */ setAttributes(Attributes attributes)698 public Builder setAttributes(Attributes attributes) { 699 this.attributes = attributes; 700 return this; 701 } 702 703 /** 704 * Sets the Service Config parsed by {@link Args#getServiceConfigParser}. 705 * This field is optional. 706 * 707 * @since 1.21.0 708 */ setServiceConfig(@ullable ConfigOrError serviceConfig)709 public Builder setServiceConfig(@Nullable ConfigOrError serviceConfig) { 710 this.serviceConfig = serviceConfig; 711 return this; 712 } 713 714 /** 715 * Constructs a new {@link ResolutionResult} from this builder. 716 * 717 * @since 1.21.0 718 */ build()719 public ResolutionResult build() { 720 return new ResolutionResult(addresses, attributes, serviceConfig); 721 } 722 } 723 } 724 725 /** 726 * Represents either a successfully parsed service config, containing all necessary parts to be 727 * later applied by the channel, or a Status containing the error encountered while parsing. 728 * 729 * @since 1.20.0 730 */ 731 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770") 732 public static final class ConfigOrError { 733 734 /** 735 * Returns a {@link ConfigOrError} for the successfully parsed config. 736 */ fromConfig(Object config)737 public static ConfigOrError fromConfig(Object config) { 738 return new ConfigOrError(config); 739 } 740 741 /** 742 * Returns a {@link ConfigOrError} for the failure to parse the config. 743 * 744 * @param status a non-OK status 745 */ fromError(Status status)746 public static ConfigOrError fromError(Status status) { 747 return new ConfigOrError(status); 748 } 749 750 private final Status status; 751 private final Object config; 752 ConfigOrError(Object config)753 private ConfigOrError(Object config) { 754 this.config = checkNotNull(config, "config"); 755 this.status = null; 756 } 757 ConfigOrError(Status status)758 private ConfigOrError(Status status) { 759 this.config = null; 760 this.status = checkNotNull(status, "status"); 761 checkArgument(!status.isOk(), "cannot use OK status: %s", status); 762 } 763 764 /** 765 * Returns config if exists, otherwise null. 766 */ 767 @Nullable getConfig()768 public Object getConfig() { 769 return config; 770 } 771 772 /** 773 * Returns error status if exists, otherwise null. 774 */ 775 @Nullable getError()776 public Status getError() { 777 return status; 778 } 779 780 @Override equals(Object o)781 public boolean equals(Object o) { 782 if (this == o) { 783 return true; 784 } 785 if (o == null || getClass() != o.getClass()) { 786 return false; 787 } 788 ConfigOrError that = (ConfigOrError) o; 789 return Objects.equal(status, that.status) && Objects.equal(config, that.config); 790 } 791 792 @Override hashCode()793 public int hashCode() { 794 return Objects.hashCode(status, config); 795 } 796 797 @Override toString()798 public String toString() { 799 if (config != null) { 800 return MoreObjects.toStringHelper(this) 801 .add("config", config) 802 .toString(); 803 } else { 804 assert status != null; 805 return MoreObjects.toStringHelper(this) 806 .add("error", status) 807 .toString(); 808 } 809 } 810 } 811 } 812