<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% // 注册对象 response.setDateHeader ("Expires", 0); // 我们注册的对象已经配置到web.xml中了 // 你也可以自己注册,代码如下: // JSONRPCBridge brg = (JSONRPCBridge)session.getAttribute(Content.RegSessionJSONRPCName); // // 如果是第一次就注册对象 // if(null == brg) // session.setAttribute(Content.RegSessionJSONRPCName, brg = new JSONRPCBridge().setSession(session)); // brg.registerObject("注册的名字(必须符合对象的命名规范)", 你要注册的对象); %><html> <head><meta http-equiv=content-type content="text/html; charset=UTF-8"> <title>JSON-RPC应用</title> <link rel="stylesheet" type="text/css" href="/jcore/resource/styles/styles.css"> </head> <body><br> <script type="text/javascript" language="JavaScript" src="/jcore/resource/javascript/Jcore.js"></script> <script type="text/javascript" language="JavaScript" src="/jcore/resource/javascript/Jweb.js"></script> <script type="text/javascript" language="JavaScript" src="/jcore/resource/javascript/Jdebug.js"></script> <script type="text/javascript" language="JavaScript"> <!-- // 功能描述:轻量级JSON-RPC AJAX框架,返回JSON-RPC client for JAVA的对象 // url 是json-rpc服务的Servlet,不传递时默认为"/jcore/JRPC" // 返回信息:JSON-RPC client for JAVA的对象 // 使用指南:var oRpc = JsonRpcClient(); // 应用范围:各种Web客户端 function JsonRpcClient(url) { url || (url = "/jcore/JRPC"); if(this == window) // 采用静态cache方式,防止使用的时候不经意的多次创建对象实例 return JsonRpcClient._cache || (JsonRpcClient._cache = new JsonRpcClient(url)); this['url'] = url; var _this = this, obj = {}; // 同步方式获取注册中的对象 AJAX({ url: url, bAsync:false, clbkFun:function() { try{eval("obj = " + arguments[0]);}catch(e) {alert(e.message + ":\n" + arguments[0])} } });
obj = obj.result; // JSON 调用java的绑定 var fnRpcCall = function() { var a = _A(arguments), // 如果第一个参数为函数就表明是使用者自己编写回调函数, // 则采用异步,否则采用同步,便于返回参数给调用者 bAsync = "function" == typeof (a[0] || ''), // 返回的对象 oRst = {}, params = _A(arguments), // 由于Array是引用型的对象,因此不能用a赋予它 toParams = function(arg) { var a = []; for(var i = 0; i < arg.length; i++) { var szTp = typeof arg[i]; if("number" == szTp || "boolean" == szTp) a.push(arg[i]); else a.push("\"" + (arg[i] || '').toString().replace(/([\r\n\t\b\f"])/gm, "\\$1") + "\""); } return "[" + a.join(",") + "]" };
// 如果第一个参数是回调函数,则去除它,不将它当参数传递到后台的Java处理 if(bAsync)params.shift(); // alert([this.id, this.methodName, szDefaultPath].join("\n")) AJAX({ "url": this.url, "bAsync": bAsync, bRawData: true, data: "{\"method\":\"" + this.methodName + "\",id:\"" + this.id + "\",\"params\":" + toParams(params) +"}", clbkFun: function() { try{ eval("var oTmp = " + arguments[0]); if("object" == typeof oTmp) { if(Array == (oTmp['constructor'] || '')) { oRst = []; for(var i = 0; i < oTmp.length; i++) { if("object" == typeof oTmp[i]) _this.fnMakeObj(oTmp[i], oRst[i] = {}); else oRst[i] = oTmp[i]; } } else _this.fnMakeObj(oTmp, oRst); } else oRst = oTmp; // 对自定义回调函数的调用 if(bAsync) try{a[0](oRst);}catch(e){} }catch(e){alert(e.message + ":\n" + arguments[0])} } }); if(!bAsync)return oRst; };
// 创建返回的对象 // o 为java中返回的对象 // oRstObj 为将返回的对象 this.fnMakeObj = function(o, oRstObj) { // 定义中间变量,以减少这样oRstObj[o.name][...]的访问,从而提高性能 // 其原理是使用了Object的引用传递思想,我们改变了oT对象, // 也就是改变了对象oRstObj[o.name] var oT = oRstObj; // 如果有名字,表示是顶级对象 if(o.name)oT = (oRstObj[o.name] = {}); for(var k in o) { if("methods" == k) { for(var i = 0; i < o[k].length; i++) { // 绑定要执行的方法名和对象id号 oT[o[k][i]] = fnRpcCall.bind ({url:_this.url,"methodName":o[k][i],"id":"" + o.id}); } delete o[k]; } // 深度处理 else if("object" == typeof o[k]) { // 如果不为他设置name,就无法产生级联对象 o[k]["name"] = k; _this.fnMakeObj(o[k], oT); } else oT[k] = o[k]; } }; for(var i = 0; i < obj.length; i++) this.fnMakeObj(obj[i], _this); }// 注意,JsonRpcClient()一般建议在一个应用中只调用一次, // 如果是在多窗口中的应用,可以在窗口之间共享对象, // 当然也可以创建JsonRpcClient()的实例 var oRpc = new JsonRpcClient(); // 将getMyList和getMyMap的调用设立中间变量 // 可以提高程序的运行性能,他们这样只执行一次 var lst = oRpc.myTest.getMyList(), oMap = oRpc.myTest.getMyMap(); function fnMyTest14() { // 效果如图14-2所示 alert("oRpc.myTest.getMyMap().get(\"myList\").get(0) = \n" + oMap.get("myList").get(0)); // 效果如图14-3所示 alert("oRpc.myTest.getMyMap().get(\"str\").join(\"\\n\") = \n" + oMap.get("str").join("\n")); // 效果如图14-4所示 alert("oRpc.myTest.szName = " + oRpc.myTest.szName); // 效果如图14-5所示 alert("oRpc.myTest.getMyList().get(1) = \n" + lst.get(1)); // 效果如图14-6所示 alert("oRpc.myTest.getMyList().get(0) = \n" + lst.get(0)); // 效果如图14-7所示 alert("oRpc.myTest.lst.get(0) = \n" + oRpc.myTest.lst.get(0)); // 我们这里操作注册对象myTest中的符合对象A的方法,效果如图14-8所示 alert(lst.get(2).Translate("今年天气真冷", "zh", "en")); } --> </script><br> <center> <button class="btn" onclick="fnMyTest14()">执行</button> </<center> </body></html> 注册Java对象提供给web端调用,接受请求并执行方法返回 结果的JSON形式的驱动设计,JSONRPCBridge.java: package jcore.jsonrpc.common; import java.io.Serializable; import java.lang.reflect.Method; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpSession; /*** * * @author 夏天 * */ public class JSONRPCBridge implements Serializable{ private static final long serialVersionUID = 1L; private HttpSession session = null; // 注册中的对象 private HashMap globalMap = new HashMap();
// 缓存顶级的被注册对象的JSON格式 private HashMap cache = new HashMap(); // 顶级被注册的名字 private HashMap topNms = new HashMap();
/*** * 返回所有注册全局的对象为JSON的字符串格式 * @return */ public String getRegObjsToString() { StringBuffer buf = new StringBuffer("{\"result\":["); int n = 0; Iterator oIt = topNms.entrySet().iterator(); while(oIt.hasNext()) { if(0 < n) buf.append(","); Map.Entry oKey = (Map.Entry)oIt.next();
// 为了提高性能,采用cache String szTmp = (String)cache.get(oKey.getKey()); if(null == szTmp) { // 只对顶级注册的进行获取, oKey.getValue()只是全局中的key szTmp = new ObjectToJSON (getObject(oKey.getValue().toString()), this).toJSON(oKey.getKey().toString()); // szTmp = szTmp.replaceFirst("\\{", "{name:\"" + oKey.getKey() + "\","); cache.put(oKey.getKey(), szTmp); // 再次调用,以便集群环境下能正常工作 session.setAttribute(Content.RegSessionJSONRPCName, this); } n++; buf.append(szTmp); } buf.append("]}"); return buf.toString(); }
/*** * 构造函数,构造后一定要setSession */ public JSONRPCBridge(){}
/*** * 构造的时候可以带上session对象 * @param session */ public JSONRPCBridge(HttpSession session) { this.session = session; }
/*** * 注册对象 * @param nHashCodeName 利用hashcode注册对象,防止同一实例注册多次 * @param o * @return this */ public JSONRPCBridge registerObject(int nHashCodeName, Object o) { String szKeyName = nHashCodeName + ""; if(null != session && null ! = szKeyName && 0 < szKeyName.trim().length()) { synchronized(globalMap) { if(null == globalMap.get(szKeyName)) { // 注册 globalMap.put(szKeyName, o); } } // 再次调用,以便集群环境下能正常工作 session.setAttribute(Content.RegSessionJSONRPCName, this); } return this; }
public JSONRPCBridge registerObject(String szKeyName, Object o) { if(null != session && null != szKeyName && 0 < szKeyName.trim().length()) { synchronized(topNms) { if(null == topNms.get(szKeyName)) topNms.put(szKeyName, o.hashCode() + ""); } return registerObject(o.hashCode(), o); } return this; }
/*** * 判断对象o是否是为不需要注册的“简单”对象 * @param o * @return */ private boolean isSimpleType(Object o) { if(null == o)return false; String szType = o.getClass().getName(); Pattern pa = Pattern.compile("^class \\[[ZBCISJFDL]"); Matcher m = pa.matcher(szType);
if(-1 < ",java.lang.String,java.util.Date,java.sql.Timestamp, java.lang.Boolean,java.lang.Character,java.lang.Short,java.lang. Integer,java.lang.Long,java.lang.Float,java.lang.Double,boolean, char,byte,short,int,long,float,double,".indexOf ("," + szType + ",") || m.find()) { // 清除对象使用关联关系,便于内存的有效利用 m = null; pa = null; szType = null; return true; } // 清除对象使用关联关系,便于内存的有效利用 m = null; pa = null; szType = null; return false; }
/*** * 执行JSON-RPC请求的方法,并返回JSON格式的结果 * @param szParm * @return */ public String ExecObjectMethod(String szParm) { try { JSONObject oJson = new JSONObject(szParm); String szName = oJson.getString("id"), szMeshod = oJson.getString("method"); JSONArray oParams = (JSONArray)oJson.get("params");
// 对Map和List的注册和获取做特殊处理,因为集合中的每个元素可能是不同的对象, // 因此注册名必须区分开来
Object o = getObject(szName); if(null != o) { Class c = o.getClass(); Method []m = c.getMethods();
for(int i = 0; i < m.length; i++) { if(szMeshod.equals(m[i].getName()) && oParams.length() == m[i].getParameterTypes().length) { try { // 构造参数 Object []aParam = new Object[oParams.length()]; for(int j = 0; j < aParam.length; j++) aParam[j] = oParams.get(j); oParams = null; Object oRst = m[i].invoke(o, aParam); aParam = null; if(null != oRst) { // 不是简单类型就注册他 if(!isSimpleType(oRst)) registerObject(oRst.hashCode(), oRst); String szOut = new ObjectToJSON(oRst, this).toJSON(null); // System.out.println(szOut); return szOut; } return "true"; // 不是简单类型就注册他 if(!isSimpleType(oRst)) registerObject(oRst.hashCode(), oRst); String szOut = new ObjectToJSON(oRst, this).toJSON(null); System.out.println(szOut); return szOut; } catch (Exception e) { e.printStackTrace(); } break; } } } } catch (ParseException e) { } return "false"; }
/*** * 根据注册路径获取注册对象,如果找不到就返回null * @param szKeyName * @return */ public Object getObject(String szKeyName) { synchronized(globalMap) { return globalMap.get(szKeyName); } }
/*** * 设置session对象 * @param session * @return */ public JSONRPCBridge setSession(HttpSession session) { this.session = session; return this; } } Java对象到JSON格式转换的实现ObjectToJSON.java: package jcore.jsonrpc.common; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /*** * 对象转换为json的串形式 * @author 夏天 * */ public class ObjectToJSON implements Serializable{ private static final long serialVersionUID = 764043683337733449L; private Object o = null; private JSONRPCBridge brige = null;
/*** * 将对象obj转换为 * @param obj 将要转换的对象 * @param szCurRegPath 被转换对象的注册名路径 * @param brige 桥接对象 */ public ObjectToJSON(Object obj, JSONRPCBridge brige) { this.o = obj; this.brige = brige; }
/*** * 将字符串中需要转义的字符进行转义,例如:",\r\n\t\f\b中文字符等 * @param string * @return */ private static String quote(String string) { if (null == string || 0 == string.length()) return "\"\""; char c; int i; int len = string.length(); StringBuffer sb = new StringBuffer(len + 4); String t; sb.append('"'); for (i = 0; i < len; i += 1) { c = string.charAt(i); switch (c) { case '\\': case '"': case '/': sb.append('\\'); sb.append(c); break; case '\b': sb.append("\\b"); break; case '\t': sb.append("\\t"); break; case '\n': sb.append("\\n"); break; case '\f': sb.append("\\f"); break; case '\r': sb.append("\\r"); break; default: if (c < ' ' || c >= 128) { t = "000" + Integer.toHexString(c); sb.append("\\u" + t.substring(t.length() - 4)); } else { sb.append(c); } } } sb.append('"'); return sb.toString(); }
/*** * 返回对象的JSON格式 */ public String toJSON(String szObjName) { StringBuffer buf = new StringBuffer(); String szSimpleTypeReg = "^(boolean|char|byte|short|int|long|float|double)$"; String szSimpleArrTypeReg = "^class \\[([A-Z])"; Pattern p = Pattern.compile(szSimpleTypeReg); Pattern pa = Pattern.compile(szSimpleArrTypeReg); int nPos = 0; try { Class c = o.getClass(); String szClassName = c.getName(); // 数组的处理 Pattern pSz = Pattern.compile("^\\[L.+$");
// 特殊对象的处理 if(szClassName.equals("java.lang.String")) return buf.append(quote(o.toString())).toString(); else if(szClassName.equals("java.util.Date") || szClassName.equals("java.sql.Timestamp")) return buf.append("new Date(").append(((Date)o) .getTime()).append(")").toString(); // 简单类型的对象封装类 else if(szClassName.equals("java.lang.Boolean")) return buf.append(((Boolean)o).booleanValue()).toString(); else if(szClassName.equals("java.lang.Character")) return buf.append("'").append(((Character)o) .charValue()).append("'").toString(); else if(szClassName.equals("java.lang.Short")) return buf.append(((Short)o).shortValue()).toString(); else if(szClassName.equals("java.lang.Integer")) return buf.append(((Integer)o).intValue()).toString(); else if(szClassName.equals("java.lang.Long")) return buf.append(((Long)o).longValue()).toString(); else if(szClassName.equals("java.lang.Float")) return buf.append(((Float)o).floatValue()).toString(); else if(szClassName.equals("java.lang.Double")) return buf.append(((Double)o).doubleValue()).toString(); // 数组的处理 else if(pSz.matcher(szClassName).find()) { Object []tmp09 = (Object [])this.o; if(0 < tmp09.length) { buf.append(new ObjectToJSON(tmp09[0], brige).toJSON(null)); for(int j = 1; j < tmp09.length; j++) buf.append(",").append(new ObjectToJSON(tmp09[j], brige).toJSON(null)); } return "[" + buf.append("]").toString(); } brige.registerObject(this.o.hashCode(), this.o); // 成员方法的处理 Method []oMs = c.getMethods(); // 可能在应用中需要过滤,不将这些方法输出: // "main","getClass","wait","wait","wait", "equals","toString","notify","notifyAll" if(0 < oMs.length) { buf.append("\"methods\":["); String szFlt = "(notifyAll)|(getClass)|(wait)|(wait)|(equals)|(notify)|(main)"; Map mMTmp = new HashMap(); for(int i = 0, k = 0; i < oMs.length; i++) { String szName = oMs[i].getName(); if(0 < szName.replaceAll(szFlt, "").length()) { // 同名的方法名只输出一次 if(null != mMTmp.get(szName)) continue; if(0 < k) buf.append(","); buf.append("\"").append(szName).append("\""); mMTmp.put(szName, (k++) + ""); } } // 就近解除对象的使用引用关系,让虚拟机尽快回收对象使用的内存 mMTmp = null; buf.append("]"); nPos++; }
// 成员变量的处理 Field []f = c.getFields(); if(0 < f.length) {
for(int i = 0; i < f.length; i++) { // 如果不是public的就继续下一轮的处理 if (!Modifier.isPublic(f[i].getModifiers())) continue; // 如果不是第一次 if(0 < nPos)buf.append(","); // 属性名 buf.append("\"").append(f[i].getName()).append("\":"); // 类型 String szType = f[i].getType().toString(); // 值 Object oValue = f[i].get(o);
// 如果为null if(null == oValue) buf.append("null"); else { // 如果是数组 Matcher m = pa.matcher(szType); if(m.find()) { buf.append("["); char cType = m.group(1).charAt(0); // L是Java对象数组,而不是简单类型的数组 switch(m.group(1).charAt(0)) { case 'Z': // boolean [] boolean []tmp01 = (boolean [])oValue; if(0 < tmp01.length) { buf.append(tmp01[0]); for(int j = 1; j < tmp01.length; j++) buf.append(",").append(tmp01[j]); } break; case 'B': // byte [] byte []tmp02 = (byte [])oValue; if(0 < tmp02.length) { buf.append(tmp02[0]); for(int j = 1; j < tmp02.length; j++) buf.append(",").append(tmp02[j]); } break; case 'C': // char [] char []tmp03 = (char [])oValue; if(0 < tmp03.length) { buf.append("'").append(tmp03[0]).append("'"); for(int j = 1; j < tmp03.length; j++) buf.append(",'").append(tmp03[j]).append("'"); } break; case 'I': // int [] int []tmp04 = (int [])oValue; if(0 < tmp04.length) { buf.append(tmp04[0]); for(int j = 1; j < tmp04.length; j++) buf.append(",").append(tmp04[j]); } break; case 'S': // short [] short []tmp05 = (short [])oValue; if(0 < tmp05.length) { buf.append(tmp05[0]); for(int j = 1; j < tmp05.length; j++) buf.append(",").append(tmp05[j]); } break; case 'J': // long [] long []tmp06 = (long [])oValue; if(0 < tmp06.length) { buf.append(tmp06[0]); for(int j = 1; j < tmp06.length; j++) buf.append(",").append(tmp06[j]); } break; case 'F': // float [] float []tmp07 = (float [])oValue; if(0 < tmp07.length) { buf.append(tmp07[0]); for(int j = 1; j < tmp07.length; j++) buf.append(",").append(tmp07[j]); } break; case 'D': // double [] double []tmp08 = (double [])oValue; if(0 < tmp08.length) { buf.append(tmp08[0]); for(int j = 1; j < tmp08.length; j++) buf.append(",").append(tmp08[j]); } break; case 'L': // 其他对象数组 Object []tmp09 = (Object [])oValue; if(0 < tmp09.length) { buf.append(new ObjectToJSON(tmp09[0], brige).toJSON(null)); for(int j = 1; j < tmp09.length; j++) buf.append(",").append(new ObjectToJSON(tmp09[j], brige).toJSON(null)); } break; } buf.append("]"); } else { // 如果是简单类型 Matcher m1 = p.matcher(szType); // m.reset(); if(m1.find()) { // 获取到简单类型变量的值 buf.append(oValue); } // 对象类型 else { buf.append(new ObjectToJSON(oValue, brige).toJSON(null)); } } } nPos++; } } }catch(Exception e) { e.printStackTrace(); } if(null != szObjName) { if(1 < buf.length())buf.append(","); buf.append("name:\"").append(szObjName).append("\""); } if(1 < buf.length())buf.append(","); buf.append("id:\"").append(this.o.hashCode()).append("\""); return "{" + buf.append(")").toString(); }} 和Web端直接进行交互的Servlet服务程序JSONRPCServlet.java: package jcore.jsonrpc.servlet; import java.io.BufferedReader; import java.io.CharArrayWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import jcore.jsonrpc.common.Content; import jcore.jsonrpc.common.JSONRPCBridge; /*** * JSON-RPC对web的服务通道 * @author 夏天 * */ public class JSONRPCServlet extends HttpServlet { private final static int buf_size = 4096; private final static long serialVersionUID = 2; private String charset = "UTF-8"; private ServletConfig config = null;
public void init(ServletConfig config)throws ServletException { this.config = config; super.init(config); }
/*** * 这里初始化启动的时候需要注册的对象 */ public void myInit(ServletConfig config, JSONRPCBridge brg) {
String szPam = config.getInitParameter("charset"); if(null != szPam) charset = szPam; szPam = config.getInitParameter("regAppClassNames"); if(null != szPam && 0 < szPam.trim().length()) { szPam = szPam.replaceAll("[\\s]", ""); String []arrTmp = szPam.split("[;]"); if(0 < arrTmp.length) { for(int i = 0; i < arrTmp.length; i++) { String []aT = arrTmp[i].split("[\\|:]"); try { Object o = Class.forName(aT[1]).newInstance(); brg.registerObject(aT[0], o); o = null; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } aT = null; } } arrTmp = null; }
} public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ClassCastException { HttpSession session = request.getSession(false); if(null == session)session = request.getSession(true); if(null != session) { JSONRPCBridge brg = (JSONRPCBridge)session. getAttribute(Content.RegSessionJSONRPCName); // 如果是第一次就注册对象 if(null == brg) { session.setAttribute(Content.RegSessionJSONRPCName, brg = new JSONRPCBridge().setSession(session)); myInit(this.config, brg); } response.setContentType("text/plain;charset=" + charset); OutputStream out = response.getOutputStream(); BufferedReader in = new BufferedReader (new InputStreamReader(request.getInputStream(), charset)); // 读取request中的JSON对象 CharArrayWriter data = new CharArrayWriter(); char buf[] = new char[buf_size]; int ret; while ((ret = in.read(buf, 0, buf_size)) != -1) data.write(buf, 0, ret);
String szData = data.toString(); byte[] bout = null; if(null != szData && 0 < szData.length()) bout = brg.ExecObjectMethod(szData).toString().getBytes("UTF-8"); // 返回注册中的对象 else { bout = brg.getRegObjsToString().getBytes(); } response.setIntHeader("Content-Length", bout.length); out.write(bout); out.flush(); out.close(); session.setAttribute(Content.RegSessionJSONRPCName, brg); } } } 测试对象的设计Test.java: package jcore.jsonrpc.test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /*** * 测试用的Java对象 * @author 夏天 * */ public class Test { public List lst = new ArrayList(); public String szName = "我是jcore.jsonrpc.test.Test的成员szName"; public int age = 30; private Map map = new HashMap();
public Test() { lst.add("构造方法中初始化的List对象"); lst.add("这只是简单的类型,后面将会有复杂的类型"); // 复合对象,因此我们在JavaScript中调用A的相关方法 lst.add(new A()); }
public List getMyList() { return lst; }
public Map getMyMap() { // 可以看出图14-6和图14-7节结果相同,是因为他们都是同一List对象 map.put("myList", lst); map.put("str", new String[]{"我是Map中复合对象的字符串", "注意,我们一般不推荐使用new Object[]{...}资源的数组形式"}); return map; } } |