package org.corfudb.runtime.clients;
import io.netty.channel.ChannelHandlerContext;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.corfudb.protocols.wireprotocol.*;
import org.corfudb.runtime.exceptions.WrongEpochException;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.CompletableFuture;
/**
* This is a base client which processes basic messages.
* It mainly handles PINGs, as well as the ACK/NACKs defined by
* the Corfu protocol.
* <p>
* Created by mwei on 12/9/15.
*/
@Slf4j
public class BaseClient implements IClient {
/**
* The router to use for the client.
*/
@Getter
@Setter
public IClientRouter router;
/** Public functions which are exposed to clients. */
/**
* Ping the endpoint, synchronously.
*
* @return True, if the endpoint was reachable, false otherwise.
*/
public boolean pingSync() {
try {
return ping().get();
} catch (Exception e) {
log.trace("Ping failed due to exception", e);
return false;
}
}
public CompletableFuture<Boolean> setRemoteEpoch(long newEpoch) {
// Set our own epoch to this epoch.
router.setEpoch(newEpoch);
return router.sendMessageAndGetCompletable(
new CorfuPayloadMsg<>(CorfuMsgType.SET_EPOCH, newEpoch));
}
public CompletableFuture<VersionInfo> getVersionInfo() {
return router.sendMessageAndGetCompletable(
new CorfuMsg(CorfuMsgType.VERSION_REQUEST));
}
/**
* Ping the endpoint, asynchronously.
*
* @return A completable future which will be completed with True if
* the endpoint is reachable, otherwise False or exceptional completion.
*/
public CompletableFuture<Boolean> ping() {
return router.sendMessageAndGetCompletable(
new CorfuMsg(CorfuMsgType.PING));
}
/**
* Reset the endpoint, asynchronously.
*
* @return A completable future which will be completed with True if
* the endpoint acks, otherwise False or exceptional completion.
*/
public CompletableFuture<Boolean> reset() {
return router.sendMessageAndGetCompletable(
new CorfuMsg(CorfuMsgType.RESET));
}
/** The handler and handlers which implement this client. */
@Getter
public ClientMsgHandler msgHandler = new ClientMsgHandler(this)
.generateHandlers(MethodHandles.lookup(), this);
/** Handle a ping request from the server.
*
* @param msg The ping request message
* @param ctx The context the message was sent under
* @param r A reference to the router
* @return The return value, null since this is a message from the server.
*/
@ClientHandler(type=CorfuMsgType.PING)
private static Object handlePing(CorfuMsg msg, ChannelHandlerContext ctx, IClientRouter r) {
r.sendResponseToServer(ctx, msg, new CorfuMsg(CorfuMsgType.PONG));
return null;
}
/** Handle a pong response from the server.
*
* @param msg The ping request message
* @param ctx The context the message was sent under
* @param r A reference to the router
* @return Always True, since the ping message was successful.
*/
@ClientHandler(type=CorfuMsgType.PONG)
private static Object handlePong(CorfuMsg msg, ChannelHandlerContext ctx, IClientRouter r) {
return true;
}
/** Handle an ACK response from the server.
*
* @param msg The ping request message
* @param ctx The context the message was sent under
* @param r A reference to the router
* @return Always True, since the ACK message was successful.
*/
@ClientHandler(type=CorfuMsgType.ACK)
private static Object handleAck(CorfuMsg msg, ChannelHandlerContext ctx, IClientRouter r) {
return true;
}
/** Handle a NACK response from the server.
*
* @param msg The ping request message
* @param ctx The context the message was sent under
* @param r A reference to the router
* @return Always True, since the ACK message was successful.
*/
@ClientHandler(type=CorfuMsgType.NACK)
private static Object handleNack(CorfuMsg msg, ChannelHandlerContext ctx, IClientRouter r) {
return false;
}
/** Handle a WRONG_EPOCH response from the server.
*
* @param msg The wrong epoch message
* @param ctx The context the message was sent under
* @param r A reference to the router
* @return none, throw a wrong epoch exception instead.
*/
@ClientHandler(type=CorfuMsgType.WRONG_EPOCH)
private static Object handleWrongEpoch(CorfuPayloadMsg<Long> msg, ChannelHandlerContext ctx, IClientRouter r) {
throw new WrongEpochException(msg.getPayload());
}
/** Handle a Version response from the server.
*
* @param msg The version message
* @param ctx The context the message was sent under
* @param r A reference to the router
* @return The versioninfo object.
*/
@ClientHandler(type=CorfuMsgType.VERSION_RESPONSE)
private static Object handleVersionResponse(JSONPayloadMsg<VersionInfo> msg,
ChannelHandlerContext ctx, IClientRouter r) {
return msg.getPayload();
}
}