📄 jsonrpcbridge.java
字号:
/* * JSON-RPC-Java - a JSON-RPC to Java Bridge with dynamic invocation * * $Id: JSONRPCBridge.java,v 1.37.2.5 2006/03/27 05:39:21 mclark Exp $ * * Copyright Metaparadigm Pte. Ltd. 2004. * Michael Clark <michael@metaparadigm.com> * * 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.metaparadigm.jsonrpc;import java.util.Map;import java.util.HashSet;import java.util.HashMap;import java.util.StringTokenizer;import java.util.ArrayList;import java.util.Iterator;import java.util.NoSuchElementException;import java.util.logging.Logger;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.lang.reflect.InvocationTargetException;import java.io.Serializable;import org.json.JSONObject;import org.json.JSONArray;/** * This class implements a bridge that unmarshalls JSON objects in JSON-RPC * request format, invokes a method on the exported object, and then marshalls * the resulting Java objects to JSON objects in JSON-RPC result format. * <p /> * An instance of the JSONRPCBridge object is automatically placed in the * HttpSession object registered under the attribute "JSONRPCBridge" by * the JSONRPCServlet. * <p /> * The bridge is implemented as session specific to improve the security * of applications by allowing exporting of object methods only to * specific HttpSessions. * <p /> * To use the bridge to allow calling of Java methods you can easily * access the HttpSession specific bridge in JSP using the usebean tag. eg. * <p /> * <code><jsp:useBean id="JSONRPCBridge" scope="session" * class="com.metaparadigm.jsonrpc.JSONRPCBridge" /></code> * <p /> * Then export the object you wish to call methods on. eg. * <p /> * <code>JSONRPCBridge.registerObject("test", testObject);</code> * <p /> * This will make available all methods of the object as * <code>test.<methodnames></code> to JSON-RPC clients. This method * should generally be performed after an authentication check to only * export specific objects to clients that are authorised to use them. * <p /> * There exists a global bridge singleton object that will allow exporting * objects to all HTTP clients. This can be used for registering factory * classes although care must be taken with authentication as these objects * will be accessible to all clients. */public class JSONRPCBridge implements Serializable{ private final static Logger log = Logger.getLogger(JSONRPCBridge.class.getName()); private boolean debug = false; public void setDebug(boolean debug) { this.debug = debug; ser.setDebug(debug); } protected boolean isDebug() { return debug; } private static JSONRPCBridge globalBridge = new JSONRPCBridge(); /** * This method retreieves the global bridge singleton. * * It should be used with care as objects should generally be * registered within session specific bridges for security reasons. * * @return returns the global bridge object. */ public static JSONRPCBridge getGlobalBridge() { return globalBridge; } // key clazz, val ClassData private static HashMap classCache = new HashMap(); // key argClazz, val HashSet<LocalArgResolverData> private static HashMap localArgResolverMap = new HashMap(); // JSONSerializer instance for this bridge private JSONSerializer ser = new JSONSerializer(); // key CallbackData private HashSet callbackSet = new HashSet(); // key "session exported class name", val Class private HashMap classMap = new HashMap(); // key "session exported instance name", val ObjectInstance private HashMap objectMap = new HashMap(); // key Integer hashcode, object held as reference protected HashMap referenceMap = new HashMap(); // ReferenceSerializer if enabled protected Serializer referenceSer = null; // key clazz, classes that should be returned as References protected HashSet referenceSet = new HashSet(); // key clazz, classes that should be returned as CallableReferences protected HashSet callableReferenceSet = new HashSet(); private static HttpServletRequestArgResolver requestResolver = new HttpServletRequestArgResolver(); private static HttpSessionArgResolver sessionResolver = new HttpSessionArgResolver(); private static JSONRPCBridgeServletArgResolver bridgeResolver = new JSONRPCBridgeServletArgResolver(); static { registerLocalArgResolver(javax.servlet.http.HttpServletRequest.class, javax.servlet.http.HttpServletRequest.class, requestResolver); registerLocalArgResolver(javax.servlet.http.HttpSession.class, javax.servlet.http.HttpServletRequest.class, sessionResolver); registerLocalArgResolver(JSONRPCBridge.class, javax.servlet.http.HttpServletRequest.class, bridgeResolver); } public JSONRPCBridge() { this(true); } public JSONRPCBridge(boolean useDefaultSerializers) { if (useDefaultSerializers) { try { ser.registerDefaultSerializers(); } catch (Exception e) { e.printStackTrace(); } } } protected static class ClassData { private Class clazz; // key methodKey, val (Method || Method[]) private HashMap methodMap; // key methodKey, val (Method || Method[]) private HashMap staticMethodMap; } protected static class CallbackData implements Serializable { private InvocationCallback cb; private Class contextInterface; public CallbackData(InvocationCallback cb, Class contextInterface) { this.cb = cb; this.contextInterface = contextInterface; } public boolean understands(Object context) { return contextInterface.isAssignableFrom(context.getClass()); } public int hashCode() { return cb.hashCode() * contextInterface.hashCode(); } public boolean equals(Object o) { CallbackData cmp = (CallbackData)o; return (cb.equals(cmp.cb) && contextInterface.equals(cmp.contextInterface)); } } protected static class LocalArgResolverData { private LocalArgResolver argResolver; private Class argClazz; private Class contextInterface; public LocalArgResolverData(LocalArgResolver argResolver, Class argClazz, Class contextInterface) { this.argResolver = argResolver; this.argClazz = argClazz; this.contextInterface = contextInterface; } public boolean understands(Object context) { return contextInterface.isAssignableFrom(context.getClass()); } public int hashCode() { return argResolver.hashCode() * argClazz.hashCode() * contextInterface.hashCode(); } public boolean equals(Object o) { LocalArgResolverData cmp = (LocalArgResolverData)o; return (argResolver.equals(cmp.argResolver) && argClazz.equals(cmp.argClazz) && contextInterface.equals(cmp.contextInterface)); } } protected static class ObjectInstance implements Serializable { private Object o; public ObjectInstance(Object o, Class clazz) { if (! clazz.isInstance(o)) { throw new ClassCastException("Attempt to register jsonrpc object with invalid class."); } this.o = o; this.clazz = clazz; } private Class clazz; public ObjectInstance(Object o) { this.o = o; clazz = o.getClass(); } public ClassData classData() { return getClassData(clazz); } } protected static class MethodKey { private String methodName; private int numArgs; public MethodKey(String methodName, int numArgs) { this.methodName = methodName; this.numArgs = numArgs; } public int hashCode() { return methodName.hashCode() * numArgs; } public boolean equals(Object o) { if(!(o instanceof MethodKey)) return false; return (methodName.equals(((MethodKey)o).methodName) && numArgs == ((MethodKey)o).numArgs); } } protected static class MethodCandidate { private Method method; private ObjectMatch match[]; public MethodCandidate(Method method) { this.method = method; match = new ObjectMatch[method.getParameterTypes().length]; } public ObjectMatch getMatch() { int mismatch = -1; for(int i=0; i < match.length; i++) { mismatch = Math.max(mismatch, match[i].mismatch); } if(mismatch == -1) return ObjectMatch.OKAY; else return new ObjectMatch(mismatch); } } private static ClassData analyzeClass(Class clazz) { log.info("analyzing " + clazz.getName()); Method methods[] = clazz.getMethods(); ClassData cd = new ClassData(); cd.clazz = clazz; // Create temporary method map HashMap staticMethodMap = new HashMap(); HashMap methodMap = new HashMap(); for(int i=0; i < methods.length; i++) { Method method = methods[i]; if(method.getDeclaringClass() == Object.class) continue; int mod = methods[i].getModifiers(); if(!Modifier.isPublic(mod)) continue; Class param[] = method.getParameterTypes(); // don't count locally resolved args int argCount=0; synchronized(localArgResolverMap) { for(int n=0; n<param.length; n++) { HashSet resolvers = (HashSet) localArgResolverMap.get(param[n]); if(resolvers != null) continue; argCount++; } } MethodKey mk = new MethodKey(method.getName(), argCount); ArrayList marr = (ArrayList)methodMap.get(mk); if(marr == null) { marr = new ArrayList(); methodMap.put(mk, marr); } marr.add(method); if(Modifier.isStatic(mod)) { marr = (ArrayList)staticMethodMap.get(mk); if(marr == null) { marr = new ArrayList(); staticMethodMap.put(mk, marr); } marr.add(method); } } cd.methodMap = new HashMap(); cd.staticMethodMap = new HashMap(); // Convert ArrayLists to arrays Iterator i = methodMap.entrySet().iterator(); while(i.hasNext()) { Map.Entry entry = (Map.Entry)i.next(); MethodKey mk = (MethodKey)entry.getKey(); ArrayList marr = (ArrayList)entry.getValue(); if(marr.size() == 1) { cd.methodMap.put(mk, marr.get(0)); } else { cd.methodMap.put(mk, marr.toArray(new Method[0])); } } i = staticMethodMap.entrySet().iterator(); while(i.hasNext()) { Map.Entry entry = (Map.Entry)i.next(); MethodKey mk = (MethodKey)entry.getKey(); ArrayList marr = (ArrayList)entry.getValue(); if(marr.size() == 1) { cd.staticMethodMap.put(mk, marr.get(0)); } else { cd.staticMethodMap.put(mk, marr.toArray(new Method[0])); } } return cd; } private static ClassData getClassData(Class clazz) { ClassData cd; synchronized (classCache) { cd = (ClassData)classCache.get(clazz); if(cd == null) { cd = analyzeClass(clazz); classCache.put(clazz, cd); } } return cd; } public void registerSerializer(Serializer s) throws Exception { ser.registerSerializer(s); } public void enableReferences() throws Exception { if(referenceSer == null) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -