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