Created
August 21, 2020 15:38
-
-
Save nsivabalan/882ea3bfa92477fb257fcca1c7d3e609 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Class that holds config parameters for a {@link UberClientCall}. The collection of runtime | |
* options for a new RPC call. This will be used by application and feature teams to set config | |
* params like like timeouts, retry policy, etc for the network calls. This mimics {@link | |
* CallOptions} in the grpc world. | |
*/ | |
public class UberCallOptions { | |
/** A blank {@code UberCallOptions} that all fields are not set. */ | |
public static final UberCallOptions DEFAULT = new UberCallOptions(); | |
// configs from CallOptions | |
@Nullable private Deadline deadline; | |
@Nullable private Executor executor; | |
@Nullable private Boolean waitForReady; | |
private Object[][] customOptions = new Object[0][2]; | |
// extra config options to be exposed to end user | |
@Nullable private String userDefinedHost; | |
@Nullable private Integer userDefinedPort; | |
// Unmodifiable list | |
private List<ClientStreamTracer.Factory> streamTracerFactories = Collections.emptyList(); | |
public UberCallOptions() {} | |
/** Copy constructor. */ | |
public UberCallOptions(UberCallOptions other) { | |
deadline = other.deadline; | |
executor = other.executor; | |
customOptions = other.customOptions; | |
userDefinedHost = other.userDefinedHost; | |
userDefinedPort = other.userDefinedPort; | |
waitForReady = other.waitForReady; | |
streamTracerFactories = other.streamTracerFactories; | |
} | |
/** @return the {@link Deadline} associated to this call. */ | |
@Nullable | |
public Deadline getDeadline() { | |
return deadline; | |
} | |
/** @return the {@link Executor} to be used for this call. */ | |
@Nullable | |
public Executor getExecutor() { | |
return executor; | |
} | |
/** @return the custom options as part of this call. */ | |
public Object[][] getCustomOptions() { | |
return customOptions; | |
} | |
/** @return the list of streamtracer factories as part of this call. */ | |
public List<ClientStreamTracer.Factory> getStreamTracerFactories() { | |
return streamTracerFactories; | |
} | |
/** @return the user defined host. */ | |
@Nullable | |
public String getUserDefinedHost() { | |
return userDefinedHost; | |
} | |
/** @return the user defined port. */ | |
@Nullable | |
public Integer getUserDefinedPort() { | |
return userDefinedPort; | |
} | |
/** | |
* Returns whether <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">'wait | |
* for ready'</a> option is enabled for the call. 'Fail fast' is the default option for gRPC calls | |
* and 'wait for ready' is the opposite to it. | |
* | |
* @return true if wait for ready is set. else false. | |
*/ | |
public boolean isWaitForReady() { | |
return waitForReady != null ? Boolean.TRUE.equals(waitForReady) : false; | |
} | |
/** @return the value for {@link #waitForReady}. */ | |
@Nullable | |
Boolean getWaitForReady() { | |
return waitForReady; | |
} | |
/** | |
* Returns a new {@code UberCallOptions} with {@code executor} to be used instead of the default | |
* executor. | |
* | |
* @param executor instance of executor to be set. | |
* @return the new {@link UberCallOptions} with updated {@link Executor}. | |
*/ | |
public UberCallOptions withExecutor(@Nullable Executor executor) { | |
UberCallOptions newOptions = new UberCallOptions(this); | |
newOptions.executor = executor; | |
return newOptions; | |
} | |
/** | |
* Returns a new {@code UberCallOptions} with the given absolute deadline. | |
* | |
* <p>This is mostly used for propagating an existing deadline. {@link #withDeadlineAfter} is the | |
* recommended way of setting a new deadline, | |
* | |
* @param deadline the deadline or {@code null} for unsetting the deadline. | |
* @return the new {@link UberCallOptions} with updated deadline. | |
*/ | |
public UberCallOptions withDeadline(@Nullable Deadline deadline) { | |
UberCallOptions newOptions = new UberCallOptions(this); | |
newOptions.deadline = deadline; | |
return newOptions; | |
} | |
/** | |
* Returns a new {@code CallOptions} with a deadline that is after the given {@code duration} from | |
* now. | |
* | |
* @param duration the duration after which call should be expired. | |
* @param unit unit of the duration. | |
* @return the new {@link UberCallOptions} with updated deadline. | |
*/ | |
public UberCallOptions withDeadlineAfter(long duration, TimeUnit unit) { | |
return withDeadline(Deadline.after(duration, unit)); | |
} | |
/** | |
* Enables <a href="https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md">'wait for | |
* ready'</a> feature for the call. 'Fail fast' is the default option for gRPC calls and 'wait for | |
* ready' is the opposite to it. | |
* | |
* @return the new {@link UberCallOptions} with updated waitForReady. | |
*/ | |
public UberCallOptions withWaitForReady() { | |
UberCallOptions newOptions = new UberCallOptions(this); | |
newOptions.waitForReady = Boolean.TRUE; | |
return newOptions; | |
} | |
/** | |
* Disables 'wait for ready' feature for the call. This method should be rarely used because the | |
* default is without 'wait for ready'. | |
* | |
* @return the new {@link UberCallOptions} with updated waitForReady. | |
*/ | |
public UberCallOptions withoutWaitForReady() { | |
UberCallOptions newOptions = new UberCallOptions(this); | |
newOptions.waitForReady = Boolean.FALSE; | |
return newOptions; | |
} | |
/** | |
* Returns a new {@code UberCallOptions} with a {@code ClientStreamTracerFactory} in addition to | |
* the existing factories. | |
* | |
* @param factory streamtracer factory to be added. | |
* @return the new {@link UberCallOptions} with added streamtracer factory. | |
* <p>This method doesn't replace existing factories, or try to de-duplicate factories. | |
*/ | |
public UberCallOptions withStreamTracerFactory(ClientStreamTracer.Factory factory) { | |
UberCallOptions newOptions = new UberCallOptions(this); | |
ArrayList<ClientStreamTracer.Factory> newList = | |
new ArrayList<>(streamTracerFactories.size() + 1); | |
newList.addAll(streamTracerFactories); | |
newList.add(factory); | |
newOptions.streamTracerFactories = Collections.unmodifiableList(newList); | |
return newOptions; | |
} | |
/** | |
* Updates user defined host for the call. | |
* | |
* @param userDefinedHost user defined host. | |
* @return the new {@link UberCallOptions} with updated userdefined host. | |
*/ | |
public UberCallOptions withUserDefinedHost(String userDefinedHost) { | |
UberCallOptions newOptions = new UberCallOptions(this); | |
newOptions.userDefinedHost = userDefinedHost; | |
return newOptions; | |
} | |
/** | |
* Updates user defined port for the call. | |
* | |
* @param userDefinedPort user defined port. | |
* @return the new {@link UberCallOptions} with updated userdefined port. | |
*/ | |
public UberCallOptions withUserDefinedPort(int userDefinedPort) { | |
UberCallOptions newOptions = new UberCallOptions(this); | |
newOptions.userDefinedPort = userDefinedPort; | |
return newOptions; | |
} | |
/** | |
* Ignores StreamtracerFactories for now. | |
* | |
* @param o instance of {@link UberCallOptions} object to be compared for equality. | |
* @return the result for comparison. | |
*/ | |
@Override | |
@SuppressWarnings("NullAway") | |
public boolean equals(Object o) { | |
if (this == o) { | |
return true; | |
} | |
if (!(o instanceof UberCallOptions)) { | |
return false; | |
} | |
UberCallOptions that = (UberCallOptions) o; | |
return ((getUserDefinedPort() == null && that.getUserDefinedPort() == null) | |
|| getUserDefinedPort().equals(that.getUserDefinedPort())) | |
&& ((getDeadline() == null && that.getDeadline() == null) | |
|| getDeadline().equals(that.getDeadline())) | |
&& ((getExecutor() == null && that.getExecutor() == null) | |
|| getExecutor().equals(getExecutor())) | |
&& isWaitForReady() == that.isWaitForReady() | |
&& Arrays.equals(getCustomOptions(), that.getCustomOptions()) | |
&& ((getUserDefinedHost() == null && that.getUserDefinedHost() == null) | |
|| getUserDefinedHost().equals(that.getUserDefinedHost())); | |
} | |
@Override | |
public int hashCode() { | |
Object[] objects = { | |
getDeadline(), getExecutor(), isWaitForReady(), getUserDefinedHost(), getUserDefinedPort() | |
}; | |
int result = Arrays.hashCode(objects); | |
result = 31 * result + Arrays.hashCode(getCustomOptions()); | |
return result; | |
} | |
/** | |
* Sets a custom option. Any existing value for the key is overwritten. | |
* | |
* @param key The option key | |
* @param value The option value. | |
* @param <T> generic type of custom key. | |
* @return the new {@link UberCallOptions} with added custom entry. | |
*/ | |
public <T> UberCallOptions withOption(UberCallOptions.Key<T> key, T value) { | |
Preconditions.checkNotNull(key, "key"); | |
Preconditions.checkNotNull(value, "value"); | |
UberCallOptions newOptions = new UberCallOptions(this); | |
int existingIdx = -1; | |
for (int i = 0; i < customOptions.length; i++) { | |
if (key.equals(customOptions[i][0])) { | |
existingIdx = i; | |
break; | |
} | |
} | |
newOptions.customOptions = new Object[customOptions.length + (existingIdx == -1 ? 1 : 0)][2]; | |
System.arraycopy(customOptions, 0, newOptions.customOptions, 0, customOptions.length); | |
if (existingIdx == -1) { | |
// Add a new option | |
newOptions.customOptions[customOptions.length] = new Object[] {key, value}; | |
} else { | |
// Replace an existing option | |
newOptions.customOptions[existingIdx] = new Object[] {key, value}; | |
} | |
return newOptions; | |
} | |
/** | |
* Get the value for a custom option or its inherent default. | |
* | |
* @param key Key identifying option | |
* @param <T> generic type of custom key. | |
* @return the value pertaining to the custom key. | |
*/ | |
@Nullable | |
public <T> T getOption(UberCallOptions.Key<T> key) { | |
Preconditions.checkNotNull(key, "key"); | |
for (int i = 0; i < customOptions.length; i++) { | |
if (key.equals(customOptions[i][0])) { | |
return (T) customOptions[i][1]; | |
} | |
} | |
return key.defaultValue; | |
} | |
/** | |
* Key for a key-value pair. Uses reference equality. | |
* | |
* @param <T> generic type of custom key. | |
*/ | |
public static final class Key<T> { | |
private final String debugString; | |
@Nullable private final T defaultValue; | |
private Key(String debugString, @Nullable T defaultValue) { | |
this.debugString = debugString; | |
this.defaultValue = defaultValue; | |
} | |
/** @return the user supplied default value for this key. */ | |
@Nullable | |
public T getDefault() { | |
return defaultValue; | |
} | |
@Override | |
public String toString() { | |
return debugString; | |
} | |
/** | |
* Factory method for creating instances of {@link UberCallOptions.Key}. The default value of | |
* the key is {@code null}. | |
* | |
* @param debugString a debug string that describes this key. | |
* @param <T> Key type | |
* @return Key object | |
*/ | |
public static <T> UberCallOptions.Key<T> create(String debugString) { | |
Preconditions.checkNotNull(debugString, "debugString"); | |
return new UberCallOptions.Key<>(debugString, /*defaultValue=*/ null); | |
} | |
/** | |
* Factory method for creating instances of {@link UberCallOptions.Key}. | |
* | |
* @param debugString a debug string that describes this key. | |
* @param defaultValue default value to return when value for key not set | |
* @param <T> Key type | |
* @return Key object | |
*/ | |
public static <T> UberCallOptions.Key<T> createWithDefault( | |
String debugString, @Nullable T defaultValue) { | |
Preconditions.checkNotNull(debugString, "debugString"); | |
return new UberCallOptions.Key<>(debugString, defaultValue); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment