/*
* (C) Copyright 2016 Kurento (http://kurento.org/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.kurento.jsonrpc;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.kurento.jsonrpc.message.Request;
import org.kurento.jsonrpc.message.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.thoughtworks.paranamer.AnnotationParanamer;
import com.thoughtworks.paranamer.Paranamer;
public class JsonRpcAndJavaMethodManager {
private static final Logger log = LoggerFactory.getLogger(JsonRpcAndJavaMethodManager.class);
private static Gson gson = new GsonBuilder().disableHtmlEscaping().create();
private Paranamer paranamer = new AnnotationParanamer();
public void executeMethod(Method m, Object object, Transaction transaction,
Request<JsonObject> request) throws IOException {
try {
Response<JsonElement> response =
execJavaMethod(transaction.getSession(), object, m, transaction, request);
if (response != null) {
response.setId(request.getId());
transaction.sendResponseObject(response);
} else {
transaction.sendVoidResponse();
}
} catch (InvocationTargetException e) {
if (e.getCause() instanceof JsonRpcErrorException) {
JsonRpcErrorException ex = (JsonRpcErrorException) e.getCause();
transaction.sendError(ex.getError());
} else {
log.error(
"Exception executing request " + request + ": " + e.getCause().getLocalizedMessage(),
e.getCause());
transaction.sendError(e.getCause());
}
} catch (Exception e) {
log.error("Exception processing request " + request, e);
transaction.sendError(e);
}
}
private Response<JsonElement> execJavaMethod(Session session, Object object, Method m,
Transaction transaction, Request<JsonObject> request)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Object[] values = calculateParamValues(session, m, request);
Object result = m.invoke(object, values);
if (result == null) {
return null;
} else {
return new Response<>(null, gson.toJsonTree(result));
}
}
private Object[] calculateParamValues(Session session, Method m, Request<JsonObject> request) {
JsonObject params = request.getParams();
String[] parameterNames = paranamer.lookupParameterNames(m, true);
Type[] parameterTypes = m.getGenericParameterTypes();
Object[] values = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
values[i] = getValueFromParam(session, m, params, parameterNames[i], parameterTypes[i]);
}
if (log.isInfoEnabled()) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < parameterNames.length; i++) {
sb.append(parameterNames[i] + "(" + parameterTypes[i] + ")=" + values[i] + ",");
}
sb.append("]");
log.debug("Executing method {} with params {}", m.getName(), params);
}
return values;
}
private Object getValueFromParam(Session session, Method m, JsonObject params,
String parameterName, Type genericType) {
if (genericType instanceof Class) {
Class<?> type = (Class<?>) genericType;
if (Session.class.isAssignableFrom(type)) {
return session;
} else {
// TODO Allow more types
JsonElement jsonElement = params.get(parameterName);
if (jsonElement != null) {
return getAsJavaType(type, jsonElement);
} else {
// TODO Fail in this case
if (type == boolean.class) {
return false;
} else if (type == int.class) {
return 0;
}
}
}
} else {
if (genericType instanceof ParameterizedType) {
ParameterizedType genericMap = (ParameterizedType) genericType;
if (Map.class.isAssignableFrom((Class<?>) genericMap.getRawType())
&& (genericMap.getActualTypeArguments()[0] == String.class)
&& (genericMap.getActualTypeArguments()[1] == String.class)) {
Map<String, String> returnParams = new HashMap<String, String>();
for (Entry<String, JsonElement> param : params.entrySet()) {
String valueStr =
!param.getValue().isJsonNull() ? param.getValue().getAsString() : null;
returnParams.put(param.getKey(), valueStr);
}
return returnParams;
}
}
}
return null;
}
private Object getAsJavaType(Class<?> type, JsonElement jsonElement) {
if (jsonElement.isJsonNull()) {
return null;
} else if (type == String.class) {
return jsonElement.getClass().equals(JsonObject.class) ? jsonElement.toString()
: jsonElement.getAsString();
} else if (type == boolean.class) {
return jsonElement.getAsBoolean();
} else if (type.isEnum()) {
return gson.fromJson(jsonElement, type);
} else if (type == int.class) {
return jsonElement.getAsInt();
} else {
return null;
}
}
}