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

grpc / grpc-java / #20349

07 Jul 2026 10:24AM UTC coverage: 89.111% (-0.01%) from 89.122%
#20349

push

github

web-flow
enable child channel plugins (#12578)

Implements https://github.com/grpc/proposal/pull/529

38030 of 42677 relevant lines covered (89.11%)

0.89 hits per line

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

82.23
/../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

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

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

97
          @Override
98
          public void onResult(ResolutionResult resolutionResult) {
99
            StatusOr<List<EquivalentAddressGroup>> addressesOrError =
1✔
100
                resolutionResult.getAddressesOrError();
1✔
101
            if (addressesOrError.hasValue()) {
1✔
102
              listener.onAddresses(addressesOrError.getValue(),
1✔
103
                  resolutionResult.getAttributes());
1✔
104
            } else {
105
              listener.onError(addressesOrError.getStatus());
1✔
106
            }
107
          }
1✔
108
      });
109
    }
110
  }
1✔
111

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

127
  /**
128
   * Stops the resolution. Updates to the Listener will stop.
129
   *
130
   * @since 1.0.0
131
   */
132
  public abstract void shutdown();
133

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

148
  /**
149
   * Factory that creates {@link NameResolver} instances.
150
   *
151
   * @since 1.0.0
152
   */
153
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
154
  public abstract static class Factory {
1✔
155
    /**
156
     * Creates a {@link NameResolver} for the given target URI, or {@code null} if the given URI
157
     * cannot be resolved by this factory. The decision should be solely based on the scheme of the
158
     * URI.
159
     *
160
     * <p>This method will eventually be deprecated and removed as part of a migration from {@code
161
     * java.net.URI} to {@code io.grpc.Uri}. Implementations will override {@link
162
     * #newNameResolver(Uri, Args)} instead.
163
     *
164
     * @param targetUri the target URI to be resolved, whose scheme must not be {@code null}
165
     * @param args other information that may be useful
166
     *
167
     * @since 1.21.0
168
     */
169
    public abstract NameResolver newNameResolver(URI targetUri, final Args args);
170

171
    /**
172
     * Creates a {@link NameResolver} for the given target URI.
173
     *
174
     * <p>Implementations return {@code null} if 'targetUri' cannot be resolved by this factory. The
175
     * decision should be solely based on the target's scheme.
176
     *
177
     * <p>All {@link NameResolver.Factory} implementations should override this method, as it will
178
     * eventually replace {@link #newNameResolver(URI, Args)}. For backwards compatibility, this
179
     * default implementation delegates to {@link #newNameResolver(URI, Args)} if 'targetUri' can be
180
     * converted to a java.net.URI.
181
     *
182
     * <p>NB: Conversion is not always possible, for example {@code scheme:#frag} is a valid {@link
183
     * Uri} but not a valid {@link URI} because its path is empty. The default implementation throws
184
     * IllegalArgumentException in these cases.
185
     *
186
     * @param targetUri the target URI to be resolved
187
     * @param args other information that may be useful
188
     * @throws IllegalArgumentException if targetUri does not have the expected form
189
     * @since 1.79
190
     */
191
    public NameResolver newNameResolver(Uri targetUri, final Args args) {
192
      // Not every io.grpc.Uri can be converted but in the ordinary ManagedChannel creation flow,
193
      // any IllegalArgumentException thrown here would have happened anyway, just earlier. That's
194
      // because parse/toString is transparent so java.net.URI#create here sees the original target
195
      // string just like it did before the io.grpc.Uri migration.
196
      //
197
      // Throwing IAE shouldn't surprise non-framework callers either. After all, many existing
198
      // Factory impls are picky about targetUri and throw IAE when it doesn't look how they expect.
199
      return newNameResolver(URI.create(targetUri.toString()), args);
1✔
200
    }
201

202
    /**
203
     * Returns the default scheme, which will be used to construct a URI when {@link
204
     * ManagedChannelBuilder#forTarget(String)} is given an authority string instead of a compliant
205
     * URI.
206
     *
207
     * @since 1.0.0
208
     */
209
    public abstract String getDefaultScheme();
210
  }
211

212
  /**
213
   * Receives address updates.
214
   *
215
   * <p>All methods are expected to return quickly.
216
   *
217
   * <p>This interface is thread-safe.
218
   *
219
   * @since 1.0.0
220
   */
221
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
222
  public interface Listener {
223
    /**
224
     * Handles updates on resolved addresses and attributes.
225
     *
226
     * <p>Implementations will not modify the given {@code servers}.
227
     *
228
     * @param servers the resolved server addresses. An empty list will trigger {@link #onError}
229
     * @param attributes extra information from naming system.
230
     * @since 1.3.0
231
     */
232
    void onAddresses(
233
        List<EquivalentAddressGroup> servers, @ResolutionResultAttr Attributes attributes);
234

235
    /**
236
     * Handles an error from the resolver. The listener is responsible for eventually invoking
237
     * {@link #refresh()} to re-attempt resolution.
238
     *
239
     * @param error a non-OK status
240
     * @since 1.0.0
241
     */
242
    void onError(Status error);
243
  }
244

245
  /**
246
   * Receives address updates.
247
   *
248
   * <p>All methods are expected to return quickly.
249
   *
250
   * <p>This is a replacement API of {@code Listener}. However, we think this new API may change
251
   * again, so we aren't yet encouraging mass-migration to it. It is fine to use and works.
252
   *
253
   * @since 1.21.0
254
   */
255
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
256
  public abstract static class Listener2 implements Listener {
1✔
257
    /**
258
     * Handles updates on resolved addresses and attributes.
259
     *
260
     * @deprecated This will be removed in 1.22.0
261
     */
262
    @Override
263
    @Deprecated
264
    @InlineMe(
265
        replacement = "this.onResult(ResolutionResult.newBuilder().setAddressesOrError("
266
            + "StatusOr.fromValue(servers)).setAttributes(attributes).build())",
267
        imports = {"io.grpc.NameResolver.ResolutionResult", "io.grpc.StatusOr"})
268
    public final void onAddresses(
269
        List<EquivalentAddressGroup> servers, @ResolutionResultAttr Attributes attributes) {
270
      // TODO(jihuncho) need to promote Listener2 if we want to use ConfigOrError
271
      // Calling onResult and not onResult2 because onResult2 can only be called from a
272
      // synchronization context.
273
      onResult(
1✔
274
          ResolutionResult.newBuilder().setAddressesOrError(
1✔
275
              StatusOr.fromValue(servers)).setAttributes(attributes).build());
1✔
276
    }
1✔
277

278
    /**
279
     * Handles updates on resolved addresses and attributes.  If
280
     * {@link ResolutionResult#getAddressesOrError()} is empty, {@link #onError(Status)} will be
281
     * called.
282
     *
283
     * <p>Newer NameResolver implementations should prefer calling onResult2. This method exists to
284
     * facilitate older {@link Listener} implementations to migrate to {@link Listener2}.
285
     *
286
     * @param resolutionResult the resolved server addresses, attributes, and Service Config.
287
     * @since 1.21.0
288
     */
289
    public abstract void onResult(ResolutionResult resolutionResult);
290

291
    /**
292
     * Handles a name resolving error from the resolver. The listener is responsible for eventually
293
     * invoking {@link NameResolver#refresh()} to re-attempt resolution.
294
     *
295
     * <p>New NameResolver implementations should prefer calling onResult2 which will have the
296
     * address resolution error in {@link ResolutionResult}'s addressesOrError. This method exists
297
     * to facilitate older implementations using {@link Listener} to migrate to {@link Listener2}.
298
     *
299
     * @param error a non-OK status
300
     * @since 1.21.0
301
     */
302
    @Override
303
    public abstract void onError(Status error);
304

305
    /**
306
     * Handles updates on resolved addresses and attributes. Must be called from the same
307
     * {@link SynchronizationContext} available in {@link NameResolver.Args} that is passed
308
     * from the channel.
309
     *
310
     * @param resolutionResult the resolved server addresses or error in address resolution,
311
     *     attributes, and Service Config or error
312
     * @return status indicating whether the resolutionResult was accepted by the listener,
313
     *     typically the result from a load balancer.
314
     * @since 1.66
315
     */
316
    public Status onResult2(ResolutionResult resolutionResult) {
317
      onResult(resolutionResult);
×
318
      return Status.OK;
×
319
    }
320
  }
321

322
  /**
323
   * Annotation for name resolution result attributes. It follows the annotation semantics defined
324
   * by {@link Attributes}.
325
   */
326
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4972")
327
  @Retention(RetentionPolicy.SOURCE)
328
  @Documented
329
  public @interface ResolutionResultAttr {}
330

331
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/11989")
332
  @ResolutionResultAttr
333
  public static final Attributes.Key<String> ATTR_BACKEND_SERVICE =
1✔
334
      Attributes.Key.create("io.grpc.NameResolver.ATTR_BACKEND_SERVICE");
1✔
335

336
  /**
337
   * Information that a {@link Factory} uses to create a {@link NameResolver}.
338
   *
339
   * <p>Args applicable to all {@link NameResolver}s are defined here using ordinary setters and
340
   * getters. This container can also hold externally-defined "custom" args that aren't so widely
341
   * useful or that would be inappropriate dependencies for this low level API. See {@link
342
   * Args#getArg} for more.
343
   *
344
   * <p>Note this class overrides neither {@code equals()} nor {@code hashCode()}.
345
   *
346
   * @since 1.21.0
347
   */
348
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
349
  public static final class Args {
350
    private final int defaultPort;
351
    private final ProxyDetector proxyDetector;
352
    private final SynchronizationContext syncContext;
353
    private final ServiceConfigParser serviceConfigParser;
354
    @Nullable private final ScheduledExecutorService scheduledExecutorService;
355
    @Nullable private final ChannelLogger channelLogger;
356
    @Nullable private final Executor executor;
357
    @Nullable private final String overrideAuthority;
358
    private final MetricRecorder metricRecorder;
359
    @Nullable private final NameResolverRegistry nameResolverRegistry;
360
    @Nullable private final IdentityHashMap<Key<?>, Object> customArgs;
361
    private final ChannelConfigurator channelConfigurator;
362

363
    private Args(Builder builder) {
1✔
364
      this.defaultPort = checkNotNull(builder.defaultPort, "defaultPort not set");
1✔
365
      this.proxyDetector = checkNotNull(builder.proxyDetector, "proxyDetector not set");
1✔
366
      this.syncContext = checkNotNull(builder.syncContext, "syncContext not set");
1✔
367
      this.serviceConfigParser =
1✔
368
          checkNotNull(builder.serviceConfigParser, "serviceConfigParser not set");
1✔
369
      this.scheduledExecutorService = builder.scheduledExecutorService;
1✔
370
      this.channelLogger = builder.channelLogger;
1✔
371
      this.executor = builder.executor;
1✔
372
      this.overrideAuthority = builder.overrideAuthority;
1✔
373
      this.metricRecorder = builder.metricRecorder != null ? builder.metricRecorder
1✔
374
          : new MetricRecorder() {};
1✔
375
      this.nameResolverRegistry = builder.nameResolverRegistry;
1✔
376
      this.customArgs = cloneCustomArgs(builder.customArgs);
1✔
377
      this.channelConfigurator = builder.channelConfigurator;
1✔
378
    }
1✔
379

380
    /**
381
     * The port number used in case the target or the underlying naming system doesn't provide a
382
     * port number.
383
     *
384
     * @since 1.21.0
385
     */
386
    // <p>TODO: Only meaningful for InetSocketAddress producers. Make this a custom arg?
387
    public int getDefaultPort() {
388
      return defaultPort;
1✔
389
    }
390

391
    /**
392
     * If the NameResolver wants to support proxy, it should inquire this {@link ProxyDetector}.
393
     * See documentation on {@link ProxyDetector} about how proxies work in gRPC.
394
     *
395
     * @since 1.21.0
396
     */
397
    public ProxyDetector getProxyDetector() {
398
      return proxyDetector;
1✔
399
    }
400

401
    /**
402
     * Returns the {@link SynchronizationContext} where {@link #start(Listener2)}, {@link #shutdown}
403
     * and {@link #refresh} are run from.
404
     *
405
     * @since 1.21.0
406
     */
407
    public SynchronizationContext getSynchronizationContext() {
408
      return syncContext;
1✔
409
    }
410

411
    /**
412
     * Returns a {@link ScheduledExecutorService} for scheduling delayed tasks.
413
     *
414
     * <p>This service is a shared resource and is only meant for quick tasks. DO NOT block or run
415
     * time-consuming tasks.
416
     *
417
     * <p>The returned service doesn't support {@link ScheduledExecutorService#shutdown shutdown()}
418
     *  and {@link ScheduledExecutorService#shutdownNow shutdownNow()}. They will throw if called.
419
     *
420
     * @since 1.26.0
421
     */
422
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454")
423
    public ScheduledExecutorService getScheduledExecutorService() {
424
      if (scheduledExecutorService == null) {
1✔
425
        throw new IllegalStateException("ScheduledExecutorService not set in Builder");
×
426
      }
427
      return scheduledExecutorService;
1✔
428
    }
429

430
    /**
431
     * Returns the {@link ServiceConfigParser}.
432
     *
433
     * @since 1.21.0
434
     */
435
    public ServiceConfigParser getServiceConfigParser() {
436
      return serviceConfigParser;
1✔
437
    }
438

439
    /**
440
     * Returns the value of a custom arg named 'key', or {@code null} if it's not set.
441
     *
442
     * <p>While ordinary {@link Args} should be universally useful and meaningful, custom arguments
443
     * can apply just to resolvers of a certain URI scheme, just to resolvers producing a particular
444
     * type of {@link java.net.SocketAddress}, or even an individual {@link NameResolver} subclass.
445
     * Custom args are identified by an instance of {@link Args.Key} which should be a constant
446
     * defined in a java package and class appropriate for the argument's scope.
447
     *
448
     * <p>{@link Args} are normally reserved for information in *support* of name resolution, not
449
     * the name to be resolved itself. However, there are rare cases where all or part of the target
450
     * name can't be represented by any standard URI scheme or can't be encoded as a String at all.
451
     * Custom args, in contrast, can hold arbitrary Java types, making them a useful work around in
452
     * these cases.
453
     *
454
     * <p>Custom args can also be used simply to avoid adding inappropriate deps to the low level
455
     * io.grpc package.
456
     */
457
    @SuppressWarnings("unchecked") // Cast is safe because all put()s go through the setArg() API.
458
    @Nullable
459
    public <T> T getArg(Key<T> key) {
460
      return customArgs != null ? (T) customArgs.get(key) : null;
1✔
461
    }
462

463
    /**
464
     * Returns the {@link ChannelLogger} for the Channel served by this NameResolver.
465
     *
466
     * @since 1.26.0
467
     */
468
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
469
    public ChannelLogger getChannelLogger() {
470
      if (channelLogger == null) {
1✔
471
        throw new IllegalStateException("ChannelLogger is not set in Builder");
×
472
      }
473
      return channelLogger;
1✔
474
    }
475

476
    /**
477
     * Returns the configurator for child channels.
478
     *
479
     * @since 1.83.0
480
     */
481
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/12574")
482
    public ChannelConfigurator getChildChannelConfigurator() {
483
      return channelConfigurator;
1✔
484
    }
485

486
    /**
487
     * Returns the Executor on which this resolver should execute long-running or I/O bound work.
488
     * Null if no Executor was set.
489
     *
490
     * @since 1.25.0
491
     */
492
    @Nullable
493
    public Executor getOffloadExecutor() {
494
      return executor;
1✔
495
    }
496

497
    /**
498
     * Returns the overrideAuthority from channel {@link ManagedChannelBuilder#overrideAuthority}.
499
     * Overrides the host name for L7 HTTP virtual host matching. Almost all name resolvers should
500
     * not use this.
501
     *
502
     * @since 1.49.0
503
     */
504
    @Nullable
505
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
506
    public String getOverrideAuthority() {
507
      return overrideAuthority;
1✔
508
    }
509

510
    /**
511
     * Returns the {@link MetricRecorder} that the channel uses to record metrics.
512
     */
513
    public MetricRecorder getMetricRecorder() {
514
      return metricRecorder;
1✔
515
    }
516

517
    /**
518
     * Returns the {@link NameResolverRegistry} that the Channel uses to look for {@link
519
     * NameResolver}s.
520
     *
521
     * @since 1.74.0
522
     */
523
    public NameResolverRegistry getNameResolverRegistry() {
524
      if (nameResolverRegistry == null) {
1✔
525
        throw new IllegalStateException("NameResolverRegistry is not set in Builder");
×
526
      }
527
      return nameResolverRegistry;
1✔
528
    }
529

530
    @Override
531
    public String toString() {
532
      return MoreObjects.toStringHelper(this)
×
533
          .add("defaultPort", defaultPort)
×
534
          .add("proxyDetector", proxyDetector)
×
535
          .add("syncContext", syncContext)
×
536
          .add("serviceConfigParser", serviceConfigParser)
×
537
          .add("customArgs", customArgs)
×
538
          .add("scheduledExecutorService", scheduledExecutorService)
×
539
          .add("channelLogger", channelLogger)
×
540
          .add("executor", executor)
×
541
          .add("overrideAuthority", overrideAuthority)
×
542
          .add("metricRecorder", metricRecorder)
×
543
          .add("nameResolverRegistry", nameResolverRegistry)
×
544
          .toString();
×
545
    }
546

547
    /**
548
     * Returns a builder with the same initial values as this object.
549
     *
550
     * @since 1.21.0
551
     */
552
    public Builder toBuilder() {
553
      Builder builder = new Builder();
1✔
554
      builder.setDefaultPort(defaultPort);
1✔
555
      builder.setProxyDetector(proxyDetector);
1✔
556
      builder.setSynchronizationContext(syncContext);
1✔
557
      builder.setServiceConfigParser(serviceConfigParser);
1✔
558
      builder.setScheduledExecutorService(scheduledExecutorService);
1✔
559
      builder.setChannelLogger(channelLogger);
1✔
560
      builder.setOffloadExecutor(executor);
1✔
561
      builder.setOverrideAuthority(overrideAuthority);
1✔
562
      builder.setMetricRecorder(metricRecorder);
1✔
563
      builder.setNameResolverRegistry(nameResolverRegistry);
1✔
564
      builder.setChildChannelConfigurator(channelConfigurator);
1✔
565
      builder.customArgs = cloneCustomArgs(customArgs);
1✔
566
      return builder;
1✔
567
    }
568

569
    /**
570
     * Creates a new builder.
571
     *
572
     * @since 1.21.0
573
     */
574
    public static Builder newBuilder() {
575
      return new Builder();
1✔
576
    }
577

578
    /**
579
     * Builder for {@link Args}.
580
     *
581
     * @since 1.21.0
582
     */
583
    public static final class Builder {
584
      private Integer defaultPort;
585
      private ProxyDetector proxyDetector;
586
      private SynchronizationContext syncContext;
587
      private ServiceConfigParser serviceConfigParser;
588
      private ScheduledExecutorService scheduledExecutorService;
589
      private ChannelLogger channelLogger;
590
      private Executor executor;
591
      private String overrideAuthority;
592
      private MetricRecorder metricRecorder;
593
      private NameResolverRegistry nameResolverRegistry;
594
      private IdentityHashMap<Key<?>, Object> customArgs;
595
      private ChannelConfigurator channelConfigurator = builder -> { };
1✔
596

597
      Builder() {
1✔
598
      }
1✔
599

600
      /**
601
       * See {@link Args#getDefaultPort}.  This is a required field.
602
       *
603
       * @since 1.21.0
604
       */
605
      public Builder setDefaultPort(int defaultPort) {
606
        this.defaultPort = defaultPort;
1✔
607
        return this;
1✔
608
      }
609

610
      /**
611
       * See {@link Args#getProxyDetector}.  This is required field.
612
       *
613
       * @since 1.21.0
614
       */
615
      public Builder setProxyDetector(ProxyDetector proxyDetector) {
616
        this.proxyDetector = checkNotNull(proxyDetector);
1✔
617
        return this;
1✔
618
      }
619

620
      /**
621
       * See {@link Args#getSynchronizationContext}.  This is a required field.
622
       *
623
       * @since 1.21.0
624
       */
625
      public Builder setSynchronizationContext(SynchronizationContext syncContext) {
626
        this.syncContext = checkNotNull(syncContext);
1✔
627
        return this;
1✔
628
      }
629

630
      /**
631
       * See {@link Args#getScheduledExecutorService}.
632
       */
633
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454")
634
      public Builder setScheduledExecutorService(
635
          ScheduledExecutorService scheduledExecutorService) {
636
        this.scheduledExecutorService = checkNotNull(scheduledExecutorService);
1✔
637
        return this;
1✔
638
      }
639

640
      /**
641
       * See {@link Args#getServiceConfigParser}.  This is a required field.
642
       *
643
       * @since 1.21.0
644
       */
645
      public Builder setServiceConfigParser(ServiceConfigParser parser) {
646
        this.serviceConfigParser = checkNotNull(parser);
1✔
647
        return this;
1✔
648
      }
649

650
      /**
651
       * See {@link Args#getChannelLogger}.
652
       *
653
       * @since 1.26.0
654
       */
655
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
656
      public Builder setChannelLogger(ChannelLogger channelLogger) {
657
        this.channelLogger = checkNotNull(channelLogger);
1✔
658
        return this;
1✔
659
      }
660

661
      /**
662
       * See {@link Args#getOffloadExecutor}. This is an optional field.
663
       *
664
       * @since 1.25.0
665
       */
666
      public Builder setOffloadExecutor(Executor executor) {
667
        this.executor = executor;
1✔
668
        return this;
1✔
669
      }
670

671
      /**
672
       * See {@link Args#getOverrideAuthority()}. This is an optional field.
673
       *
674
       * @since 1.49.0
675
       */
676
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
677
      public Builder setOverrideAuthority(String authority) {
678
        this.overrideAuthority = authority;
1✔
679
        return this;
1✔
680
      }
681

682
      /** See {@link Args#getArg(Key)}. */
683
      public <T> Builder setArg(Key<T> key, T value) {
684
        checkNotNull(key, "key");
1✔
685
        checkNotNull(value, "value");
1✔
686
        if (customArgs == null) {
1✔
687
          customArgs = new IdentityHashMap<>();
1✔
688
        }
689
        customArgs.put(key, value);
1✔
690
        return this;
1✔
691
      }
692

693
      /**
694
       * See {@link Args#getMetricRecorder()}. This is an optional field.
695
       */
696
      public Builder setMetricRecorder(MetricRecorder metricRecorder) {
697
        this.metricRecorder = checkNotNull(metricRecorder, "metricRecorder");
1✔
698
        return this;
1✔
699
      }
700

701
      /**
702
       * See {@link Args#getNameResolverRegistry}.  This is an optional field.
703
       *
704
       * @since 1.74.0
705
       */
706
      public Builder setNameResolverRegistry(NameResolverRegistry registry) {
707
        this.nameResolverRegistry = registry;
1✔
708
        return this;
1✔
709
      }
710

711
      /**
712
       * See {@link Args#getChildChannelConfigurator()}. This is an optional field.
713
       *
714
       * @since 1.83.0
715
       */
716
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/12574")
717
      public Builder setChildChannelConfigurator(ChannelConfigurator channelConfigurator) {
718
        this.channelConfigurator = checkNotNull(channelConfigurator, "channelConfigurator");
1✔
719
        return this;
1✔
720
      }
721

722
      /**
723
       * Builds an {@link Args}.
724
       *
725
       * @since 1.21.0
726
       */
727
      public Args build() {
728
        return new Args(this);
1✔
729
      }
730
    }
731

732
    /**
733
     * Identifies an externally-defined custom argument that can be stored in {@link Args}.
734
     *
735
     * <p>Uses reference equality so keys should be defined as global constants.
736
     *
737
     * @param <T> type of values that can be stored under this key
738
     */
739
    @Immutable
740
    @SuppressWarnings("UnusedTypeParameter")
741
    public static final class Key<T> {
742
      private final String debugString;
743

744
      private Key(String debugString) {
1✔
745
        this.debugString = debugString;
1✔
746
      }
1✔
747

748
      @Override
749
      public String toString() {
750
        return debugString;
×
751
      }
752

753
      /**
754
       * Creates a new instance of {@link Key}.
755
       *
756
       * @param debugString a string used to describe the key, used for debugging.
757
       * @param <T> Key type
758
       * @return a new instance of Key
759
       */
760
      public static <T> Key<T> create(String debugString) {
761
        return new Key<>(debugString);
1✔
762
      }
763
    }
764
  }
765

766
  /**
767
   * Parses and validates service configuration.
768
   *
769
   * @since 1.21.0
770
   */
771
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
772
  public abstract static class ServiceConfigParser {
1✔
773
    /**
774
     * Parses and validates the service configuration chosen by the name resolver.  This will
775
     * return a {@link ConfigOrError} which contains either the successfully parsed config, or the
776
     * {@link Status} representing the failure to parse.  Implementations are expected to not throw
777
     * exceptions but return a Status representing the failure.  The value inside the
778
     * {@link ConfigOrError} should implement {@code equals()} and {@code hashCode()}.
779
     *
780
     * @param rawServiceConfig The {@link Map} representation of the service config
781
     * @return a tuple of the fully parsed and validated channel configuration, else the Status.
782
     * @since 1.21.0
783
     */
784
    public abstract ConfigOrError parseServiceConfig(Map<String, ?> rawServiceConfig);
785
  }
786

787
  /**
788
   * Represents the results from a Name Resolver.
789
   *
790
   * @since 1.21.0
791
   */
792
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
793
  public static final class ResolutionResult {
794
    private final StatusOr<List<EquivalentAddressGroup>> addressesOrError;
795
    @ResolutionResultAttr
796
    private final Attributes attributes;
797
    @Nullable
798
    private final ConfigOrError serviceConfig;
799

800
    ResolutionResult(
801
        StatusOr<List<EquivalentAddressGroup>> addressesOrError,
802
        @ResolutionResultAttr Attributes attributes,
803
        ConfigOrError serviceConfig) {
1✔
804
      this.addressesOrError = addressesOrError;
1✔
805
      this.attributes = checkNotNull(attributes, "attributes");
1✔
806
      this.serviceConfig = serviceConfig;
1✔
807
    }
1✔
808

809
    /**
810
     * Constructs a new builder of a name resolution result.
811
     *
812
     * @since 1.21.0
813
     */
814
    public static Builder newBuilder() {
815
      return new Builder();
1✔
816
    }
817

818
    /**
819
     * Converts these results back to a builder.
820
     *
821
     * @since 1.21.0
822
     */
823
    public Builder toBuilder() {
824
      return newBuilder()
1✔
825
          .setAddressesOrError(addressesOrError)
1✔
826
          .setAttributes(attributes)
1✔
827
          .setServiceConfig(serviceConfig);
1✔
828
    }
829

830
    /**
831
     * Gets the addresses resolved by name resolution.
832
     *
833
     * @since 1.21.0
834
     * @deprecated Will be superseded by getAddressesOrError
835
     */
836
    @Deprecated
837
    public List<EquivalentAddressGroup> getAddresses() {
838
      return addressesOrError.getValue();
×
839
    }
840

841
    /**
842
     * Gets the addresses resolved by name resolution or the error in doing so.
843
     *
844
     * @since 1.65.0
845
     */
846
    public StatusOr<List<EquivalentAddressGroup>> getAddressesOrError() {
847
      return addressesOrError;
1✔
848
    }
849

850
    /**
851
     * Gets the attributes associated with the addresses resolved by name resolution.  If there are
852
     * no attributes, {@link Attributes#EMPTY} will be returned.
853
     *
854
     * @since 1.21.0
855
     */
856
    @ResolutionResultAttr
857
    public Attributes getAttributes() {
858
      return attributes;
1✔
859
    }
860

861
    /**
862
     * Gets the Service Config parsed by {@link Args#getServiceConfigParser}.
863
     *
864
     * @since 1.21.0
865
     */
866
    @Nullable
867
    public ConfigOrError getServiceConfig() {
868
      return serviceConfig;
1✔
869
    }
870

871
    @Override
872
    public String toString() {
873
      ToStringHelper stringHelper = MoreObjects.toStringHelper(this);
1✔
874
      stringHelper.add("addressesOrError", addressesOrError.toString());
1✔
875
      stringHelper.add("attributes", attributes);
1✔
876
      stringHelper.add("serviceConfigOrError", serviceConfig);
1✔
877
      return stringHelper.toString();
1✔
878
    }
879

880
    /**
881
     * Useful for testing.  May be slow to calculate.
882
     */
883
    @Override
884
    public boolean equals(Object obj) {
885
      if (!(obj instanceof ResolutionResult)) {
×
886
        return false;
×
887
      }
888
      ResolutionResult that = (ResolutionResult) obj;
×
889
      return Objects.equal(this.addressesOrError, that.addressesOrError)
×
890
          && Objects.equal(this.attributes, that.attributes)
×
891
          && Objects.equal(this.serviceConfig, that.serviceConfig);
×
892
    }
893

894
    /**
895
     * Useful for testing.  May be slow to calculate.
896
     */
897
    @Override
898
    public int hashCode() {
899
      return Objects.hashCode(addressesOrError, attributes, serviceConfig);
1✔
900
    }
901

902
    /**
903
     * A builder for {@link ResolutionResult}.
904
     *
905
     * @since 1.21.0
906
     */
907
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
908
    public static final class Builder {
909
      private StatusOr<List<EquivalentAddressGroup>> addresses =
1✔
910
          StatusOr.fromValue(Collections.emptyList());
1✔
911
      private Attributes attributes = Attributes.EMPTY;
1✔
912
      @Nullable
913
      private ConfigOrError serviceConfig;
914
      //  Make sure to update #toBuilder above!
915

916
      Builder() {}
1✔
917

918
      /**
919
       * Sets the addresses resolved by name resolution.  This field is required.
920
       *
921
       * @since 1.21.0
922
       * @deprecated Will be superseded by setAddressesOrError
923
       */
924
      @Deprecated
925
      public Builder setAddresses(List<EquivalentAddressGroup> addresses) {
926
        setAddressesOrError(StatusOr.fromValue(addresses));
1✔
927
        return this;
1✔
928
      }
929

930
      /**
931
       * Sets the addresses resolved by name resolution or the error in doing so. This field is
932
       * required.
933
       * @param addresses Resolved addresses or an error in resolving addresses
934
       */
935
      public Builder setAddressesOrError(StatusOr<List<EquivalentAddressGroup>> addresses) {
936
        this.addresses = checkNotNull(addresses, "StatusOr addresses cannot be null.");
1✔
937
        return this;
1✔
938
      }
939

940
      /**
941
       * Sets the attributes for the addresses resolved by name resolution.  If unset,
942
       * {@link Attributes#EMPTY} will be used as a default.
943
       *
944
       * @since 1.21.0
945
       */
946
      public Builder setAttributes(Attributes attributes) {
947
        this.attributes = attributes;
1✔
948
        return this;
1✔
949
      }
950

951
      /**
952
       * Sets the Service Config parsed by {@link Args#getServiceConfigParser}.
953
       * This field is optional.
954
       *
955
       * @since 1.21.0
956
       */
957
      public Builder setServiceConfig(@Nullable ConfigOrError serviceConfig) {
958
        this.serviceConfig = serviceConfig;
1✔
959
        return this;
1✔
960
      }
961

962
      /**
963
       * Constructs a new {@link ResolutionResult} from this builder.
964
       *
965
       * @since 1.21.0
966
       */
967
      public ResolutionResult build() {
968
        return new ResolutionResult(addresses, attributes, serviceConfig);
1✔
969
      }
970
    }
971
  }
972

973
  /**
974
   * Represents either a successfully parsed service config, containing all necessary parts to be
975
   * later applied by the channel, or a Status containing the error encountered while parsing.
976
   *
977
   * @since 1.20.0
978
   */
979
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
1✔
980
  public static final class ConfigOrError {
981

982
    /**
983
     * Returns a {@link ConfigOrError} for the successfully parsed config.
984
     */
985
    public static ConfigOrError fromConfig(Object config) {
986
      return new ConfigOrError(config);
1✔
987
    }
988

989
    /**
990
     * Returns a {@link ConfigOrError} for the failure to parse the config.
991
     *
992
     * @param status a non-OK status
993
     */
994
    public static ConfigOrError fromError(Status status) {
995
      return new ConfigOrError(status);
1✔
996
    }
997

998
    private final Status status;
999
    private final Object config;
1000

1001
    private ConfigOrError(Object config) {
1✔
1002
      this.config = checkNotNull(config, "config");
1✔
1003
      this.status = null;
1✔
1004
    }
1✔
1005

1006
    private ConfigOrError(Status status) {
1✔
1007
      this.config = null;
1✔
1008
      this.status = checkNotNull(status, "status");
1✔
1009
      checkArgument(!status.isOk(), "cannot use OK status: %s", status);
1✔
1010
    }
1✔
1011

1012
    /**
1013
     * Returns config if exists, otherwise null.
1014
     */
1015
    @Nullable
1016
    public Object getConfig() {
1017
      return config;
1✔
1018
    }
1019

1020
    /**
1021
     * Returns error status if exists, otherwise null.
1022
     */
1023
    @Nullable
1024
    public Status getError() {
1025
      return status;
1✔
1026
    }
1027

1028
    @Override
1029
    public boolean equals(Object o) {
1030
      if (this == o) {
1✔
1031
        return true;
×
1032
      }
1033
      if (o == null || getClass() != o.getClass()) {
1✔
1034
        return false;
×
1035
      }
1036
      ConfigOrError that = (ConfigOrError) o;
1✔
1037
      return Objects.equal(status, that.status) && Objects.equal(config, that.config);
1✔
1038
    }
1039

1040
    @Override
1041
    public int hashCode() {
1042
      return Objects.hashCode(status, config);
1✔
1043
    }
1044

1045
    @Override
1046
    public String toString() {
1047
      if (config != null) {
1✔
1048
        return MoreObjects.toStringHelper(this)
1✔
1049
            .add("config", config)
1✔
1050
            .toString();
1✔
1051
      } else {
1052
        assert status != null;
×
1053
        return MoreObjects.toStringHelper(this)
×
1054
            .add("error", status)
×
1055
            .toString();
×
1056
      }
1057
    }
1058
  }
1059

1060
  @Nullable
1061
  private static IdentityHashMap<Args.Key<?>, Object> cloneCustomArgs(
1062
          @Nullable IdentityHashMap<Args.Key<?>, Object> customArgs) {
1063
    return customArgs != null ? new IdentityHashMap<>(customArgs) : null;
1✔
1064
  }
1065
}
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