• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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