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

grpc / grpc-java / #19563

26 Nov 2024 12:47AM UTC coverage: 88.559% (-0.02%) from 88.582%
#19563

push

github

web-flow
xds: Add counter and gauge metrics  (#11661)

Adds the following xDS client metrics defined in [A78](https://github.com/grpc/proposal/blob/master/A78-grpc-metrics-wrr-pf-xds.md#xdsclient).

Counters
- grpc.xds_client.server_failure
- grpc.xds_client.resource_updates_valid
- grpc.xds_client.resource_updates_invalid

Gauges
- grpc.xds_client.connected
- grpc.xds_client.resources

33368 of 37679 relevant lines covered (88.56%)

0.89 hits per line

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

82.28
/../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.onResult(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
      // Calling onResult and not onResult2 because onResult2 can only be called from a
229
      // synchronization context.
230
      onResult(
1✔
231
          ResolutionResult.newBuilder().setAddressesOrError(
1✔
232
              StatusOr.fromValue(servers)).setAttributes(attributes).build());
1✔
233
    }
1✔
234

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

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

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

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

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

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

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

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

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

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

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

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

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

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

411
    /**
412
     * Returns the {@link MetricRecorder} that the channel uses to record metrics.
413
     */
414
    @Nullable
415
    public MetricRecorder getMetricRecorder() {
416
      return metricRecorder;
1✔
417
    }
418

419

420
    @Override
421
    public String toString() {
422
      return MoreObjects.toStringHelper(this)
×
423
          .add("defaultPort", defaultPort)
×
424
          .add("proxyDetector", proxyDetector)
×
425
          .add("syncContext", syncContext)
×
426
          .add("serviceConfigParser", serviceConfigParser)
×
427
          .add("scheduledExecutorService", scheduledExecutorService)
×
428
          .add("channelLogger", channelLogger)
×
429
          .add("executor", executor)
×
430
          .add("overrideAuthority", overrideAuthority)
×
431
          .add("metricRecorder", metricRecorder)
×
432
          .toString();
×
433
    }
434

435
    /**
436
     * Returns a builder with the same initial values as this object.
437
     *
438
     * @since 1.21.0
439
     */
440
    public Builder toBuilder() {
441
      Builder builder = new Builder();
1✔
442
      builder.setDefaultPort(defaultPort);
1✔
443
      builder.setProxyDetector(proxyDetector);
1✔
444
      builder.setSynchronizationContext(syncContext);
1✔
445
      builder.setServiceConfigParser(serviceConfigParser);
1✔
446
      builder.setScheduledExecutorService(scheduledExecutorService);
1✔
447
      builder.setChannelLogger(channelLogger);
1✔
448
      builder.setOffloadExecutor(executor);
1✔
449
      builder.setOverrideAuthority(overrideAuthority);
1✔
450
      builder.setMetricRecorder(metricRecorder);
1✔
451
      return builder;
1✔
452
    }
453

454
    /**
455
     * Creates a new builder.
456
     *
457
     * @since 1.21.0
458
     */
459
    public static Builder newBuilder() {
460
      return new Builder();
1✔
461
    }
462

463
    /**
464
     * Builder for {@link Args}.
465
     *
466
     * @since 1.21.0
467
     */
468
    public static final class Builder {
469
      private Integer defaultPort;
470
      private ProxyDetector proxyDetector;
471
      private SynchronizationContext syncContext;
472
      private ServiceConfigParser serviceConfigParser;
473
      private ScheduledExecutorService scheduledExecutorService;
474
      private ChannelLogger channelLogger;
475
      private Executor executor;
476
      private String overrideAuthority;
477
      private MetricRecorder metricRecorder;
478

479
      Builder() {
1✔
480
      }
1✔
481

482
      /**
483
       * See {@link Args#getDefaultPort}.  This is a required field.
484
       *
485
       * @since 1.21.0
486
       */
487
      public Builder setDefaultPort(int defaultPort) {
488
        this.defaultPort = defaultPort;
1✔
489
        return this;
1✔
490
      }
491

492
      /**
493
       * See {@link Args#getProxyDetector}.  This is required field.
494
       *
495
       * @since 1.21.0
496
       */
497
      public Builder setProxyDetector(ProxyDetector proxyDetector) {
498
        this.proxyDetector = checkNotNull(proxyDetector);
1✔
499
        return this;
1✔
500
      }
501

502
      /**
503
       * See {@link Args#getSynchronizationContext}.  This is a required field.
504
       *
505
       * @since 1.21.0
506
       */
507
      public Builder setSynchronizationContext(SynchronizationContext syncContext) {
508
        this.syncContext = checkNotNull(syncContext);
1✔
509
        return this;
1✔
510
      }
511

512
      /**
513
       * See {@link Args#getScheduledExecutorService}.
514
       */
515
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6454")
516
      public Builder setScheduledExecutorService(
517
          ScheduledExecutorService scheduledExecutorService) {
518
        this.scheduledExecutorService = checkNotNull(scheduledExecutorService);
1✔
519
        return this;
1✔
520
      }
521

522
      /**
523
       * See {@link Args#getServiceConfigParser}.  This is a required field.
524
       *
525
       * @since 1.21.0
526
       */
527
      public Builder setServiceConfigParser(ServiceConfigParser parser) {
528
        this.serviceConfigParser = checkNotNull(parser);
1✔
529
        return this;
1✔
530
      }
531

532
      /**
533
       * See {@link Args#getChannelLogger}.
534
       *
535
       * @since 1.26.0
536
       */
537
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/6438")
538
      public Builder setChannelLogger(ChannelLogger channelLogger) {
539
        this.channelLogger = checkNotNull(channelLogger);
1✔
540
        return this;
1✔
541
      }
542

543
      /**
544
       * See {@link Args#getOffloadExecutor}. This is an optional field.
545
       *
546
       * @since 1.25.0
547
       */
548
      public Builder setOffloadExecutor(Executor executor) {
549
        this.executor = executor;
1✔
550
        return this;
1✔
551
      }
552

553
      /**
554
       * See {@link Args#getOverrideAuthority()}. This is an optional field.
555
       *
556
       * @since 1.49.0
557
       */
558
      @ExperimentalApi("https://github.com/grpc/grpc-java/issues/9406")
559
      public Builder setOverrideAuthority(String authority) {
560
        this.overrideAuthority = authority;
1✔
561
        return this;
1✔
562
      }
563

564
      /**
565
       * See {@link Args#getMetricRecorder()}. This is an optional field.
566
       */
567
      public Builder setMetricRecorder(MetricRecorder metricRecorder) {
568
        this.metricRecorder = metricRecorder;
1✔
569
        return this;
1✔
570
      }
571

572
      /**
573
       * Builds an {@link Args}.
574
       *
575
       * @since 1.21.0
576
       */
577
      public Args build() {
578
        return
1✔
579
            new Args(
580
                defaultPort, proxyDetector, syncContext, serviceConfigParser,
581
                scheduledExecutorService, channelLogger, executor, overrideAuthority,
582
                metricRecorder);
583
      }
584
    }
585
  }
586

587
  /**
588
   * Parses and validates service configuration.
589
   *
590
   * @since 1.21.0
591
   */
592
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
593
  public abstract static class ServiceConfigParser {
1✔
594
    /**
595
     * Parses and validates the service configuration chosen by the name resolver.  This will
596
     * return a {@link ConfigOrError} which contains either the successfully parsed config, or the
597
     * {@link Status} representing the failure to parse.  Implementations are expected to not throw
598
     * exceptions but return a Status representing the failure.  The value inside the
599
     * {@link ConfigOrError} should implement {@code equals()} and {@code hashCode()}.
600
     *
601
     * @param rawServiceConfig The {@link Map} representation of the service config
602
     * @return a tuple of the fully parsed and validated channel configuration, else the Status.
603
     * @since 1.21.0
604
     */
605
    public abstract ConfigOrError parseServiceConfig(Map<String, ?> rawServiceConfig);
606
  }
607

608
  /**
609
   * Represents the results from a Name Resolver.
610
   *
611
   * @since 1.21.0
612
   */
613
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
614
  public static final class ResolutionResult {
615
    private final StatusOr<List<EquivalentAddressGroup>> addressesOrError;
616
    @ResolutionResultAttr
617
    private final Attributes attributes;
618
    @Nullable
619
    private final ConfigOrError serviceConfig;
620

621
    ResolutionResult(
622
        StatusOr<List<EquivalentAddressGroup>> addressesOrError,
623
        @ResolutionResultAttr Attributes attributes,
624
        ConfigOrError serviceConfig) {
1✔
625
      this.addressesOrError = addressesOrError;
1✔
626
      this.attributes = checkNotNull(attributes, "attributes");
1✔
627
      this.serviceConfig = serviceConfig;
1✔
628
    }
1✔
629

630
    /**
631
     * Constructs a new builder of a name resolution result.
632
     *
633
     * @since 1.21.0
634
     */
635
    public static Builder newBuilder() {
636
      return new Builder();
1✔
637
    }
638

639
    /**
640
     * Converts these results back to a builder.
641
     *
642
     * @since 1.21.0
643
     */
644
    public Builder toBuilder() {
645
      return newBuilder()
1✔
646
          .setAddressesOrError(addressesOrError)
1✔
647
          .setAttributes(attributes)
1✔
648
          .setServiceConfig(serviceConfig);
1✔
649
    }
650

651
    /**
652
     * Gets the addresses resolved by name resolution.
653
     *
654
     * @since 1.21.0
655
     * @deprecated Will be superseded by getAddressesOrError
656
     */
657
    @Deprecated
658
    public List<EquivalentAddressGroup> getAddresses() {
659
      return addressesOrError.getValue();
1✔
660
    }
661

662
    /**
663
     * Gets the addresses resolved by name resolution or the error in doing so.
664
     *
665
     * @since 1.65.0
666
     */
667
    public StatusOr<List<EquivalentAddressGroup>> getAddressesOrError() {
668
      return addressesOrError;
1✔
669
    }
670

671
    /**
672
     * Gets the attributes associated with the addresses resolved by name resolution.  If there are
673
     * no attributes, {@link Attributes#EMPTY} will be returned.
674
     *
675
     * @since 1.21.0
676
     */
677
    @ResolutionResultAttr
678
    public Attributes getAttributes() {
679
      return attributes;
1✔
680
    }
681

682
    /**
683
     * Gets the Service Config parsed by {@link Args#getServiceConfigParser}.
684
     *
685
     * @since 1.21.0
686
     */
687
    @Nullable
688
    public ConfigOrError getServiceConfig() {
689
      return serviceConfig;
1✔
690
    }
691

692
    @Override
693
    public String toString() {
694
      ToStringHelper stringHelper = MoreObjects.toStringHelper(this);
1✔
695
      stringHelper.add("addressesOrError", addressesOrError.toString());
1✔
696
      stringHelper.add("attributes", attributes);
1✔
697
      stringHelper.add("serviceConfigOrError", serviceConfig);
1✔
698
      return stringHelper.toString();
1✔
699
    }
700

701
    /**
702
     * Useful for testing.  May be slow to calculate.
703
     */
704
    @Override
705
    public boolean equals(Object obj) {
706
      if (!(obj instanceof ResolutionResult)) {
×
707
        return false;
×
708
      }
709
      ResolutionResult that = (ResolutionResult) obj;
×
710
      return Objects.equal(this.addressesOrError, that.addressesOrError)
×
711
          && Objects.equal(this.attributes, that.attributes)
×
712
          && Objects.equal(this.serviceConfig, that.serviceConfig);
×
713
    }
714

715
    /**
716
     * Useful for testing.  May be slow to calculate.
717
     */
718
    @Override
719
    public int hashCode() {
720
      return Objects.hashCode(addressesOrError, attributes, serviceConfig);
1✔
721
    }
722

723
    /**
724
     * A builder for {@link ResolutionResult}.
725
     *
726
     * @since 1.21.0
727
     */
728
    @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
729
    public static final class Builder {
730
      private StatusOr<List<EquivalentAddressGroup>> addresses =
1✔
731
          StatusOr.fromValue(Collections.emptyList());
1✔
732
      private Attributes attributes = Attributes.EMPTY;
1✔
733
      @Nullable
734
      private ConfigOrError serviceConfig;
735
      //  Make sure to update #toBuilder above!
736

737
      Builder() {}
1✔
738

739
      /**
740
       * Sets the addresses resolved by name resolution.  This field is required.
741
       *
742
       * @since 1.21.0
743
       * @deprecated Will be superseded by setAddressesOrError
744
       */
745
      @Deprecated
746
      public Builder setAddresses(List<EquivalentAddressGroup> addresses) {
747
        setAddressesOrError(StatusOr.fromValue(addresses));
1✔
748
        return this;
1✔
749
      }
750

751
      /**
752
       * Sets the addresses resolved by name resolution or the error in doing so. This field is
753
       * required.
754
       * @param addresses Resolved addresses or an error in resolving addresses
755
       */
756
      public Builder setAddressesOrError(StatusOr<List<EquivalentAddressGroup>> addresses) {
757
        this.addresses = checkNotNull(addresses, "StatusOr addresses cannot be null.");
1✔
758
        return this;
1✔
759
      }
760

761
      /**
762
       * Sets the attributes for the addresses resolved by name resolution.  If unset,
763
       * {@link Attributes#EMPTY} will be used as a default.
764
       *
765
       * @since 1.21.0
766
       */
767
      public Builder setAttributes(Attributes attributes) {
768
        this.attributes = attributes;
1✔
769
        return this;
1✔
770
      }
771

772
      /**
773
       * Sets the Service Config parsed by {@link Args#getServiceConfigParser}.
774
       * This field is optional.
775
       *
776
       * @since 1.21.0
777
       */
778
      public Builder setServiceConfig(@Nullable ConfigOrError serviceConfig) {
779
        this.serviceConfig = serviceConfig;
1✔
780
        return this;
1✔
781
      }
782

783
      /**
784
       * Constructs a new {@link ResolutionResult} from this builder.
785
       *
786
       * @since 1.21.0
787
       */
788
      public ResolutionResult build() {
789
        return new ResolutionResult(addresses, attributes, serviceConfig);
1✔
790
      }
791
    }
792
  }
793

794
  /**
795
   * Represents either a successfully parsed service config, containing all necessary parts to be
796
   * later applied by the channel, or a Status containing the error encountered while parsing.
797
   *
798
   * @since 1.20.0
799
   */
800
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1770")
1✔
801
  public static final class ConfigOrError {
802

803
    /**
804
     * Returns a {@link ConfigOrError} for the successfully parsed config.
805
     */
806
    public static ConfigOrError fromConfig(Object config) {
807
      return new ConfigOrError(config);
1✔
808
    }
809

810
    /**
811
     * Returns a {@link ConfigOrError} for the failure to parse the config.
812
     *
813
     * @param status a non-OK status
814
     */
815
    public static ConfigOrError fromError(Status status) {
816
      return new ConfigOrError(status);
1✔
817
    }
818

819
    private final Status status;
820
    private final Object config;
821

822
    private ConfigOrError(Object config) {
1✔
823
      this.config = checkNotNull(config, "config");
1✔
824
      this.status = null;
1✔
825
    }
1✔
826

827
    private ConfigOrError(Status status) {
1✔
828
      this.config = null;
1✔
829
      this.status = checkNotNull(status, "status");
1✔
830
      checkArgument(!status.isOk(), "cannot use OK status: %s", status);
1✔
831
    }
1✔
832

833
    /**
834
     * Returns config if exists, otherwise null.
835
     */
836
    @Nullable
837
    public Object getConfig() {
838
      return config;
1✔
839
    }
840

841
    /**
842
     * Returns error status if exists, otherwise null.
843
     */
844
    @Nullable
845
    public Status getError() {
846
      return status;
1✔
847
    }
848

849
    @Override
850
    public boolean equals(Object o) {
851
      if (this == o) {
1✔
852
        return true;
×
853
      }
854
      if (o == null || getClass() != o.getClass()) {
1✔
855
        return false;
×
856
      }
857
      ConfigOrError that = (ConfigOrError) o;
1✔
858
      return Objects.equal(status, that.status) && Objects.equal(config, that.config);
1✔
859
    }
860

861
    @Override
862
    public int hashCode() {
863
      return Objects.hashCode(status, config);
1✔
864
    }
865

866
    @Override
867
    public String toString() {
868
      if (config != null) {
1✔
869
        return MoreObjects.toStringHelper(this)
1✔
870
            .add("config", config)
1✔
871
            .toString();
1✔
872
      } else {
873
        assert status != null;
×
874
        return MoreObjects.toStringHelper(this)
×
875
            .add("error", status)
×
876
            .toString();
×
877
      }
878
    }
879
  }
880
}
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