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