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

grpc / grpc-java / #19789

23 Apr 2025 04:45AM UTC coverage: 88.551% (-0.001%) from 88.552%
#19789

push

github

web-flow
Update psm-dualstack.cfg (#11950) (#12029)

120 minutes has not been sufficient, causing frequent VM timeout errors in the test runs.

33684 of 38039 relevant lines covered (88.55%)

0.89 hits per line

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

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

303
    private Args(Builder builder) {
1✔
304
      this.defaultPort = checkNotNull(builder.defaultPort, "defaultPort not set");
1✔
305
      this.proxyDetector = checkNotNull(builder.proxyDetector, "proxyDetector not set");
1✔
306
      this.syncContext = checkNotNull(builder.syncContext, "syncContext not set");
1✔
307
      this.serviceConfigParser =
1✔
308
          checkNotNull(builder.serviceConfigParser, "serviceConfigParser not set");
1✔
309
      this.scheduledExecutorService = builder.scheduledExecutorService;
1✔
310
      this.channelLogger = builder.channelLogger;
1✔
311
      this.executor = builder.executor;
1✔
312
      this.overrideAuthority = builder.overrideAuthority;
1✔
313
      this.metricRecorder = builder.metricRecorder;
1✔
314
      this.customArgs = cloneCustomArgs(builder.customArgs);
1✔
315
    }
1✔
316

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

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

338
    /**
339
     * Returns the {@link SynchronizationContext} where {@link #start(Listener2)}, {@link #shutdown}
340
     * and {@link #refresh} are run from.
341
     *
342
     * @since 1.21.0
343
     */
344
    public SynchronizationContext getSynchronizationContext() {
345
      return syncContext;
1✔
346
    }
347

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

367
    /**
368
     * Returns the {@link ServiceConfigParser}.
369
     *
370
     * @since 1.21.0
371
     */
372
    public ServiceConfigParser getServiceConfigParser() {
373
      return serviceConfigParser;
1✔
374
    }
375

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

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

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

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

437
    /**
438
     * Returns the {@link MetricRecorder} that the channel uses to record metrics.
439
     */
440
    @Nullable
441
    public MetricRecorder getMetricRecorder() {
442
      return metricRecorder;
1✔
443
    }
444

445

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

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

482
    /**
483
     * Creates a new builder.
484
     *
485
     * @since 1.21.0
486
     */
487
    public static Builder newBuilder() {
488
      return new Builder();
1✔
489
    }
490

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

508
      Builder() {
1✔
509
      }
1✔
510

511
      /**
512
       * See {@link Args#getDefaultPort}.  This is a required field.
513
       *
514
       * @since 1.21.0
515
       */
516
      public Builder setDefaultPort(int defaultPort) {
517
        this.defaultPort = defaultPort;
1✔
518
        return this;
1✔
519
      }
520

521
      /**
522
       * See {@link Args#getProxyDetector}.  This is required field.
523
       *
524
       * @since 1.21.0
525
       */
526
      public Builder setProxyDetector(ProxyDetector proxyDetector) {
527
        this.proxyDetector = checkNotNull(proxyDetector);
1✔
528
        return this;
1✔
529
      }
530

531
      /**
532
       * See {@link Args#getSynchronizationContext}.  This is a required field.
533
       *
534
       * @since 1.21.0
535
       */
536
      public Builder setSynchronizationContext(SynchronizationContext syncContext) {
537
        this.syncContext = checkNotNull(syncContext);
1✔
538
        return this;
1✔
539
      }
540

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

551
      /**
552
       * See {@link Args#getServiceConfigParser}.  This is a required field.
553
       *
554
       * @since 1.21.0
555
       */
556
      public Builder setServiceConfigParser(ServiceConfigParser parser) {
557
        this.serviceConfigParser = checkNotNull(parser);
1✔
558
        return this;
1✔
559
      }
560

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

572
      /**
573
       * See {@link Args#getOffloadExecutor}. This is an optional field.
574
       *
575
       * @since 1.25.0
576
       */
577
      public Builder setOffloadExecutor(Executor executor) {
578
        this.executor = executor;
1✔
579
        return this;
1✔
580
      }
581

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

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

604
      /**
605
       * See {@link Args#getMetricRecorder()}. This is an optional field.
606
       */
607
      public Builder setMetricRecorder(MetricRecorder metricRecorder) {
608
        this.metricRecorder = metricRecorder;
1✔
609
        return this;
1✔
610
      }
611

612
      /**
613
       * Builds an {@link Args}.
614
       *
615
       * @since 1.21.0
616
       */
617
      public Args build() {
618
        return new Args(this);
1✔
619
      }
620
    }
621

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

634
      private Key(String debugString) {
1✔
635
        this.debugString = debugString;
1✔
636
      }
1✔
637

638
      @Override
639
      public String toString() {
640
        return debugString;
×
641
      }
642

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

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

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

690
    ResolutionResult(
691
        StatusOr<List<EquivalentAddressGroup>> addressesOrError,
692
        @ResolutionResultAttr Attributes attributes,
693
        ConfigOrError serviceConfig) {
1✔
694
      this.addressesOrError = addressesOrError;
1✔
695
      this.attributes = checkNotNull(attributes, "attributes");
1✔
696
      this.serviceConfig = serviceConfig;
1✔
697
    }
1✔
698

699
    /**
700
     * Constructs a new builder of a name resolution result.
701
     *
702
     * @since 1.21.0
703
     */
704
    public static Builder newBuilder() {
705
      return new Builder();
1✔
706
    }
707

708
    /**
709
     * Converts these results back to a builder.
710
     *
711
     * @since 1.21.0
712
     */
713
    public Builder toBuilder() {
714
      return newBuilder()
1✔
715
          .setAddressesOrError(addressesOrError)
1✔
716
          .setAttributes(attributes)
1✔
717
          .setServiceConfig(serviceConfig);
1✔
718
    }
719

720
    /**
721
     * Gets the addresses resolved by name resolution.
722
     *
723
     * @since 1.21.0
724
     * @deprecated Will be superseded by getAddressesOrError
725
     */
726
    @Deprecated
727
    public List<EquivalentAddressGroup> getAddresses() {
728
      return addressesOrError.getValue();
1✔
729
    }
730

731
    /**
732
     * Gets the addresses resolved by name resolution or the error in doing so.
733
     *
734
     * @since 1.65.0
735
     */
736
    public StatusOr<List<EquivalentAddressGroup>> getAddressesOrError() {
737
      return addressesOrError;
1✔
738
    }
739

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

751
    /**
752
     * Gets the Service Config parsed by {@link Args#getServiceConfigParser}.
753
     *
754
     * @since 1.21.0
755
     */
756
    @Nullable
757
    public ConfigOrError getServiceConfig() {
758
      return serviceConfig;
1✔
759
    }
760

761
    @Override
762
    public String toString() {
763
      ToStringHelper stringHelper = MoreObjects.toStringHelper(this);
1✔
764
      stringHelper.add("addressesOrError", addressesOrError.toString());
1✔
765
      stringHelper.add("attributes", attributes);
1✔
766
      stringHelper.add("serviceConfigOrError", serviceConfig);
1✔
767
      return stringHelper.toString();
1✔
768
    }
769

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

784
    /**
785
     * Useful for testing.  May be slow to calculate.
786
     */
787
    @Override
788
    public int hashCode() {
789
      return Objects.hashCode(addressesOrError, attributes, serviceConfig);
1✔
790
    }
791

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

806
      Builder() {}
1✔
807

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

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

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

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

852
      /**
853
       * Constructs a new {@link ResolutionResult} from this builder.
854
       *
855
       * @since 1.21.0
856
       */
857
      public ResolutionResult build() {
858
        return new ResolutionResult(addresses, attributes, serviceConfig);
1✔
859
      }
860
    }
861
  }
862

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

872
    /**
873
     * Returns a {@link ConfigOrError} for the successfully parsed config.
874
     */
875
    public static ConfigOrError fromConfig(Object config) {
876
      return new ConfigOrError(config);
1✔
877
    }
878

879
    /**
880
     * Returns a {@link ConfigOrError} for the failure to parse the config.
881
     *
882
     * @param status a non-OK status
883
     */
884
    public static ConfigOrError fromError(Status status) {
885
      return new ConfigOrError(status);
1✔
886
    }
887

888
    private final Status status;
889
    private final Object config;
890

891
    private ConfigOrError(Object config) {
1✔
892
      this.config = checkNotNull(config, "config");
1✔
893
      this.status = null;
1✔
894
    }
1✔
895

896
    private ConfigOrError(Status status) {
1✔
897
      this.config = null;
1✔
898
      this.status = checkNotNull(status, "status");
1✔
899
      checkArgument(!status.isOk(), "cannot use OK status: %s", status);
1✔
900
    }
1✔
901

902
    /**
903
     * Returns config if exists, otherwise null.
904
     */
905
    @Nullable
906
    public Object getConfig() {
907
      return config;
1✔
908
    }
909

910
    /**
911
     * Returns error status if exists, otherwise null.
912
     */
913
    @Nullable
914
    public Status getError() {
915
      return status;
1✔
916
    }
917

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

930
    @Override
931
    public int hashCode() {
932
      return Objects.hashCode(status, config);
1✔
933
    }
934

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

950
  @Nullable
951
  private static IdentityHashMap<Args.Key<?>, Object> cloneCustomArgs(
952
          @Nullable IdentityHashMap<Args.Key<?>, Object> customArgs) {
953
    return customArgs != null ? new IdentityHashMap<>(customArgs) : null;
1✔
954
  }
955
}
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