package com.kurento.kmf.rabbitmq.server; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; //import org.springframework.amqp.rabbit.core.RabbitTemplate; import com.google.gson.JsonElement; import com.google.gson.JsonNull; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.kurento.kmf.common.Address; import com.kurento.kmf.jsonrpcconnector.DefaultJsonRpcHandler; import com.kurento.kmf.jsonrpcconnector.JsonRpcHandler; import com.kurento.kmf.jsonrpcconnector.JsonUtils; import com.kurento.kmf.jsonrpcconnector.Transaction; import com.kurento.kmf.jsonrpcconnector.client.JsonRpcClient; import com.kurento.kmf.jsonrpcconnector.client.JsonRpcClientLocal; import com.kurento.kmf.jsonrpcconnector.internal.message.Request; import com.kurento.kmf.jsonrpcconnector.internal.message.Response; import com.kurento.kmf.jsonrpcconnector.internal.message.ResponseError; import com.kurento.kmf.rabbitmq.RabbitMqManager; import com.kurento.kmf.rabbitmq.RabbitMqManager.BrokerMessageReceiverWithResponse; import com.kurento.kmf.rabbitmq.RabbitTemplate; import com.kurento.tool.rom.transport.jsonrpcconnector.RomJsonRpcConstants; public class JsonRpcServerRabbitMq { private static final Logger log = LoggerFactory .getLogger(JsonRpcServerRabbitMq.class); private Map<String, MediaPipelineInfo> pipelinesById = new ConcurrentHashMap<>(); private Map<String, MediaPipelineInfo> pipelinesBySubscription = new ConcurrentHashMap<>(); private JsonRpcClient client; private RabbitMqManager rabbitMq; private RabbitTemplate template; // TODO: Maybe we need to implement a pure JsonRpcServerRabbitMq with // handler parameter instead of this client > handler communication public JsonRpcServerRabbitMq(JsonRpcHandler<?> handler) { this(new JsonRpcClientLocal(handler)); } public JsonRpcServerRabbitMq(JsonRpcClient client) { this(client, new Address("127.0.0.1", 5672)); } public JsonRpcServerRabbitMq(JsonRpcClient client, Address rabbitMqAddress) { this.client = client; this.rabbitMq = new RabbitMqManager(rabbitMqAddress); } public void start() { this.rabbitMq.connect(); this.template = rabbitMq.createServerTemplate(); rabbitMq.addMessageReceiverWithResponse( RabbitMqManager.PIPELINE_CREATION_QUEUE, new BrokerMessageReceiverWithResponse() { @Override public String onMessage(String message) { return pipelineCreationQueueRequest(message); } }); this.client .setServerRequestHandler(new DefaultJsonRpcHandler<JsonObject>() { @Override public void handleRequest(Transaction transaction, Request<JsonObject> request) throws Exception { processEventFromServer(request); } }); } private String pipelineCreationQueueRequest(String message) { log.debug("[PCQ] --> {}", message); Request<JsonObject> request = JsonUtils.fromJsonRequest(message, JsonObject.class); String response = createMediaPipeline(request).toString(); log.debug("[PCQ] <-- {}", response); return response; } private Response<JsonElement> createMediaPipeline( Request<JsonObject> request) { try { JsonElement response = client.sendRequest(request.getMethod(), request.getParams()); final String pipelineId = getValue(response); rabbitMq.declarePipelineQueue(pipelineId); rabbitMq.addMessageReceiverWithResponse(pipelineId, new BrokerMessageReceiverWithResponse() { @Override public String onMessage(String message) { return pipelineQueueRequest(pipelineId, message); } }); String exchange = rabbitMq.declareEventsExchange(pipelineId); MediaPipelineInfo pipeline = new MediaPipelineInfo(pipelineId, pipelineId, exchange); this.pipelinesById.put(pipelineId, pipeline); return new Response<JsonElement>(request.getId(), new JsonPrimitive(pipelineId)); } catch (Exception e) { return new Response<JsonElement>(request.getId(), ResponseError.newFromException(request.getId(), e)); } } private void processEventFromServer(Request<JsonObject> request) { if (!RomJsonRpcConstants.ONEVENT_METHOD.equals(request.getMethod())) { log.warn("Unrecognized server message {}", request); return; } JsonObject value = request.getParams().get("value").getAsJsonObject(); String subscriptionId = value.get( RomJsonRpcConstants.ONEVENT_SUBSCRIPTION).getAsString(); String objectId = value.get(RomJsonRpcConstants.ONEVENT_OBJECT) .getAsString(); value.addProperty(RomJsonRpcConstants.ONEVENT_OBJECT, objectId); String type = value.get(RomJsonRpcConstants.ONEVENT_TYPE).getAsString(); final String eventRoutingKey = rabbitMq .createRoutingKey(objectId, type); MediaPipelineInfo pipelineInfo = pipelinesBySubscription .get(subscriptionId); if (pipelineInfo == null) { log.debug("PipelinesBySubscription: " + pipelinesBySubscription); } rabbitMq.send(pipelineInfo.getEventsExchange(), eventRoutingKey, request.toString(), template); } private String pipelineQueueRequest(final String pipelineId, String message) { log.debug("[PQ] --> {}", message); String response = onPipelineMessage(pipelineId, pipelineId, message); log.debug("[PQ] <-- {}", response); return response; } public String onPipelineMessage(String brokerPipelineId, String realPipelineId, String message) { Request<JsonObject> request = JsonUtils.fromJsonRequest(message, JsonObject.class); try { switch (request.getMethod()) { case RomJsonRpcConstants.CREATE_METHOD: return createMediaElement(brokerPipelineId, request).toString(); case RomJsonRpcConstants.INVOKE_METHOD: return invokeOperation(request).toString(); case RomJsonRpcConstants.SUBSCRIBE_METHOD: return subscribeMessage(brokerPipelineId, request).toString(); case RomJsonRpcConstants.RELEASE_METHOD: return release(realPipelineId, brokerPipelineId, request) .toString(); default: return invokeOperation(request).toString(); } } catch (Exception e) { log.warn("Exception processing request from client. ", e); return new Response<JsonElement>(request.getId(), ResponseError.newFromException(request.getId(), e)) .toString(); } } private Object release(String realPipelineId, String brokerPipelineId, Request<JsonObject> request) { String objectId = request.getParams() .get(RomJsonRpcConstants.RELEASE_OBJECT).getAsString(); Response<JsonElement> response = invokeOperation(request); if (!response.isError() && pipelinesById.containsKey(objectId)) { pipelinesById.remove(objectId); } return response; } private Response<JsonElement> createMediaElement(String brokerPipelineId, Request<JsonObject> request) { try { JsonElement response = client.sendRequest(request.getMethod(), request.getParams()); String realObjectId = getValue(response); return new Response<JsonElement>(request.getId(), new JsonPrimitive(realObjectId)); } catch (Exception e) { return new Response<JsonElement>(request.getId(), ResponseError.newFromException(request.getId(), e)); } } private Response<JsonElement> invokeOperation(Request<JsonObject> request) { try { JsonElement result = client.sendRequest(request.getMethod(), request.getParams()); // TODO: Improve this handling of null responses if (result == null || result instanceof JsonNull) { result = new JsonObject(); } return new Response<JsonElement>(request.getId(), result); } catch (Exception e) { return new Response<JsonElement>(request.getId(), ResponseError.newFromException(request.getId(), e)); } } private Response<JsonElement> subscribeMessage(String pipelineId, Request<JsonObject> request) { Response<JsonElement> response = invokeOperation(request); if (!response.isError()) { String subscriptionId = ((JsonObject) response.getResult()).get( "value").getAsString(); this.pipelinesBySubscription.put(subscriptionId, pipelinesById.get(pipelineId)); } return response; } public void destroy() throws IOException { if (client != null) { client.close(); } this.rabbitMq.destroy(); } private String getValue(JsonElement response) { if (response == null) { return null; } if (response instanceof JsonPrimitive) { return response.getAsString(); } if (response instanceof JsonObject) { JsonObject json = (JsonObject) response; return getValue(json.entrySet().iterator().next().getValue()); } throw new RuntimeException( "Can't extract a single value from jsonElement: " + response); } }