2828import java .lang .annotation .RetentionPolicy ;
2929import java .net .URI ;
3030import java .util .Collections ;
31+ import java .util .IdentityHashMap ;
3132import java .util .List ;
3233import java .util .Map ;
3334import java .util .concurrent .Executor ;
3435import java .util .concurrent .ScheduledExecutorService ;
3536import javax .annotation .Nullable ;
37+ import javax .annotation .concurrent .Immutable ;
3638import javax .annotation .concurrent .ThreadSafe ;
3739
3840/**
@@ -276,7 +278,12 @@ public Status onResult2(ResolutionResult resolutionResult) {
276278 /**
277279 * Information that a {@link Factory} uses to create a {@link NameResolver}.
278280 *
279- * <p>Note this class doesn't override neither {@code equals()} nor {@code hashCode()}.
281+ * <p>Args applicable to all {@link NameResolver}s are defined here using ordinary setters and
282+ * getters. This container can also hold externally-defined "custom" args that aren't so widely
283+ * useful or that would be inappropriate dependencies for this low level API. See {@link
284+ * Args#getArg} for more.
285+ *
286+ * <p>Note this class overrides neither {@code equals()} nor {@code hashCode()}.
280287 *
281288 * @since 1.21.0
282289 */
@@ -291,26 +298,20 @@ public static final class Args {
291298 @ Nullable private final Executor executor ;
292299 @ Nullable private final String overrideAuthority ;
293300 @ Nullable private final MetricRecorder metricRecorder ;
301+ @ Nullable private final IdentityHashMap <Key <?>, Object > customArgs ;
294302
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 ) {
305- this .defaultPort = checkNotNull (defaultPort , "defaultPort not set" );
306- this .proxyDetector = checkNotNull (proxyDetector , "proxyDetector not set" );
307- this .syncContext = checkNotNull (syncContext , "syncContext not set" );
308- this .serviceConfigParser = checkNotNull (serviceConfigParser , "serviceConfigParser not set" );
309- this .scheduledExecutorService = scheduledExecutorService ;
310- this .channelLogger = channelLogger ;
311- this .executor = executor ;
312- this .overrideAuthority = overrideAuthority ;
313- this .metricRecorder = metricRecorder ;
303+ private Args (Builder builder ) {
304+ this .defaultPort = checkNotNull (builder .defaultPort , "defaultPort not set" );
305+ this .proxyDetector = checkNotNull (builder .proxyDetector , "proxyDetector not set" );
306+ this .syncContext = checkNotNull (builder .syncContext , "syncContext not set" );
307+ this .serviceConfigParser =
308+ checkNotNull (builder .serviceConfigParser , "serviceConfigParser not set" );
309+ this .scheduledExecutorService = builder .scheduledExecutorService ;
310+ this .channelLogger = builder .channelLogger ;
311+ this .executor = builder .executor ;
312+ this .overrideAuthority = builder .overrideAuthority ;
313+ this .metricRecorder = builder .metricRecorder ;
314+ this .customArgs = cloneCustomArgs (builder .customArgs );
314315 }
315316
316317 /**
@@ -319,6 +320,7 @@ private Args(
319320 *
320321 * @since 1.21.0
321322 */
323+ // <p>TODO: Only meaningful for InetSocketAddress producers. Make this a custom arg?
322324 public int getDefaultPort () {
323325 return defaultPort ;
324326 }
@@ -371,6 +373,30 @@ public ServiceConfigParser getServiceConfigParser() {
371373 return serviceConfigParser ;
372374 }
373375
376+ /**
377+ * Returns the value of a custom arg named 'key', or {@code null} if it's not set.
378+ *
379+ * <p>While ordinary {@link Args} should be universally useful and meaningful, custom arguments
380+ * can apply just to resolvers of a certain URI scheme, just to resolvers producing a particular
381+ * type of {@link java.net.SocketAddress}, or even an individual {@link NameResolver} subclass.
382+ * Custom args are identified by an instance of {@link Args.Key} which should be a constant
383+ * defined in a java package and class appropriate for the argument's scope.
384+ *
385+ * <p>{@link Args} are normally reserved for information in *support* of name resolution, not
386+ * the name to be resolved itself. However, there are rare cases where all or part of the target
387+ * name can't be represented by any standard URI scheme or can't be encoded as a String at all.
388+ * Custom args, in contrast, can hold arbitrary Java types, making them a useful work around in
389+ * these cases.
390+ *
391+ * <p>Custom args can also be used simply to avoid adding inappropriate deps to the low level
392+ * io.grpc package.
393+ */
394+ @ SuppressWarnings ("unchecked" ) // Cast is safe because all put()s go through the setArg() API.
395+ @ Nullable
396+ public <T > T getArg (Key <T > key ) {
397+ return customArgs != null ? (T ) customArgs .get (key ) : null ;
398+ }
399+
374400 /**
375401 * Returns the {@link ChannelLogger} for the Channel served by this NameResolver.
376402 *
@@ -424,6 +450,7 @@ public String toString() {
424450 .add ("proxyDetector" , proxyDetector )
425451 .add ("syncContext" , syncContext )
426452 .add ("serviceConfigParser" , serviceConfigParser )
453+ .add ("customArgs" , customArgs )
427454 .add ("scheduledExecutorService" , scheduledExecutorService )
428455 .add ("channelLogger" , channelLogger )
429456 .add ("executor" , executor )
@@ -448,6 +475,7 @@ public Builder toBuilder() {
448475 builder .setOffloadExecutor (executor );
449476 builder .setOverrideAuthority (overrideAuthority );
450477 builder .setMetricRecorder (metricRecorder );
478+ builder .customArgs = cloneCustomArgs (customArgs );
451479 return builder ;
452480 }
453481
@@ -475,6 +503,7 @@ public static final class Builder {
475503 private Executor executor ;
476504 private String overrideAuthority ;
477505 private MetricRecorder metricRecorder ;
506+ private IdentityHashMap <Key <?>, Object > customArgs ;
478507
479508 Builder () {
480509 }
@@ -561,6 +590,17 @@ public Builder setOverrideAuthority(String authority) {
561590 return this ;
562591 }
563592
593+ /** See {@link Args#getArg(Key)}. */
594+ public <T > Builder setArg (Key <T > key , T value ) {
595+ checkNotNull (key , "key" );
596+ checkNotNull (value , "value" );
597+ if (customArgs == null ) {
598+ customArgs = new IdentityHashMap <>();
599+ }
600+ customArgs .put (key , value );
601+ return this ;
602+ }
603+
564604 /**
565605 * See {@link Args#getMetricRecorder()}. This is an optional field.
566606 */
@@ -575,11 +615,40 @@ public Builder setMetricRecorder(MetricRecorder metricRecorder) {
575615 * @since 1.21.0
576616 */
577617 public Args build () {
578- return
579- new Args (
580- defaultPort , proxyDetector , syncContext , serviceConfigParser ,
581- scheduledExecutorService , channelLogger , executor , overrideAuthority ,
582- metricRecorder );
618+ return new Args (this );
619+ }
620+ }
621+
622+ /**
623+ * Identifies an externally-defined custom argument that can be stored in {@link Args}.
624+ *
625+ * <p>Uses reference equality so keys should be defined as global constants.
626+ *
627+ * @param <T> type of values that can be stored under this key
628+ */
629+ @ Immutable
630+ @ SuppressWarnings ("UnusedTypeParameter" )
631+ public static final class Key <T > {
632+ private final String debugString ;
633+
634+ private Key (String debugString ) {
635+ this .debugString = debugString ;
636+ }
637+
638+ @ Override
639+ public String toString () {
640+ return debugString ;
641+ }
642+
643+ /**
644+ * Creates a new instance of {@link Key}.
645+ *
646+ * @param debugString a string used to describe the key, used for debugging.
647+ * @param <T> Key type
648+ * @return a new instance of Key
649+ */
650+ public static <T > Key <T > create (String debugString ) {
651+ return new Key <>(debugString );
583652 }
584653 }
585654 }
@@ -877,4 +946,10 @@ public String toString() {
877946 }
878947 }
879948 }
949+
950+ @ Nullable
951+ private static IdentityHashMap <Args .Key <?>, Object > cloneCustomArgs (
952+ @ Nullable IdentityHashMap <Args .Key <?>, Object > customArgs ) {
953+ return customArgs != null ? new IdentityHashMap <>(customArgs ) : null ;
954+ }
880955}
0 commit comments