nl.topicus.jdbc.shaded.io.grpc.internal.ConnectivityStateManager Maven / Gradle / Ivy
Show all versions of spanner-jdbc Show documentation
/*
* Copyright 2016, gRPC Authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nl.topicus.jdbc.shaded.io.grpc.internal;
import static nl.topicus.jdbc.shaded.com.google.common.base.Preconditions.checkNotNull;
import static nl.topicus.jdbc.shaded.com.google.common.base.Preconditions.checkState;
import nl.topicus.jdbc.shaded.io.grpc.ConnectivityState;
import nl.topicus.jdbc.shaded.io.grpc.ManagedChannel;
import java.util.ArrayList;
import java.util.concurrent.Executor;
import nl.topicus.jdbc.shaded.javax.annotation.Nonnull;
import nl.topicus.jdbc.shaded.javax.annotation.Nullable;
import nl.topicus.jdbc.shaded.javax.annotation.concurrent.NotThreadSafe;
/**
* Manages connectivity states of the channel. Used for {@link ManagedChannel#getState} to read the
* current state of the channel, for {@link ManagedChannel#notifyWhenStateChanged} to add
* listeners to state change events, and for {@link nl.topicus.jdbc.shaded.io.grpc.LoadBalancer.Helper#updateBalancingState
* LoadBalancer.Helper#updateBalancingState} to update the state and run the {@link #gotoState}s.
*/
@NotThreadSafe
final class ConnectivityStateManager {
private ArrayList listeners = new ArrayList();
private volatile ConnectivityState state = ConnectivityState.IDLE;
/**
* Adds a listener for state change event.
*
* The {@code executor} must be one that can run RPC call listeners.
*/
void notifyWhenStateChanged(Runnable callback, Executor executor, ConnectivityState source) {
checkNotNull(callback, "callback");
checkNotNull(executor, "executor");
checkNotNull(source, "source");
Listener stateChangeListener = new Listener(callback, executor);
if (state != source) {
stateChangeListener.runInExecutor();
} else {
listeners.add(stateChangeListener);
}
}
/**
* Connectivity state is changed to the specified value. Will trigger some notifications that have
* been registered earlier by {@link ManagedChannel#notifyWhenStateChanged}.
*/
void gotoState(@Nonnull ConnectivityState newState) {
checkNotNull(newState, "newState");
checkState(!isDisabled(), "ConnectivityStateManager is already disabled");
gotoNullableState(newState);
}
private void gotoNullableState(@Nullable ConnectivityState newState) {
if (state != newState && state != ConnectivityState.SHUTDOWN) {
state = newState;
if (listeners.isEmpty()) {
return;
}
// Swap out callback list before calling them, because a callback may register new callbacks,
// if run in direct executor, can cause ConcurrentModificationException.
ArrayList savedListeners = listeners;
listeners = new ArrayList();
for (Listener listener : savedListeners) {
listener.runInExecutor();
}
}
}
/**
* Gets the current connectivity state of the channel. This method is threadsafe.
*/
ConnectivityState getState() {
ConnectivityState stateCopy = state;
if (stateCopy == null) {
throw new UnsupportedOperationException("Channel state API is not implemented");
}
return stateCopy;
}
/**
* Call this method when the channel learns from the load balancer that channel state API is not
* supported.
*/
void disable() {
gotoNullableState(null);
}
/**
* This method is threadsafe.
*/
boolean isDisabled() {
return state == null;
}
private static final class Listener {
final Runnable callback;
final Executor executor;
Listener(Runnable callback, Executor executor) {
this.callback = callback;
this.executor = executor;
}
void runInExecutor() {
executor.execute(callback);
}
}
}