2 * Copyright (C) 2013 EBay Software Foundation
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 * Authors : Ashwin Raveendran, Madhu Venugopal
10 package org.opendaylight.ovsdb.lib.jsonrpc;
12 import io.netty.channel.Channel;
14 import java.lang.reflect.InvocationHandler;
15 import java.lang.reflect.InvocationTargetException;
16 import java.lang.reflect.Method;
17 import java.util.List;
19 import java.util.UUID;
21 import org.opendaylight.controller.sal.core.Node;
22 import org.opendaylight.ovsdb.lib.message.OvsdbRPC;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
26 import com.fasterxml.jackson.core.JsonProcessingException;
27 import com.fasterxml.jackson.databind.JavaType;
28 import com.fasterxml.jackson.databind.JsonNode;
29 import com.fasterxml.jackson.databind.ObjectMapper;
30 import com.fasterxml.jackson.databind.type.TypeFactory;
31 import com.google.common.collect.Maps;
32 import com.google.common.reflect.Reflection;
33 import com.google.common.reflect.TypeToken;
34 import com.google.common.util.concurrent.ListenableFuture;
35 import com.google.common.util.concurrent.SettableFuture;
37 public class JsonRpcEndpoint {
39 protected static final Logger logger = LoggerFactory.getLogger(JsonRpcEndpoint.class);
41 public class CallContext {
43 JsonRpc10Request request;
44 SettableFuture<Object> future;
46 public CallContext(JsonRpc10Request request, Method method, SettableFuture<Object> future) {
48 this.request = request;
52 public Method getMethod() {
56 public JsonRpc10Request getRequest() {
60 public SettableFuture<Object> getFuture() {
65 ObjectMapper objectMapper;
67 Map<String, CallContext> methodContext = Maps.newHashMap();
68 Map<Node, OvsdbRPC.Callback> requestCallbacks = Maps.newHashMap();
70 public JsonRpcEndpoint(ObjectMapper objectMapper, Channel channel) {
71 this.objectMapper = objectMapper;
72 this.nettyChannel = channel;
75 public <T> T getClient(final Node node, Class<T> klazz) {
77 return Reflection.newProxy(klazz, new InvocationHandler() {
79 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
80 if (method.getName().equals(OvsdbRPC.REGISTER_CALLBACK_METHOD)) {
81 if ((args == null) || args.length != 1 || !(args[0] instanceof OvsdbRPC.Callback)) return false;
82 requestCallbacks.put(node, (OvsdbRPC.Callback)args[0]);
86 JsonRpc10Request request = new JsonRpc10Request(UUID.randomUUID().toString());
87 request.setMethod(method.getName());
89 if (args != null && args.length != 0) {
90 List<Object> params = null;
92 if (args.length == 1) {
93 if (args[0] instanceof Params) {
94 params = ((Params) args[0]).params();
95 } else if (args[0] instanceof List) {
96 params = (List<Object>) args[0];
100 throw new RuntimeException("do not understand this argument yet");
102 request.setParams(params);
106 String s = objectMapper.writeValueAsString(request);
107 logger.trace("{}", s);
109 SettableFuture<Object> sf = SettableFuture.create();
110 methodContext.put(request.getId(), new CallContext(request, method, sf));
112 nettyChannel.writeAndFlush(s);
120 public void processResult(JsonNode response) throws NoSuchMethodException {
122 logger.trace("Response : {}", response.toString());
123 CallContext returnCtxt = methodContext.get(response.get("id").asText());
124 if (returnCtxt == null) return;
126 if (ListenableFuture.class == returnCtxt.getMethod().getReturnType()) {
127 TypeToken<?> retType = TypeToken.of(
128 returnCtxt.getMethod().getGenericReturnType())
129 .resolveType(ListenableFuture.class.getMethod("get").getGenericReturnType());
130 JavaType javaType = TypeFactory.defaultInstance().constructType (retType.getType());
132 JsonNode result = response.get("result");
133 logger.trace("Result : {}", result.toString());
135 Object result1 = objectMapper.convertValue(result, javaType);
136 JsonNode error = response.get("error");
137 if (error != null && !error.isNull()) {
138 logger.error("Error : {}", error.toString());
141 returnCtxt.getFuture().set(result1);
144 throw new RuntimeException("donno how to deal with this");
148 public void processRequest(Node node, JsonNode requestJson) {
149 JsonRpc10Request request = new JsonRpc10Request(requestJson.get("id").asText());
150 request.setMethod(requestJson.get("method").asText());
151 logger.trace("Request : {} {}", requestJson.get("method"), requestJson.get("params"));
152 OvsdbRPC.Callback callback = requestCallbacks.get(node);
153 if (callback != null) {
154 Method[] methods = callback.getClass().getDeclaredMethods();
155 for (Method m : methods) {
156 if (m.getName().equals(request.getMethod())) {
157 Class<?>[] parameters = m.getParameterTypes();
158 JsonNode params = requestJson.get("params");
159 Object param = objectMapper.convertValue(params, parameters[1]);
161 m.invoke(callback, node, param);
162 } catch (IllegalAccessException | InvocationTargetException e) {
163 logger.error("Unable to invoke callback " + m.getName(), e);
170 // Echo dont need any special processing. hence handling it internally.
172 if (request.getMethod().equals("echo")) {
173 JsonRpc10Response response = new JsonRpc10Response(request.getId());
174 response.setError(null);
177 s = objectMapper.writeValueAsString(response);
178 nettyChannel.writeAndFlush(s);
179 } catch (JsonProcessingException e) {
180 logger.error("Exception while processing JSON string " + s, e );
185 logger.error("No handler for Request : {} on {}",requestJson.toString(), node);
188 public Map<String, CallContext> getMethodContext() {
189 return methodContext;