}
protected void doRPC(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
JSON json = null;
boolean isBatch = false;
List<Object> responseList = new ArrayList<Object>();
try {
ExternalContext.start(getServletConfig(), getServletContext(), request, response);
container.start(request, response);
String uri = (request.getContextPath().equals("/")) ?
request.getRequestURI() :
request.getRequestURI().substring(request.getContextPath().length());
Route route = null;
for (RouteMapping m : config.mappings.values()) {
if ((route = m.matches(request, uri)) != null) {
container.debug("Route found: " + request.getMethod() + " " + uri);
break;
}
}
if (route == null || !isJSONType(request.getContentType())) {
response.sendError(SC_NOT_FOUND, "Not Found");
return;
}
json = container.createJSON(request.getLocale());
// request processing
List<Object> requestList = new ArrayList<Object>(0);
Object value = json.parse(request.getReader());
if (value instanceof List<?> && !((List<?>)value).isEmpty()) {
requestList = cast(value);
isBatch = true;
} else if (value instanceof Map<?,?> && !((Map<?,?>)value).isEmpty()) {
requestList = Arrays.asList(value);
} else {
throw new IllegalArgumentException("Request is empty.");
}
for (int i = 0; i < requestList.size(); i++) {
Map<?,?> req = (Map<?,?>)requestList.get(i);
String rjsonrpc = null;
String rmethod = null;
Object rparams = null;
Object rid = null;
Object result = null;
Map<String, Object> error = null;
try {
if (req.get("jsonrpc") == null || "2.0".equals(req.get("jsonrpc"))) {
rjsonrpc = (String)req.get("jsonrpc");
} else {
throw new IllegalArgumentException("jsonrpc is unrecognized version: " + req.get("jsonrpc"));
}
if (req.get("method") instanceof String) {
rmethod = (String)req.get("method");
if (rjsonrpc != null && rmethod.startsWith("rpc.")) {
container.warn("Method names that begin with 'rpc.' are reserved for system extensions.");
}
} else {
throw new IllegalArgumentException("method must " + ((req.get("method") == null) ? "not be null." : "be string."));
}
if (req.get("params") instanceof List<?> || (rjsonrpc != null && req.get("params") instanceof Map<?, ?>)) {
rparams = req.get("params");
} else if (rjsonrpc != null && req.get("params") == null) {
rparams = new ArrayList<Object>(0);
} else {
throw new IllegalArgumentException("params must be array" + ((rjsonrpc != null) ? " or object." : "."));
}
if (rjsonrpc == null || (req.get("id") == null || req.get("id") instanceof String || req.get("id") instanceof Number)) {
rid = req.get("id");
} else {
throw new IllegalArgumentException("id must be string, number or null.");
}
String subcompName = null;
String methodName = rmethod;
if (route.getParameter("class") == null) {
int sep = rmethod.lastIndexOf('.');
subcompName = (sep != -1) ? rmethod.substring(0, sep) : null;
methodName = (sep != -1) ? rmethod.substring(sep+1) : rmethod;
}
Object component = container.getComponent(route.getComponentClass(container, subcompName));
if (component == null) {
throw new NoSuchMethodException("Method not found: " + rmethod);
}
List<?> params = (rparams instanceof List<?>) ? (List<?>)rparams : Arrays.asList(rparams);
Method method = container.getMethod(component, methodName, params);
if (method == null) {
throw new NoSuchMethodException("Method not found: " + rmethod);
}
json.setContext(component);
result = container.execute(json, component, method, params);
} catch (Exception e) {
error = new LinkedHashMap<String, Object>();
if (e instanceof IllegalArgumentException) {
container.debug("Invalid Request.", e);
error.put("code", -32600);
error.put("message", "Invalid Request.");
} else if (e instanceof ClassNotFoundException) {
container.debug("Class Not Found.", e);
error.put("code", -32601);
error.put("message", "Method not found.");
} else if (e instanceof NoSuchMethodException) {
container.debug("Method Not Found.", e);
error.put("code", -32601);
error.put("message", "Method not found.");
} else if (e instanceof JSONException) {
container.debug("Invalid params.", e);
error.put("code", -32602);
error.put("message", "Invalid params.");
} else if (e instanceof InvocationTargetException) {
Throwable cause = e.getCause();
container.debug("Fails to invoke method.", cause);
if (cause instanceof Error) {
throw (Error)cause;
} else if (cause instanceof IllegalStateException || cause instanceof UnsupportedOperationException) {
error.put("code", -32601);
error.put("message", "Method not found.");
} else if (cause instanceof IllegalArgumentException) {
error.put("code", -32602);
error.put("message", "Invalid params.");
} else {
Integer errorCode = null;
for (Map.Entry<String, Integer> entry : config.errors.entrySet()) {
Class<?> cls = ClassUtil.findClass(entry.getKey());
if (cls.isAssignableFrom(cause.getClass()) && entry.getValue() != null) {
errorCode = entry.getValue();
break;
}
}
if (errorCode != null) {
error.put("code", errorCode);
error.put("message", cause.getClass().getSimpleName() + ": " + cause.getMessage());
error.put("data", cause);
} else {
container.error("Internal error occurred.", cause);
error.put("code", -32603);
error.put("message", "Internal error.");
}
}
} else {
container.error("Internal error occurred.", e);
error.put("code", -32603);
error.put("message", "Internal error.");
}
}
// it's notification when id was null
if (rmethod != null && (rjsonrpc == null && rid == null) || (rjsonrpc != null && req != null && !req.containsKey("id"))) {
continue;
}
Map<String, Object> responseData = new LinkedHashMap<String, Object>();
if (rjsonrpc != null) responseData.put("jsonrpc", rjsonrpc);
if (rjsonrpc == null || result != null) responseData.put("result", result);
if (rjsonrpc == null || error != null) responseData.put("error", error);
responseData.put("id", rid);
responseList.add(responseData);
}
} catch (Exception e) {
Map<String, Object> error = new LinkedHashMap<String, Object>();
if (e instanceof JSONException) {
container.debug("Fails to parse JSON.", e);
error.put("code", -32700);
error.put("message", "Parse error.");
error.put("data", e);
} else {
container.debug("Invalid Request.", e);
error.put("code", -32600);
error.put("message", "Invalid Request.");
}
Map<String, Object> responseData = new LinkedHashMap<String, Object>();
responseData.put("jsonrpc", "2.0");
responseData.put("error", error);
responseData.put("id", null);
responseList.add(responseData);
} finally {
try {
container.end(request, response);
} finally {
ExternalContext.end();
}
}
if (response.isCommitted()) return;
// it's notification when id was null for all requests.
if (responseList.isEmpty()) {
response.setStatus(SC_ACCEPTED);
return;
}
// response processing
response.setContentType("application/json");
Writer writer = response.getWriter();
Object target = (isBatch) ? responseList : responseList.get(0);
json.setContext(target);
json.setPrettyPrint(container.isDebugMode());
json.format(target, writer);
}