Package org.json.rpc.server

Source Code of org.json.rpc.server.JsonRpcExecutor

/*
* 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 org.json.rpc.server;

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 org.json.rpc.commons.GsonTypeChecker;
import org.json.rpc.commons.JsonRpcErrorCodes;
import org.json.rpc.commons.JsonRpcException;
import org.json.rpc.commons.JsonRpcRemoteException;
import org.json.rpc.commons.RpcIntroSpection;
import org.json.rpc.commons.TypeChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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;


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;

    public JsonRpcExecutor() {
        this(new GsonTypeChecker());
    }

    @SuppressWarnings("unchecked")
    public JsonRpcExecutor(TypeChecker typeChecker) {
        this.typeChecker = typeChecker;
        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(JsonRpcServerTransport 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.readRequest();
            LOG.debug("JSON-RPC >>  {}", 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.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();
            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();
            LOG.debug("JSON-RPC result <<  {}", responseData);
            transport.writeResponse(responseData);
        } catch (Exception e) {
            LOG.warn("unable to write response : " + resp, e);
        }
    }

    private void sendError(JsonRpcServerTransport transport, JsonObject resp, JsonRpcRemoteException e) {
        sendError(transport, resp, e.getCode(), e.getMessage(), e.getData());
    }

    private void sendError(JsonRpcServerTransport 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.writeResponse(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 {
        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 new 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>();
        Gson gson = new Gson();
        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);
    }


}
TOP

Related Classes of org.json.rpc.server.JsonRpcExecutor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.