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

grpc / grpc-java / #19907

16 Jul 2025 09:09AM UTC coverage: 88.592% (+0.01%) from 88.579%
#19907

push

github

web-flow
api: Add more Javadoc for NameResolver.Listener2 interface (#12220)

34690 of 39157 relevant lines covered (88.59%)

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
     * <p>Newer NameResolver implementations should prefer calling onResult2. This method exists to
243
     * facilitate older {@link Listener} implementations to migrate to {@link Listener2}.
244
     *
245
     * @param resolutionResult the resolved server addresses, attributes, and Service Config.
246
     * @since 1.21.0
247
     */
248
    public abstract void onResult(ResolutionResult resolutionResult);
249

250
    /**
251
     * Handles a name resolving error from the resolver. The listener is responsible for eventually
252
     * invoking {@link NameResolver#refresh()} to re-attempt resolution.
253
     *
254
     * <p>New NameResolver implementations should prefer calling onResult2 which will have the
255
     * address resolution error in {@link ResolutionResult}'s addressesOrError. This method exists
256
     * to facilitate older implementations using {@link Listener} to migrate to {@link Listener2}.
257
     *
258
     * @param error a non-OK status
259
     * @since 1.21.0
260
     */
261
    @Override
262
    public abstract void onError(Status error);
263

264
    /**
265
     * Handles updates on resolved addresses and attributes. Must be called from the same
266
     * {@link SynchronizationContext} available in {@link NameResolver.Args} that is passed
267
     * from the channel.
268
     *
269
     * @param resolutionResult the resolved server addresses or error in address resolution,
270
     *     attributes, and Service Config or error
271
     * @return status indicating whether the resolutionResult was accepted by the listener,
272
     *     typically the result from a load balancer.
273
     * @since 1.66
274
     */
275
    public Status onResult2(ResolutionResult resolutionResult) {
276
      onResult(resolutionResult);
×
277
      return Status.OK;
×
278
    }
279
  }
280

281
  /**
282
   * Annotation for name resolution result attributes. It follows the annotation semantics defined
283
   * by {@link Attributes}.
284
   */
285
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4972")
286
  @Retention(RetentionPolicy.SOURCE)
287
  @Documented
288
  public @interface ResolutionResultAttr {}
289

290
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11989")
291
  @ResolutionResultAttr
292
  public static final Attributes.Key<String> ATTR_BACKEND_SERVICE =
1✔
293
      Attributes.Key.create("io.grpc.NameResolver.ATTR_BACKEND_SERVICE");
1✔
294

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

321
    private Args(Builder builder) {
1✔
322
      this.defaultPort = checkNotNull(builder.defaultPort, "defaultPort not set");
1✔
323
      this.proxyDetector = checkNotNull(builder.proxyDetector, "proxyDetector not set");
1✔
324
      this.syncContext = checkNotNull(builder.syncContext, "syncContext not set");
1✔
325
      this.serviceConfigParser =
1✔
326
          checkNotNull(builder.serviceConfigParser, "serviceConfigParser not set");
1✔
327
      this.scheduledExecutorService = builder.scheduledExecutorService;
1✔
328
      this.channelLogger = builder.channelLogger;
1✔
329
      this.executor = builder.executor;
1✔
330
      this.overrideAuthority = builder.overrideAuthority;
1✔
331
      this.metricRecorder = builder.metricRecorder;
1✔
332
      this.nameResolverRegistry = builder.nameResolverRegistry;
1✔
333
      this.customArgs = cloneCustomArgs(builder.customArgs);
1✔
334
    }
1✔
335

336
    /**
337
     * The port number used in case the target or the underlying naming system doesn't provide a
338
     * port number.
339
     *
340
     * @since 1.21.0
341
     */
342
    // <p>TODO: Only meaningful for InetSocketAddress producers. Make this a custom arg?
343
    public int getDefaultPort() {
344
      return defaultPort;
1✔
345
    }
346

347
    /**
348
     * If the NameResolver wants to support proxy, it should inquire this {@link ProxyDetector}.
349
     * See documentation on {@link ProxyDetector} about how proxies work in gRPC.
350
     *
351
     * @since 1.21.0
352
     */
353
    public ProxyDetector getProxyDetector() {
354
      return proxyDetector;
1✔
355
    }
356

357
    /**
358
     * Returns the {@link SynchronizationContext} where {@link #start(Listener2)}, {@link #shutdown}
359
     * and {@link #refresh} are run from.
360
     *
361
     * @since 1.21.0
362
     */
363
    public SynchronizationContext getSynchronizationContext() {
364
      return syncContext;
1✔
365
    }
366

367
    /**
368
     * Returns a {@link ScheduledExecutorService} for scheduling delayed tasks.
369
     *
370
     * <p>This service is a shared resource and is only meant for quick tasks. DO NOT block or run
371
     * time-consuming tasks.
372
     *
373
     * <p>The returned service doesn't support {@link ScheduledExecutorService#shutdown shutdown()}
374
     *  and {@link ScheduledExecutorService#shutdownNow shutdownNow()}. They will throw if called.
375
     *
376
     * @since 1.26.0
377
     */
378
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454")
379
    public ScheduledExecutorService getScheduledExecutorService() {
380
      if (scheduledExecutorService == null) {
1✔
381
        throw new IllegalStateException("ScheduledExecutorService not set in Builder");
×
382
      }
383
      return scheduledExecutorService;
1✔
384
    }
385

386
    /**
387
     * Returns the {@link ServiceConfigParser}.
388
     *
389
     * @since 1.21.0
390
     */
391
    public ServiceConfigParser getServiceConfigParser() {
392
      return serviceConfigParser;
1✔
393
    }
394

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

419
    /**
420
     * Returns the {@link ChannelLogger} for the Channel served by this NameResolver.
421
     *
422
     * @since 1.26.0
423
     */
424
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
425
    public ChannelLogger getChannelLogger() {
426
      if (channelLogger == null) {
1✔
427
        throw new IllegalStateException("ChannelLogger is not set in Builder");
×
428
      }
429
      return channelLogger;
1✔
430
    }
431

432
    /**
433
     * Returns the Executor on which this resolver should execute long-running or I/O bound work.
434
     * Null if no Executor was set.
435
     *
436
     * @since 1.25.0
437
     */
438
    @Nullable
439
    public Executor getOffloadExecutor() {
440
      return executor;
1✔
441
    }
442

443
    /**
444
     * Returns the overrideAuthority from channel {@link ManagedChannelBuilder#overrideAuthority}.
445
     * Overrides the host name for L7 HTTP virtual host matching. Almost all name resolvers should
446
     * not use this.
447
     *
448
     * @since 1.49.0
449
     */
450
    @Nullable
451
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
452
    public String getOverrideAuthority() {
453
      return overrideAuthority;
1✔
454
    }
455

456
    /**
457
     * Returns the {@link MetricRecorder} that the channel uses to record metrics.
458
     */
459
    @Nullable
460
    public MetricRecorder getMetricRecorder() {
461
      return metricRecorder;
1✔
462
    }
463

464
    /**
465
     * Returns the {@link NameResolverRegistry} that the Channel uses to look for {@link
466
     * NameResolver}s.
467
     *
468
     * @since 1.74.0
469
     */
470
    public NameResolverRegistry getNameResolverRegistry() {
471
      if (nameResolverRegistry == null) {
1✔
472
        throw new IllegalStateException("NameResolverRegistry is not set in Builder");
×
473
      }
474
      return nameResolverRegistry;
1✔
475
    }
476

477
    @Override
478
    public String toString() {
479
      return MoreObjects.toStringHelper(this)
×
480
          .add("defaultPort", defaultPort)
×
481
          .add("proxyDetector", proxyDetector)
×
482
          .add("syncContext", syncContext)
×
483
          .add("serviceConfigParser", serviceConfigParser)
×
484
          .add("customArgs", customArgs)
×
485
          .add("scheduledExecutorService", scheduledExecutorService)
×
486
          .add("channelLogger", channelLogger)
×
487
          .add("executor", executor)
×
488
          .add("overrideAuthority", overrideAuthority)
×
489
          .add("metricRecorder", metricRecorder)
×
490
          .add("nameResolverRegistry", nameResolverRegistry)
×
491
          .toString();
×
492
    }
493

494
    /**
495
     * Returns a builder with the same initial values as this object.
496
     *
497
     * @since 1.21.0
498
     */
499
    public Builder toBuilder() {
500
      Builder builder = new Builder();
1✔
501
      builder.setDefaultPort(defaultPort);
1✔
502
      builder.setProxyDetector(proxyDetector);
1✔
503
      builder.setSynchronizationContext(syncContext);
1✔
504
      builder.setServiceConfigParser(serviceConfigParser);
1✔
505
      builder.setScheduledExecutorService(scheduledExecutorService);
1✔
506
      builder.setChannelLogger(channelLogger);
1✔
507
      builder.setOffloadExecutor(executor);
1✔
508
      builder.setOverrideAuthority(overrideAuthority);
1✔
509
      builder.setMetricRecorder(metricRecorder);
1✔
510
      builder.setNameResolverRegistry(nameResolverRegistry);
1✔
511
      builder.customArgs = cloneCustomArgs(customArgs);
1✔
512
      return builder;
1✔
513
    }
514

515
    /**
516
     * Creates a new builder.
517
     *
518
     * @since 1.21.0
519
     */
520
    public static Builder newBuilder() {
521
      return new Builder();
1✔
522
    }
523

524
    /**
525
     * Builder for {@link Args}.
526
     *
527
     * @since 1.21.0
528
     */
529
    public static final class Builder {
530
      private Integer defaultPort;
531
      private ProxyDetector proxyDetector;
532
      private SynchronizationContext syncContext;
533
      private ServiceConfigParser serviceConfigParser;
534
      private ScheduledExecutorService scheduledExecutorService;
535
      private ChannelLogger channelLogger;
536
      private Executor executor;
537
      private String overrideAuthority;
538
      private MetricRecorder metricRecorder;
539
      private NameResolverRegistry nameResolverRegistry;
540
      private IdentityHashMap<Key<?>, Object> customArgs;
541

542
      Builder() {
1✔
543
      }
1✔
544

545
      /**
546
       * See {@link Args#getDefaultPort}.  This is a required field.
547
       *
548
       * @since 1.21.0
549
       */
550
      public Builder setDefaultPort(int defaultPort) {
551
        this.defaultPort = defaultPort;
1✔
552
        return this;
1✔
553
      }
554

555
      /**
556
       * See {@link Args#getProxyDetector}.  This is required field.
557
       *
558
       * @since 1.21.0
559
       */
560
      public Builder setProxyDetector(ProxyDetector proxyDetector) {
561
        this.proxyDetector = checkNotNull(proxyDetector);
1✔
562
        return this;
1✔
563
      }
564

565
      /**
566
       * See {@link Args#getSynchronizationContext}.  This is a required field.
567
       *
568
       * @since 1.21.0
569
       */
570
      public Builder setSynchronizationContext(SynchronizationContext syncContext) {
571
        this.syncContext = checkNotNull(syncContext);
1✔
572
        return this;
1✔
573
      }
574

575
      /**
576
       * See {@link Args#getScheduledExecutorService}.
577
       */
578
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454")
579
      public Builder setScheduledExecutorService(
580
          ScheduledExecutorService scheduledExecutorService) {
581
        this.scheduledExecutorService = checkNotNull(scheduledExecutorService);
1✔
582
        return this;
1✔
583
      }
584

585
      /**
586
       * See {@link Args#getServiceConfigParser}.  This is a required field.
587
       *
588
       * @since 1.21.0
589
       */
590
      public Builder setServiceConfigParser(ServiceConfigParser parser) {
591
        this.serviceConfigParser = checkNotNull(parser);
1✔
592
        return this;
1✔
593
      }
594

595
      /**
596
       * See {@link Args#getChannelLogger}.
597
       *
598
       * @since 1.26.0
599
       */
600
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
601
      public Builder setChannelLogger(ChannelLogger channelLogger) {
602
        this.channelLogger = checkNotNull(channelLogger);
1✔
603
        return this;
1✔
604
      }
605

606
      /**
607
       * See {@link Args#getOffloadExecutor}. This is an optional field.
608
       *
609
       * @since 1.25.0
610
       */
611
      public Builder setOffloadExecutor(Executor executor) {
612
        this.executor = executor;
1✔
613
        return this;
1✔
614
      }
615

616
      /**
617
       * See {@link Args#getOverrideAuthority()}. This is an optional field.
618
       *
619
       * @since 1.49.0
620
       */
621
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
622
      public Builder setOverrideAuthority(String authority) {
623
        this.overrideAuthority = authority;
1✔
624
        return this;
1✔
625
      }
626

627
      /** See {@link Args#getArg(Key)}. */
628
      public <T> Builder setArg(Key<T> key, T value) {
629
        checkNotNull(key, "key");
1✔
630
        checkNotNull(value, "value");
1✔
631
        if (customArgs == null) {
1✔
632
          customArgs = new IdentityHashMap<>();
1✔
633
        }
634
        customArgs.put(key, value);
1✔
635
        return this;
1✔
636
      }
637

638
      /**
639
       * See {@link Args#getMetricRecorder()}. This is an optional field.
640
       */
641
      public Builder setMetricRecorder(MetricRecorder metricRecorder) {
642
        this.metricRecorder = metricRecorder;
1✔
643
        return this;
1✔
644
      }
645

646
      /**
647
       * See {@link Args#getNameResolverRegistry}.  This is an optional field.
648
       *
649
       * @since 1.74.0
650
       */
651
      public Builder setNameResolverRegistry(NameResolverRegistry registry) {
652
        this.nameResolverRegistry = registry;
1✔
653
        return this;
1✔
654
      }
655

656
      /**
657
       * Builds an {@link Args}.
658
       *
659
       * @since 1.21.0
660
       */
661
      public Args build() {
662
        return new Args(this);
1✔
663
      }
664
    }
665

666
    /**
667
     * Identifies an externally-defined custom argument that can be stored in {@link Args}.
668
     *
669
     * <p>Uses reference equality so keys should be defined as global constants.
670
     *
671
     * @param <T> type of values that can be stored under this key
672
     */
673
    @Immutable
674
    @SuppressWarnings("UnusedTypeParameter")
675
    public static final class Key<T> {
676
      private final String debugString;
677

678
      private Key(String debugString) {
1✔
679
        this.debugString = debugString;
1✔
680
      }
1✔
681

682
      @Override
683
      public String toString() {
684
        return debugString;
×
685
      }
686

687
      /**
688
       * Creates a new instance of {@link Key}.
689
       *
690
       * @param debugString a string used to describe the key, used for debugging.
691
       * @param <T> Key type
692
       * @return a new instance of Key
693
       */
694
      public static <T> Key<T> create(String debugString) {
695
        return new Key<>(debugString);
1✔
696
      }
697
    }
698
  }
699

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

721
  /**
722
   * Represents the results from a Name Resolver.
723
   *
724
   * @since 1.21.0
725
   */
726
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
727
  public static final class ResolutionResult {
728
    private final StatusOr<List<EquivalentAddressGroup>> addressesOrError;
729
    @ResolutionResultAttr
730
    private final Attributes attributes;
731
    @Nullable
732
    private final ConfigOrError serviceConfig;
733

734
    ResolutionResult(
735
        StatusOr<List<EquivalentAddressGroup>> addressesOrError,
736
        @ResolutionResultAttr Attributes attributes,
737
        ConfigOrError serviceConfig) {
1✔
738
      this.addressesOrError = addressesOrError;
1✔
739
      this.attributes = checkNotNull(attributes, "attributes");
1✔
740
      this.serviceConfig = serviceConfig;
1✔
741
    }
1✔
742

743
    /**
744
     * Constructs a new builder of a name resolution result.
745
     *
746
     * @since 1.21.0
747
     */
748
    public static Builder newBuilder() {
749
      return new Builder();
1✔
750
    }
751

752
    /**
753
     * Converts these results back to a builder.
754
     *
755
     * @since 1.21.0
756
     */
757
    public Builder toBuilder() {
758
      return newBuilder()
×
759
          .setAddressesOrError(addressesOrError)
×
760
          .setAttributes(attributes)
×
761
          .setServiceConfig(serviceConfig);
×
762
    }
763

764
    /**
765
     * Gets the addresses resolved by name resolution.
766
     *
767
     * @since 1.21.0
768
     * @deprecated Will be superseded by getAddressesOrError
769
     */
770
    @Deprecated
771
    public List<EquivalentAddressGroup> getAddresses() {
772
      return addressesOrError.getValue();
×
773
    }
774

775
    /**
776
     * Gets the addresses resolved by name resolution or the error in doing so.
777
     *
778
     * @since 1.65.0
779
     */
780
    public StatusOr<List<EquivalentAddressGroup>> getAddressesOrError() {
781
      return addressesOrError;
1✔
782
    }
783

784
    /**
785
     * Gets the attributes associated with the addresses resolved by name resolution.  If there are
786
     * no attributes, {@link Attributes#EMPTY} will be returned.
787
     *
788
     * @since 1.21.0
789
     */
790
    @ResolutionResultAttr
791
    public Attributes getAttributes() {
792
      return attributes;
1✔
793
    }
794

795
    /**
796
     * Gets the Service Config parsed by {@link Args#getServiceConfigParser}.
797
     *
798
     * @since 1.21.0
799
     */
800
    @Nullable
801
    public ConfigOrError getServiceConfig() {
802
      return serviceConfig;
1✔
803
    }
804

805
    @Override
806
    public String toString() {
807
      ToStringHelper stringHelper = MoreObjects.toStringHelper(this);
1✔
808
      stringHelper.add("addressesOrError", addressesOrError.toString());
1✔
809
      stringHelper.add("attributes", attributes);
1✔
810
      stringHelper.add("serviceConfigOrError", serviceConfig);
1✔
811
      return stringHelper.toString();
1✔
812
    }
813

814
    /**
815
     * Useful for testing.  May be slow to calculate.
816
     */
817
    @Override
818
    public boolean equals(Object obj) {
819
      if (!(obj instanceof ResolutionResult)) {
×
820
        return false;
×
821
      }
822
      ResolutionResult that = (ResolutionResult) obj;
×
823
      return Objects.equal(this.addressesOrError, that.addressesOrError)
×
824
          && Objects.equal(this.attributes, that.attributes)
×
825
          && Objects.equal(this.serviceConfig, that.serviceConfig);
×
826
    }
827

828
    /**
829
     * Useful for testing.  May be slow to calculate.
830
     */
831
    @Override
832
    public int hashCode() {
833
      return Objects.hashCode(addressesOrError, attributes, serviceConfig);
1✔
834
    }
835

836
    /**
837
     * A builder for {@link ResolutionResult}.
838
     *
839
     * @since 1.21.0
840
     */
841
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
842
    public static final class Builder {
843
      private StatusOr<List<EquivalentAddressGroup>> addresses =
1✔
844
          StatusOr.fromValue(Collections.emptyList());
1✔
845
      private Attributes attributes = Attributes.EMPTY;
1✔
846
      @Nullable
847
      private ConfigOrError serviceConfig;
848
      //  Make sure to update #toBuilder above!
849

850
      Builder() {}
1✔
851

852
      /**
853
       * Sets the addresses resolved by name resolution.  This field is required.
854
       *
855
       * @since 1.21.0
856
       * @deprecated Will be superseded by setAddressesOrError
857
       */
858
      @Deprecated
859
      public Builder setAddresses(List<EquivalentAddressGroup> addresses) {
860
        setAddressesOrError(StatusOr.fromValue(addresses));
1✔
861
        return this;
1✔
862
      }
863

864
      /**
865
       * Sets the addresses resolved by name resolution or the error in doing so. This field is
866
       * required.
867
       * @param addresses Resolved addresses or an error in resolving addresses
868
       */
869
      public Builder setAddressesOrError(StatusOr<List<EquivalentAddressGroup>> addresses) {
870
        this.addresses = checkNotNull(addresses, "StatusOr addresses cannot be null.");
1✔
871
        return this;
1✔
872
      }
873

874
      /**
875
       * Sets the attributes for the addresses resolved by name resolution.  If unset,
876
       * {@link Attributes#EMPTY} will be used as a default.
877
       *
878
       * @since 1.21.0
879
       */
880
      public Builder setAttributes(Attributes attributes) {
881
        this.attributes = attributes;
1✔
882
        return this;
1✔
883
      }
884

885
      /**
886
       * Sets the Service Config parsed by {@link Args#getServiceConfigParser}.
887
       * This field is optional.
888
       *
889
       * @since 1.21.0
890
       */
891
      public Builder setServiceConfig(@Nullable ConfigOrError serviceConfig) {
892
        this.serviceConfig = serviceConfig;
1✔
893
        return this;
1✔
894
      }
895

896
      /**
897
       * Constructs a new {@link ResolutionResult} from this builder.
898
       *
899
       * @since 1.21.0
900
       */
901
      public ResolutionResult build() {
902
        return new ResolutionResult(addresses, attributes, serviceConfig);
1✔
903
      }
904
    }
905
  }
906

907
  /**
908
   * Represents either a successfully parsed service config, containing all necessary parts to be
909
   * later applied by the channel, or a Status containing the error encountered while parsing.
910
   *
911
   * @since 1.20.0
912
   */
913
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
1✔
914
  public static final class ConfigOrError {
915

916
    /**
917
     * Returns a {@link ConfigOrError} for the successfully parsed config.
918
     */
919
    public static ConfigOrError fromConfig(Object config) {
920
      return new ConfigOrError(config);
1✔
921
    }
922

923
    /**
924
     * Returns a {@link ConfigOrError} for the failure to parse the config.
925
     *
926
     * @param status a non-OK status
927
     */
928
    public static ConfigOrError fromError(Status status) {
929
      return new ConfigOrError(status);
1✔
930
    }
931

932
    private final Status status;
933
    private final Object config;
934

935
    private ConfigOrError(Object config) {
1✔
936
      this.config = checkNotNull(config, "config");
1✔
937
      this.status = null;
1✔
938
    }
1✔
939

940
    private ConfigOrError(Status status) {
1✔
941
      this.config = null;
1✔
942
      this.status = checkNotNull(status, "status");
1✔
943
      checkArgument(!status.isOk(), "cannot use OK status: %s", status);
1✔
944
    }
1✔
945

946
    /**
947
     * Returns config if exists, otherwise null.
948
     */
949
    @Nullable
950
    public Object getConfig() {
951
      return config;
1✔
952
    }
953

954
    /**
955
     * Returns error status if exists, otherwise null.
956
     */
957
    @Nullable
958
    public Status getError() {
959
      return status;
1✔
960
    }
961

962
    @Override
963
    public boolean equals(Object o) {
964
      if (this == o) {
1✔
965
        return true;
×
966
      }
967
      if (o == null || getClass() != o.getClass()) {
1✔
968
        return false;
×
969
      }
970
      ConfigOrError that = (ConfigOrError) o;
1✔
971
      return Objects.equal(status, that.status) && Objects.equal(config, that.config);
1✔
972
    }
973

974
    @Override
975
    public int hashCode() {
976
      return Objects.hashCode(status, config);
1✔
977
    }
978

979
    @Override
980
    public String toString() {
981
      if (config != null) {
1✔
982
        return MoreObjects.toStringHelper(this)
1✔
983
            .add("config", config)
1✔
984
            .toString();
1✔
985
      } else {
986
        assert status != null;
×
987
        return MoreObjects.toStringHelper(this)
×
988
            .add("error", status)
×
989
            .toString();
×
990
      }
991
    }
992
  }
993

994
  @Nullable
995
  private static IdentityHashMap<Args.Key<?>, Object> cloneCustomArgs(
996
          @Nullable IdentityHashMap<Args.Key<?>, Object> customArgs) {
997
    return customArgs != null ? new IdentityHashMap<>(customArgs) : null;
1✔
998
  }
999
}
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

© 2026 Coveralls, Inc