8357342b4fb5eb4d657a82514aa05593b9e122e9
[ovsdb.git] / ovsdb / src / main / java / org / opendaylight / ovsdb / lib / jsonrpc / JsonRpcEndpoint.java
1 /*
2  * Copyright (C) 2013 EBay Software Foundation
3  *
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
7  *
8  * Authors : Ashwin Raveendran, Madhu Venugopal
9  */
10 package org.opendaylight.ovsdb.lib.jsonrpc;
11
12 import io.netty.channel.Channel;
13
14 import java.lang.reflect.InvocationHandler;
15 import java.lang.reflect.InvocationTargetException;
16 import java.lang.reflect.Method;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.UUID;
20
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;
25
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;
36
37 public class JsonRpcEndpoint {
38
39     protected static final Logger logger = LoggerFactory.getLogger(JsonRpcEndpoint.class);
40
41     public class CallContext {
42         Method method;
43         JsonRpc10Request request;
44         SettableFuture<Object> future;
45
46         public CallContext(JsonRpc10Request request, Method method, SettableFuture<Object> future) {
47             this.method = method;
48             this.request = request;
49             this.future = future;
50         }
51
52         public Method getMethod() {
53             return method;
54         }
55
56         public JsonRpc10Request getRequest() {
57             return request;
58         }
59
60         public SettableFuture<Object> getFuture() {
61             return future;
62         }
63     }
64
65     ObjectMapper objectMapper;
66     Channel nettyChannel;
67     Map<String, CallContext> methodContext = Maps.newHashMap();
68     Map<Node, OvsdbRPC.Callback> requestCallbacks = Maps.newHashMap();
69
70     public JsonRpcEndpoint(ObjectMapper objectMapper, Channel channel) {
71         this.objectMapper = objectMapper;
72         this.nettyChannel = channel;
73     }
74
75     public <T> T getClient(final Node node, Class<T> klazz) {
76
77         return Reflection.newProxy(klazz, new InvocationHandler() {
78             @Override
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]);
83                     return true;
84                 }
85
86                 JsonRpc10Request request = new JsonRpc10Request(UUID.randomUUID().toString());
87                 request.setMethod(method.getName());
88
89                 if (args != null && args.length != 0) {
90                     List<Object> params = null;
91
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];
97                         }
98
99                         if (params == null) {
100                             throw new RuntimeException("do not understand this argument yet");
101                         }
102                         request.setParams(params);
103                     }
104                 }
105
106                 String s = objectMapper.writeValueAsString(request);
107                 logger.trace("{}", s);
108
109                 SettableFuture<Object> sf = SettableFuture.create();
110                 methodContext.put(request.getId(), new CallContext(request, method, sf));
111
112                 nettyChannel.writeAndFlush(s);
113
114                 return sf;
115             }
116         }
117         );
118     }
119
120     public void processResult(JsonNode response) throws NoSuchMethodException {
121
122         logger.trace("Response : {}", response.toString());
123         CallContext returnCtxt = methodContext.get(response.get("id").asText());
124         if (returnCtxt == null) return;
125
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());
131
132             JsonNode result = response.get("result");
133             logger.trace("Result : {}", result.toString());
134
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());
139             }
140
141             returnCtxt.getFuture().set(result1);
142
143         } else {
144             throw new RuntimeException("donno how to deal with this");
145         }
146     }
147
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]);
160                     try {
161                         m.invoke(callback, node, param);
162                     } catch (IllegalAccessException | InvocationTargetException e) {
163                         logger.error("Unable to invoke callback " + m.getName(), e);
164                     }
165                     return;
166                 }
167             }
168         }
169
170         // Echo dont need any special processing. hence handling it internally.
171
172         if (request.getMethod().equals("echo")) {
173             JsonRpc10Response response = new JsonRpc10Response(request.getId());
174             response.setError(null);
175             String s = null;
176             try {
177                 s = objectMapper.writeValueAsString(response);
178                 nettyChannel.writeAndFlush(s);
179             } catch (JsonProcessingException e) {
180                 logger.error("Exception while processing JSON string " + s, e );
181             }
182             return;
183         }
184
185         logger.error("No handler for Request : {} on {}",requestJson.toString(), node);
186     }
187
188     public Map<String, CallContext> getMethodContext() {
189         return methodContext;
190     }
191 }