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

grpc / grpc-java / #19551

14 Nov 2024 04:34PM UTC coverage: 84.627% (+0.01%) from 84.614%
#19551

push

github

web-flow
api: When forwarding from Listener onAddresses to Listener2 continue to use onResult (#11666) (#11688)

When forwarding from Listener onAddresses to Listener2 continue to use onResult and not onResult2 because the latter requires to be called from within synchronization context and it breaks existing code that didn't need to do so when using the old Listener interface.

33855 of 40005 relevant lines covered (84.63%)

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.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

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

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

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

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

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

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

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

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

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

408

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

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

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

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

465
      Builder() {
1✔
466
      }
1✔
467

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

714
      Builder() {}
1✔
715

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

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

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

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

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

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

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

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

796
    private final Status status;
797
    private final Object config;
798

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

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

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

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

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

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

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