package com.kurento.kmf.thrift.jsonrpcconnector;
import static com.kurento.kmf.jsonrpcconnector.JsonUtils.fromJsonRequest;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import org.apache.thrift.TException;
import org.apache.thrift.async.AsyncMethodCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.kurento.kmf.jsonrpcconnector.JsonUtils;
import com.kurento.kmf.jsonrpcconnector.KeepAliveManager;
import com.kurento.kmf.jsonrpcconnector.TransportException;
import com.kurento.kmf.jsonrpcconnector.client.Continuation;
import com.kurento.kmf.jsonrpcconnector.client.JsonRpcClient;
import com.kurento.kmf.jsonrpcconnector.internal.JsonRpcRequestSenderHelper;
import com.kurento.kmf.jsonrpcconnector.internal.client.TransactionImpl.ResponseSender;
import com.kurento.kmf.jsonrpcconnector.internal.message.Message;
import com.kurento.kmf.jsonrpcconnector.internal.message.Request;
import com.kurento.kmf.jsonrpcconnector.internal.message.Response;
import com.kurento.kmf.thrift.ThriftInterfaceConfiguration;
import com.kurento.kmf.thrift.ThriftServer;
import com.kurento.kmf.thrift.ThriftTransportException;
import com.kurento.kmf.thrift.internal.ThriftInterfaceExecutorService;
import com.kurento.kmf.thrift.pool.ThriftClientPoolService;
import com.kurento.kms.thrift.api.KmsMediaHandlerService.Iface;
import com.kurento.kms.thrift.api.KmsMediaHandlerService.Processor;
import com.kurento.kms.thrift.api.KmsMediaServerService.AsyncClient;
import com.kurento.kms.thrift.api.KmsMediaServerService.AsyncClient.invokeJsonRpc_call;
import com.kurento.kms.thrift.api.KmsMediaServerService.Client;
public class JsonRpcClientThrift extends JsonRpcClient {
private static final Logger log = LoggerFactory
.getLogger(JsonRpcClientThrift.class);
private ThriftClientPoolService clientPool;
private ThriftServer server;
private InetSocketAddress localHandlerAddress;
private final ResponseSender dummyResponseSenderForEvents = new ResponseSender() {
@Override
public void sendResponse(Message message) throws IOException {
log.warn(
"The thrift client is trying to send the response '{}' for "
+ "a request from server. But with Thrift it is not possible",
message);
}
};
public JsonRpcClientThrift() {
this("127.0.0.1", 9090, "127.0.0.1", 9191);
}
public JsonRpcClientThrift(String serverAddress, int serverPort,
String localAddress, int localPort) {
this(new ThriftClientPoolService(new ThriftInterfaceConfiguration(
serverAddress, serverPort)),
new ThriftInterfaceExecutorService(
new ThriftInterfaceConfiguration(serverAddress,
serverPort)), new InetSocketAddress(
localAddress, localPort));
}
public JsonRpcClientThrift(ThriftClientPoolService clientPool,
ThriftInterfaceExecutorService executorService,
InetSocketAddress localHandlerAddress) {
this.clientPool = clientPool;
this.localHandlerAddress = localHandlerAddress;
this.rsHelper = new JsonRpcRequestSenderHelper() {
@Override
public <P, R> Response<R> internalSendRequest(Request<P> request,
Class<R> resultClass) throws IOException {
try {
return internalSendRequestThrift(request, resultClass);
} catch (ThriftTransportException e) {
throw new TransportException(
"Error sendind request to server", e);
}
}
@Override
protected void internalSendRequest(
Request<? extends Object> request,
Class<JsonElement> resultClass,
Continuation<Response<JsonElement>> continuation) {
internalSendRequestThrift(request, resultClass, continuation);
}
};
final Processor<Iface> clientProcessor = new Processor<Iface>(
new Iface() {
@Override
public void eventJsonRpc(String request) throws TException {
try {
log.debug("<-Req {}", request.trim());
JsonObject message = JsonUtils.fromJson(request,
JsonObject.class);
handleRequestFromServer(message);
} catch (Exception e) {
log.error(
"Exception while processing a request received from server",
e);
}
}
});
server = new ThriftServer(clientProcessor, executorService,
localHandlerAddress);
server.start();
keepAliveManager = new KeepAliveManager(this,
KeepAliveManager.Mode.PER_CLIENT);
keepAliveManager.start();
}
private void handleRequestFromServer(JsonObject message) throws IOException {
handlerManager.handleRequest(session,
fromJsonRequest(message, JsonElement.class),
dummyResponseSenderForEvents);
}
private <P> void processRequest(Request<P> request) {
// TODO Remove this hack -----------------------
if (request.getMethod().equals("subscribe")) {
log.debug(
"Adding local address info to subscription request. ip:{} port:{}",
localHandlerAddress.getHostString(),
localHandlerAddress.getPort());
JsonObject params = (JsonObject) request.getParams();
params.addProperty("ip", localHandlerAddress.getHostString());
params.addProperty("port",
Integer.valueOf(localHandlerAddress.getPort()));
}
// ---------------------------------------------
}
/**
*
* @param request
* the request
* @param resultClass
* the expected result class
* @return The response from the media server
* @throws ThriftTransportException
* if the request could not be sent to the media server due to a
* problem in the transport
*/
public <P, R> Response<R> internalSendRequestThrift(Request<P> request,
Class<R> resultClass) throws ThriftTransportException {
Client client = clientPool.acquireSync();
try {
log.debug("Req-> {}", request);
processRequest(request);
String responseStr = client.invokeJsonRpc(request.toString());
log.debug("<-Res {}", responseStr.trim());
Response<R> response = JsonUtils.fromJsonResponse(responseStr,
resultClass);
return response;
} catch (TException e) {
throw new ThriftTransportException(
"Error sending request to the remote server", e);
} finally {
clientPool.release(client);
}
}
protected void internalSendRequestThrift(Request<? extends Object> request,
final Class<JsonElement> resultClass,
final Continuation<Response<JsonElement>> continuation) {
log.debug("Req-> {}", request);
processRequest(request);
sendRequest(request, resultClass, continuation, true);
}
private void sendRequest(final Request<? extends Object> request,
final Class<JsonElement> resultClass,
final Continuation<Response<JsonElement>> continuation,
final boolean retry) {
final AsyncClient client = clientPool.acquireAsync();
try {
client.invokeJsonRpc(request.toString(),
new AsyncMethodCallback<AsyncClient.invokeJsonRpc_call>() {
@Override
public void onError(Exception exception) {
clientPool.release(client);
log.error(
"[Client] Error sending request to server",
exception);
if (retry && exception instanceof ConnectException) {
sendRequest(request, resultClass, continuation,
false);
} else {
continuation.onError(exception);
}
}
@Override
public void onComplete(invokeJsonRpc_call thriftResponse) {
clientPool.release(client);
try {
String responseStr = thriftResponse.getResult();
log.debug("<-Res {}", responseStr.trim());
Response<JsonElement> response = JsonUtils
.fromJsonResponse(responseStr,
resultClass);
continuation.onSuccess(response);
} catch (TException e) {
continuation.onError(e);
}
}
});
} catch (TException e) {
log.error("Error on sendRequest", e);
continuation.onError(e);
}
}
@Override
public void close() throws IOException {
if (server != null) {
server.destroy();
}
if (keepAliveManager != null) {
keepAliveManager.stop();
}
}
}