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

grpc / grpc-java / #19390

02 Aug 2024 03:10PM UTC coverage: 84.454% (-0.02%) from 84.476%
#19390

push

github

web-flow
Introduce onResult2 in NameResolver Listener2 that returns Status

Lets the Name Resolver receive the status of the acceptance of the name resolution by the load balancer.

33268 of 39392 relevant lines covered (84.45%)

0.84 hits per line

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

69.86
/../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.Objects;
24
import com.google.errorprone.annotations.InlineMe;
25
import java.lang.annotation.Documented;
26
import java.lang.annotation.Retention;
27
import java.lang.annotation.RetentionPolicy;
28
import java.net.URI;
29
import java.util.ArrayList;
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) {
×
88
      start((Listener2) listener);
×
89
    } else {
90
      start(new Listener2() {
×
91
          @Override
92
          public void onError(Status error) {
93
            listener.onError(error);
×
94
          }
×
95

96
          @Override
97
          public void onResult(ResolutionResult resolutionResult) {
98
            listener.onAddresses(resolutionResult.getAddresses(), resolutionResult.getAttributes());
×
99
          }
×
100
      });
101
    }
102
  }
×
103

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

403

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

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

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

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

460
      Builder() {
1✔
461
      }
1✔
462

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

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

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

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

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

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

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

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

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

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

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

593
    ResolutionResult(
594
        List<EquivalentAddressGroup> addresses,
595
        @ResolutionResultAttr Attributes attributes,
596
        ConfigOrError serviceConfig) {
1✔
597
      this.addresses = Collections.unmodifiableList(new ArrayList<>(addresses));
1✔
598
      this.attributes = checkNotNull(attributes, "attributes");
1✔
599
      this.serviceConfig = serviceConfig;
1✔
600
    }
1✔
601

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

611
    /**
612
     * Converts these results back to a builder.
613
     *
614
     * @since 1.21.0
615
     */
616
    public Builder toBuilder() {
617
      return newBuilder()
1✔
618
          .setAddresses(addresses)
1✔
619
          .setAttributes(attributes)
1✔
620
          .setServiceConfig(serviceConfig);
1✔
621
    }
622

623
    /**
624
     * Gets the addresses resolved by name resolution.
625
     *
626
     * @since 1.21.0
627
     */
628
    public List<EquivalentAddressGroup> getAddresses() {
629
      return addresses;
1✔
630
    }
631

632
    /**
633
     * Gets the attributes associated with the addresses resolved by name resolution.  If there are
634
     * no attributes, {@link Attributes#EMPTY} will be returned.
635
     *
636
     * @since 1.21.0
637
     */
638
    @ResolutionResultAttr
639
    public Attributes getAttributes() {
640
      return attributes;
1✔
641
    }
642

643
    /**
644
     * Gets the Service Config parsed by {@link Args#getServiceConfigParser}.
645
     *
646
     * @since 1.21.0
647
     */
648
    @Nullable
649
    public ConfigOrError getServiceConfig() {
650
      return serviceConfig;
1✔
651
    }
652

653
    @Override
654
    public String toString() {
655
      return MoreObjects.toStringHelper(this)
×
656
          .add("addresses", addresses)
×
657
          .add("attributes", attributes)
×
658
          .add("serviceConfig", serviceConfig)
×
659
          .toString();
×
660
    }
661

662
    /**
663
     * Useful for testing.  May be slow to calculate.
664
     */
665
    @Override
666
    public boolean equals(Object obj) {
667
      if (!(obj instanceof ResolutionResult)) {
×
668
        return false;
×
669
      }
670
      ResolutionResult that = (ResolutionResult) obj;
×
671
      return Objects.equal(this.addresses, that.addresses)
×
672
          && Objects.equal(this.attributes, that.attributes)
×
673
          && Objects.equal(this.serviceConfig, that.serviceConfig);
×
674
    }
675

676
    /**
677
     * Useful for testing.  May be slow to calculate.
678
     */
679
    @Override
680
    public int hashCode() {
681
      return Objects.hashCode(addresses, attributes, serviceConfig);
×
682
    }
683

684
    /**
685
     * A builder for {@link ResolutionResult}.
686
     *
687
     * @since 1.21.0
688
     */
689
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
690
    public static final class Builder {
691
      private List<EquivalentAddressGroup> addresses = Collections.emptyList();
1✔
692
      private Attributes attributes = Attributes.EMPTY;
1✔
693
      @Nullable
694
      private ConfigOrError serviceConfig;
695
      //  Make sure to update #toBuilder above!
696

697
      Builder() {}
1✔
698

699
      /**
700
       * Sets the addresses resolved by name resolution.  This field is required.
701
       *
702
       * @since 1.21.0
703
       */
704
      public Builder setAddresses(List<EquivalentAddressGroup> addresses) {
705
        this.addresses = addresses;
1✔
706
        return this;
1✔
707
      }
708

709
      /**
710
       * Sets the attributes for the addresses resolved by name resolution.  If unset,
711
       * {@link Attributes#EMPTY} will be used as a default.
712
       *
713
       * @since 1.21.0
714
       */
715
      public Builder setAttributes(Attributes attributes) {
716
        this.attributes = attributes;
1✔
717
        return this;
1✔
718
      }
719

720
      /**
721
       * Sets the Service Config parsed by {@link Args#getServiceConfigParser}.
722
       * This field is optional.
723
       *
724
       * @since 1.21.0
725
       */
726
      public Builder setServiceConfig(@Nullable ConfigOrError serviceConfig) {
727
        this.serviceConfig = serviceConfig;
1✔
728
        return this;
1✔
729
      }
730

731
      /**
732
       * Constructs a new {@link ResolutionResult} from this builder.
733
       *
734
       * @since 1.21.0
735
       */
736
      public ResolutionResult build() {
737
        return new ResolutionResult(addresses, attributes, serviceConfig);
1✔
738
      }
739
    }
740
  }
741

742
  /**
743
   * Represents either a successfully parsed service config, containing all necessary parts to be
744
   * later applied by the channel, or a Status containing the error encountered while parsing.
745
   *
746
   * @since 1.20.0
747
   */
748
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
1✔
749
  public static final class ConfigOrError {
750

751
    /**
752
     * Returns a {@link ConfigOrError} for the successfully parsed config.
753
     */
754
    public static ConfigOrError fromConfig(Object config) {
755
      return new ConfigOrError(config);
1✔
756
    }
757

758
    /**
759
     * Returns a {@link ConfigOrError} for the failure to parse the config.
760
     *
761
     * @param status a non-OK status
762
     */
763
    public static ConfigOrError fromError(Status status) {
764
      return new ConfigOrError(status);
1✔
765
    }
766

767
    private final Status status;
768
    private final Object config;
769

770
    private ConfigOrError(Object config) {
1✔
771
      this.config = checkNotNull(config, "config");
1✔
772
      this.status = null;
1✔
773
    }
1✔
774

775
    private ConfigOrError(Status status) {
1✔
776
      this.config = null;
1✔
777
      this.status = checkNotNull(status, "status");
1✔
778
      checkArgument(!status.isOk(), "cannot use OK status: %s", status);
1✔
779
    }
1✔
780

781
    /**
782
     * Returns config if exists, otherwise null.
783
     */
784
    @Nullable
785
    public Object getConfig() {
786
      return config;
1✔
787
    }
788

789
    /**
790
     * Returns error status if exists, otherwise null.
791
     */
792
    @Nullable
793
    public Status getError() {
794
      return status;
1✔
795
    }
796

797
    @Override
798
    public boolean equals(Object o) {
799
      if (this == o) {
1✔
800
        return true;
×
801
      }
802
      if (o == null || getClass() != o.getClass()) {
1✔
803
        return false;
×
804
      }
805
      ConfigOrError that = (ConfigOrError) o;
1✔
806
      return Objects.equal(status, that.status) && Objects.equal(config, that.config);
1✔
807
    }
808

809
    @Override
810
    public int hashCode() {
811
      return Objects.hashCode(status, config);
×
812
    }
813

814
    @Override
815
    public String toString() {
816
      if (config != null) {
1✔
817
        return MoreObjects.toStringHelper(this)
1✔
818
            .add("config", config)
1✔
819
            .toString();
1✔
820
      } else {
821
        assert status != null;
×
822
        return MoreObjects.toStringHelper(this)
×
823
            .add("error", status)
×
824
            .toString();
×
825
      }
826
    }
827
  }
828
}
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