package org.nutz.mvc.impl; import java.io.PrintWriter; import java.io.Reader; import java.io.StringWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.nutz.http.Request; import org.nutz.http.Sender; import org.nutz.http.Request.METHOD; import org.nutz.http.Response; import org.nutz.json.Json; import org.nutz.lang.Each; import org.nutz.lang.Invoking; import org.nutz.lang.Lang; import org.nutz.lang.Mirror; import org.nutz.lang.Strings; import org.nutz.lang.random.R; import org.nutz.lang.util.NutMap; /** * JSON-RPC 简单封装 * <p/> * <code>@At<p/>@Ok("json")<p/>public NutMap jsonrpc(Reader r){<p/>return JsonRPC.invoke(this);<p/>}</code> * * @author wendal * @since 1.r.56 * */ @SuppressWarnings("unchecked") public class JsonRPC { public static final int ParseError = -32700; public static final int InvalidRequest = -32600; public static final int MethodNotFound = -32601; public static final int InvalidParams = -32602; public static final int InternalError = -32603; public static final int ServerError = -32000; public static final String Version = "2.0"; /** * 服务端 */ public static NutMap invoke(final Object obj, Reader r) { final NutMap resp = new NutMap(); resp.setv("jsonrpc", Version); Object req; try { req = Json.fromJson(r); } catch (Exception e) { return resp.setv("error", error(ParseError, "Parse error", E(e))); } if (req == null) { return resp.setv("error", error(InvalidRequest, "Invalid Request", "json is null")); } if (req instanceof Iterable) {// rpc批量调用 final List<NutMap> results = new ArrayList<NutMap>(); Lang.each(req, new Each<Object>() { public void invoke(int index, Object ele, int length) { if (ele instanceof Map) { results.add(JsonRPC.invoke(obj, new NutMap((Map<String, Object>) ele))); } else { NutMap _resp = new NutMap(); _resp.setv("jsonrpc", Version).setv("error", error(InvalidRequest, "Invalid Request", "not map or list")); results.add(_resp); } } }); return resp.setv("result", results); } else if (req instanceof Map) { // 单一调用 return invoke(obj, new NutMap((Map<String, Object>) req)); } else { // 传的是什么鸟,拒绝 return resp.setv("error", error(InvalidRequest, "Invalid Request", "not map or list")); } } public static NutMap invoke(Object obj, NutMap req) { NutMap resp = new NutMap(); String version = req.getString("jsonrpc", Version); String id = req.getString("id"); resp.setv("id", id); resp.setv("jsonrpc", version); String method = req.getString("method"); if (Strings.isBlank(method)) { return resp.setv("error", error(InvalidRequest, "Invalid Request", "method name is blank")); } List<Object> params = req.getList("params", Object.class, Collections.EMPTY_LIST); Invoking ing; try { ing = Mirror.me(obj).getInvoking(method, params.toArray()); } catch (Exception e) { return resp.setv("error", error(MethodNotFound, "Method not found", E(e))); } try { return resp.setv("result", ing.invoke(obj)); } catch (Exception e) { return resp.setv("error", error(ServerError, e.getMessage(), E(e))); } catch (Throwable e) { StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); e.printStackTrace(writer); return resp.setv("error", error(InternalError, e.getMessage(), E(e))); } } protected static NutMap error(int code, String message, Object data) { return new NutMap().setv("code", code).setv("message", message).setv("data", data); } protected static String E(Throwable e) { StringWriter sw = new StringWriter(); PrintWriter writer = new PrintWriter(sw); e.printStackTrace(writer); return sw.toString(); } /** * 客户端. 用于生成一个代理接口的实例,透明访问json-rpc服务 * @param klass 需要代理的接口 * @param endpoint jsonrpc URL入口 * @param namespace 命名空间,非json-rpc标准,扩展用,不需要就传null * @param timeout 超时设置,若永不超时,设置为-1 * @return 代理实例 */ public static <T> T mapper(Class<T> klass, final String endpoint, final String namespace, final int timeout) { return (T)Proxy.newProxyInstance(klass.getClassLoader(), new Class<?>[]{klass}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { NutMap jreq = new NutMap(); jreq.setv("jsonrpc", "2.0").setv("id", R.UU32()).setv("method", method.getName()); if (!Strings.isBlank(namespace)) { jreq.put("namespace", namespace); } jreq.setv("params", args); Request req = Request.create(endpoint, METHOD.POST); req.setData(Json.toJson(jreq)); Response resp = Sender.create(req).setTimeout(timeout).send(); if (resp.isOK()) { if (method.getReturnType() == Void.class) return null; return Json.fromJson(method.getGenericReturnType(), resp.getReader()); } throw new RuntimeException("resp code="+resp.getStatus()); } }); } }