@@ -670,12 +670,14 @@ public static final class CreateSubchannelArgs {
670670 private final List <EquivalentAddressGroup > addrs ;
671671 private final Attributes attrs ;
672672 private final SubchannelStateListener stateListener ;
673+ private final Object [][] customOptions ;
673674
674675 private CreateSubchannelArgs (
675- List <EquivalentAddressGroup > addrs , Attributes attrs ,
676+ List <EquivalentAddressGroup > addrs , Attributes attrs , Object [][] customOptions ,
676677 SubchannelStateListener stateListener ) {
677678 this .addrs = checkNotNull (addrs , "addresses are not set" );
678679 this .attrs = checkNotNull (attrs , "attrs" );
680+ this .customOptions = checkNotNull (customOptions , "customOptions" );
679681 this .stateListener = checkNotNull (stateListener , "SubchannelStateListener is not set" );
680682 }
681683
@@ -693,6 +695,22 @@ public Attributes getAttributes() {
693695 return attrs ;
694696 }
695697
698+ /**
699+ * Get the value for a custom option or its inherent default.
700+ *
701+ * @param key Key identifying option
702+ */
703+ @ SuppressWarnings ("unchecked" )
704+ public <T > T getOption (Key <T > key ) {
705+ Preconditions .checkNotNull (key , "key" );
706+ for (int i = 0 ; i < customOptions .length ; i ++) {
707+ if (key .equals (customOptions [i ][0 ])) {
708+ return (T ) customOptions [i ][1 ];
709+ }
710+ }
711+ return key .defaultValue ;
712+ }
713+
696714 /**
697715 * Returns the state listener.
698716 */
@@ -744,13 +762,45 @@ public boolean equals(Object other) {
744762
745763 @ ExperimentalApi ("https://github.com/grpc/grpc-java/issues/1771" )
746764 public static final class Builder {
765+
747766 private List <EquivalentAddressGroup > addrs ;
748767 private Attributes attrs = Attributes .EMPTY ;
749768 private SubchannelStateListener stateListener ;
769+ private Object [][] customOptions = new Object [0 ][2 ];
750770
751771 Builder () {
752772 }
753773
774+ /**
775+ * Add a custom option. Any existing value for the key is overwritten.
776+ *
777+ * <p>This is an <strong>optional</strong> property.
778+ *
779+ * @param key the option key
780+ * @param value the option value
781+ */
782+ public <T > Builder addOption (Key <T > key , T value ) {
783+ Preconditions .checkNotNull (key , "key" );
784+ Preconditions .checkNotNull (value , "value" );
785+
786+ int existingIdx = -1 ;
787+ for (int i = 0 ; i < customOptions .length ; i ++) {
788+ if (key .equals (customOptions [i ][0 ])) {
789+ existingIdx = i ;
790+ break ;
791+ }
792+ }
793+
794+ if (existingIdx == -1 ) {
795+ Object [][] newCustomOptions = new Object [customOptions .length + 1 ][2 ];
796+ System .arraycopy (customOptions , 0 , newCustomOptions , 0 , customOptions .length );
797+ customOptions = newCustomOptions ;
798+ existingIdx = customOptions .length - 1 ;
799+ }
800+ customOptions [existingIdx ] = new Object []{key , value };
801+ return this ;
802+ }
803+
754804 /**
755805 * The addresses to connect to. All addresses are considered equivalent and will be tried
756806 * in the order they are provided.
@@ -773,7 +823,7 @@ public Builder setAddresses(List<EquivalentAddressGroup> addrs) {
773823 this .addrs = Collections .unmodifiableList (new ArrayList <>(addrs ));
774824 return this ;
775825 }
776-
826+
777827 /**
778828 * Attributes provided here will be included in {@link Subchannel#getAttributes}.
779829 *
@@ -800,7 +850,60 @@ public Builder setStateListener(SubchannelStateListener listener) {
800850 * Creates a new args object.
801851 */
802852 public CreateSubchannelArgs build () {
803- return new CreateSubchannelArgs (addrs , attrs , stateListener );
853+ return new CreateSubchannelArgs (addrs , attrs , customOptions , stateListener );
854+ }
855+ }
856+
857+ /**
858+ * Key for a key-value pair. Uses reference equality.
859+ */
860+ @ ExperimentalApi ("https://github.com/grpc/grpc-java/issues/1771" )
861+ public static final class Key <T > {
862+
863+ private final String debugString ;
864+ private final T defaultValue ;
865+
866+ private Key (String debugString , T defaultValue ) {
867+ this .debugString = debugString ;
868+ this .defaultValue = defaultValue ;
869+ }
870+
871+ /**
872+ * Factory method for creating instances of {@link Key}. The default value of the key is
873+ * {@code null}.
874+ *
875+ * @param debugString a debug string that describes this key.
876+ * @param <T> Key type
877+ * @return Key object
878+ */
879+ public static <T > Key <T > create (String debugString ) {
880+ Preconditions .checkNotNull (debugString , "debugString" );
881+ return new Key <>(debugString , /*defaultValue=*/ null );
882+ }
883+
884+ /**
885+ * Factory method for creating instances of {@link Key}.
886+ *
887+ * @param debugString a debug string that describes this key.
888+ * @param defaultValue default value to return when value for key not set
889+ * @param <T> Key type
890+ * @return Key object
891+ */
892+ public static <T > Key <T > createWithDefault (String debugString , T defaultValue ) {
893+ Preconditions .checkNotNull (debugString , "debugString" );
894+ return new Key <>(debugString , defaultValue );
895+ }
896+
897+ /**
898+ * Returns the user supplied default value for this key.
899+ */
900+ public T getDefault () {
901+ return defaultValue ;
902+ }
903+
904+ @ Override
905+ public String toString () {
906+ return debugString ;
804907 }
805908 }
806909 }
0 commit comments