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

grpc / grpc-java / #19497

07 Oct 2024 12:25PM UTC coverage: 84.656% (+0.06%) from 84.599%
#19497

push

github

web-flow
On result2 resolution result have addresses or error (#11330)

Combined success / error status passed via ResolutionResult to the NameResolver.Listener2 interface's onResult2 method - Addresses in the success case or address resolution error in the failure case now get set in ResolutionResult::addressesOrError by the internal name resolvers.

33771 of 39892 relevant lines covered (84.66%)

0.85 hits per line

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

82.24
/../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.List;
32
import java.util.Map;
33
import java.util.concurrent.Executor;
34
import java.util.concurrent.ScheduledExecutorService;
35
import javax.annotation.Nullable;
36
import javax.annotation.concurrent.ThreadSafe;
37

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

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

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

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

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

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

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

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

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

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

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

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

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

253
    /**
254
     * Handles updates on resolved addresses and attributes.
255
     *
256
     * @param resolutionResult the resolved server addresses, attributes, and Service Config.
257
     * @since 1.66
258
     */
259
    public Status onResult2(ResolutionResult resolutionResult) {
260
      onResult(resolutionResult);
1✔
261
      return Status.OK;
1✔
262
    }
263
  }
264

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

274
  /**
275
   * Information that a {@link Factory} uses to create a {@link NameResolver}.
276
   *
277
   * <p>Note this class doesn't override neither {@code equals()} nor {@code hashCode()}.
278
   *
279
   * @since 1.21.0
280
   */
281
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
282
  public static final class Args {
283
    private final int defaultPort;
284
    private final ProxyDetector proxyDetector;
285
    private final SynchronizationContext syncContext;
286
    private final ServiceConfigParser serviceConfigParser;
287
    @Nullable private final ScheduledExecutorService scheduledExecutorService;
288
    @Nullable private final ChannelLogger channelLogger;
289
    @Nullable private final Executor executor;
290
    @Nullable private final String overrideAuthority;
291

292
    private Args(
293
        Integer defaultPort,
294
        ProxyDetector proxyDetector,
295
        SynchronizationContext syncContext,
296
        ServiceConfigParser serviceConfigParser,
297
        @Nullable ScheduledExecutorService scheduledExecutorService,
298
        @Nullable ChannelLogger channelLogger,
299
        @Nullable Executor executor,
300
        @Nullable String overrideAuthority) {
1✔
301
      this.defaultPort = checkNotNull(defaultPort, "defaultPort not set");
1✔
302
      this.proxyDetector = checkNotNull(proxyDetector, "proxyDetector not set");
1✔
303
      this.syncContext = checkNotNull(syncContext, "syncContext not set");
1✔
304
      this.serviceConfigParser = checkNotNull(serviceConfigParser, "serviceConfigParser not set");
1✔
305
      this.scheduledExecutorService = scheduledExecutorService;
1✔
306
      this.channelLogger = channelLogger;
1✔
307
      this.executor = executor;
1✔
308
      this.overrideAuthority = overrideAuthority;
1✔
309
    }
1✔
310

311
    /**
312
     * The port number used in case the target or the underlying naming system doesn't provide a
313
     * port number.
314
     *
315
     * @since 1.21.0
316
     */
317
    public int getDefaultPort() {
318
      return defaultPort;
1✔
319
    }
320

321
    /**
322
     * If the NameResolver wants to support proxy, it should inquire this {@link ProxyDetector}.
323
     * See documentation on {@link ProxyDetector} about how proxies work in gRPC.
324
     *
325
     * @since 1.21.0
326
     */
327
    public ProxyDetector getProxyDetector() {
328
      return proxyDetector;
1✔
329
    }
330

331
    /**
332
     * Returns the {@link SynchronizationContext} where {@link #start(Listener2)}, {@link #shutdown}
333
     * and {@link #refresh} are run from.
334
     *
335
     * @since 1.21.0
336
     */
337
    public SynchronizationContext getSynchronizationContext() {
338
      return syncContext;
1✔
339
    }
340

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

360
    /**
361
     * Returns the {@link ServiceConfigParser}.
362
     *
363
     * @since 1.21.0
364
     */
365
    public ServiceConfigParser getServiceConfigParser() {
366
      return serviceConfigParser;
1✔
367
    }
368

369
    /**
370
     * Returns the {@link ChannelLogger} for the Channel served by this NameResolver.
371
     *
372
     * @since 1.26.0
373
     */
374
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
375
    public ChannelLogger getChannelLogger() {
376
      if (channelLogger == null) {
1✔
377
        throw new IllegalStateException("ChannelLogger is not set in Builder");
×
378
      }
379
      return channelLogger;
1✔
380
    }
381

382
    /**
383
     * Returns the Executor on which this resolver should execute long-running or I/O bound work.
384
     * Null if no Executor was set.
385
     *
386
     * @since 1.25.0
387
     */
388
    @Nullable
389
    public Executor getOffloadExecutor() {
390
      return executor;
1✔
391
    }
392

393
    /**
394
     * Returns the overrideAuthority from channel {@link ManagedChannelBuilder#overrideAuthority}.
395
     * Overrides the host name for L7 HTTP virtual host matching. Almost all name resolvers should
396
     * not use this.
397
     *
398
     * @since 1.49.0
399
     */
400
    @Nullable
401
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
402
    public String getOverrideAuthority() {
403
      return overrideAuthority;
1✔
404
    }
405

406

407
    @Override
408
    public String toString() {
409
      return MoreObjects.toStringHelper(this)
×
410
          .add("defaultPort", defaultPort)
×
411
          .add("proxyDetector", proxyDetector)
×
412
          .add("syncContext", syncContext)
×
413
          .add("serviceConfigParser", serviceConfigParser)
×
414
          .add("scheduledExecutorService", scheduledExecutorService)
×
415
          .add("channelLogger", channelLogger)
×
416
          .add("executor", executor)
×
417
          .add("overrideAuthority", overrideAuthority)
×
418
          .toString();
×
419
    }
420

421
    /**
422
     * Returns a builder with the same initial values as this object.
423
     *
424
     * @since 1.21.0
425
     */
426
    public Builder toBuilder() {
427
      Builder builder = new Builder();
1✔
428
      builder.setDefaultPort(defaultPort);
1✔
429
      builder.setProxyDetector(proxyDetector);
1✔
430
      builder.setSynchronizationContext(syncContext);
1✔
431
      builder.setServiceConfigParser(serviceConfigParser);
1✔
432
      builder.setScheduledExecutorService(scheduledExecutorService);
1✔
433
      builder.setChannelLogger(channelLogger);
1✔
434
      builder.setOffloadExecutor(executor);
1✔
435
      builder.setOverrideAuthority(overrideAuthority);
1✔
436
      return builder;
1✔
437
    }
438

439
    /**
440
     * Creates a new builder.
441
     *
442
     * @since 1.21.0
443
     */
444
    public static Builder newBuilder() {
445
      return new Builder();
1✔
446
    }
447

448
    /**
449
     * Builder for {@link Args}.
450
     *
451
     * @since 1.21.0
452
     */
453
    public static final class Builder {
454
      private Integer defaultPort;
455
      private ProxyDetector proxyDetector;
456
      private SynchronizationContext syncContext;
457
      private ServiceConfigParser serviceConfigParser;
458
      private ScheduledExecutorService scheduledExecutorService;
459
      private ChannelLogger channelLogger;
460
      private Executor executor;
461
      private String overrideAuthority;
462

463
      Builder() {
1✔
464
      }
1✔
465

466
      /**
467
       * See {@link Args#getDefaultPort}.  This is a required field.
468
       *
469
       * @since 1.21.0
470
       */
471
      public Builder setDefaultPort(int defaultPort) {
472
        this.defaultPort = defaultPort;
1✔
473
        return this;
1✔
474
      }
475

476
      /**
477
       * See {@link Args#getProxyDetector}.  This is required field.
478
       *
479
       * @since 1.21.0
480
       */
481
      public Builder setProxyDetector(ProxyDetector proxyDetector) {
482
        this.proxyDetector = checkNotNull(proxyDetector);
1✔
483
        return this;
1✔
484
      }
485

486
      /**
487
       * See {@link Args#getSynchronizationContext}.  This is a required field.
488
       *
489
       * @since 1.21.0
490
       */
491
      public Builder setSynchronizationContext(SynchronizationContext syncContext) {
492
        this.syncContext = checkNotNull(syncContext);
1✔
493
        return this;
1✔
494
      }
495

496
      /**
497
       * See {@link Args#getScheduledExecutorService}.
498
       */
499
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454")
500
      public Builder setScheduledExecutorService(
501
          ScheduledExecutorService scheduledExecutorService) {
502
        this.scheduledExecutorService = checkNotNull(scheduledExecutorService);
1✔
503
        return this;
1✔
504
      }
505

506
      /**
507
       * See {@link Args#getServiceConfigParser}.  This is a required field.
508
       *
509
       * @since 1.21.0
510
       */
511
      public Builder setServiceConfigParser(ServiceConfigParser parser) {
512
        this.serviceConfigParser = checkNotNull(parser);
1✔
513
        return this;
1✔
514
      }
515

516
      /**
517
       * See {@link Args#getChannelLogger}.
518
       *
519
       * @since 1.26.0
520
       */
521
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
522
      public Builder setChannelLogger(ChannelLogger channelLogger) {
523
        this.channelLogger = checkNotNull(channelLogger);
1✔
524
        return this;
1✔
525
      }
526

527
      /**
528
       * See {@link Args#getOffloadExecutor}. This is an optional field.
529
       *
530
       * @since 1.25.0
531
       */
532
      public Builder setOffloadExecutor(Executor executor) {
533
        this.executor = executor;
1✔
534
        return this;
1✔
535
      }
536

537
      /**
538
       * See {@link Args#getOverrideAuthority()}. This is an optional field.
539
       *
540
       * @since 1.49.0
541
       */
542
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
543
      public Builder setOverrideAuthority(String authority) {
544
        this.overrideAuthority = authority;
1✔
545
        return this;
1✔
546
      }
547

548
      /**
549
       * Builds an {@link Args}.
550
       *
551
       * @since 1.21.0
552
       */
553
      public Args build() {
554
        return
1✔
555
            new Args(
556
                defaultPort, proxyDetector, syncContext, serviceConfigParser,
557
                scheduledExecutorService, channelLogger, executor, overrideAuthority);
558
      }
559
    }
560
  }
561

562
  /**
563
   * Parses and validates service configuration.
564
   *
565
   * @since 1.21.0
566
   */
567
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
568
  public abstract static class ServiceConfigParser {
1✔
569
    /**
570
     * Parses and validates the service configuration chosen by the name resolver.  This will
571
     * return a {@link ConfigOrError} which contains either the successfully parsed config, or the
572
     * {@link Status} representing the failure to parse.  Implementations are expected to not throw
573
     * exceptions but return a Status representing the failure.  The value inside the
574
     * {@link ConfigOrError} should implement {@code equals()} and {@code hashCode()}.
575
     *
576
     * @param rawServiceConfig The {@link Map} representation of the service config
577
     * @return a tuple of the fully parsed and validated channel configuration, else the Status.
578
     * @since 1.21.0
579
     */
580
    public abstract ConfigOrError parseServiceConfig(Map<String, ?> rawServiceConfig);
581
  }
582

583
  /**
584
   * Represents the results from a Name Resolver.
585
   *
586
   * @since 1.21.0
587
   */
588
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
589
  public static final class ResolutionResult {
590
    private final StatusOr<List<EquivalentAddressGroup>> addressesOrError;
591
    @ResolutionResultAttr
592
    private final Attributes attributes;
593
    @Nullable
594
    private final ConfigOrError serviceConfig;
595

596
    ResolutionResult(
597
        StatusOr<List<EquivalentAddressGroup>> addressesOrError,
598
        @ResolutionResultAttr Attributes attributes,
599
        ConfigOrError serviceConfig) {
1✔
600
      this.addressesOrError = addressesOrError;
1✔
601
      this.attributes = checkNotNull(attributes, "attributes");
1✔
602
      this.serviceConfig = serviceConfig;
1✔
603
    }
1✔
604

605
    /**
606
     * Constructs a new builder of a name resolution result.
607
     *
608
     * @since 1.21.0
609
     */
610
    public static Builder newBuilder() {
611
      return new Builder();
1✔
612
    }
613

614
    /**
615
     * Converts these results back to a builder.
616
     *
617
     * @since 1.21.0
618
     */
619
    public Builder toBuilder() {
620
      return newBuilder()
1✔
621
          .setAddressesOrError(addressesOrError)
1✔
622
          .setAttributes(attributes)
1✔
623
          .setServiceConfig(serviceConfig);
1✔
624
    }
625

626
    /**
627
     * Gets the addresses resolved by name resolution.
628
     *
629
     * @since 1.21.0
630
     * @deprecated Will be superseded by getAddressesOrError
631
     */
632
    @Deprecated
633
    public List<EquivalentAddressGroup> getAddresses() {
634
      return addressesOrError.getValue();
1✔
635
    }
636

637
    /**
638
     * Gets the addresses resolved by name resolution or the error in doing so.
639
     *
640
     * @since 1.65.0
641
     */
642
    public StatusOr<List<EquivalentAddressGroup>> getAddressesOrError() {
643
      return addressesOrError;
1✔
644
    }
645

646
    /**
647
     * Gets the attributes associated with the addresses resolved by name resolution.  If there are
648
     * no attributes, {@link Attributes#EMPTY} will be returned.
649
     *
650
     * @since 1.21.0
651
     */
652
    @ResolutionResultAttr
653
    public Attributes getAttributes() {
654
      return attributes;
1✔
655
    }
656

657
    /**
658
     * Gets the Service Config parsed by {@link Args#getServiceConfigParser}.
659
     *
660
     * @since 1.21.0
661
     */
662
    @Nullable
663
    public ConfigOrError getServiceConfig() {
664
      return serviceConfig;
1✔
665
    }
666

667
    @Override
668
    public String toString() {
669
      ToStringHelper stringHelper = MoreObjects.toStringHelper(this);
1✔
670
      stringHelper.add("addressesOrError", addressesOrError.toString());
1✔
671
      stringHelper.add("attributes", attributes);
1✔
672
      stringHelper.add("serviceConfigOrError", serviceConfig);
1✔
673
      return stringHelper.toString();
1✔
674
    }
675

676
    /**
677
     * Useful for testing.  May be slow to calculate.
678
     */
679
    @Override
680
    public boolean equals(Object obj) {
681
      if (!(obj instanceof ResolutionResult)) {
×
682
        return false;
×
683
      }
684
      ResolutionResult that = (ResolutionResult) obj;
×
685
      return Objects.equal(this.addressesOrError, that.addressesOrError)
×
686
          && Objects.equal(this.attributes, that.attributes)
×
687
          && Objects.equal(this.serviceConfig, that.serviceConfig);
×
688
    }
689

690
    /**
691
     * Useful for testing.  May be slow to calculate.
692
     */
693
    @Override
694
    public int hashCode() {
695
      return Objects.hashCode(addressesOrError, attributes, serviceConfig);
1✔
696
    }
697

698
    /**
699
     * A builder for {@link ResolutionResult}.
700
     *
701
     * @since 1.21.0
702
     */
703
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
704
    public static final class Builder {
705
      private StatusOr<List<EquivalentAddressGroup>> addresses =
1✔
706
          StatusOr.fromValue(Collections.emptyList());
1✔
707
      private Attributes attributes = Attributes.EMPTY;
1✔
708
      @Nullable
709
      private ConfigOrError serviceConfig;
710
      //  Make sure to update #toBuilder above!
711

712
      Builder() {}
1✔
713

714
      /**
715
       * Sets the addresses resolved by name resolution.  This field is required.
716
       *
717
       * @since 1.21.0
718
       * @deprecated Will be superseded by setAddressesOrError
719
       */
720
      @Deprecated
721
      public Builder setAddresses(List<EquivalentAddressGroup> addresses) {
722
        setAddressesOrError(StatusOr.fromValue(addresses));
1✔
723
        return this;
1✔
724
      }
725

726
      /**
727
       * Sets the addresses resolved by name resolution or the error in doing so. This field is
728
       * required.
729
       * @param addresses Resolved addresses or an error in resolving addresses
730
       */
731
      public Builder setAddressesOrError(StatusOr<List<EquivalentAddressGroup>> addresses) {
732
        this.addresses = checkNotNull(addresses, "StatusOr addresses cannot be null.");
1✔
733
        return this;
1✔
734
      }
735

736
      /**
737
       * Sets the attributes for the addresses resolved by name resolution.  If unset,
738
       * {@link Attributes#EMPTY} will be used as a default.
739
       *
740
       * @since 1.21.0
741
       */
742
      public Builder setAttributes(Attributes attributes) {
743
        this.attributes = attributes;
1✔
744
        return this;
1✔
745
      }
746

747
      /**
748
       * Sets the Service Config parsed by {@link Args#getServiceConfigParser}.
749
       * This field is optional.
750
       *
751
       * @since 1.21.0
752
       */
753
      public Builder setServiceConfig(@Nullable ConfigOrError serviceConfig) {
754
        this.serviceConfig = serviceConfig;
1✔
755
        return this;
1✔
756
      }
757

758
      /**
759
       * Constructs a new {@link ResolutionResult} from this builder.
760
       *
761
       * @since 1.21.0
762
       */
763
      public ResolutionResult build() {
764
        return new ResolutionResult(addresses, attributes, serviceConfig);
1✔
765
      }
766
    }
767
  }
768

769
  /**
770
   * Represents either a successfully parsed service config, containing all necessary parts to be
771
   * later applied by the channel, or a Status containing the error encountered while parsing.
772
   *
773
   * @since 1.20.0
774
   */
775
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
1✔
776
  public static final class ConfigOrError {
777

778
    /**
779
     * Returns a {@link ConfigOrError} for the successfully parsed config.
780
     */
781
    public static ConfigOrError fromConfig(Object config) {
782
      return new ConfigOrError(config);
1✔
783
    }
784

785
    /**
786
     * Returns a {@link ConfigOrError} for the failure to parse the config.
787
     *
788
     * @param status a non-OK status
789
     */
790
    public static ConfigOrError fromError(Status status) {
791
      return new ConfigOrError(status);
1✔
792
    }
793

794
    private final Status status;
795
    private final Object config;
796

797
    private ConfigOrError(Object config) {
1✔
798
      this.config = checkNotNull(config, "config");
1✔
799
      this.status = null;
1✔
800
    }
1✔
801

802
    private ConfigOrError(Status status) {
1✔
803
      this.config = null;
1✔
804
      this.status = checkNotNull(status, "status");
1✔
805
      checkArgument(!status.isOk(), "cannot use OK status: %s", status);
1✔
806
    }
1✔
807

808
    /**
809
     * Returns config if exists, otherwise null.
810
     */
811
    @Nullable
812
    public Object getConfig() {
813
      return config;
1✔
814
    }
815

816
    /**
817
     * Returns error status if exists, otherwise null.
818
     */
819
    @Nullable
820
    public Status getError() {
821
      return status;
1✔
822
    }
823

824
    @Override
825
    public boolean equals(Object o) {
826
      if (this == o) {
1✔
827
        return true;
×
828
      }
829
      if (o == null || getClass() != o.getClass()) {
1✔
830
        return false;
×
831
      }
832
      ConfigOrError that = (ConfigOrError) o;
1✔
833
      return Objects.equal(status, that.status) && Objects.equal(config, that.config);
1✔
834
    }
835

836
    @Override
837
    public int hashCode() {
838
      return Objects.hashCode(status, config);
1✔
839
    }
840

841
    @Override
842
    public String toString() {
843
      if (config != null) {
1✔
844
        return MoreObjects.toStringHelper(this)
1✔
845
            .add("config", config)
1✔
846
            .toString();
1✔
847
      } else {
848
        assert status != null;
×
849
        return MoreObjects.toStringHelper(this)
×
850
            .add("error", status)
×
851
            .toString();
×
852
      }
853
    }
854
  }
855
}
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