package io.airlift.airship.coordinator;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.airlift.airship.shared.CoordinatorStatus;
import io.airlift.airship.shared.CoordinatorStatusRepresentation;
import io.airlift.http.client.AsyncHttpClient;
import io.airlift.http.client.Request;
import io.airlift.json.JsonCodec;
import javax.annotation.concurrent.GuardedBy;
import java.net.URI;
import java.util.concurrent.atomic.AtomicLong;
import static io.airlift.airship.shared.CoordinatorLifecycleState.OFFLINE;
import static io.airlift.airship.shared.CoordinatorLifecycleState.PROVISIONING;
import static io.airlift.airship.shared.HttpUriBuilder.uriBuilderFrom;
import static io.airlift.http.client.JsonResponseHandler.createJsonResponseHandler;
public class HttpRemoteCoordinator
implements RemoteCoordinator
{
private final JsonCodec<CoordinatorStatusRepresentation> coordinatorStatusCodec;
@GuardedBy("this")
private CoordinatorStatus coordinatorStatus;
private final AsyncHttpClient httpClient;
private final AtomicLong failureCount = new AtomicLong();
public HttpRemoteCoordinator(CoordinatorStatus coordinatorStatus,
String environment,
AsyncHttpClient httpClient,
JsonCodec<CoordinatorStatusRepresentation> coordinatorStatusCodec)
{
Preconditions.checkNotNull(coordinatorStatus, "coordinatorStatus is null");
Preconditions.checkNotNull(environment, "environment is null");
Preconditions.checkNotNull(httpClient, "httpClient is null");
this.coordinatorStatus = coordinatorStatus;
this.httpClient = httpClient;
this.coordinatorStatusCodec = coordinatorStatusCodec;
}
@Override
public synchronized CoordinatorStatus status()
{
return coordinatorStatus;
}
@Override
public synchronized void setInternalUri(URI internalUri)
{
coordinatorStatus = coordinatorStatus.changeInternalUri(internalUri);
}
@Override
public ListenableFuture<?> updateStatus()
{
final CoordinatorStatus coordinatorStatus = status();
URI internalUri = coordinatorStatus.getInternalUri();
if (internalUri == null) {
return Futures.immediateFuture(null);
}
Request request = Request.Builder.prepareGet()
.setUri(uriBuilderFrom(internalUri).replacePath("/v1/coordinator/").build())
.build();
ListenableFuture<CoordinatorStatusRepresentation> future = httpClient.executeAsync(request, createJsonResponseHandler(coordinatorStatusCodec));
Futures.addCallback(future, new FutureCallback<CoordinatorStatusRepresentation>()
{
@Override
public void onSuccess(CoordinatorStatusRepresentation result)
{
// todo deal with out of order responses
setStatus(result.toCoordinatorStatus(coordinatorStatus.getInstanceId(), coordinatorStatus.getInstanceType()));
failureCount.set(0);
}
@Override
public void onFailure(Throwable t)
{
// error talking to coordinator -- mark coordinator offline
if (coordinatorStatus.getState() != PROVISIONING && failureCount.incrementAndGet() > 5) {
setStatus(coordinatorStatus.changeState(OFFLINE));
}
}
});
return future;
}
public synchronized void setStatus(CoordinatorStatus coordinatorStatus)
{
Preconditions.checkNotNull(coordinatorStatus, "coordinatorStatus is null");
this.coordinatorStatus = coordinatorStatus;
}
}