2b9bab06c1a14dfab261947457e04dbf7a3e8562
[ovsdb.git] / ovsdb / src / main / java / org / opendaylight / ovsdb / lib / jsonrpc / JsonRpcEndpoint.java
1 package org.opendaylight.ovsdb.lib.jsonrpc;
2
3 import com.fasterxml.jackson.core.JsonProcessingException;
4 import com.fasterxml.jackson.databind.JavaType;
5 import com.fasterxml.jackson.databind.JsonNode;
6 import com.fasterxml.jackson.databind.ObjectMapper;
7 import com.fasterxml.jackson.databind.type.TypeFactory;
8 import com.google.common.collect.Maps;
9 import com.google.common.reflect.Reflection;
10 import com.google.common.reflect.TypeToken;
11 import com.google.common.util.concurrent.ListenableFuture;
12 import com.google.common.util.concurrent.SettableFuture;
13
14 import org.opendaylight.controller.sal.core.Node;
15 import org.opendaylight.ovsdb.lib.message.OVSDB;
16 import org.slf4j.Logger;
17 import org.slf4j.LoggerFactory;
18
19 import java.lang.reflect.InvocationHandler;
20 import java.lang.reflect.Method;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.UUID;
24 import io.netty.channel.Channel;
25
26 public class JsonRpcEndpoint {
27
28     protected static final Logger logger = LoggerFactory.getLogger(JsonRpcEndpoint.class);
29
30     public class CallContext {
31         Method method;
32         JsonRpc10Request request;
33         SettableFuture<Object> future;
34
35         public CallContext(JsonRpc10Request request, Method method, SettableFuture<Object> future) {
36             this.method = method;
37             this.request = request;
38             this.future = future;
39         }
40
41         public Method getMethod() {
42             return method;
43         }
44
45         public JsonRpc10Request getRequest() {
46             return request;
47         }
48
49         public SettableFuture<Object> getFuture() {
50             return future;
51         }
52     }
53
54     ObjectMapper objectMapper;
55     Channel nettyChannel;
56     Map<String, CallContext> methodContext = Maps.newHashMap();
57     Map<Node, OVSDB.Callback> requestCallbacks = Maps.newHashMap();
58
59     public JsonRpcEndpoint(ObjectMapper objectMapper, Channel channel) {
60         this.objectMapper = objectMapper;
61         this.nettyChannel = channel;
62     }
63
64     public <T> T getClient(final Node node, Class<T> klazz) {
65
66         return Reflection.newProxy(klazz, new InvocationHandler() {
67             @Override
68             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
69                 if (method.getName().equals(OVSDB.REGISTER_CALLBACK_METHOD)) {
70                     if ((args == null) || args.length != 1 || !(args[0] instanceof OVSDB.Callback)) return false;
71                     requestCallbacks.put(node, (OVSDB.Callback)args[0]);
72                     return true;
73                 }
74
75                 JsonRpc10Request request = new JsonRpc10Request(UUID.randomUUID().toString());
76                 request.setMethod(method.getName());
77
78                 if (args != null && args.length != 0) {
79                     List<Object> params = null;
80
81                     if (args.length == 1) {
82                         if (args[0] instanceof Params) {
83                             params = ((Params) args[0]).params();
84                         } else if (args[0] instanceof List) {
85                             params = (List<Object>) args[0];
86                         }
87
88                         if (params == null) {
89                             throw new RuntimeException("do not understand this argument yet");
90                         }
91                         request.setParams(params);
92                     }
93                 }
94
95                 String s = objectMapper.writeValueAsString(request);
96                 logger.debug("{}", s);
97
98                 SettableFuture<Object> sf = SettableFuture.create();
99                 methodContext.put(request.getId(), new CallContext(request, method, sf));
100
101                 nettyChannel.writeAndFlush(s);
102
103                 return sf;
104             }
105         }
106         );
107     }
108
109     public void processResult(JsonNode response) throws NoSuchMethodException {
110
111         CallContext returnCtxt = methodContext.get(response.get("id").asText());
112         if (returnCtxt == null) return;
113
114         if (ListenableFuture.class == returnCtxt.getMethod().getReturnType()) {
115             TypeToken<?> retType = TypeToken.of(
116                     returnCtxt.getMethod().getGenericReturnType())
117                     .resolveType(ListenableFuture.class.getMethod("get").getGenericReturnType());
118             JavaType javaType =  TypeFactory.defaultInstance().constructType (retType.getType());
119
120             JsonNode result = response.get("result");
121             logger.debug("Response : {}", result.toString());
122
123             Object result1 = objectMapper.convertValue(result, javaType);
124             JsonNode error = response.get("error");
125             if (error != null) {
126                 logger.debug("Error : {}", error.toString());
127             }
128
129             returnCtxt.getFuture().set(result1);
130
131         } else {
132             throw new RuntimeException("donno how to deal with this");
133         }
134     }
135
136     public void processRequest(Node node, JsonNode requestJson) {
137         JsonRpc10Request request = new JsonRpc10Request(requestJson.get("id").asText());
138         request.setMethod(requestJson.get("method").asText());
139         logger.debug("Request : {} {}", requestJson.get("method"), requestJson.get("params"));
140         OVSDB.Callback callback = requestCallbacks.get(node);
141         if (callback != null) {
142             Method[] methods = callback.getClass().getDeclaredMethods();
143             for (Method m : methods) {
144                 if (m.getName().equals(request.getMethod())) {
145                     Class<?>[] parameters = m.getParameterTypes();
146                     JsonNode params = requestJson.get("params");
147                     Object param = objectMapper.convertValue(params, parameters[1]);
148                     try {
149                         m.invoke(callback, node, param);
150                     } catch (Exception e) {
151                         e.printStackTrace();
152                     }
153                     return;
154                 }
155             }
156         }
157
158         // Echo dont need any special processing. hence handling it internally.
159
160         if (request.getMethod().equals("echo")) {
161             JsonRpc10Response response = new JsonRpc10Response(request.getId());
162             response.setError(null);
163             try {
164                 String s = objectMapper.writeValueAsString(response);
165                 nettyChannel.writeAndFlush(s);
166             } catch (JsonProcessingException e) {
167                 e.printStackTrace();
168             }
169         }
170
171         logger.error("No handler for Request : {} on {}",requestJson.toString(), node);
172     }
173
174     public Map<String, CallContext> getMethodContext() {
175         return methodContext;
176     }
177 }