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

grpc / grpc-java / #19908

16 Jul 2025 07:54PM UTC coverage: 88.593% (+0.07%) from 88.528%
#19908

push

github

ejona86
Revert "xds: Convert CdsLb to XdsDepManager"

This reverts commit 297ab05ef.

b/430347751 shows multiple concerning behaviors in the xDS stack with
the new A74 config update model. XdsDepManager and CdsLB2 still seem to
be working correctly, but the change is exacerbated issues in other
parts of the stack, like RingHashConfig not having equals fixed in
a8de9f07ab.

Revert only for the v1.74.x release, leaving it on master.

34647 of 39108 relevant lines covered (88.59%)

0.89 hits per line

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

79.1
/../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 IdentityHashMap<Key<?>, Object> customArgs;
307

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

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

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

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

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

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

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

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

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

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

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

450

451
    @Override
452
    public String toString() {
453
      return MoreObjects.toStringHelper(this)
×
454
          .add("defaultPort", defaultPort)
×
455
          .add("proxyDetector", proxyDetector)
×
456
          .add("syncContext", syncContext)
×
457
          .add("serviceConfigParser", serviceConfigParser)
×
458
          .add("customArgs", customArgs)
×
459
          .add("scheduledExecutorService", scheduledExecutorService)
×
460
          .add("channelLogger", channelLogger)
×
461
          .add("executor", executor)
×
462
          .add("overrideAuthority", overrideAuthority)
×
463
          .add("metricRecorder", metricRecorder)
×
464
          .toString();
×
465
    }
466

467
    /**
468
     * Returns a builder with the same initial values as this object.
469
     *
470
     * @since 1.21.0
471
     */
472
    public Builder toBuilder() {
473
      Builder builder = new Builder();
1✔
474
      builder.setDefaultPort(defaultPort);
1✔
475
      builder.setProxyDetector(proxyDetector);
1✔
476
      builder.setSynchronizationContext(syncContext);
1✔
477
      builder.setServiceConfigParser(serviceConfigParser);
1✔
478
      builder.setScheduledExecutorService(scheduledExecutorService);
1✔
479
      builder.setChannelLogger(channelLogger);
1✔
480
      builder.setOffloadExecutor(executor);
1✔
481
      builder.setOverrideAuthority(overrideAuthority);
1✔
482
      builder.setMetricRecorder(metricRecorder);
1✔
483
      builder.customArgs = cloneCustomArgs(customArgs);
1✔
484
      return builder;
1✔
485
    }
486

487
    /**
488
     * Creates a new builder.
489
     *
490
     * @since 1.21.0
491
     */
492
    public static Builder newBuilder() {
493
      return new Builder();
1✔
494
    }
495

496
    /**
497
     * Builder for {@link Args}.
498
     *
499
     * @since 1.21.0
500
     */
501
    public static final class Builder {
502
      private Integer defaultPort;
503
      private ProxyDetector proxyDetector;
504
      private SynchronizationContext syncContext;
505
      private ServiceConfigParser serviceConfigParser;
506
      private ScheduledExecutorService scheduledExecutorService;
507
      private ChannelLogger channelLogger;
508
      private Executor executor;
509
      private String overrideAuthority;
510
      private MetricRecorder metricRecorder;
511
      private IdentityHashMap<Key<?>, Object> customArgs;
512

513
      Builder() {
1✔
514
      }
1✔
515

516
      /**
517
       * See {@link Args#getDefaultPort}.  This is a required field.
518
       *
519
       * @since 1.21.0
520
       */
521
      public Builder setDefaultPort(int defaultPort) {
522
        this.defaultPort = defaultPort;
1✔
523
        return this;
1✔
524
      }
525

526
      /**
527
       * See {@link Args#getProxyDetector}.  This is required field.
528
       *
529
       * @since 1.21.0
530
       */
531
      public Builder setProxyDetector(ProxyDetector proxyDetector) {
532
        this.proxyDetector = checkNotNull(proxyDetector);
1✔
533
        return this;
1✔
534
      }
535

536
      /**
537
       * See {@link Args#getSynchronizationContext}.  This is a required field.
538
       *
539
       * @since 1.21.0
540
       */
541
      public Builder setSynchronizationContext(SynchronizationContext syncContext) {
542
        this.syncContext = checkNotNull(syncContext);
1✔
543
        return this;
1✔
544
      }
545

546
      /**
547
       * See {@link Args#getScheduledExecutorService}.
548
       */
549
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454")
550
      public Builder setScheduledExecutorService(
551
          ScheduledExecutorService scheduledExecutorService) {
552
        this.scheduledExecutorService = checkNotNull(scheduledExecutorService);
1✔
553
        return this;
1✔
554
      }
555

556
      /**
557
       * See {@link Args#getServiceConfigParser}.  This is a required field.
558
       *
559
       * @since 1.21.0
560
       */
561
      public Builder setServiceConfigParser(ServiceConfigParser parser) {
562
        this.serviceConfigParser = checkNotNull(parser);
1✔
563
        return this;
1✔
564
      }
565

566
      /**
567
       * See {@link Args#getChannelLogger}.
568
       *
569
       * @since 1.26.0
570
       */
571
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
572
      public Builder setChannelLogger(ChannelLogger channelLogger) {
573
        this.channelLogger = checkNotNull(channelLogger);
1✔
574
        return this;
1✔
575
      }
576

577
      /**
578
       * See {@link Args#getOffloadExecutor}. This is an optional field.
579
       *
580
       * @since 1.25.0
581
       */
582
      public Builder setOffloadExecutor(Executor executor) {
583
        this.executor = executor;
1✔
584
        return this;
1✔
585
      }
586

587
      /**
588
       * See {@link Args#getOverrideAuthority()}. This is an optional field.
589
       *
590
       * @since 1.49.0
591
       */
592
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
593
      public Builder setOverrideAuthority(String authority) {
594
        this.overrideAuthority = authority;
1✔
595
        return this;
1✔
596
      }
597

598
      /** See {@link Args#getArg(Key)}. */
599
      public <T> Builder setArg(Key<T> key, T value) {
600
        checkNotNull(key, "key");
1✔
601
        checkNotNull(value, "value");
1✔
602
        if (customArgs == null) {
1✔
603
          customArgs = new IdentityHashMap<>();
1✔
604
        }
605
        customArgs.put(key, value);
1✔
606
        return this;
1✔
607
      }
608

609
      /**
610
       * See {@link Args#getMetricRecorder()}. This is an optional field.
611
       */
612
      public Builder setMetricRecorder(MetricRecorder metricRecorder) {
613
        this.metricRecorder = metricRecorder;
1✔
614
        return this;
1✔
615
      }
616

617
      /**
618
       * Builds an {@link Args}.
619
       *
620
       * @since 1.21.0
621
       */
622
      public Args build() {
623
        return new Args(this);
1✔
624
      }
625
    }
626

627
    /**
628
     * Identifies an externally-defined custom argument that can be stored in {@link Args}.
629
     *
630
     * <p>Uses reference equality so keys should be defined as global constants.
631
     *
632
     * @param <T> type of values that can be stored under this key
633
     */
634
    @Immutable
635
    @SuppressWarnings("UnusedTypeParameter")
636
    public static final class Key<T> {
637
      private final String debugString;
638

639
      private Key(String debugString) {
1✔
640
        this.debugString = debugString;
1✔
641
      }
1✔
642

643
      @Override
644
      public String toString() {
645
        return debugString;
×
646
      }
647

648
      /**
649
       * Creates a new instance of {@link Key}.
650
       *
651
       * @param debugString a string used to describe the key, used for debugging.
652
       * @param <T> Key type
653
       * @return a new instance of Key
654
       */
655
      public static <T> Key<T> create(String debugString) {
656
        return new Key<>(debugString);
1✔
657
      }
658
    }
659
  }
660

661
  /**
662
   * Parses and validates service configuration.
663
   *
664
   * @since 1.21.0
665
   */
666
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
667
  public abstract static class ServiceConfigParser {
1✔
668
    /**
669
     * Parses and validates the service configuration chosen by the name resolver.  This will
670
     * return a {@link ConfigOrError} which contains either the successfully parsed config, or the
671
     * {@link Status} representing the failure to parse.  Implementations are expected to not throw
672
     * exceptions but return a Status representing the failure.  The value inside the
673
     * {@link ConfigOrError} should implement {@code equals()} and {@code hashCode()}.
674
     *
675
     * @param rawServiceConfig The {@link Map} representation of the service config
676
     * @return a tuple of the fully parsed and validated channel configuration, else the Status.
677
     * @since 1.21.0
678
     */
679
    public abstract ConfigOrError parseServiceConfig(Map<String, ?> rawServiceConfig);
680
  }
681

682
  /**
683
   * Represents the results from a Name Resolver.
684
   *
685
   * @since 1.21.0
686
   */
687
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
688
  public static final class ResolutionResult {
689
    private final StatusOr<List<EquivalentAddressGroup>> addressesOrError;
690
    @ResolutionResultAttr
691
    private final Attributes attributes;
692
    @Nullable
693
    private final ConfigOrError serviceConfig;
694

695
    ResolutionResult(
696
        StatusOr<List<EquivalentAddressGroup>> addressesOrError,
697
        @ResolutionResultAttr Attributes attributes,
698
        ConfigOrError serviceConfig) {
1✔
699
      this.addressesOrError = addressesOrError;
1✔
700
      this.attributes = checkNotNull(attributes, "attributes");
1✔
701
      this.serviceConfig = serviceConfig;
1✔
702
    }
1✔
703

704
    /**
705
     * Constructs a new builder of a name resolution result.
706
     *
707
     * @since 1.21.0
708
     */
709
    public static Builder newBuilder() {
710
      return new Builder();
1✔
711
    }
712

713
    /**
714
     * Converts these results back to a builder.
715
     *
716
     * @since 1.21.0
717
     */
718
    public Builder toBuilder() {
719
      return newBuilder()
×
720
          .setAddressesOrError(addressesOrError)
×
721
          .setAttributes(attributes)
×
722
          .setServiceConfig(serviceConfig);
×
723
    }
724

725
    /**
726
     * Gets the addresses resolved by name resolution.
727
     *
728
     * @since 1.21.0
729
     * @deprecated Will be superseded by getAddressesOrError
730
     */
731
    @Deprecated
732
    public List<EquivalentAddressGroup> getAddresses() {
733
      return addressesOrError.getValue();
×
734
    }
735

736
    /**
737
     * Gets the addresses resolved by name resolution or the error in doing so.
738
     *
739
     * @since 1.65.0
740
     */
741
    public StatusOr<List<EquivalentAddressGroup>> getAddressesOrError() {
742
      return addressesOrError;
1✔
743
    }
744

745
    /**
746
     * Gets the attributes associated with the addresses resolved by name resolution.  If there are
747
     * no attributes, {@link Attributes#EMPTY} will be returned.
748
     *
749
     * @since 1.21.0
750
     */
751
    @ResolutionResultAttr
752
    public Attributes getAttributes() {
753
      return attributes;
1✔
754
    }
755

756
    /**
757
     * Gets the Service Config parsed by {@link Args#getServiceConfigParser}.
758
     *
759
     * @since 1.21.0
760
     */
761
    @Nullable
762
    public ConfigOrError getServiceConfig() {
763
      return serviceConfig;
1✔
764
    }
765

766
    @Override
767
    public String toString() {
768
      ToStringHelper stringHelper = MoreObjects.toStringHelper(this);
1✔
769
      stringHelper.add("addressesOrError", addressesOrError.toString());
1✔
770
      stringHelper.add("attributes", attributes);
1✔
771
      stringHelper.add("serviceConfigOrError", serviceConfig);
1✔
772
      return stringHelper.toString();
1✔
773
    }
774

775
    /**
776
     * Useful for testing.  May be slow to calculate.
777
     */
778
    @Override
779
    public boolean equals(Object obj) {
780
      if (!(obj instanceof ResolutionResult)) {
×
781
        return false;
×
782
      }
783
      ResolutionResult that = (ResolutionResult) obj;
×
784
      return Objects.equal(this.addressesOrError, that.addressesOrError)
×
785
          && Objects.equal(this.attributes, that.attributes)
×
786
          && Objects.equal(this.serviceConfig, that.serviceConfig);
×
787
    }
788

789
    /**
790
     * Useful for testing.  May be slow to calculate.
791
     */
792
    @Override
793
    public int hashCode() {
794
      return Objects.hashCode(addressesOrError, attributes, serviceConfig);
1✔
795
    }
796

797
    /**
798
     * A builder for {@link ResolutionResult}.
799
     *
800
     * @since 1.21.0
801
     */
802
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
803
    public static final class Builder {
804
      private StatusOr<List<EquivalentAddressGroup>> addresses =
1✔
805
          StatusOr.fromValue(Collections.emptyList());
1✔
806
      private Attributes attributes = Attributes.EMPTY;
1✔
807
      @Nullable
808
      private ConfigOrError serviceConfig;
809
      //  Make sure to update #toBuilder above!
810

811
      Builder() {}
1✔
812

813
      /**
814
       * Sets the addresses resolved by name resolution.  This field is required.
815
       *
816
       * @since 1.21.0
817
       * @deprecated Will be superseded by setAddressesOrError
818
       */
819
      @Deprecated
820
      public Builder setAddresses(List<EquivalentAddressGroup> addresses) {
821
        setAddressesOrError(StatusOr.fromValue(addresses));
1✔
822
        return this;
1✔
823
      }
824

825
      /**
826
       * Sets the addresses resolved by name resolution or the error in doing so. This field is
827
       * required.
828
       * @param addresses Resolved addresses or an error in resolving addresses
829
       */
830
      public Builder setAddressesOrError(StatusOr<List<EquivalentAddressGroup>> addresses) {
831
        this.addresses = checkNotNull(addresses, "StatusOr addresses cannot be null.");
1✔
832
        return this;
1✔
833
      }
834

835
      /**
836
       * Sets the attributes for the addresses resolved by name resolution.  If unset,
837
       * {@link Attributes#EMPTY} will be used as a default.
838
       *
839
       * @since 1.21.0
840
       */
841
      public Builder setAttributes(Attributes attributes) {
842
        this.attributes = attributes;
1✔
843
        return this;
1✔
844
      }
845

846
      /**
847
       * Sets the Service Config parsed by {@link Args#getServiceConfigParser}.
848
       * This field is optional.
849
       *
850
       * @since 1.21.0
851
       */
852
      public Builder setServiceConfig(@Nullable ConfigOrError serviceConfig) {
853
        this.serviceConfig = serviceConfig;
1✔
854
        return this;
1✔
855
      }
856

857
      /**
858
       * Constructs a new {@link ResolutionResult} from this builder.
859
       *
860
       * @since 1.21.0
861
       */
862
      public ResolutionResult build() {
863
        return new ResolutionResult(addresses, attributes, serviceConfig);
1✔
864
      }
865
    }
866
  }
867

868
  /**
869
   * Represents either a successfully parsed service config, containing all necessary parts to be
870
   * later applied by the channel, or a Status containing the error encountered while parsing.
871
   *
872
   * @since 1.20.0
873
   */
874
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
1✔
875
  public static final class ConfigOrError {
876

877
    /**
878
     * Returns a {@link ConfigOrError} for the successfully parsed config.
879
     */
880
    public static ConfigOrError fromConfig(Object config) {
881
      return new ConfigOrError(config);
1✔
882
    }
883

884
    /**
885
     * Returns a {@link ConfigOrError} for the failure to parse the config.
886
     *
887
     * @param status a non-OK status
888
     */
889
    public static ConfigOrError fromError(Status status) {
890
      return new ConfigOrError(status);
1✔
891
    }
892

893
    private final Status status;
894
    private final Object config;
895

896
    private ConfigOrError(Object config) {
1✔
897
      this.config = checkNotNull(config, "config");
1✔
898
      this.status = null;
1✔
899
    }
1✔
900

901
    private ConfigOrError(Status status) {
1✔
902
      this.config = null;
1✔
903
      this.status = checkNotNull(status, "status");
1✔
904
      checkArgument(!status.isOk(), "cannot use OK status: %s", status);
1✔
905
    }
1✔
906

907
    /**
908
     * Returns config if exists, otherwise null.
909
     */
910
    @Nullable
911
    public Object getConfig() {
912
      return config;
1✔
913
    }
914

915
    /**
916
     * Returns error status if exists, otherwise null.
917
     */
918
    @Nullable
919
    public Status getError() {
920
      return status;
1✔
921
    }
922

923
    @Override
924
    public boolean equals(Object o) {
925
      if (this == o) {
1✔
926
        return true;
×
927
      }
928
      if (o == null || getClass() != o.getClass()) {
1✔
929
        return false;
×
930
      }
931
      ConfigOrError that = (ConfigOrError) o;
1✔
932
      return Objects.equal(status, that.status) && Objects.equal(config, that.config);
1✔
933
    }
934

935
    @Override
936
    public int hashCode() {
937
      return Objects.hashCode(status, config);
1✔
938
    }
939

940
    @Override
941
    public String toString() {
942
      if (config != null) {
1✔
943
        return MoreObjects.toStringHelper(this)
1✔
944
            .add("config", config)
1✔
945
            .toString();
1✔
946
      } else {
947
        assert status != null;
×
948
        return MoreObjects.toStringHelper(this)
×
949
            .add("error", status)
×
950
            .toString();
×
951
      }
952
    }
953
  }
954

955
  @Nullable
956
  private static IdentityHashMap<Args.Key<?>, Object> cloneCustomArgs(
957
          @Nullable IdentityHashMap<Args.Key<?>, Object> customArgs) {
958
    return customArgs != null ? new IdentityHashMap<>(customArgs) : null;
1✔
959
  }
960
}
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