/* * (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; } } }