• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #19870

17 Jun 2025 10:14PM UTC coverage: 88.543% (-0.01%) from 88.556%
#19870

push

github

ejona86
xds: Add logical dns cluster support to XdsDepManager

ClusterResolverLb gets the NameResolverRegistry from
LoadBalancer.Helper, so a new API was added in NameResover.Args to
propagate the same object to the name resolver tree.

RetryingNameResolver was exposed to xds. This is expected to be
temporary, as the retrying is being removed from ManagedChannelImpl and
moved into the resolvers. At that point, DnsNameResolverProvider would
wrap DnsNameResolver with a similar API to RetryingNameResolver and xds
would no longer be responsible.

34637 of 39119 relevant lines covered (88.54%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.92
/../api/src/main/java/io/grpc/NameResolver.java
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.MoreObjects.ToStringHelper;
24
import com.google.common.base.Objects;
25
import com.google.errorprone.annotations.InlineMe;
26
import java.lang.annotation.Documented;
27
import java.lang.annotation.Retention;
28
import java.lang.annotation.RetentionPolicy;
29
import java.net.URI;
30
import java.util.Collections;
31
import java.util.IdentityHashMap;
32
import java.util.List;
33
import java.util.Map;
34
import java.util.concurrent.Executor;
35
import java.util.concurrent.ScheduledExecutorService;
36
import javax.annotation.Nullable;
37
import javax.annotation.concurrent.Immutable;
38
import javax.annotation.concurrent.ThreadSafe;
39

40
/**
41
 * A pluggable component that resolves a target {@link URI} and return addresses to the caller.
42
 *
43
 * <p>A {@code NameResolver} uses the URI's scheme to determine whether it can resolve it, and uses
44
 * the components after the scheme for actual resolution.
45
 *
46
 * <p>The addresses and attributes of a target may be changed over time, thus the caller registers a
47
 * {@link Listener} to receive continuous updates.
48
 *
49
 * <p>A {@code NameResolver} does not need to automatically re-resolve on failure. Instead, the
50
 * {@link Listener} is responsible for eventually (after an appropriate backoff period) invoking
51
 * {@link #refresh()}.
52
 *
53
 * <p>Implementations <strong>don't need to be thread-safe</strong>.  All methods are guaranteed to
54
 * be called sequentially.  Additionally, all methods that have side-effects, i.e.,
55
 * {@link #start(Listener2)}, {@link #shutdown} and {@link #refresh} are called from the same
56
 * {@link SynchronizationContext} as returned by {@link Args#getSynchronizationContext}. <strong>Do
57
 * not block</strong> within the synchronization context; blocking I/O and time-consuming tasks
58
 * should be offloaded to a separate thread, generally {@link Args#getOffloadExecutor}.
59
 *
60
 * @since 1.0.0
61
 */
62
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
63
public abstract class NameResolver {
1✔
64
  /**
65
   * Returns the authority used to authenticate connections to servers.  It <strong>must</strong> be
66
   * from a trusted source, because if the authority is tampered with, RPCs may be sent to the
67
   * attackers which may leak sensitive user data.
68
   *
69
   * <p>An implementation must generate it without blocking, typically in line, and
70
   * <strong>must</strong> keep it unchanged. {@code NameResolver}s created from the same factory
71
   * with the same argument must return the same authority.
72
   *
73
   * @since 1.0.0
74
   */
75
  public abstract String getServiceAuthority();
76

77
  /**
78
   * Starts the resolution. The method is not supposed to throw any exceptions. That might cause the
79
   * Channel that the name resolver is serving to crash. Errors should be propagated
80
   * through {@link Listener#onError}.
81
   * 
82
   * <p>An instance may not be started more than once, by any overload of this method, even after
83
   * an intervening call to {@link #shutdown}.
84
   *
85
   * @param listener used to receive updates on the target
86
   * @since 1.0.0
87
   */
88
  public void start(final Listener listener) {
89
    if (listener instanceof Listener2) {
1✔
90
      start((Listener2) listener);
×
91
    } else {
92
      start(new Listener2() {
1✔
93
          @Override
94
          public void onError(Status error) {
95
            listener.onError(error);
1✔
96
          }
1✔
97

98
          @Override
99
          public void onResult(ResolutionResult resolutionResult) {
100
            listener.onAddresses(resolutionResult.getAddressesOrError().getValue(),
1✔
101
                resolutionResult.getAttributes());
1✔
102
          }
1✔
103
      });
104
    }
105
  }
1✔
106

107
  /**
108
   * Starts the resolution. The method is not supposed to throw any exceptions. That might cause the
109
   * Channel that the name resolver is serving to crash. Errors should be propagated
110
   * through {@link Listener2#onError}.
111
   * 
112
   * <p>An instance may not be started more than once, by any overload of this method, even after
113
   * an intervening call to {@link #shutdown}.
114
   *
115
   * @param listener used to receive updates on the target
116
   * @since 1.21.0
117
   */
118
  public void start(Listener2 listener) {
119
    start((Listener) listener);
×
120
  }
×
121

122
  /**
123
   * Stops the resolution. Updates to the Listener will stop.
124
   *
125
   * @since 1.0.0
126
   */
127
  public abstract void shutdown();
128

129
  /**
130
   * Re-resolve the name.
131
   *
132
   * <p>Can only be called after {@link #start} has been called.
133
   *
134
   * <p>This is only a hint. Implementation takes it as a signal but may not start resolution
135
   * immediately. It should never throw.
136
   *
137
   * <p>The default implementation is no-op.
138
   *
139
   * @since 1.0.0
140
   */
141
  public void refresh() {}
1✔
142

143
  /**
144
   * Factory that creates {@link NameResolver} instances.
145
   *
146
   * @since 1.0.0
147
   */
148
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
149
  public abstract static class Factory {
1✔
150
    /**
151
     * Creates a {@link NameResolver} for the given target URI, or {@code null} if the given URI
152
     * cannot be resolved by this factory. The decision should be solely based on the scheme of the
153
     * URI.
154
     *
155
     * @param targetUri the target URI to be resolved, whose scheme must not be {@code null}
156
     * @param args other information that may be useful
157
     *
158
     * @since 1.21.0
159
     */
160
    public abstract NameResolver newNameResolver(URI targetUri, final Args args);
161

162
    /**
163
     * Returns the default scheme, which will be used to construct a URI when {@link
164
     * ManagedChannelBuilder#forTarget(String)} is given an authority string instead of a compliant
165
     * URI.
166
     *
167
     * @since 1.0.0
168
     */
169
    public abstract String getDefaultScheme();
170
  }
171

172
  /**
173
   * Receives address updates.
174
   *
175
   * <p>All methods are expected to return quickly.
176
   *
177
   * @since 1.0.0
178
   */
179
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
180
  @ThreadSafe
181
  public interface Listener {
182
    /**
183
     * Handles updates on resolved addresses and attributes.
184
     *
185
     * <p>Implementations will not modify the given {@code servers}.
186
     *
187
     * @param servers the resolved server addresses. An empty list will trigger {@link #onError}
188
     * @param attributes extra information from naming system.
189
     * @since 1.3.0
190
     */
191
    void onAddresses(
192
        List<EquivalentAddressGroup> servers, @ResolutionResultAttr Attributes attributes);
193

194
    /**
195
     * Handles an error from the resolver. The listener is responsible for eventually invoking
196
     * {@link #refresh()} to re-attempt resolution.
197
     *
198
     * @param error a non-OK status
199
     * @since 1.0.0
200
     */
201
    void onError(Status error);
202
  }
203

204
  /**
205
   * Receives address updates.
206
   *
207
   * <p>All methods are expected to return quickly.
208
   *
209
   * <p>This is a replacement API of {@code Listener}. However, we think this new API may change
210
   * again, so we aren't yet encouraging mass-migration to it. It is fine to use and works.
211
   *
212
   * @since 1.21.0
213
   */
214
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
215
  public abstract static class Listener2 implements Listener {
1✔
216
    /**
217
     * Handles updates on resolved addresses and attributes.
218
     *
219
     * @deprecated This will be removed in 1.22.0
220
     */
221
    @Override
222
    @Deprecated
223
    @InlineMe(
224
        replacement = "this.onResult(ResolutionResult.newBuilder().setAddressesOrError("
225
            + "StatusOr.fromValue(servers)).setAttributes(attributes).build())",
226
        imports = {"io.grpc.NameResolver.ResolutionResult", "io.grpc.StatusOr"})
227
    public final void onAddresses(
228
        List<EquivalentAddressGroup> servers, @ResolutionResultAttr Attributes attributes) {
229
      // TODO(jihuncho) need to promote Listener2 if we want to use ConfigOrError
230
      // Calling onResult and not onResult2 because onResult2 can only be called from a
231
      // synchronization context.
232
      onResult(
1✔
233
          ResolutionResult.newBuilder().setAddressesOrError(
1✔
234
              StatusOr.fromValue(servers)).setAttributes(attributes).build());
1✔
235
    }
1✔
236

237
    /**
238
     * Handles updates on resolved addresses and attributes.  If
239
     * {@link ResolutionResult#getAddressesOrError()} is empty, {@link #onError(Status)} will be
240
     * called.
241
     *
242
     * @param resolutionResult the resolved server addresses, attributes, and Service Config.
243
     * @since 1.21.0
244
     */
245
    public abstract void onResult(ResolutionResult resolutionResult);
246

247
    /**
248
     * Handles a name resolving error from the resolver. The listener is responsible for eventually
249
     * invoking {@link NameResolver#refresh()} to re-attempt resolution.
250
     *
251
     * @param error a non-OK status
252
     * @since 1.21.0
253
     */
254
    @Override
255
    public abstract void onError(Status error);
256

257
    /**
258
     * Handles updates on resolved addresses and attributes.
259
     *
260
     * @param resolutionResult the resolved server addresses, attributes, and Service Config.
261
     * @since 1.66
262
     */
263
    public Status onResult2(ResolutionResult resolutionResult) {
264
      onResult(resolutionResult);
×
265
      return Status.OK;
×
266
    }
267
  }
268

269
  /**
270
   * Annotation for name resolution result attributes. It follows the annotation semantics defined
271
   * by {@link Attributes}.
272
   */
273
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4972")
274
  @Retention(RetentionPolicy.SOURCE)
275
  @Documented
276
  public @interface ResolutionResultAttr {}
277

278
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11989")
279
  @ResolutionResultAttr
280
  public static final Attributes.Key<String> ATTR_BACKEND_SERVICE =
1✔
281
      Attributes.Key.create("io.grpc.NameResolver.ATTR_BACKEND_SERVICE");
1✔
282

283
  /**
284
   * Information that a {@link Factory} uses to create a {@link NameResolver}.
285
   *
286
   * <p>Args applicable to all {@link NameResolver}s are defined here using ordinary setters and
287
   * getters. This container can also hold externally-defined "custom" args that aren't so widely
288
   * useful or that would be inappropriate dependencies for this low level API. See {@link
289
   * Args#getArg} for more.
290
   *
291
   * <p>Note this class overrides neither {@code equals()} nor {@code hashCode()}.
292
   *
293
   * @since 1.21.0
294
   */
295
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
296
  public static final class Args {
297
    private final int defaultPort;
298
    private final ProxyDetector proxyDetector;
299
    private final SynchronizationContext syncContext;
300
    private final ServiceConfigParser serviceConfigParser;
301
    @Nullable private final ScheduledExecutorService scheduledExecutorService;
302
    @Nullable private final ChannelLogger channelLogger;
303
    @Nullable private final Executor executor;
304
    @Nullable private final String overrideAuthority;
305
    @Nullable private final MetricRecorder metricRecorder;
306
    @Nullable private final NameResolverRegistry nameResolverRegistry;
307
    @Nullable private final IdentityHashMap<Key<?>, Object> customArgs;
308

309
    private Args(Builder builder) {
1✔
310
      this.defaultPort = checkNotNull(builder.defaultPort, "defaultPort not set");
1✔
311
      this.proxyDetector = checkNotNull(builder.proxyDetector, "proxyDetector not set");
1✔
312
      this.syncContext = checkNotNull(builder.syncContext, "syncContext not set");
1✔
313
      this.serviceConfigParser =
1✔
314
          checkNotNull(builder.serviceConfigParser, "serviceConfigParser not set");
1✔
315
      this.scheduledExecutorService = builder.scheduledExecutorService;
1✔
316
      this.channelLogger = builder.channelLogger;
1✔
317
      this.executor = builder.executor;
1✔
318
      this.overrideAuthority = builder.overrideAuthority;
1✔
319
      this.metricRecorder = builder.metricRecorder;
1✔
320
      this.nameResolverRegistry = builder.nameResolverRegistry;
1✔
321
      this.customArgs = cloneCustomArgs(builder.customArgs);
1✔
322
    }
1✔
323

324
    /**
325
     * The port number used in case the target or the underlying naming system doesn't provide a
326
     * port number.
327
     *
328
     * @since 1.21.0
329
     */
330
    // <p>TODO: Only meaningful for InetSocketAddress producers. Make this a custom arg?
331
    public int getDefaultPort() {
332
      return defaultPort;
1✔
333
    }
334

335
    /**
336
     * If the NameResolver wants to support proxy, it should inquire this {@link ProxyDetector}.
337
     * See documentation on {@link ProxyDetector} about how proxies work in gRPC.
338
     *
339
     * @since 1.21.0
340
     */
341
    public ProxyDetector getProxyDetector() {
342
      return proxyDetector;
1✔
343
    }
344

345
    /**
346
     * Returns the {@link SynchronizationContext} where {@link #start(Listener2)}, {@link #shutdown}
347
     * and {@link #refresh} are run from.
348
     *
349
     * @since 1.21.0
350
     */
351
    public SynchronizationContext getSynchronizationContext() {
352
      return syncContext;
1✔
353
    }
354

355
    /**
356
     * Returns a {@link ScheduledExecutorService} for scheduling delayed tasks.
357
     *
358
     * <p>This service is a shared resource and is only meant for quick tasks. DO NOT block or run
359
     * time-consuming tasks.
360
     *
361
     * <p>The returned service doesn't support {@link ScheduledExecutorService#shutdown shutdown()}
362
     *  and {@link ScheduledExecutorService#shutdownNow shutdownNow()}. They will throw if called.
363
     *
364
     * @since 1.26.0
365
     */
366
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454")
367
    public ScheduledExecutorService getScheduledExecutorService() {
368
      if (scheduledExecutorService == null) {
1✔
369
        throw new IllegalStateException("ScheduledExecutorService not set in Builder");
×
370
      }
371
      return scheduledExecutorService;
1✔
372
    }
373

374
    /**
375
     * Returns the {@link ServiceConfigParser}.
376
     *
377
     * @since 1.21.0
378
     */
379
    public ServiceConfigParser getServiceConfigParser() {
380
      return serviceConfigParser;
1✔
381
    }
382

383
    /**
384
     * Returns the value of a custom arg named 'key', or {@code null} if it's not set.
385
     *
386
     * <p>While ordinary {@link Args} should be universally useful and meaningful, custom arguments
387
     * can apply just to resolvers of a certain URI scheme, just to resolvers producing a particular
388
     * type of {@link java.net.SocketAddress}, or even an individual {@link NameResolver} subclass.
389
     * Custom args are identified by an instance of {@link Args.Key} which should be a constant
390
     * defined in a java package and class appropriate for the argument's scope.
391
     *
392
     * <p>{@link Args} are normally reserved for information in *support* of name resolution, not
393
     * the name to be resolved itself. However, there are rare cases where all or part of the target
394
     * name can't be represented by any standard URI scheme or can't be encoded as a String at all.
395
     * Custom args, in contrast, can hold arbitrary Java types, making them a useful work around in
396
     * these cases.
397
     *
398
     * <p>Custom args can also be used simply to avoid adding inappropriate deps to the low level
399
     * io.grpc package.
400
     */
401
    @SuppressWarnings("unchecked") // Cast is safe because all put()s go through the setArg() API.
402
    @Nullable
403
    public <T> T getArg(Key<T> key) {
404
      return customArgs != null ? (T) customArgs.get(key) : null;
1✔
405
    }
406

407
    /**
408
     * Returns the {@link ChannelLogger} for the Channel served by this NameResolver.
409
     *
410
     * @since 1.26.0
411
     */
412
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
413
    public ChannelLogger getChannelLogger() {
414
      if (channelLogger == null) {
1✔
415
        throw new IllegalStateException("ChannelLogger is not set in Builder");
×
416
      }
417
      return channelLogger;
1✔
418
    }
419

420
    /**
421
     * Returns the Executor on which this resolver should execute long-running or I/O bound work.
422
     * Null if no Executor was set.
423
     *
424
     * @since 1.25.0
425
     */
426
    @Nullable
427
    public Executor getOffloadExecutor() {
428
      return executor;
1✔
429
    }
430

431
    /**
432
     * Returns the overrideAuthority from channel {@link ManagedChannelBuilder#overrideAuthority}.
433
     * Overrides the host name for L7 HTTP virtual host matching. Almost all name resolvers should
434
     * not use this.
435
     *
436
     * @since 1.49.0
437
     */
438
    @Nullable
439
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
440
    public String getOverrideAuthority() {
441
      return overrideAuthority;
1✔
442
    }
443

444
    /**
445
     * Returns the {@link MetricRecorder} that the channel uses to record metrics.
446
     */
447
    @Nullable
448
    public MetricRecorder getMetricRecorder() {
449
      return metricRecorder;
1✔
450
    }
451

452
    /**
453
     * Returns the {@link NameResolverRegistry} that the Channel uses to look for {@link
454
     * NameResolver}s.
455
     *
456
     * @since 1.74.0
457
     */
458
    public NameResolverRegistry getNameResolverRegistry() {
459
      if (nameResolverRegistry == null) {
1✔
460
        throw new IllegalStateException("NameResolverRegistry is not set in Builder");
×
461
      }
462
      return nameResolverRegistry;
1✔
463
    }
464

465
    @Override
466
    public String toString() {
467
      return MoreObjects.toStringHelper(this)
×
468
          .add("defaultPort", defaultPort)
×
469
          .add("proxyDetector", proxyDetector)
×
470
          .add("syncContext", syncContext)
×
471
          .add("serviceConfigParser", serviceConfigParser)
×
472
          .add("customArgs", customArgs)
×
473
          .add("scheduledExecutorService", scheduledExecutorService)
×
474
          .add("channelLogger", channelLogger)
×
475
          .add("executor", executor)
×
476
          .add("overrideAuthority", overrideAuthority)
×
477
          .add("metricRecorder", metricRecorder)
×
478
          .add("nameResolverRegistry", nameResolverRegistry)
×
479
          .toString();
×
480
    }
481

482
    /**
483
     * Returns a builder with the same initial values as this object.
484
     *
485
     * @since 1.21.0
486
     */
487
    public Builder toBuilder() {
488
      Builder builder = new Builder();
1✔
489
      builder.setDefaultPort(defaultPort);
1✔
490
      builder.setProxyDetector(proxyDetector);
1✔
491
      builder.setSynchronizationContext(syncContext);
1✔
492
      builder.setServiceConfigParser(serviceConfigParser);
1✔
493
      builder.setScheduledExecutorService(scheduledExecutorService);
1✔
494
      builder.setChannelLogger(channelLogger);
1✔
495
      builder.setOffloadExecutor(executor);
1✔
496
      builder.setOverrideAuthority(overrideAuthority);
1✔
497
      builder.setMetricRecorder(metricRecorder);
1✔
498
      builder.setNameResolverRegistry(nameResolverRegistry);
1✔
499
      builder.customArgs = cloneCustomArgs(customArgs);
1✔
500
      return builder;
1✔
501
    }
502

503
    /**
504
     * Creates a new builder.
505
     *
506
     * @since 1.21.0
507
     */
508
    public static Builder newBuilder() {
509
      return new Builder();
1✔
510
    }
511

512
    /**
513
     * Builder for {@link Args}.
514
     *
515
     * @since 1.21.0
516
     */
517
    public static final class Builder {
518
      private Integer defaultPort;
519
      private ProxyDetector proxyDetector;
520
      private SynchronizationContext syncContext;
521
      private ServiceConfigParser serviceConfigParser;
522
      private ScheduledExecutorService scheduledExecutorService;
523
      private ChannelLogger channelLogger;
524
      private Executor executor;
525
      private String overrideAuthority;
526
      private MetricRecorder metricRecorder;
527
      private NameResolverRegistry nameResolverRegistry;
528
      private IdentityHashMap<Key<?>, Object> customArgs;
529

530
      Builder() {
1✔
531
      }
1✔
532

533
      /**
534
       * See {@link Args#getDefaultPort}.  This is a required field.
535
       *
536
       * @since 1.21.0
537
       */
538
      public Builder setDefaultPort(int defaultPort) {
539
        this.defaultPort = defaultPort;
1✔
540
        return this;
1✔
541
      }
542

543
      /**
544
       * See {@link Args#getProxyDetector}.  This is required field.
545
       *
546
       * @since 1.21.0
547
       */
548
      public Builder setProxyDetector(ProxyDetector proxyDetector) {
549
        this.proxyDetector = checkNotNull(proxyDetector);
1✔
550
        return this;
1✔
551
      }
552

553
      /**
554
       * See {@link Args#getSynchronizationContext}.  This is a required field.
555
       *
556
       * @since 1.21.0
557
       */
558
      public Builder setSynchronizationContext(SynchronizationContext syncContext) {
559
        this.syncContext = checkNotNull(syncContext);
1✔
560
        return this;
1✔
561
      }
562

563
      /**
564
       * See {@link Args#getScheduledExecutorService}.
565
       */
566
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454")
567
      public Builder setScheduledExecutorService(
568
          ScheduledExecutorService scheduledExecutorService) {
569
        this.scheduledExecutorService = checkNotNull(scheduledExecutorService);
1✔
570
        return this;
1✔
571
      }
572

573
      /**
574
       * See {@link Args#getServiceConfigParser}.  This is a required field.
575
       *
576
       * @since 1.21.0
577
       */
578
      public Builder setServiceConfigParser(ServiceConfigParser parser) {
579
        this.serviceConfigParser = checkNotNull(parser);
1✔
580
        return this;
1✔
581
      }
582

583
      /**
584
       * See {@link Args#getChannelLogger}.
585
       *
586
       * @since 1.26.0
587
       */
588
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
589
      public Builder setChannelLogger(ChannelLogger channelLogger) {
590
        this.channelLogger = checkNotNull(channelLogger);
1✔
591
        return this;
1✔
592
      }
593

594
      /**
595
       * See {@link Args#getOffloadExecutor}. This is an optional field.
596
       *
597
       * @since 1.25.0
598
       */
599
      public Builder setOffloadExecutor(Executor executor) {
600
        this.executor = executor;
1✔
601
        return this;
1✔
602
      }
603

604
      /**
605
       * See {@link Args#getOverrideAuthority()}. This is an optional field.
606
       *
607
       * @since 1.49.0
608
       */
609
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
610
      public Builder setOverrideAuthority(String authority) {
611
        this.overrideAuthority = authority;
1✔
612
        return this;
1✔
613
      }
614

615
      /** See {@link Args#getArg(Key)}. */
616
      public <T> Builder setArg(Key<T> key, T value) {
617
        checkNotNull(key, "key");
1✔
618
        checkNotNull(value, "value");
1✔
619
        if (customArgs == null) {
1✔
620
          customArgs = new IdentityHashMap<>();
1✔
621
        }
622
        customArgs.put(key, value);
1✔
623
        return this;
1✔
624
      }
625

626
      /**
627
       * See {@link Args#getMetricRecorder()}. This is an optional field.
628
       */
629
      public Builder setMetricRecorder(MetricRecorder metricRecorder) {
630
        this.metricRecorder = metricRecorder;
1✔
631
        return this;
1✔
632
      }
633

634
      /**
635
       * See {@link Args#getNameResolverRegistry}.  This is an optional field.
636
       *
637
       * @since 1.74.0
638
       */
639
      public Builder setNameResolverRegistry(NameResolverRegistry registry) {
640
        this.nameResolverRegistry = registry;
1✔
641
        return this;
1✔
642
      }
643

644
      /**
645
       * Builds an {@link Args}.
646
       *
647
       * @since 1.21.0
648
       */
649
      public Args build() {
650
        return new Args(this);
1✔
651
      }
652
    }
653

654
    /**
655
     * Identifies an externally-defined custom argument that can be stored in {@link Args}.
656
     *
657
     * <p>Uses reference equality so keys should be defined as global constants.
658
     *
659
     * @param <T> type of values that can be stored under this key
660
     */
661
    @Immutable
662
    @SuppressWarnings("UnusedTypeParameter")
663
    public static final class Key<T> {
664
      private final String debugString;
665

666
      private Key(String debugString) {
1✔
667
        this.debugString = debugString;
1✔
668
      }
1✔
669

670
      @Override
671
      public String toString() {
672
        return debugString;
×
673
      }
674

675
      /**
676
       * Creates a new instance of {@link Key}.
677
       *
678
       * @param debugString a string used to describe the key, used for debugging.
679
       * @param <T> Key type
680
       * @return a new instance of Key
681
       */
682
      public static <T> Key<T> create(String debugString) {
683
        return new Key<>(debugString);
1✔
684
      }
685
    }
686
  }
687

688
  /**
689
   * Parses and validates service configuration.
690
   *
691
   * @since 1.21.0
692
   */
693
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
694
  public abstract static class ServiceConfigParser {
1✔
695
    /**
696
     * Parses and validates the service configuration chosen by the name resolver.  This will
697
     * return a {@link ConfigOrError} which contains either the successfully parsed config, or the
698
     * {@link Status} representing the failure to parse.  Implementations are expected to not throw
699
     * exceptions but return a Status representing the failure.  The value inside the
700
     * {@link ConfigOrError} should implement {@code equals()} and {@code hashCode()}.
701
     *
702
     * @param rawServiceConfig The {@link Map} representation of the service config
703
     * @return a tuple of the fully parsed and validated channel configuration, else the Status.
704
     * @since 1.21.0
705
     */
706
    public abstract ConfigOrError parseServiceConfig(Map<String, ?> rawServiceConfig);
707
  }
708

709
  /**
710
   * Represents the results from a Name Resolver.
711
   *
712
   * @since 1.21.0
713
   */
714
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
715
  public static final class ResolutionResult {
716
    private final StatusOr<List<EquivalentAddressGroup>> addressesOrError;
717
    @ResolutionResultAttr
718
    private final Attributes attributes;
719
    @Nullable
720
    private final ConfigOrError serviceConfig;
721

722
    ResolutionResult(
723
        StatusOr<List<EquivalentAddressGroup>> addressesOrError,
724
        @ResolutionResultAttr Attributes attributes,
725
        ConfigOrError serviceConfig) {
1✔
726
      this.addressesOrError = addressesOrError;
1✔
727
      this.attributes = checkNotNull(attributes, "attributes");
1✔
728
      this.serviceConfig = serviceConfig;
1✔
729
    }
1✔
730

731
    /**
732
     * Constructs a new builder of a name resolution result.
733
     *
734
     * @since 1.21.0
735
     */
736
    public static Builder newBuilder() {
737
      return new Builder();
1✔
738
    }
739

740
    /**
741
     * Converts these results back to a builder.
742
     *
743
     * @since 1.21.0
744
     */
745
    public Builder toBuilder() {
746
      return newBuilder()
×
747
          .setAddressesOrError(addressesOrError)
×
748
          .setAttributes(attributes)
×
749
          .setServiceConfig(serviceConfig);
×
750
    }
751

752
    /**
753
     * Gets the addresses resolved by name resolution.
754
     *
755
     * @since 1.21.0
756
     * @deprecated Will be superseded by getAddressesOrError
757
     */
758
    @Deprecated
759
    public List<EquivalentAddressGroup> getAddresses() {
760
      return addressesOrError.getValue();
×
761
    }
762

763
    /**
764
     * Gets the addresses resolved by name resolution or the error in doing so.
765
     *
766
     * @since 1.65.0
767
     */
768
    public StatusOr<List<EquivalentAddressGroup>> getAddressesOrError() {
769
      return addressesOrError;
1✔
770
    }
771

772
    /**
773
     * Gets the attributes associated with the addresses resolved by name resolution.  If there are
774
     * no attributes, {@link Attributes#EMPTY} will be returned.
775
     *
776
     * @since 1.21.0
777
     */
778
    @ResolutionResultAttr
779
    public Attributes getAttributes() {
780
      return attributes;
1✔
781
    }
782

783
    /**
784
     * Gets the Service Config parsed by {@link Args#getServiceConfigParser}.
785
     *
786
     * @since 1.21.0
787
     */
788
    @Nullable
789
    public ConfigOrError getServiceConfig() {
790
      return serviceConfig;
1✔
791
    }
792

793
    @Override
794
    public String toString() {
795
      ToStringHelper stringHelper = MoreObjects.toStringHelper(this);
1✔
796
      stringHelper.add("addressesOrError", addressesOrError.toString());
1✔
797
      stringHelper.add("attributes", attributes);
1✔
798
      stringHelper.add("serviceConfigOrError", serviceConfig);
1✔
799
      return stringHelper.toString();
1✔
800
    }
801

802
    /**
803
     * Useful for testing.  May be slow to calculate.
804
     */
805
    @Override
806
    public boolean equals(Object obj) {
807
      if (!(obj instanceof ResolutionResult)) {
×
808
        return false;
×
809
      }
810
      ResolutionResult that = (ResolutionResult) obj;
×
811
      return Objects.equal(this.addressesOrError, that.addressesOrError)
×
812
          && Objects.equal(this.attributes, that.attributes)
×
813
          && Objects.equal(this.serviceConfig, that.serviceConfig);
×
814
    }
815

816
    /**
817
     * Useful for testing.  May be slow to calculate.
818
     */
819
    @Override
820
    public int hashCode() {
821
      return Objects.hashCode(addressesOrError, attributes, serviceConfig);
1✔
822
    }
823

824
    /**
825
     * A builder for {@link ResolutionResult}.
826
     *
827
     * @since 1.21.0
828
     */
829
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
830
    public static final class Builder {
831
      private StatusOr<List<EquivalentAddressGroup>> addresses =
1✔
832
          StatusOr.fromValue(Collections.emptyList());
1✔
833
      private Attributes attributes = Attributes.EMPTY;
1✔
834
      @Nullable
835
      private ConfigOrError serviceConfig;
836
      //  Make sure to update #toBuilder above!
837

838
      Builder() {}
1✔
839

840
      /**
841
       * Sets the addresses resolved by name resolution.  This field is required.
842
       *
843
       * @since 1.21.0
844
       * @deprecated Will be superseded by setAddressesOrError
845
       */
846
      @Deprecated
847
      public Builder setAddresses(List<EquivalentAddressGroup> addresses) {
848
        setAddressesOrError(StatusOr.fromValue(addresses));
1✔
849
        return this;
1✔
850
      }
851

852
      /**
853
       * Sets the addresses resolved by name resolution or the error in doing so. This field is
854
       * required.
855
       * @param addresses Resolved addresses or an error in resolving addresses
856
       */
857
      public Builder setAddressesOrError(StatusOr<List<EquivalentAddressGroup>> addresses) {
858
        this.addresses = checkNotNull(addresses, "StatusOr addresses cannot be null.");
1✔
859
        return this;
1✔
860
      }
861

862
      /**
863
       * Sets the attributes for the addresses resolved by name resolution.  If unset,
864
       * {@link Attributes#EMPTY} will be used as a default.
865
       *
866
       * @since 1.21.0
867
       */
868
      public Builder setAttributes(Attributes attributes) {
869
        this.attributes = attributes;
1✔
870
        return this;
1✔
871
      }
872

873
      /**
874
       * Sets the Service Config parsed by {@link Args#getServiceConfigParser}.
875
       * This field is optional.
876
       *
877
       * @since 1.21.0
878
       */
879
      public Builder setServiceConfig(@Nullable ConfigOrError serviceConfig) {
880
        this.serviceConfig = serviceConfig;
1✔
881
        return this;
1✔
882
      }
883

884
      /**
885
       * Constructs a new {@link ResolutionResult} from this builder.
886
       *
887
       * @since 1.21.0
888
       */
889
      public ResolutionResult build() {
890
        return new ResolutionResult(addresses, attributes, serviceConfig);
1✔
891
      }
892
    }
893
  }
894

895
  /**
896
   * Represents either a successfully parsed service config, containing all necessary parts to be
897
   * later applied by the channel, or a Status containing the error encountered while parsing.
898
   *
899
   * @since 1.20.0
900
   */
901
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
1✔
902
  public static final class ConfigOrError {
903

904
    /**
905
     * Returns a {@link ConfigOrError} for the successfully parsed config.
906
     */
907
    public static ConfigOrError fromConfig(Object config) {
908
      return new ConfigOrError(config);
1✔
909
    }
910

911
    /**
912
     * Returns a {@link ConfigOrError} for the failure to parse the config.
913
     *
914
     * @param status a non-OK status
915
     */
916
    public static ConfigOrError fromError(Status status) {
917
      return new ConfigOrError(status);
1✔
918
    }
919

920
    private final Status status;
921
    private final Object config;
922

923
    private ConfigOrError(Object config) {
1✔
924
      this.config = checkNotNull(config, "config");
1✔
925
      this.status = null;
1✔
926
    }
1✔
927

928
    private ConfigOrError(Status status) {
1✔
929
      this.config = null;
1✔
930
      this.status = checkNotNull(status, "status");
1✔
931
      checkArgument(!status.isOk(), "cannot use OK status: %s", status);
1✔
932
    }
1✔
933

934
    /**
935
     * Returns config if exists, otherwise null.
936
     */
937
    @Nullable
938
    public Object getConfig() {
939
      return config;
1✔
940
    }
941

942
    /**
943
     * Returns error status if exists, otherwise null.
944
     */
945
    @Nullable
946
    public Status getError() {
947
      return status;
1✔
948
    }
949

950
    @Override
951
    public boolean equals(Object o) {
952
      if (this == o) {
1✔
953
        return true;
×
954
      }
955
      if (o == null || getClass() != o.getClass()) {
1✔
956
        return false;
×
957
      }
958
      ConfigOrError that = (ConfigOrError) o;
1✔
959
      return Objects.equal(status, that.status) && Objects.equal(config, that.config);
1✔
960
    }
961

962
    @Override
963
    public int hashCode() {
964
      return Objects.hashCode(status, config);
1✔
965
    }
966

967
    @Override
968
    public String toString() {
969
      if (config != null) {
1✔
970
        return MoreObjects.toStringHelper(this)
1✔
971
            .add("config", config)
1✔
972
            .toString();
1✔
973
      } else {
974
        assert status != null;
×
975
        return MoreObjects.toStringHelper(this)
×
976
            .add("error", status)
×
977
            .toString();
×
978
      }
979
    }
980
  }
981

982
  @Nullable
983
  private static IdentityHashMap<Args.Key<?>, Object> cloneCustomArgs(
984
          @Nullable IdentityHashMap<Args.Key<?>, Object> customArgs) {
985
    return customArgs != null ? new IdentityHashMap<>(customArgs) : null;
1✔
986
  }
987
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc