Java tutorial
/* * Copyright (C) 2011 ritwik.net * * 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 com.torben.androidchat.JSONRPC.server; import java.io.PrintWriter; import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import android.util.Log; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.torben.androidchat.JSONRPC.client.HttpJsonRpcClientTransport; import com.torben.androidchat.JSONRPC.commons.GsonTypeChecker; import com.torben.androidchat.JSONRPC.commons.JsonRpcErrorCodes; import com.torben.androidchat.JSONRPC.commons.JsonRpcException; import com.torben.androidchat.JSONRPC.commons.JsonRpcRemoteException; import com.torben.androidchat.JSONRPC.commons.RpcIntroSpection; import com.torben.androidchat.JSONRPC.commons.TypeChecker; public final class JsonRpcExecutor implements RpcIntroSpection { private static final Logger LOG = LoggerFactory.getLogger(JsonRpcExecutor.class); private static final Pattern METHOD_PATTERN = Pattern .compile("([_a-zA-Z][_a-zA-Z0-9]*)\\.([_a-zA-Z][_a-zA-Z0-9]*)"); private final Map<String, HandleEntry<?>> handlers; private final TypeChecker typeChecker; private volatile boolean locked; private final Gson gson; public JsonRpcExecutor() { this(new GsonTypeChecker(), new Gson()); } public JsonRpcExecutor(Gson gson) { this(new GsonTypeChecker(), gson); } public JsonRpcExecutor(TypeChecker typeChecker) { this(typeChecker, new Gson()); } @SuppressWarnings("unchecked") public JsonRpcExecutor(TypeChecker typeChecker, Gson gson) { this.typeChecker = typeChecker; this.gson = gson; this.handlers = new HashMap<String, HandleEntry<?>>(); addHandler("system", this, RpcIntroSpection.class); } public boolean isLocked() { return locked; } public <T> void addHandler(String name, T handler, Class<T>... classes) { if (locked) { throw new JsonRpcException("executor has been locked, can't add more handlers"); } synchronized (handlers) { HandleEntry<T> handleEntry = new HandleEntry<T>(typeChecker, handler, classes); if (this.handlers.containsKey(name)) { throw new IllegalArgumentException("handler already exists"); } this.handlers.put(name, handleEntry); } } public void execute(HttpJsonRpcClientTransport transport) { if (!locked) { synchronized (handlers) { locked = true; } LOG.info("locking executor to avoid modification"); } String methodName = null; JsonArray params = null; JsonObject resp = new JsonObject(); resp.addProperty("jsonrpc", "2.0"); String errorMessage = null; Integer errorCode = null; String errorData = null; JsonObject req = null; try { String requestData = transport.threadedCall(null); if (requestData == null) { Log.v("EXECUTOR", "no Data"); return; } Log.v("EXECUTOR", requestData); JsonParser parser = new JsonParser(); req = (JsonObject) parser.parse(new StringReader(requestData)); } catch (Throwable t) { errorCode = JsonRpcErrorCodes.PARSE_ERROR_CODE; errorMessage = "unable to parse json-rpc request"; errorData = getStackTrace(t); Log.v("EXECUTOR", "Somethign went wrong parsing"); LOG.warn(errorMessage, t); sendError(transport, resp, errorCode, errorMessage, errorData); return; } try { assert req != null; resp.add("id", req.get("id")); methodName = req.getAsJsonPrimitive("method").getAsString(); Log.v("EXECUTOR", "metohdname = " + methodName); params = (JsonArray) req.get("params"); if (params == null) { params = new JsonArray(); } } catch (Throwable t) { errorCode = JsonRpcErrorCodes.INVALID_REQUEST_ERROR_CODE; errorMessage = "unable to read request"; errorData = getStackTrace(t); LOG.warn(errorMessage, t); sendError(transport, resp, errorCode, errorMessage, errorData); return; } try { JsonElement result = executeMethod(methodName, params); resp.add("result", result); } catch (Throwable t) { LOG.warn("exception occured while executing : " + methodName, t); if (t instanceof JsonRpcRemoteException) { sendError(transport, resp, (JsonRpcRemoteException) t); return; } errorCode = JsonRpcErrorCodes.getServerError(1); errorMessage = t.getMessage(); errorData = getStackTrace(t); sendError(transport, resp, errorCode, errorMessage, errorData); return; } try { String responseData = resp.toString(); transport.threadedCall(responseData); } catch (Exception e) { LOG.warn("unable to write response : " + resp, e); } } private void sendError(HttpJsonRpcClientTransport transport, JsonObject resp, JsonRpcRemoteException e) { sendError(transport, resp, e.getCode(), e.getMessage(), e.getData()); } private void sendError(HttpJsonRpcClientTransport transport, JsonObject resp, Integer code, String message, String data) { JsonObject error = new JsonObject(); if (code != null) { error.addProperty("code", code); } if (message != null) { error.addProperty("message", message); } if (data != null) { error.addProperty("data", data); } resp.add("error", error); resp.remove("result"); String responseData = resp.toString(); LOG.debug("JSON-RPC error << {}", responseData); try { transport.threadedCall(responseData); } catch (Exception e) { LOG.error("unable to write error response : " + responseData, e); } } private String getStackTrace(Throwable t) { StringWriter str = new StringWriter(); PrintWriter w = new PrintWriter(str); t.printStackTrace(w); w.close(); return str.toString(); } private JsonElement executeMethod(String methodName, JsonArray params) throws Throwable { Log.v("EXECUTOR", "try to execute Method: " + methodName); try { Matcher mat = METHOD_PATTERN.matcher(methodName); if (!mat.find()) { throw new JsonRpcRemoteException(JsonRpcErrorCodes.INVALID_REQUEST_ERROR_CODE, "invalid method name", null); } String handleName = mat.group(1); methodName = mat.group(2); HandleEntry<?> handleEntry = handlers.get(handleName); if (handleEntry == null) { throw new JsonRpcRemoteException(JsonRpcErrorCodes.METHOD_NOT_FOUND_ERROR_CODE, "no such method exists", null); } Method executableMethod = null; for (Method m : handleEntry.getMethods()) { if (!m.getName().equals(methodName)) { continue; } if (canExecute(m, params)) { executableMethod = m; break; } } if (executableMethod == null) { throw new JsonRpcRemoteException(JsonRpcErrorCodes.METHOD_NOT_FOUND_ERROR_CODE, "no such method exists", null); } Object result = executableMethod.invoke(handleEntry.getHandler(), getParameters(executableMethod, params)); return gson.toJsonTree(result); } catch (Throwable t) { if (t instanceof InvocationTargetException) { t = ((InvocationTargetException) t).getTargetException(); } if (t instanceof JsonRpcRemoteException) { throw (JsonRpcRemoteException) t; } throw new JsonRpcRemoteException(JsonRpcErrorCodes.getServerError(0), t.getMessage(), getStackTrace(t)); } } public boolean canExecute(Method method, JsonArray params) { if (method.getParameterTypes().length != params.size()) { return false; } return true; } public Object[] getParameters(Method method, JsonArray params) { List<Object> list = new ArrayList<Object>(); Class<?>[] types = method.getParameterTypes(); for (int i = 0; i < types.length; i++) { JsonElement p = params.get(i); Object o = gson.fromJson(p.toString(), types[i]); list.add(o); } return list.toArray(); } public String[] listMethods() { Set<String> methods = new TreeSet<String>(); for (String name : this.handlers.keySet()) { HandleEntry<?> handleEntry = this.handlers.get(name); for (String method : handleEntry.getSignatures().keySet()) { methods.add(name + "." + method); } } String[] arr = new String[methods.size()]; return methods.toArray(arr); } public String[] methodSignature(String method) { if (method == null) { throw new NullPointerException("method"); } Matcher mat = METHOD_PATTERN.matcher(method); if (!mat.find()) { throw new IllegalArgumentException("invalid method name"); } String handleName = mat.group(1); String methodName = mat.group(2); Set<String> signatures = new TreeSet<String>(); HandleEntry<?> handleEntry = handlers.get(handleName); if (handleEntry == null) { throw new IllegalArgumentException("no such method exists"); } for (Method m : handleEntry.getMethods()) { if (!m.getName().equals(methodName)) { continue; } String[] sign = handleEntry.getSignatures().get(m.getName()); StringBuffer buff = new StringBuffer(sign[0]); for (int i = 1; i < sign.length; i++) { buff.append(",").append(sign[i]); } signatures.add(buff.toString()); } if (signatures.size() == 0) { throw new IllegalArgumentException("no such method exists"); } String[] arr = new String[signatures.size()]; return signatures.toArray(arr); } }