/** * */ package io.client.thrift; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.net.ProtocolException; import java.net.Socket; import java.net.URL; import java.net.URLConnection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import javax.net.SocketFactory; import org.json.JSONArray; import org.json.JSONObject; import io.client.thrift.Json.Strategy; import io.client.thrift.annotaion.Index; /** * 使用 HTTP + JSON 协议 * <p> * 只有这一个文件,适合轻量级通信,如单一接口。由于依赖的 org.json 包 在 android.jar中 已存在,代码整体尺寸很小。 * * @author HouKangxi * */ public class ClientInterfaceFactory { private ClientInterfaceFactory() { } private static ConcurrentHashMap<Long, Object> ifaceCache = new ConcurrentHashMap<Long, Object>(); /** * 获得与服务端通信的接口对象 * <p> * 调用者可以实现自定义的 SocketFactory来内部配置Socket参数(如超时时间,SSL等),也可以通过返回包装的Socket来实现连接池<br/> * SocketFactory::createSocket(String host,int ip)//NOTE: 实际传入createSocket(methodName,flag) * * @param ifaceClass * - 接口class * @param factory * - 套接字工厂类, 注意:需要实现 createSocket() 方法,需要实现hashCode()方法来区分factory * @return 接口对象 */ @SuppressWarnings("unchecked") public static <INTERFACE> INTERFACE getClientInterface(Class<INTERFACE> ifaceClass, SocketFactory factory) { long part1 = ifaceClass.getName().hashCode(); final Long KEY = (part1 << 32) | factory.hashCode(); INTERFACE iface = (INTERFACE) ifaceCache.get(KEY); if (iface == null) { iface = (INTERFACE) Proxy.newProxyInstance(ifaceClass.getClassLoader(), new Class[] { ifaceClass }, new Handler(factory)); ifaceCache.putIfAbsent(KEY, iface); } return iface; } @SuppressWarnings("unchecked") public static <INTERFACE> INTERFACE getClientInterface(Class<INTERFACE> ifaceClass, String host) { long part1 = ifaceClass.getName().hashCode(); final Long KEY = (part1 << 32) | host.hashCode(); INTERFACE iface = (INTERFACE) ifaceCache.get(KEY); if (iface == null) { iface = (INTERFACE) Proxy.newProxyInstance(ifaceClass.getClassLoader(), new Class[] { ifaceClass }, new Handler(host)); ifaceCache.putIfAbsent(KEY, iface); } return iface; } private static ConcurrentHashMap<Class<?>, Map<Object, Field>> fieldCache = new ConcurrentHashMap<Class<?>, Map<Object, Field>>(); private static class Handler extends Json.Strategy implements InvocationHandler { final AtomicInteger seqIdHolder = new AtomicInteger(0); final SocketFactory factory; String host; Handler(SocketFactory factory) { this.factory = factory; } Handler(String host) { factory = null; this.host = host; } { // 只处理public 且非 static 的字段 publicFieldsOnly(); } @Override public String fieldName(Field field) { Index id = field.getAnnotation(Index.class); if (id != null) { return String.valueOf(id.value()); } return super.fieldName(field); } @Override public Field field(Class<?> cls, String fieldName) throws NoSuchFieldException, SecurityException { char c0 = fieldName.charAt(0); if (c0 >= '0' && c0 <= '9') { Map<Object, Field> cache = fieldCache.get(cls); if (cache == null) { Field[] fs = cls.getFields(); cache = new HashMap<Object, Field>(fs.length); for (Field f : fs) { Index id = f.getAnnotation(Index.class); if (id != null) { cache.put(id.value(), f); } } fieldCache.putIfAbsent(cls, cache); } Field fd = cache.get(Integer.parseInt(fieldName)); return fd; } return super.field(cls, fieldName); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (args == null || args.length == 0) { if (methodName.equals("toString")) { return Handler.class.getName() + "@" + System.identityHashCode(this); } if (methodName.equals("hashCode")) { return System.identityHashCode(this); } } int seqId = seqIdHolder.incrementAndGet(); // StringBuilder sb = new StringBuilder(256); sb.append('[').append('"').append(methodName).append('"')// .append(',').append(1).append(',').append(seqId).append(',') .append(args == null ? "[]" : Json.toJson(args, this)).append(']'); String jsonStr = sb.toString(); // System.out.println("=========== Request ============"); System.out.println(jsonStr); // Socket connection = null; Reader reader = null; try { InputStream in; OutputStream out; if (factory != null) { connection = factory.createSocket(methodName, 0); out = connection.getOutputStream(); out.write(jsonStr.getBytes()); out.flush(); in = connection.getInputStream(); } else { URLConnection conn = new URL(host).openConnection(); conn.setDoOutput(true); conn.connect(); out = conn.getOutputStream(); out.write(jsonStr.getBytes()); out.flush(); in = conn.getInputStream(); } int firstByte = in.read(); reader = new InputStreamReader(in, "UTF-8"); sb = new StringBuilder(512); if (firstByte == '[') { sb.append((char) firstByte); } char[] charbuf = new char[512]; int len; while ((len = reader.read(charbuf)) > 0) { sb.append(charbuf, 0, len); if (len < charbuf.length) { break; } } jsonStr = sb.toString(); } catch (IOException ex) { jsonStr = ""; } finally { try { if (connection != null) { connection.close(); } else if (reader != null) { reader.close(); } } catch (IOException e) { } } System.out.println("=========== response json: ==========="); System.out.println(jsonStr); if (jsonStr == null || jsonStr.length() < 1) { return null; } return read(jsonStr, method.getReturnType(), method.getExceptionTypes(), seqId, this); } } private static <T> T read(String json, Type resultBeanClass, Class<?>[] exceptionsTypes, int seqid_, Strategy strategy) throws Throwable { JSONArray arr = new JSONArray(json); String methodName = arr.getString(0); int type = arr.getInt(1); int seqid = arr.getInt(2); if (type == 3) { JSONObject respFull = arr.getJSONObject(arr.length() - 1); String errMsg = respFull.getString("message"); throw new ProtocolException(errMsg); } // 验证seqId 与请求的是否相同 if (seqid != seqid_) { throw new ProtocolException(methodName + " failed: out of sequence response"); } JSONObject respFull = arr.getJSONObject(arr.length() - 1); Object respObj = respFull.opt("success"); if (respObj == null) { respObj = respFull.opt("0"); } if (respObj == null) { respObj = respFull.opt("ex"); if (respObj == null) { respObj = respFull.opt("1"); } if (respObj != null && exceptionsTypes != null && exceptionsTypes.length > 0) { resultBeanClass = exceptionsTypes[0]; String objson = Json.toJson(resultBeanClass, respObj, strategy); Object ex = Json.fromJson(objson, resultBeanClass, strategy); if (ex instanceof Throwable) { throw (Throwable) ex; } } return null; } else { String objson = Json.toJson(resultBeanClass, respObj, strategy); T bean = Json.fromJson(objson, resultBeanClass, strategy); // return bean; } } }