From cea7d4cd62cc0347dd0d7bda2a98a657258003dd Mon Sep 17 00:00:00 2001
From: jiangping <jp@doumee.com>
Date: 星期二, 16 一月 2024 15:50:54 +0800
Subject: [PATCH] 调整

---
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/MoResponse.java                                     |   66 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestString.java                         |   36 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Expose.java                                    |   92 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/common/EmayHttpResultCode.java                                  |   69 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/ExclusionStrategy.java                                     |  121 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/request/EmayHttpRequest.java                                    |  147 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Primitives.java                                   |  116 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/package-info.java                                          |   14 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TreeTypeAdapter.java                                       |  132 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Since.java                                     |   68 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/MobileAndContent.java                                 |   38 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/GsonBuilder.java                                           |  661 +
 server/emaysms/src/main/java/cn/emay/sdk/util/PropertiesUtil.java                                                  |  271 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/reflect/TypeToken.java                                     |  299 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TypeAdapter.java                                           |  327 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonIOException.java                                       |   47 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonToken.java                                      |   85 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonDeserializationContext.java                            |   50 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java           |   82 
 server/emaysms/src/main/java/cn/emay/sdk/common/CommonConstants.java                                               |   20 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Streams.java                                      |  132 
 server/service/src/main/java/com/doumee/service/business/impl/InterfaceLogServiceImpl.java                         |  157 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/BalanceResponse.java                                |   34 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java |   68 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ArrayTypeAdapter.java                        |   98 
 server/emaysms/src/main/java/cn/emay/sdk/client/SmsSDKClient.java                                                  |  237 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonScope.java                                      |   71 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/CustomSmsIdAndMobile.java                             |   43 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/ReportRequest.java                                   |   33 
 server/emaysms/src/main/java/cn/emay/sdk/util/Md5.java                                                             |   52 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/JsonAdapter.java                               |  105 
 server/service/src/main/java/com/doumee/service/business/third/SignService.java                                    |  310 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/BalanceRequest.java                                  |   12 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBatchOnlyRequest.java                             |   45 
 server/service/pom.xml                                                                                             |   13 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/DefaultDateTypeAdapter.java                                |  118 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LinkedHashTreeMap.java                            |  880 ++
 server/service/src/main/java/com/doumee/dao/business/model/InterfaceLog.java                                       |   91 
 server/emaysms/src/main/java/cn/emay/sdk/EmayTool.java                                                             |  217 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/BaseRequest.java                                     |   38 
 server/emaysms/src/main/java/cn/emay/sdk/core/service/SDKService.java                                              |   87 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/Gson.java                                                  | 1132 +++
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/package-info.java                                 |    8 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/MoRequest.java                                       |   33 
 server/emaysms/src/main/java/cn/emay/sdk/util/StringUtil.java                                                      |   19 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldAttributes.java                                       |  173 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LazilyParsedNumber.java                           |  100 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ObjectTypeAdapter.java                       |  112 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseStringPraser.java          |   34 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/LongSerializationPolicy.java                               |   61 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/InstanceCreator.java                                       |  108 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonStreamParser.java                                      |  133 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/ConstructorConstructor.java                       |  245 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/SmsIdAndMobile.java                                   |   54 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonObject.java                                            |  214 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Excluder.java                                     |  252 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsSingleRequest.java                                |   64 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseString.java                |  107 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Until.java                                     |   73 
 server/emaysms/src/main/java/cn/emay/sdk/util/OnlyIdGenerator.java                                                 |  101 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TypeAdapters.java                            |  965 ++
 server/pom.xml                                                                                                     |    1 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonElement.java                                           |  365 +
 server/emaysms/src/main/java/cn/emay/sdk/util/HttpUtil.java                                                        |  103 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseBytes.java                 |   89 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/ReportResponse.java                                 |  119 
 server/dianziqian/src/main/java/com/jzq/JzqHttpApiTool.java                                                        |  274 
 server/emaysms/src/main/java/cn/emay/sdk/util/AES.java                                                             |  141 
 server/service/src/main/java/com/doumee/dao/business/InterfaceLogMapper.java                                       |   11 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestKV.java                             |   53 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TimeTypeAdapter.java                         |   70 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/CustomSmsIdAndMobileAndContent.java                   |   54 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/RetrieveReportRequest.java                           |   52 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/SerializedName.java                            |  106 
 server/emaysms/src/main/java/cn/emay/sdk/util/exception/SDKParamsException.java                                    |   23 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseBytesPraser.java           |   24 
 server/emaysms/src/main/java/cn/emay/sdk/util/HostUtil.java                                                        |  228 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/SendSmsData.java                                      |   75 
 server/emaysms/src/test/java/cn/emay/test/Test.java                                                                |  197 
 server/service/src/main/java/com/doumee/core/utils/Constants.java                                                  |    9 
 server/service/src/main/java/com/doumee/dao/system/model/SystemDictData.java                                       |    2 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/client/EmayHttpClient.java                                      |  349 +
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalityRequest.java                           |   34 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/RetrieveReportResponse.java                         |   36 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/SmsResponse.java                                    |   57 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/SqlDateTypeAdapter.java                      |   68 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonParseException.java                                    |   74 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/MalformedJsonException.java                         |   46 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalityAllRequest.java                        |   33 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonPrimitive.java                                         |  350 +
 server/emaysms/src/main/java/cn/emay/sdk/util/DateUtil.java                                                        |  193 
 server/emaysms/src/main/java/cn/emay/sdk/core/service/security/SDKSecurityServiceImpl.java                         |   89 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonTreeWriter.java                          |  227 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ReflectiveTypeAdapterFactory.java            |  257 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/package-info.java                              |    7 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/$Gson$Preconditions.java                          |   49 
 server/platform/src/main/resources/application.yml                                                                 |    2 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestBytes.java                          |   37 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonWriter.java                                     |  654 +
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/$Gson$Types.java                                  |  584 +
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TypeAdapterFactory.java                                    |  187 
 server/emaysms/src/main/java/cn/emay/sdk/util/GZIPUtils.java                                                       |  105 
 server/service/src/main/java/com/doumee/service/business/third/EmayService.java                                    |   91 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/CollectionTypeAdapterFactory.java            |  102 
 server/emaysms/src/main/java/cn/emay/sdk/util/http/response/EmayHttpResponsePraser.java                            |   39 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalitySimpleRequest.java                     |   29 
 server/service/src/main/java/com/doumee/service/business/InterfaceLogService.java                                  |   98 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonDeserializer.java                                      |  110 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonReader.java                                     | 1624 ++++
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonArray.java                                             |  428 +
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/UnsafeAllocator.java                              |  102 
 server/emaysms/src/main/java/cn/emay/sdk/task/ContrastHostTask.java                                                |   31 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/DateTypeAdapter.java                         |   89 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/JsonReaderInternalAccess.java                     |   33 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/JsonHelper.java                                                 |  130 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSerializer.java                                        |  111 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldNamingPolicy.java                                     |  185 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonNull.java                                              |   64 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSerializationContext.java                              |   58 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/cache/UrlDTO.java                                                |   34 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/MapTypeAdapterFactory.java                   |  281 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBatchRequest.java                                 |   51 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSyntaxException.java                                   |   48 
 server/platform/src/main/java/com/doumee/api/InterfaceLogController.java                                           |   89 
 /dev/null                                                                                                          |   25 
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/ResultModel.java                                      |   29 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LinkedTreeMap.java                                |  643 +
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBaseRequest.java                                  |   49 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/reflect/package-info.java                                  |    7 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonParser.java                                            |  100 
 server/emaysms/pom.xml                                                                                             |   20 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldNamingStrategy.java                                   |   42 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/ObjectConstructor.java                            |   33 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/util/ISO8601Utils.java                       |  380 +
 server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/PersonalityParams.java                                |   76 
 server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonTreeReader.java                          |  246 
 136 files changed, 20,454 insertions(+), 27 deletions(-)

diff --git a/server/dianziqian/src/main/java/com/doumee/Main.java b/server/dianziqian/src/main/java/com/doumee/Main.java
deleted file mode 100644
index ada0d0d..0000000
--- a/server/dianziqian/src/main/java/com/doumee/Main.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.doumee;
-
-public class Main {
-    public static void main(String[] args) {
-        System.out.println("Hello world!");
-    }
-}
\ No newline at end of file
diff --git a/server/dianziqian/src/main/java/com/jzq/JzqHttpApiTool.java b/server/dianziqian/src/main/java/com/jzq/JzqHttpApiTool.java
new file mode 100644
index 0000000..3ed7476
--- /dev/null
+++ b/server/dianziqian/src/main/java/com/jzq/JzqHttpApiTool.java
@@ -0,0 +1,274 @@
+package com.jzq;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.jzq.common.ResultInfo;
+import com.jzq.common.bean.sign.SignatoryReq;
+import com.jzq.common.http.HttpClientUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+
+import java.io.File;
+import java.util.Date;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * <ul>
+ * <li>椤圭洰鍚嶇О : 鍚庡彴鏈嶅姟</li>
+ * <li>鏂囦欢鍚嶇О : JzqHttpApiTest</li>
+ * <li>鍒涘缓鏃堕棿 : 2019/8/23 9:14</li>
+ * <li>鎻� 杩� :  鐢ㄤ簬jzq鐨刟pi鏈嶈姹�
+ * </ul>
+ *
+ * @author yfx
+ * @title 鐢ㄤ簬jzq鐨刟pi鏈嶈姹�
+ */
+@Slf4j
+public class JzqHttpApiTool {
+    /**
+     * 鍚涘瓙绛炬祴璇曠幆澧僰ey鍜屾帴鍙e湴鍧�锛�
+     * appKey锛歞cb4bd535a09df3c
+     * appSecret锛歜87c346edcb4bd535a09df3ca8c45d9a
+     * services_url锛歨ttps://api.sandbox.junziqian.com
+     * 寮�鍙戞枃妗�: https://s.junziqian.com/api_doc/index.html
+     */
+    private static String SERVICE_URL="https://api.sandbox.junziqian.com";
+    private static  String APP_KEY="dcb4bd535a09df3c";
+    private static  String APP_SECRET="b87c346edcb4bd535a09df3ca8c45d9a";
+
+    public JzqHttpApiTool(String url,String appKey,String appSecret){
+        SERVICE_URL = url;
+        APP_SECRET = appSecret;
+        APP_KEY = appKey;
+        initParams();
+    }
+
+    //璇锋眰鐨刡ody鍐呭弬鏁�
+    private static  Map<String, Object> bodyParams;
+
+    public void initParams(){
+        long ts=System.currentTimeMillis();
+        String nonce=DigestUtils.md5Hex(System.currentTimeMillis()+"");
+        String sign=DigestUtils.sha256Hex("nonce"+nonce+"ts"+ts+"app_key"+APP_KEY+"app_secret"+APP_SECRET);
+        bodyParams=new IdentityHashMap<>();
+        bodyParams.put("ts",ts);
+        bodyParams.put("app_key",APP_KEY);
+        bodyParams.put("sign",sign);
+        bodyParams.put("nonce",nonce);//杩欏彧鍙槸涓轰簡鐢熸垚涓�涓殢鏈哄��
+    }
+
+    /**
+     * 1.ping鏈嶅姟
+     */
+    public boolean ping(){
+        initParams();
+        try {
+            Map<String, Object>  params=bodyParams;
+            String url=SERVICE_URL+"/v2/ping";
+            //鎵�鏈夊弬鏁拌鍏ヤ簡body涓�
+            String str= HttpClientUtils.init().getPost(url,null,params,false);
+            log.info("杩斿洖缁撴灉涓�:"+str);
+            ResultInfo ri= JSONObject.parseObject(str,ResultInfo.class);
+            return ri.isSuccess();
+        }catch (Exception e){
+
+        }
+        return false;
+    }
+
+
+    /**
+     * 鍙戣捣绛剧害 (鍚涘瓙绛惧悗鍙伴厤缃ā鐗圛D鍙戣捣)
+     * @param fullname
+     * @param creditCode
+     * @param legalName
+     * @param email
+     * @param businessimg
+     * @param notifyUrl
+     * @return
+     */
+    public boolean organizationCreate (String fullname,String creditCode,String legalName,String email,File  businessimg,String notifyUrl){
+        try {
+            initParams();
+            Map<String, Object>  params=bodyParams;
+            String url=SERVICE_URL+"/v2/user/organizationCreate";
+            params.put("name",fullname);
+            params.put("identificationType",1);
+            params.put("organizationRegNo",creditCode);
+            params.put("organizationType",0);
+            params.put("organizationCode",creditCode);
+            params.put("organizationRegImg",businessimg);
+            params.put("notifyUrl",notifyUrl);
+            params.put("legalName",legalName);
+            params.put("emailOrMobile",email);
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  true;
+            }
+            System.out.println(str);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return false;
+
+    }
+    /**
+     * 鍙戣捣绛剧害 (鍚涘瓙绛惧悗鍙伴厤缃ā鐗圛D鍙戣捣)
+     * @param fullname
+     * @param creditCode
+     * @param legalName
+     * @param email
+     * @param businessimg
+     * @param notifyUrl
+     * @return
+     */
+    public boolean organizationReApply(String fullname,String creditCode,String legalName,String email,File  businessimg,String notifyUrl){
+        try {
+            initParams();
+            Map<String, Object>  params=bodyParams;
+            String url=SERVICE_URL+"/v2/user/organizationReapply";
+            params.put("name",fullname);
+            params.put("identificationType",1);
+            params.put("organizationRegNo",creditCode);
+            params.put("organizationType",0);
+            params.put("organizationCode",creditCode);
+            params.put("organizationRegImg",businessimg);
+            params.put("notifyUrl",notifyUrl);
+            params.put("legalName",legalName);
+            params.put("emailOrMobile",email);
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  true;
+            }
+            System.out.println(str);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return false;
+
+    }
+
+    /**
+     * 鏌ヨ浼佷笟绛剧害鐘舵�� 瀹℃壒鐘舵��,0姝e湪鐢宠1閫氳繃2椹冲洖
+     * @param email
+     * @return
+     */
+    public int  organizationAuditStatus (String email){
+        try {
+            initParams();
+            Map<String, Object>  params=bodyParams;
+            String url=SERVICE_URL+"/v2/user/organizationAuditStatus";
+            params.put("emailOrMobile",email);
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            System.out.println(str);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  json.getJSONObject("data").getIntValue("status");
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return  -1;//鏌ヨ澶辫触
+    }
+
+    /**
+     * 鍙戣捣绛剧害
+     * @param name
+     * @param file
+     * @param fullname
+     * @param creditCoe
+     * @param email
+     * @param postionJson
+     */
+    public String applySign(String name,File file,String fullname,String creditCoe,String email,String postionJson){
+      try {
+          String url=SERVICE_URL+"/v2/sign/applySign";
+          initParams();
+          Map<String, Object>  params=bodyParams;
+          params.put("contractName",name); //鍚堝悓鍚嶇О
+          params.put("serverCa",1); //浣跨敤浜戣瘉涔�
+          params.put("file",file);
+          params.put("dealType",5); //鎸囧畾鍚堝悓鏂囦欢绛剧讲鏂瑰紡 5 涓洪儴鍒嗚嚜鍔ㄧ
+          params.put("positionType",0); //鎸囧畾閫氳繃琛ㄥ崟鍩熸柟寮忚缃瀛椾綅缃�
+          params.put("fileType",0);
+          params.put("needQifengSign",1);
+          JSONArray signatories=new JSONArray();
+          SignatoryReq sReq=new SignatoryReq();
+          sReq.setFullName(fullname); //浼佷笟濮撳悕
+          sReq.setIdentityType(11); //璇佷欢绫诲瀷
+          sReq.setIdentityCard(creditCoe);//钀ヤ笟鎵х収鍙�
+          sReq.setEmail(email); //鍦ㄥ悰瀛愮娉ㄥ唽璁よ瘉鐨勯偖绠�
+//        sReq.setChapteJson("[{\"page\":0,\"chaptes\":[{\"offsetX\":0.12,\"offsetY\":0.23}]},{\"page\":1,\"chaptes\":[{\"offsetX\":0.45,\"offsetY\":0.67}]}]");
+          sReq.setChapteJson(postionJson);
+          sReq.setNoNeedVerify(1);
+          signatories.add(sReq);
+          params.put("signatories",signatories.toJSONString());
+          System.out.println(signatories.toJSONString());
+          String str= HttpClientUtils.init().getPost(url,null,params,true);
+          System.out.println(str);
+          JSONObject json = JSONObject.parseObject(str);
+          if(json!=null && json.getBoolean("success")){
+              return  json.getString("data");
+          }
+      }catch (Exception e){
+
+      }
+      return null;
+
+    }
+
+
+    /**
+     * 鑾峰彇绛剧讲閾炬帴鍦板潃
+     * @param applyNo
+     * @param name
+     * @param creditCode
+     */
+    public String signLink(String applyNo,String name,String creditCode) {
+        try {
+            initParams();
+            Map<String, Object> params = bodyParams;
+            String url = SERVICE_URL + "/v2/sign/link";
+            params.put("applyNo",applyNo); //鍙戣捣鍚堝悓绛剧讲鎺ュ彛杩斿洖鐨凙PL缂栧彿
+            params.put("fullName",name); //鍙戣捣鍚堝悓绛剧讲鎺ュ彛闇�瑕佹墜鍔ㄧ缃插璞$殑濮撳悕
+            params.put("identityCard",creditCode); //鍙戣捣鍚堝悓绛剧讲鎺ュ彛涓渶瑕佹墜鍔ㄧ缃插璞$殑璇佷欢鍙�
+            params.put("identityType",11); //璇佷欢绫诲瀷锛屼釜浜�1 锛屼紒涓�11
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            System.out.println(str);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  json.getString("data");
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return  null;
+    }
+    /**
+     * 鑾峰彇绛剧讲閾炬帴鍦板潃
+     * @param applyNo
+     */
+    public String  linkFile(String applyNo) {
+
+        try {
+            initParams();
+            Map<String, Object> params = bodyParams;
+            String url = SERVICE_URL + "/v2/sign/linkFile";
+            //鏋勫缓璇锋眰鍙傛暟
+            params.put("applyNo",applyNo); //鍙戣捣鍚堝悓绛剧讲鎺ュ彛杩斿洖鐨凙PL缂栧彿
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            System.out.println(str);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  json.getString("data");
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return  null;
+    }
+
+}
diff --git a/server/emaysms/pom.xml b/server/emaysms/pom.xml
new file mode 100644
index 0000000..c41337e
--- /dev/null
+++ b/server/emaysms/pom.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.doumee</groupId>
+        <artifactId>yunyibao</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>emaysms</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+</project>
\ No newline at end of file
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/EmayTool.java b/server/emaysms/src/main/java/cn/emay/sdk/EmayTool.java
new file mode 100644
index 0000000..8d3b9ee
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/EmayTool.java
@@ -0,0 +1,217 @@
+package cn.emay.test;
+
+import java.io.IOException;
+
+import cn.emay.sdk.client.SmsSDKClient;
+import cn.emay.sdk.core.dto.sms.common.CustomSmsIdAndMobile;
+import cn.emay.sdk.core.dto.sms.common.CustomSmsIdAndMobileAndContent;
+import cn.emay.sdk.core.dto.sms.common.PersonalityParams;
+import cn.emay.sdk.core.dto.sms.common.ResultModel;
+import cn.emay.sdk.core.dto.sms.request.BalanceRequest;
+import cn.emay.sdk.core.dto.sms.request.MoRequest;
+import cn.emay.sdk.core.dto.sms.request.ReportRequest;
+import cn.emay.sdk.core.dto.sms.request.RetrieveReportRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchOnlyRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityAllRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsSingleRequest;
+import cn.emay.sdk.core.dto.sms.response.BalanceResponse;
+import cn.emay.sdk.core.dto.sms.response.MoResponse;
+import cn.emay.sdk.core.dto.sms.response.ReportResponse;
+import cn.emay.sdk.core.dto.sms.response.RetrieveReportResponse;
+import cn.emay.sdk.core.dto.sms.response.SmsResponse;
+import cn.emay.sdk.util.exception.SDKParamsException;
+
+public class EmayTool {
+	private static SmsSDKClient client ;
+
+	public SmsSDKClient getClient() {
+		return client;
+	}
+
+	public void setClient(SmsSDKClient client) {
+		this.client = client;
+	}
+
+	public EmayTool(String ip, int port,String appId,String appKey){
+		try {
+			this.client = new SmsSDKClient(ip, port, appId, appKey);
+		}catch (Exception e){
+
+		}
+	}
+	public static void main(String[] args) throws SDKParamsException, IOException {
+		new EmayTool("www.btom.cn",8080,"8SDK-EMY-6699-RIXTP","5EA482CE6A904271")
+				.sendSingleSms("15345690849","銆愯眴绫崇鎶�銆戣繖鏄垜浠殑绗竴鏉$煭淇″摝");
+		// sendBatchOnlySms();
+		// sendBatchSms();
+		// sendPersonalitySms();
+		// sendPersonalityAllSMS();
+		// getMo();
+		// getReport();
+		// getBalance();
+//		retrieveReport();
+	}
+
+	public static boolean sendSingleSms(String mobile,String content) throws SDKParamsException {
+		if(client == null){
+			return false;
+		}
+		String customSmsId = "1";
+		String extendedCode = "01";
+
+		SmsSingleRequest request = new SmsSingleRequest(mobile, content, customSmsId, extendedCode, "");
+		ResultModel<SmsResponse> result = client.sendSingleSms(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse response = result.getResult();
+			System.out.println("sendSingleSms:" + response.toString());
+			return  true;
+		} else {
+			System.out.println("璇锋眰澶辫触");
+			return false;
+		}
+	}
+
+	public static void sendBatchOnlySms() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		String[] mobiles = { "13800000000", "13800000001" };
+		String content = "鐭俊鍐呭";
+		String extendedCode = "01";
+		SmsBatchOnlyRequest request = new SmsBatchOnlyRequest(mobiles, content, "", extendedCode);
+		ResultModel<SmsResponse[]> result = client.sendBatchOnlySms(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse[] responses = result.getResult();
+			for (SmsResponse response : responses) {
+				System.out.println("sendBatchOnlySms:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void sendBatchSms() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		CustomSmsIdAndMobile[] cm = new CustomSmsIdAndMobile[2];
+		cm[0] = new CustomSmsIdAndMobile("1", "13800000000");
+		cm[1] = new CustomSmsIdAndMobile("2", "13800000001");
+		String content = "鐭俊鍐呭";
+		String extendedCode = "01";
+		SmsBatchRequest request = new SmsBatchRequest(cm, content, "", extendedCode);
+		ResultModel<SmsResponse[]> result = client.sendBatchSms(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse[] responses = result.getResult();
+			for (SmsResponse response : responses) {
+				System.out.println("sendBatchSms:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void sendPersonalitySms() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		String extendedCode = "01";
+
+		CustomSmsIdAndMobileAndContent[] smses = new CustomSmsIdAndMobileAndContent[2];
+		smses[0] = new CustomSmsIdAndMobileAndContent("1", "13800000000", "鐭俊鍐呭1");
+		smses[1] = new CustomSmsIdAndMobileAndContent("2", "13800000001", "鐭俊鍐呭2");
+		SmsPersonalityRequest request = new SmsPersonalityRequest(smses, "", extendedCode);
+		ResultModel<SmsResponse[]> result = client.sendPersonalitySms(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse[] responses = result.getResult();
+			for (SmsResponse response : responses) {
+				System.out.println("sendPersonalitySms:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void sendPersonalityAllSMS() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+
+		PersonalityParams[] smses = new PersonalityParams[2];
+		smses[0] = new PersonalityParams("1", "13800000000", "鐭俊鍐呭1", "1", null);
+		smses[1] = new PersonalityParams("2", "13800000001", "鐭俊鍐呭2", "2", null);
+		SmsPersonalityAllRequest request = new SmsPersonalityAllRequest(smses);
+		ResultModel<SmsResponse[]> result = client.sendPersonalityAllSMS(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse[] responses = result.getResult();
+			for (SmsResponse response : responses) {
+				System.out.println("sendPersonalityAllSMS:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void getReport() throws SDKParamsException {
+		ReportRequest request = new ReportRequest();
+		ResultModel<ReportResponse[]> result = client.getReport(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			ReportResponse[] responses = result.getResult();
+			for (ReportResponse response : responses) {
+				System.out.println("getReport:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+
+	}
+
+	public static void getMo() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		MoRequest request = new MoRequest();
+		ResultModel<MoResponse[]> result = client.getMo(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			MoResponse[] responses = result.getResult();
+			for (MoResponse response : responses) {
+				System.out.println("getMo:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void getBalance() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		BalanceRequest request = new BalanceRequest();
+		ResultModel<BalanceResponse> result = client.getBalance(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			BalanceResponse response = result.getResult();
+			System.out.println("getBalance:" + response.getBalance());
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void retrieveReport() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		String startTime = "20180120110000";
+		String endTime = "20180120110500";
+		String smsid = "15167713536420020356";
+		RetrieveReportRequest reportRequest = new RetrieveReportRequest();
+		reportRequest.setSmsId(smsid);
+		reportRequest.setStartTime(startTime);
+		reportRequest.setEndTime(endTime);
+		ResultModel<RetrieveReportResponse> result = client.retrieveReport(reportRequest);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			RetrieveReportResponse response = result.getResult();
+			System.out.println("retrieveReport:" + response.getCode());
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/client/SmsSDKClient.java b/server/emaysms/src/main/java/cn/emay/sdk/client/SmsSDKClient.java
new file mode 100644
index 0000000..7d945ad
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/client/SmsSDKClient.java
@@ -0,0 +1,237 @@
+package cn.emay.sdk.client;
+
+import java.io.File;
+import java.util.Date;
+
+import cn.emay.sdk.common.CommonConstants;
+import cn.emay.sdk.core.dto.sms.common.ResultModel;
+import cn.emay.sdk.core.dto.sms.request.BalanceRequest;
+import cn.emay.sdk.core.dto.sms.request.MoRequest;
+import cn.emay.sdk.core.dto.sms.request.ReportRequest;
+import cn.emay.sdk.core.dto.sms.request.RetrieveReportRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchOnlyRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityAllRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsSingleRequest;
+import cn.emay.sdk.core.dto.sms.response.BalanceResponse;
+import cn.emay.sdk.core.dto.sms.response.MoResponse;
+import cn.emay.sdk.core.dto.sms.response.ReportResponse;
+import cn.emay.sdk.core.dto.sms.response.RetrieveReportResponse;
+import cn.emay.sdk.core.dto.sms.response.SmsResponse;
+import cn.emay.sdk.core.service.SDKService;
+import cn.emay.sdk.core.service.security.SDKSecurityServiceImpl;
+import cn.emay.sdk.task.ContrastHostTask;
+import cn.emay.sdk.util.DateUtil;
+import cn.emay.sdk.util.HostUtil;
+import cn.emay.sdk.util.Md5;
+import cn.emay.sdk.util.StringUtil;
+import cn.emay.sdk.util.exception.SDKParamsException;
+import cn.emay.sdk.util.http.common.EmayHttpResultCode;
+
+public class SmsSDKClient {
+
+	/**
+	 * 浜跨編鏈嶅姟甯愬彿
+	 */
+	private String appId;
+	/**
+	 * 浜跨編鏈嶅姟瀵嗙爜
+	 */
+	private String secretkey;
+
+	private SDKService service;
+	/**
+	 * 浜跨編http璇锋眰鍦板潃
+	 */
+	private String host;
+	/**
+	 * SDK缂撳瓨鐩綍
+	 */
+	private File file;
+
+	/**
+	 * @param ip
+	 *            浜跨編缃戝叧IP
+	 * @param port
+	 *            浜跨編缃戝叧绔彛
+	 * @param appId
+	 *            浜跨編鏈嶅姟甯愬彿
+	 * @param secretkey
+	 *            浜跨編鏈嶅姟瀵嗙爜
+	 * @throws SDKParamsException
+	 */
+	public SmsSDKClient(String ip, int port, String appId, String secretkey) throws SDKParamsException {
+		if (StringUtil.isEmpty(appId) || StringUtil.isEmpty(secretkey) || StringUtil.isEmpty(ip) || port <= 0) {
+			throw new SDKParamsException("SDK params error");
+		}
+		this.appId = appId;
+		this.secretkey = secretkey;
+		if (!ip.toLowerCase().startsWith("http://")) {
+			ip = "http://" + ip;
+		}
+		host = ip + ":" + port;
+		CommonConstants.isBest = true;
+		CommonConstants.bestUrl = host;
+		service = new SDKSecurityServiceImpl();
+	}
+
+	/**
+	 * @param appId
+	 *            浜跨編鏈嶅姟甯愬彿
+	 * @param secretkey
+	 *            浜跨編鏈嶅姟瀵嗙爜
+	 * @throws SDKParamsException
+	 */
+	public SmsSDKClient(String appId, String secretkey) throws SDKParamsException {
+		this(appId, secretkey, "");
+	}
+
+	/**
+	 * 
+	 * @param appId
+	 *            浜跨編鏈嶅姟甯愬彿
+	 * @param secretkey
+	 *            浜跨編鏈嶅姟瀵嗙爜
+	 * @param filePath
+	 *            缂撳瓨鏁版嵁鐩綍(鐢ㄤ簬瀛樺偍璇锋眰鍦板潃)
+	 * @throws SDKParamsException
+	 */
+	public SmsSDKClient(String appId, String secretkey, String filePath) throws SDKParamsException {
+		if (StringUtil.isEmpty(appId) || StringUtil.isEmpty(secretkey)) {
+			throw new SDKParamsException("SDK params error");
+		}
+		this.appId = appId;
+		this.secretkey = secretkey;
+		file = HostUtil.creatCacheFile(filePath);
+		String url = HostUtil.getFileUrl(file, appId);
+		if (StringUtil.isEmpty(url)) {
+			HostUtil.getSDKInter();
+			if (CommonConstants.interList == null || CommonConstants.interList.isEmpty()) {
+				throw new SDKParamsException("SDK Request interface address exception");
+			}
+			new Thread(new ContrastHostTask(file, appId, secretkey)).start();
+			CommonConstants.bestUrl = CommonConstants.interList.get(0);
+		} else {
+			CommonConstants.isBest = true;
+			CommonConstants.bestUrl = url;
+		}
+		service = new SDKSecurityServiceImpl();
+	}
+
+	/**
+	 * 鐘舵�佹姤鍛婇噸鏂拌幏鍙�
+	 * 
+	 * @param reportRequest
+	 * @return
+	 */
+	public ResultModel<RetrieveReportResponse> retrieveReport(RetrieveReportRequest reportRequest) {
+		String timestamp = DateUtil.toString(new Date(), "yyyyMMddHHmmss");
+		String sign = Md5.md5((appId + secretkey + timestamp).getBytes());
+		String url = (host == null || host.equals("")) ? CommonConstants.bestUrl : host;
+		return service.retrieveReport(appId, timestamp, sign, url, reportRequest);
+	}
+
+	/**
+	 * 鍙戦�佸崟鏉$煭淇�
+	 * 
+	 * @param request
+	 * @return
+	 */
+
+	public ResultModel<SmsResponse> sendSingleSms(SmsSingleRequest request) {
+		String url = (host == null || host.equals("")) ? CommonConstants.bestUrl : host;
+		return service.sendSingleSms(appId, secretkey, url, request);
+	}
+
+	/**
+	 * 鍙戦�佹壒娆$煭淇°�愰潪鑷畾涔塻msid銆�
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<SmsResponse[]> sendBatchOnlySms(SmsBatchOnlyRequest request) {
+		String url = (host == null || host.equals("")) ? CommonConstants.bestUrl : host;
+		return service.sendBatchOnlySms(appId, secretkey, url, request);
+	}
+
+	/**
+	 * 鍙戦�佹壒娆$煭淇°�愯嚜瀹氫箟smsid銆�
+	 * 
+	 * @param request
+	 * @return
+	 * @throws SDKParamsException
+	 */
+	public ResultModel<SmsResponse[]> sendBatchSms(SmsBatchRequest request) throws SDKParamsException {
+		String url = (host == null || host.equals("")) ? CommonConstants.bestUrl : host;
+		return service.sendBatchSms(appId, secretkey, url, request);
+	}
+
+	/**
+	 * 鍙戦�佷釜鎬х煭淇�
+	 * 
+	 * @param request
+	 * @return
+	 */
+
+	public ResultModel<SmsResponse[]> sendPersonalitySms(SmsPersonalityRequest request) throws SDKParamsException {
+		String url = (host == null || host.equals("")) ? CommonConstants.bestUrl : host;
+		return service.sendPersonalitySms(appId, secretkey, url, request);
+	}
+
+	/**
+	 * 鍙戦�佷釜鎬х煭淇°�愬叏灞炴�т釜鎬с��
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<SmsResponse[]> sendPersonalityAllSMS(SmsPersonalityAllRequest request) throws SDKParamsException {
+		String url = (host == null || host.equals("")) ? CommonConstants.bestUrl : host;
+		return service.sendPersonalityAllSms(appId, secretkey, url, request);
+	}
+
+	/**
+	 * 鑾峰彇浣欓
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<BalanceResponse> getBalance(BalanceRequest request) {
+		String url = (host == null || host.equals("")) ? CommonConstants.bestUrl : host;
+		return service.getBalance(appId, secretkey, url, request);
+	}
+
+	/**
+	 * 鑾峰彇鐘舵�佹姤鍛�
+	 * 
+	 * @param request
+	 * @return
+	 */
+
+	public ResultModel<ReportResponse[]> getReport(ReportRequest reportRequest) {
+		String url = "";
+		if (CommonConstants.isBest) {
+			url = CommonConstants.bestUrl;
+		} else {
+			return new ResultModel<ReportResponse[]>(EmayHttpResultCode.SUCCESS.getCode(), new ReportResponse[0]);
+		}
+		return service.getReport(appId, secretkey, url, reportRequest);
+	}
+
+	/**
+	 * 鑾峰彇涓婅鐭俊
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<MoResponse[]> getMo(MoRequest request) {
+		String url = "";
+		if (CommonConstants.isBest) {
+			url = CommonConstants.bestUrl;
+		} else {
+			return new ResultModel<MoResponse[]>(EmayHttpResultCode.SUCCESS.getCode(), new MoResponse[0]);
+		}
+		return service.getMo(appId, secretkey, url, request);
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/common/CommonConstants.java b/server/emaysms/src/main/java/cn/emay/sdk/common/CommonConstants.java
new file mode 100644
index 0000000..002bee4
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/common/CommonConstants.java
@@ -0,0 +1,20 @@
+package cn.emay.sdk.common;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CommonConstants {
+
+	public final static String algorithm = "AES/ECB/PKCS5Padding";
+
+	public final static String getSdkUrl = "http://geturl.eucp.b2m.cn/sdk/getInters";
+
+	public final static String fileName = "emay-temp.txt";
+
+	public static List<String> interList = new ArrayList<String>();
+
+	public static String bestUrl = "";
+
+	public static boolean isBest = false;
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/cache/UrlDTO.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/cache/UrlDTO.java
new file mode 100644
index 0000000..627d03a
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/cache/UrlDTO.java
@@ -0,0 +1,34 @@
+package cn.emay.sdk.core.dto.cache;
+
+public class UrlDTO {
+
+	public String appId;
+
+	public String url;
+
+	public UrlDTO() {
+
+	}
+
+	public UrlDTO(String appId, String url) {
+		this.appId = appId;
+		this.url = url;
+	}
+
+	public String getAppId() {
+		return appId;
+	}
+
+	public void setAppId(String appId) {
+		this.appId = appId;
+	}
+
+	public String getUrl() {
+		return url;
+	}
+
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/CustomSmsIdAndMobile.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/CustomSmsIdAndMobile.java
new file mode 100644
index 0000000..2d04f3f
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/CustomSmsIdAndMobile.java
@@ -0,0 +1,43 @@
+package cn.emay.sdk.core.dto.sms.common;
+
+import java.io.Serializable;
+
+/**
+ * 鑷畾涔塖MSID 鎵嬫満鍙�
+ * @author Frank
+ *
+ */
+public class CustomSmsIdAndMobile implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	
+	private String customSmsId;
+	
+	private String mobile;
+	
+	public CustomSmsIdAndMobile(){
+		
+	}
+	
+	public CustomSmsIdAndMobile(String customSmsId,String mobile){
+		this.customSmsId = customSmsId;
+		this.mobile = mobile;
+	}
+
+	public String getCustomSmsId() {
+		return customSmsId;
+	}
+
+	public void setCustomSmsId(String customSmsId) {
+		this.customSmsId = customSmsId;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/CustomSmsIdAndMobileAndContent.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/CustomSmsIdAndMobileAndContent.java
new file mode 100644
index 0000000..23b4a04
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/CustomSmsIdAndMobileAndContent.java
@@ -0,0 +1,54 @@
+package cn.emay.sdk.core.dto.sms.common;
+
+import java.io.Serializable;
+
+/**
+ * 鑷畾涔塖MSID 鎵嬫満鍙� 鍐呭
+ * @author Frank
+ *
+ */
+public class CustomSmsIdAndMobileAndContent implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	
+	private String customSmsId;
+	
+	private String mobile;
+	
+	private String content;
+
+	public CustomSmsIdAndMobileAndContent(){
+		
+	}
+	
+	public CustomSmsIdAndMobileAndContent(String customSmsId,String mobile,String content){
+		this.customSmsId = customSmsId;
+		this.mobile = mobile;
+		this.content = content;
+	}
+	
+	public String getCustomSmsId() {
+		return customSmsId;
+	}
+
+	public void setCustomSmsId(String customSmsId) {
+		this.customSmsId = customSmsId;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/MobileAndContent.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/MobileAndContent.java
new file mode 100644
index 0000000..ca3e7d8
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/MobileAndContent.java
@@ -0,0 +1,38 @@
+package cn.emay.sdk.core.dto.sms.common;
+
+import java.io.Serializable;
+
+public class MobileAndContent implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private String mobile;
+
+	private String content;
+
+	public MobileAndContent() {
+
+	}
+
+	public MobileAndContent(String mobile, String content) {
+		this.mobile = mobile;
+		this.content = content;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/PersonalityParams.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/PersonalityParams.java
new file mode 100644
index 0000000..8b55b2f
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/PersonalityParams.java
@@ -0,0 +1,76 @@
+package cn.emay.sdk.core.dto.sms.common;
+
+import java.io.Serializable;
+
+/**
+ * 鑷畾涔塖MSID 鎵嬫満鍙� 鍐呭
+ * @author Frank
+ *
+ */
+public class PersonalityParams implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	
+	private String customSmsId;
+	
+	private String mobile;
+	
+	private String content;
+	
+	private String extendedCode;
+	
+	private String timerTime;
+
+	public PersonalityParams(){
+		
+	}
+	
+	public PersonalityParams(String customSmsId,String mobile,String content,String extendedCode,String timerTime){
+		this.customSmsId = customSmsId;
+		this.mobile = mobile;
+		this.content = content;
+		this.timerTime = timerTime;
+		this.extendedCode = extendedCode;
+	}
+	
+	public String getCustomSmsId() {
+		return customSmsId;
+	}
+
+	public void setCustomSmsId(String customSmsId) {
+		this.customSmsId = customSmsId;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public String getExtendedCode() {
+		return extendedCode;
+	}
+
+	public void setExtendedCode(String extendedCode) {
+		this.extendedCode = extendedCode;
+	}
+
+	public String getTimerTime() {
+		return timerTime;
+	}
+
+	public void setTimerTime(String timerTime) {
+		this.timerTime = timerTime;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/ResultModel.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/ResultModel.java
new file mode 100644
index 0000000..89727a6
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/ResultModel.java
@@ -0,0 +1,29 @@
+package cn.emay.sdk.core.dto.sms.common;
+
+public class ResultModel<T> {
+
+	private String code;
+	private T result;
+
+	public ResultModel(String code, T result) {
+		this.code = code;
+		this.result = result;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public T getResult() {
+		return result;
+	}
+
+	public void setResult(T result) {
+		this.result = result;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/SendSmsData.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/SendSmsData.java
new file mode 100644
index 0000000..49f74fd
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/SendSmsData.java
@@ -0,0 +1,75 @@
+package cn.emay.sdk.core.dto.sms.common;
+
+import java.io.Serializable;
+
+/**
+ * 鍙戦�佺煭淇″疄浣撶被
+ * 
+ * @author Hcs
+ *
+ */
+public class SendSmsData implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private String smsId;//
+
+	private String customerId;
+
+	private String smscontent;// 鐭俊鍐呭
+
+	private String sendTime;// 瀹氭椂鏃堕棿
+
+	private String mobile; // smsIdAndMobiles
+
+	private String extendedCode;// 鎵╁睍鐮�
+
+	public String getSmscontent() {
+		return smscontent;
+	}
+
+	public void setSmscontent(String smscontent) {
+		this.smscontent = smscontent;
+	}
+
+	public String getSendTime() {
+		return sendTime;
+	}
+
+	public void setSendTime(String sendTime) {
+		this.sendTime = sendTime;
+	}
+
+	public String getExtendedCode() {
+		return extendedCode;
+	}
+
+	public void setExtendedCode(String extendedCode) {
+		this.extendedCode = extendedCode;
+	}
+
+	public String getCustomerId() {
+		return customerId;
+	}
+
+	public void setCustomerId(String customerId) {
+		this.customerId = customerId;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public String getSmsId() {
+		return smsId;
+	}
+
+	public void setSmsId(String smsId) {
+		this.smsId = smsId;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/SmsIdAndMobile.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/SmsIdAndMobile.java
new file mode 100644
index 0000000..f900291
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/common/SmsIdAndMobile.java
@@ -0,0 +1,54 @@
+package cn.emay.sdk.core.dto.sms.common;
+
+import java.io.Serializable;
+
+/**
+ * 鑷畾涔塖MSID涓庢墜鏈哄彿
+ * @author Frank
+ *
+ */
+public class SmsIdAndMobile implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+	
+	private String smsId;
+	
+	private String customSmsId;
+	
+	private String mobile;
+	
+	public SmsIdAndMobile(){
+		
+	}
+	
+	public SmsIdAndMobile(String smsId,String customSmsId,String mobile){
+		this.smsId = smsId;
+		this.customSmsId = customSmsId;
+		this.mobile = mobile;
+	}
+
+	public String getCustomSmsId() {
+		return customSmsId;
+	}
+
+	public void setCustomSmsId(String customSmsId) {
+		this.customSmsId = customSmsId;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public String getSmsId() {
+		return smsId;
+	}
+
+	public void setSmsId(String smsId) {
+		this.smsId = smsId;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/BalanceRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/BalanceRequest.java
new file mode 100644
index 0000000..7fe0c55
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/BalanceRequest.java
@@ -0,0 +1,12 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+/**
+ * 璇锋眰Balance鍙傛暟
+ * @author Frank
+ *
+ */
+public class BalanceRequest  extends BaseRequest {
+	
+	private static final long serialVersionUID = 1L;
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/BaseRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/BaseRequest.java
new file mode 100644
index 0000000..a18f04a
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/BaseRequest.java
@@ -0,0 +1,38 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+import java.io.Serializable;
+
+public class BaseRequest implements Serializable{
+	
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 璇锋眰鏃堕棿
+	 */
+	private long requestTime = System.currentTimeMillis();
+	
+	/**
+	 * 璇锋眰鏈夋晥鏃堕棿(绉�)<br/>
+	 * 鏈嶅姟鍣ㄦ帴鍙楁椂闂翠笌璇锋眰鏃堕棿瀵规瘮锛屽鏋滆秴杩囨湁鏁堟椂闂达紝鎷掔粷姝ゆ璇锋眰<br/>
+	 * 闃叉琚綉缁滄姄鍖呬笉鏂彂閫佸悓涓�鏉¤姹�<br/>
+	 * 榛樿1鍒嗛挓鏈夋晥鏈�
+	 */
+	private int requestValidPeriod  = 60;
+
+	public long getRequestTime() {
+		return requestTime;
+	}
+	
+	public void setRequestTime(long requestTime) {
+		this.requestTime = requestTime;
+	}
+
+	public int getRequestValidPeriod() {
+		return requestValidPeriod;
+	}
+
+	public void setRequestValidPeriod(int requestValidPeriod) {
+		this.requestValidPeriod = requestValidPeriod;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/MoRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/MoRequest.java
new file mode 100644
index 0000000..06d6d5f
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/MoRequest.java
@@ -0,0 +1,33 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+/**
+ * 璇锋眰涓婅鐭俊鍙傛暟
+ * 
+ * @author Frank
+ *
+ */
+public class MoRequest extends BaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 璇锋眰鏁伴噺<br/>
+	 * 鏈�澶�500
+	 */
+	private int number = 500;
+
+	public int getNumber() {
+		if (number <= 0 || number > 500) {
+			number = 500;
+		}
+		return number;
+	}
+
+	public void setNumber(int number) {
+		if (number > 500) {
+			number = 500;
+		}
+		this.number = number;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/ReportRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/ReportRequest.java
new file mode 100644
index 0000000..8337cc4
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/ReportRequest.java
@@ -0,0 +1,33 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+/**
+ * 璇锋眰鐘舵�佹姤鍛婂弬鏁�
+ * 
+ * @author Frank
+ *
+ */
+public class ReportRequest extends BaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 璇锋眰鏁伴噺<br/>
+	 * 鏈�澶�500
+	 */
+	private int number = 500;
+
+	public int getNumber() {
+		if (number <= 0 || number > 500) {
+			number = 500;
+		}
+		return number;
+	}
+
+	public void setNumber(int number) {
+		if (number > 500) {
+			number = 500;
+		}
+		this.number = number;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/RetrieveReportRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/RetrieveReportRequest.java
new file mode 100644
index 0000000..ea395b6
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/RetrieveReportRequest.java
@@ -0,0 +1,52 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+/**
+ * 鐘舵�佹姤鍛婇噸鏂拌幏鍙栧弬鏁�
+ * 
+ * @author Hcs
+ *
+ */
+public class RetrieveReportRequest extends BaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 鐘舵�佹姤鍛婂紑濮嬫椂闂�(蹇呭~锛屽搴旂煭淇℃彁浜ゆ椂闂达紝鍙兘涓哄綋鍓嶆椂闂�30澶╁墠鑼冨洿锛屼笉鑳藉ぇ浜庡綋鍓嶆椂闂�) 鏍煎紡锛歽yyyMMddHHmmss 14浣�
+	 */
+	private String startTime;
+	/**
+	 * 鐘舵�佹姤鍛婄粨鏉熸椂闂�(蹇呭~,瀵瑰簲鐭俊鎻愪氦鏃堕棿,鍙兘涓哄綋鍓嶆椂闂�30澶╁墠鑼冨洿,涓嶈兘澶т簬褰撳墠鏃堕棿,搴斿ぇ浜庡紑濮嬫椂闂�,寮�濮嬬粨鏉熸椂闂存渶澶ч棿闅斾负10鍒嗛挓,寤鸿5鍒嗛挓浠ュ唴)
+	 * 鏍煎紡锛歽yyyMMddHHmmss 14浣�
+	 */
+	private String endTime;
+
+	/**
+	 * 鐭俊鐨剆msId(閫夊~)锛屽涓敤鍗婅閫楀彿鍒嗛殧锛屾渶澶�1000涓紝濡� 123123123,321321321
+	 */
+	private String smsId;
+
+	public String getStartTime() {
+		return startTime;
+	}
+
+	public void setStartTime(String startTime) {
+		this.startTime = startTime;
+	}
+
+	public String getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
+
+	public String getSmsId() {
+		return smsId;
+	}
+
+	public void setSmsId(String smsId) {
+		this.smsId = smsId;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBaseRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBaseRequest.java
new file mode 100644
index 0000000..aa5d5e3
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBaseRequest.java
@@ -0,0 +1,49 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+/**
+ * 鍩虹璇锋眰鍙傛暟
+ * 
+ * @author Hcs
+ *
+ */
+public class SmsBaseRequest extends BaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 瀹氭椂鏃堕棿 yyyy-MM-dd HH:mm:ss
+	 */
+	private String timerTime;
+
+	/**
+	 * 鎵╁睍鐮�
+	 */
+	private String extendedCode;
+
+	public SmsBaseRequest() {
+		super();
+	}
+
+	public SmsBaseRequest(String timerTime, String extendedCode) {
+		super();
+		this.timerTime = timerTime;
+		this.extendedCode = extendedCode;
+	}
+
+	public String getTimerTime() {
+		return timerTime;
+	}
+
+	public void setTimerTime(String timerTime) {
+		this.timerTime = timerTime;
+	}
+
+	public String getExtendedCode() {
+		return extendedCode;
+	}
+
+	public void setExtendedCode(String extendedCode) {
+		this.extendedCode = extendedCode;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBatchOnlyRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBatchOnlyRequest.java
new file mode 100644
index 0000000..515d017
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBatchOnlyRequest.java
@@ -0,0 +1,45 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+/**
+ * 鎵归噺鐭俊鍙戦�佸弬鏁�
+ * 
+ * @author Frank
+ *
+ */
+public class SmsBatchOnlyRequest extends SmsBaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 鎵嬫満鍙蜂笌鑷畾涔塖msId
+	 */
+	private String[] mobiles;
+
+	/**
+	 * 鐭俊鍐呭
+	 */
+	private String content;
+
+	public SmsBatchOnlyRequest(String[] mobiles, String content, String timerTime, String extendedCode) {
+		super(timerTime, extendedCode);
+		this.mobiles = mobiles;
+		this.content = content;
+	}
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public String[] getMobiles() {
+		return mobiles;
+	}
+
+	public void setMobiles(String[] mobiles) {
+		this.mobiles = mobiles;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBatchRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBatchRequest.java
new file mode 100644
index 0000000..5d11a16
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsBatchRequest.java
@@ -0,0 +1,51 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+import cn.emay.sdk.core.dto.sms.common.CustomSmsIdAndMobile;
+
+/**
+ * 鎵归噺鐭俊鍙戦�佸弬鏁�
+ * 
+ * @author Frank
+ *
+ */
+public class SmsBatchRequest extends SmsBaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 鎵嬫満鍙蜂笌鑷畾涔塖msId
+	 */
+	private CustomSmsIdAndMobile[] smses;
+
+	public SmsBatchRequest() {
+		super();
+	}
+
+	public SmsBatchRequest(CustomSmsIdAndMobile[] smses, String content, String timerTime, String extendedCode) {
+		super(timerTime, extendedCode);
+		this.smses = smses;
+		this.content = content;
+	}
+
+	/**
+	 * 鐭俊鍐呭
+	 */
+	private String content;
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public CustomSmsIdAndMobile[] getSmses() {
+		return smses;
+	}
+
+	public void setSmses(CustomSmsIdAndMobile[] smses) {
+		this.smses = smses;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalityAllRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalityAllRequest.java
new file mode 100644
index 0000000..2e61fed
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalityAllRequest.java
@@ -0,0 +1,33 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+import cn.emay.sdk.core.dto.sms.common.PersonalityParams;
+
+/**
+ * 鎵归噺鐭俊鍙戦�佸弬鏁�
+ * 
+ * @author Frank
+ *
+ */
+public class SmsPersonalityAllRequest extends SmsBaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	private PersonalityParams[] smses;
+
+	public SmsPersonalityAllRequest() {
+		super();
+	}
+
+	public SmsPersonalityAllRequest(PersonalityParams[] smses) {
+		this.smses = smses;
+	}
+
+	public PersonalityParams[] getSmses() {
+		return smses;
+	}
+
+	public void setSmses(PersonalityParams[] smses) {
+		this.smses = smses;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalityRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalityRequest.java
new file mode 100644
index 0000000..3488fad
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalityRequest.java
@@ -0,0 +1,34 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+import cn.emay.sdk.core.dto.sms.common.CustomSmsIdAndMobileAndContent;
+
+/**
+ * 鎵归噺鐭俊鍙戦�佸弬鏁�
+ * 
+ * @author Frank
+ *
+ */
+public class SmsPersonalityRequest extends SmsBaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	private CustomSmsIdAndMobileAndContent[] smses;
+
+	public SmsPersonalityRequest() {
+		super();
+	}
+
+	public SmsPersonalityRequest(CustomSmsIdAndMobileAndContent[] smses, String timerTime, String extendedCode) {
+		super(timerTime, extendedCode);
+		this.smses = smses;
+	}
+
+	public CustomSmsIdAndMobileAndContent[] getSmses() {
+		return smses;
+	}
+
+	public void setSmses(CustomSmsIdAndMobileAndContent[] smses) {
+		this.smses = smses;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalitySimpleRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalitySimpleRequest.java
new file mode 100644
index 0000000..18818c9
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsPersonalitySimpleRequest.java
@@ -0,0 +1,29 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+import cn.emay.sdk.core.dto.sms.common.MobileAndContent;
+
+public class SmsPersonalitySimpleRequest extends SmsBaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	private String customSmsId;
+
+	private MobileAndContent[] mobileAndContents;
+
+	public String getCustomSmsId() {
+		return customSmsId;
+	}
+
+	public void setCustomSmsId(String customSmsId) {
+		this.customSmsId = customSmsId;
+	}
+
+	public MobileAndContent[] getMobileAndContents() {
+		return mobileAndContents;
+	}
+
+	public void setMobileAndContents(MobileAndContent[] mobileAndContents) {
+		this.mobileAndContents = mobileAndContents;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsSingleRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsSingleRequest.java
new file mode 100644
index 0000000..2d2a4a0
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/request/SmsSingleRequest.java
@@ -0,0 +1,64 @@
+package cn.emay.sdk.core.dto.sms.request;
+
+/**
+ * 鍗曟潯鐭俊鍙戦�佸弬鏁�
+ * 
+ * @author Frank
+ *
+ */
+public class SmsSingleRequest extends SmsBaseRequest {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 鐢佃瘽鍙风爜
+	 */
+	private String mobile;
+
+	/**
+	 * 鐭俊鍐呭
+	 */
+	private String content;
+
+	/**
+	 * 鑷畾涔塻msid
+	 */
+	private String customSmsId;
+
+	public SmsSingleRequest() {
+
+	}
+
+	public SmsSingleRequest(String mobile, String content, String customSmsId, String extendedCode, String timerTime) {
+		this.mobile = mobile;
+		this.content = content;
+		this.customSmsId = customSmsId;
+		this.setExtendedCode(extendedCode);
+		this.setTimerTime(timerTime);
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public String getCustomSmsId() {
+		return customSmsId;
+	}
+
+	public void setCustomSmsId(String customSmsId) {
+		this.customSmsId = customSmsId;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/BalanceResponse.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/BalanceResponse.java
new file mode 100644
index 0000000..d6297fc
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/BalanceResponse.java
@@ -0,0 +1,34 @@
+package cn.emay.sdk.core.dto.sms.response;
+
+import java.io.Serializable;
+
+
+/**
+ * 浣欓鏁版嵁
+ * @author Frank
+ *
+ */
+public class BalanceResponse implements Serializable{
+	
+	private static final long serialVersionUID = 1L;
+
+	private long balance;// 浣欓
+
+
+	public BalanceResponse() {
+
+	}
+
+	public BalanceResponse(long balance) {
+		this.balance = balance;
+	}
+
+	public long getBalance() {
+		return balance;
+	}
+
+	public void setBalance(long balance) {
+		this.balance = balance;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/MoResponse.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/MoResponse.java
new file mode 100644
index 0000000..fba5ae7
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/MoResponse.java
@@ -0,0 +1,66 @@
+package cn.emay.sdk.core.dto.sms.response;
+
+import java.io.Serializable;
+
+/**
+ * 涓婅鏁版嵁
+ * 
+ * @author Frank
+ *
+ */
+public class MoResponse implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private String mobile;// 鎵嬫満鍙�
+
+	private String extendedCode; // 鎵╁睍鐮�
+
+	private String content;// 鍐呭
+
+	private String moTime;// 鎵嬫満涓婅鏃堕棿
+
+	public MoResponse() {
+
+	}
+
+	public MoResponse(String mobile, String extendedCode, String content, String moTime) {
+		this.mobile = mobile;
+		this.extendedCode = extendedCode;
+		this.content = content;
+		this.moTime = moTime;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public String getExtendedCode() {
+		return extendedCode;
+	}
+
+	public void setExtendedCode(String extendedCode) {
+		this.extendedCode = extendedCode;
+	}
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public String getMoTime() {
+		return moTime;
+	}
+
+	public void setMoTime(String moTime) {
+		this.moTime = moTime;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/ReportResponse.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/ReportResponse.java
new file mode 100644
index 0000000..47e20b6
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/ReportResponse.java
@@ -0,0 +1,119 @@
+package cn.emay.sdk.core.dto.sms.response;
+
+import java.io.Serializable;
+
+/**
+ * 鐘舵�佹姤鍛婃暟鎹�
+ * 
+ * @author Frank
+ *
+ */
+public class ReportResponse implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	private String interSmsId;// 鎺ュ彛鑷畾涔塈D
+
+	private String smsId;// 鐭俊鍞竴鏍囪瘑
+
+	private String customSmsId;// 瀹㈡埛鑷畾涔塖msId
+
+	private String state;// 鎴愬姛澶辫触鏍囪瘑
+
+	private String desc;// 鐘舵�佹姤鍛婃弿杩�
+
+	private String mobile;// 鎵嬫満鍙�
+
+	private String receiveTime;// 鐘舵�佹姤鍛婅繑鍥炴椂闂�
+
+	private String submitTime;// 淇℃伅鎻愪氦鏃堕棿
+
+	private String extendedCode;// 鎵╁睍鐮�
+
+	public ReportResponse(String smsId, String customSmsId, String state, String desc, String mobile, String receiveTime, String submitTime, String extendedCode) {
+		this.smsId = smsId;
+		this.customSmsId = customSmsId;
+		this.state = state;
+		this.desc = desc;
+		this.mobile = mobile;
+		this.receiveTime = receiveTime;
+		this.submitTime = submitTime;
+		this.extendedCode = extendedCode;
+	}
+
+	public ReportResponse() {
+	}
+
+	public String getCustomSmsId() {
+		return customSmsId;
+	}
+
+	public void setCustomSmsId(String customSmsId) {
+		this.customSmsId = customSmsId;
+	}
+
+	public String getState() {
+		return state;
+	}
+
+	public void setState(String state) {
+		this.state = state;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public String getReceiveTime() {
+		return receiveTime;
+	}
+
+	public void setReceiveTime(String receiveTime) {
+		this.receiveTime = receiveTime;
+	}
+
+	public String getDesc() {
+		return desc;
+	}
+
+	public void setDesc(String desc) {
+		this.desc = desc;
+	}
+
+	public String getSmsId() {
+		return smsId;
+	}
+
+	public void setSmsId(String smsId) {
+		this.smsId = smsId;
+	}
+
+	public String getSubmitTime() {
+		return submitTime;
+	}
+
+	public void setSubmitTime(String submitTime) {
+		this.submitTime = submitTime;
+	}
+
+	public String getExtendedCode() {
+		return extendedCode;
+	}
+
+	public void setExtendedCode(String extendedCode) {
+		this.extendedCode = extendedCode;
+	}
+
+	public String getInterSmsId() {
+		return interSmsId;
+	}
+
+	public void setInterSmsId(String interSmsId) {
+		this.interSmsId = interSmsId;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/RetrieveReportResponse.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/RetrieveReportResponse.java
new file mode 100644
index 0000000..cfd60a8
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/RetrieveReportResponse.java
@@ -0,0 +1,36 @@
+package cn.emay.sdk.core.dto.sms.response;
+
+import java.io.Serializable;
+
+/**
+ * 鐘舵�佹姤鍛婇噸鏂拌幏鍙栫粨鏋�
+ * 
+ * @author Hcs
+ *
+ */
+public class RetrieveReportResponse implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 鍝嶅簲鐘舵�佺爜
+	 */
+	private String code;
+
+	public RetrieveReportResponse() {
+
+	}
+
+	public RetrieveReportResponse(String code) {
+		this.code = code;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/SmsResponse.java b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/SmsResponse.java
new file mode 100644
index 0000000..32a685b
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/dto/sms/response/SmsResponse.java
@@ -0,0 +1,57 @@
+package cn.emay.sdk.core.dto.sms.response;
+
+import java.io.Serializable;
+
+/**
+ * 鍗曟潯鐭俊鍙戦�佸搷搴�
+ * @author Frank
+ *
+ */
+public class SmsResponse implements Serializable {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 绯荤粺鍞竴smsId
+	 */
+	private String smsId;
+	
+	private String mobile;
+	
+	private String customSmsId;
+	
+	public SmsResponse(){
+		
+	}
+	
+	public SmsResponse(String smsId,String mobile,String customSmsId){
+		this.smsId = smsId;
+		this.mobile = mobile;
+		this.customSmsId = customSmsId;
+	}
+
+	public String getSmsId() {
+		return smsId;
+	}
+
+	public void setSmsId(String smsId) {
+		this.smsId = smsId;
+	}
+
+	public String getMobile() {
+		return mobile;
+	}
+
+	public void setMobile(String mobile) {
+		this.mobile = mobile;
+	}
+
+	public String getCustomSmsId() {
+		return customSmsId;
+	}
+
+	public void setCustomSmsId(String customSmsId) {
+		this.customSmsId = customSmsId;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/service/SDKService.java b/server/emaysms/src/main/java/cn/emay/sdk/core/service/SDKService.java
new file mode 100644
index 0000000..4d3114e
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/service/SDKService.java
@@ -0,0 +1,87 @@
+package cn.emay.sdk.core.service;
+
+import cn.emay.sdk.core.dto.sms.common.ResultModel;
+import cn.emay.sdk.core.dto.sms.request.BalanceRequest;
+import cn.emay.sdk.core.dto.sms.request.MoRequest;
+import cn.emay.sdk.core.dto.sms.request.ReportRequest;
+import cn.emay.sdk.core.dto.sms.request.RetrieveReportRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchOnlyRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityAllRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsSingleRequest;
+import cn.emay.sdk.core.dto.sms.response.BalanceResponse;
+import cn.emay.sdk.core.dto.sms.response.MoResponse;
+import cn.emay.sdk.core.dto.sms.response.ReportResponse;
+import cn.emay.sdk.core.dto.sms.response.RetrieveReportResponse;
+import cn.emay.sdk.core.dto.sms.response.SmsResponse;
+import cn.emay.sdk.util.exception.SDKParamsException;
+
+public interface SDKService {
+
+	public ResultModel<RetrieveReportResponse> retrieveReport(String appId, String timestamp, String sign, String host, RetrieveReportRequest reportRequest);
+
+	/**
+	 * 鍙戦�佸崟鏉$煭淇�
+	 * 
+	 * @return
+	 */
+	public ResultModel<SmsResponse> sendSingleSms(String appId, String secretKey, String host, SmsSingleRequest request);
+
+	/**
+	 * 鍙戦�佹壒娆$煭淇�
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<SmsResponse[]> sendBatchOnlySms(String appId, String secretKey, String host, SmsBatchOnlyRequest request);
+
+	/**
+	 * 鍙戦�佹壒娆$煭淇�
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<SmsResponse[]> sendBatchSms(String appId, String secretKey, String host, SmsBatchRequest request) throws SDKParamsException;
+
+	/**
+	 * 鍙戦�佷釜鎬х煭淇�
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<SmsResponse[]> sendPersonalitySms(String appId, String secretKey, String host, SmsPersonalityRequest request) throws SDKParamsException;
+
+	/**
+	 * 鍙戦�佹壒娆$煭淇�
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<SmsResponse[]> sendPersonalityAllSms(String appId, String secretKey, String host, SmsPersonalityAllRequest request) throws SDKParamsException;
+
+	/**
+	 * 鑾峰彇浣欓
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<BalanceResponse> getBalance(String appId, String secretKey, String host, BalanceRequest request);
+
+	/**
+	 * 鑾峰彇鐘舵�佹姤鍛�
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<ReportResponse[]> getReport(String appId, String secretKey, String host, ReportRequest reportRequest);
+
+	/**
+	 * 鑾峰彇涓婅鐭俊
+	 * 
+	 * @param request
+	 * @return
+	 */
+	public ResultModel<MoResponse[]> getMo(String appId, String secretKey, String host, MoRequest request);
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/core/service/security/SDKSecurityServiceImpl.java b/server/emaysms/src/main/java/cn/emay/sdk/core/service/security/SDKSecurityServiceImpl.java
new file mode 100644
index 0000000..cd883c1
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/core/service/security/SDKSecurityServiceImpl.java
@@ -0,0 +1,89 @@
+package cn.emay.sdk.core.service.security;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import cn.emay.sdk.core.dto.sms.common.ResultModel;
+import cn.emay.sdk.core.dto.sms.request.BalanceRequest;
+import cn.emay.sdk.core.dto.sms.request.MoRequest;
+import cn.emay.sdk.core.dto.sms.request.ReportRequest;
+import cn.emay.sdk.core.dto.sms.request.RetrieveReportRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchOnlyRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityAllRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsSingleRequest;
+import cn.emay.sdk.core.dto.sms.response.BalanceResponse;
+import cn.emay.sdk.core.dto.sms.response.MoResponse;
+import cn.emay.sdk.core.dto.sms.response.ReportResponse;
+import cn.emay.sdk.core.dto.sms.response.RetrieveReportResponse;
+import cn.emay.sdk.core.dto.sms.response.SmsResponse;
+import cn.emay.sdk.core.service.SDKService;
+import cn.emay.sdk.util.HttpUtil;
+import cn.emay.sdk.util.exception.SDKParamsException;
+
+public class SDKSecurityServiceImpl implements SDKService {
+
+	@Override
+	public ResultModel<SmsResponse> sendSingleSms(String appId, String secretKey, String host, SmsSingleRequest request) {
+		ResultModel<SmsResponse> result = HttpUtil.request(appId, secretKey, host + "/inter/sendSingleSMS", request, SmsResponse.class);
+		return result;
+	}
+
+	@Override
+	public ResultModel<SmsResponse[]> sendBatchOnlySms(String appId, String secretKey, String host, SmsBatchOnlyRequest request) {
+		ResultModel<SmsResponse[]> result = HttpUtil.request(appId, secretKey, host + "/inter/sendBatchOnlySMS", request, SmsResponse[].class);
+		return result;
+	}
+
+	@Override
+	public ResultModel<SmsResponse[]> sendBatchSms(String appId, String secretKey, String host, SmsBatchRequest request) throws SDKParamsException {
+		ResultModel<SmsResponse[]> result = HttpUtil.request(appId, secretKey, host + "/inter/sendBatchSMS", request, SmsResponse[].class);
+		return result;
+	}
+
+	@Override
+	public ResultModel<SmsResponse[]> sendPersonalitySms(String appId, String secretKey, String host, SmsPersonalityRequest request) throws SDKParamsException {
+		ResultModel<SmsResponse[]> result = HttpUtil.request(appId, secretKey, host + "/inter/sendPersonalitySMS", request, SmsResponse[].class);
+		return result;
+	}
+
+	@Override
+	public ResultModel<SmsResponse[]> sendPersonalityAllSms(String appId, String secretKey, String host, SmsPersonalityAllRequest request) throws SDKParamsException {
+		ResultModel<SmsResponse[]> result = HttpUtil.request(appId, secretKey, host + "/inter/sendPersonalityAllSMS", request, SmsResponse[].class);
+		return result;
+	}
+
+	@Override
+	public ResultModel<BalanceResponse> getBalance(String appId, String secretKey, String host, BalanceRequest request) {
+		ResultModel<BalanceResponse> result = HttpUtil.request(appId, secretKey, host + "/inter/getBalance", request, BalanceResponse.class);
+		return result;
+	}
+
+	@Override
+	public ResultModel<ReportResponse[]> getReport(String appId, String secretKey, String host, ReportRequest reportRequest) {
+		ResultModel<ReportResponse[]> result = HttpUtil.request(appId, secretKey, host + "/inter/getReport", reportRequest, ReportResponse[].class);
+		return result;
+	}
+
+	@Override
+	public ResultModel<MoResponse[]> getMo(String appId, String secretKey, String host, MoRequest request) {
+		ResultModel<MoResponse[]> result = HttpUtil.request(appId, secretKey, host + "/inter/getMo", request, MoResponse[].class);
+		return result;
+	}
+
+	@Override
+	public ResultModel<RetrieveReportResponse> retrieveReport(String appId, String timestamp, String sign, String host, RetrieveReportRequest reportRequest) {
+		Map<String, String> params = new HashMap<String, String>();
+		params.put("appId", appId);
+		params.put("timestamp", timestamp);
+		params.put("sign", sign);
+		params.put("startTime", reportRequest.getStartTime());
+		params.put("endTime", reportRequest.getEndTime());
+		params.put("smsId", reportRequest.getSmsId());
+		ResultModel<String> result = HttpUtil.request(params, host + "/report/retrieveReport", "UTF-8", String.class);
+		ResultModel<RetrieveReportResponse> resultModel = new ResultModel<RetrieveReportResponse>(result.getCode(), new RetrieveReportResponse(result.getCode()));
+		return resultModel;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/task/ContrastHostTask.java b/server/emaysms/src/main/java/cn/emay/sdk/task/ContrastHostTask.java
new file mode 100644
index 0000000..c29384f
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/task/ContrastHostTask.java
@@ -0,0 +1,31 @@
+package cn.emay.sdk.task;
+
+import java.io.File;
+
+import cn.emay.sdk.common.CommonConstants;
+import cn.emay.sdk.util.HostUtil;
+
+public class ContrastHostTask implements Runnable {
+
+	public File file;
+
+	public String appId;
+
+	public String secretkey;
+
+	public ContrastHostTask(File file, String appId, String secretkey) {
+		this.file = file;
+		this.appId = appId;
+		this.secretkey = secretkey;
+	}
+
+	@Override
+	public void run() {
+		if (CommonConstants.interList != null) {
+			CommonConstants.bestUrl = HostUtil.getUrl(appId, secretkey);
+			HostUtil.contrastWrite(file, appId, CommonConstants.bestUrl);
+			CommonConstants.isBest = true;
+		}
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/AES.java b/server/emaysms/src/main/java/cn/emay/sdk/util/AES.java
new file mode 100644
index 0000000..0e990cc
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/AES.java
@@ -0,0 +1,141 @@
+package cn.emay.sdk.util;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * AES鍔犺В瀵嗗伐鍏�
+ * 
+ * @author Frank
+ *
+ */
+public class AES {
+	
+	public final static String ALGORITHM_AEPP = "AES/ECB/PKCS5Padding";
+
+	/**
+	 * AES鍔犲瘑
+	 * 
+	 * @param content
+	 *            鍐呭
+	 * @param password
+	 *            瀵嗛挜
+	 * @param algorithm
+	 *            绠楁硶
+	 * @return 鍔犲瘑鍚庢暟鎹�
+	 */
+	public static byte[] encrypt(byte[] content, byte[] password, String algorithm) {
+		if (content == null || password == null)
+			return null;
+		try {
+			Cipher cipher = null;
+			if (algorithm.endsWith("PKCS7Padding")) {
+				cipher = Cipher.getInstance(algorithm, "BC");
+			} else {
+				cipher = Cipher.getInstance(algorithm);
+			}
+			cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(password, "AES"));
+			return cipher.doFinal(content);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * AES瑙e瘑
+	 * 
+	 * @param content
+	 *            鍔犲瘑鍐呭
+	 * @param password
+	 *            瀵嗛挜
+	 * @param algorithm
+	 *            绠楁硶
+	 * @return 瑙e瘑鍚庢暟鎹�
+	 */
+	public static byte[] decrypt(byte[] content, byte[] password, String algorithm) {
+		if (content == null || password == null)
+			return null;
+		try {
+			Cipher cipher = null;
+			if (algorithm.endsWith("PKCS7Padding")) {
+				cipher = Cipher.getInstance(algorithm, "BC");
+			} else {
+				cipher = Cipher.getInstance(algorithm);
+			}
+			cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(password, "AES"));
+			byte[] bytes = cipher.doFinal(content);
+			return bytes;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * AES鍔犲瘑
+	 * 
+	 * @param content
+	 *            鍐呭
+	 * @param password
+	 *            瀵嗛挜
+	 * @param algorithm
+	 *            绠楁硶
+	 * @param ivStr
+	 *            鍚戦噺
+	 * @return 鍔犲瘑鍚庢暟鎹�
+	 */
+	public static byte[] encrypt(byte[] content, byte[] password, byte[] ivStr, String algorithm) {
+		if (content == null || password == null)
+			return null;
+		try {
+			Cipher cipher = null;
+			if (algorithm.endsWith("PKCS7Padding")) {
+				cipher = Cipher.getInstance(algorithm, "BC");
+			} else {
+				cipher = Cipher.getInstance(algorithm);
+			}
+			IvParameterSpec iv = new IvParameterSpec(ivStr);
+			cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(password, "AES"), iv);
+			return cipher.doFinal(content);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	/**
+	 * AES瑙e瘑
+	 * 
+	 * @param content
+	 *            鍔犲瘑鍐呭
+	 * @param password
+	 *            瀵嗛挜
+	 * @param algorithm
+	 *            绠楁硶
+	 * @param ivStr
+	 *            鍚戦噺
+	 * @return 瑙e瘑鍚庢暟鎹�
+	 */
+	public static byte[] decrypt(byte[] content, byte[] password, byte[] ivStr, String algorithm) {
+		if (content == null || password == null)
+			return null;
+		try {
+			Cipher cipher = null;
+			if (algorithm.endsWith("PKCS7Padding")) {
+				cipher = Cipher.getInstance(algorithm, "BC");
+			} else {
+				cipher = Cipher.getInstance(algorithm);
+			}
+			IvParameterSpec iv = new IvParameterSpec(ivStr);
+			cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(password, "AES"), iv);
+			byte[] bytes = cipher.doFinal(content);
+			return bytes;
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/DateUtil.java b/server/emaysms/src/main/java/cn/emay/sdk/util/DateUtil.java
new file mode 100644
index 0000000..675b4f2
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/DateUtil.java
@@ -0,0 +1,193 @@
+package cn.emay.sdk.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+public class DateUtil {
+
+	public static final long ONE_HOUR_TIME_LONG = 3600000;
+
+	public static String toString(Date date, String format) {
+		String dateStr = null;
+		try {
+			SimpleDateFormat sdf = new SimpleDateFormat(format);
+			dateStr = sdf.format(date);
+		} catch (Exception e) {
+		}
+		return dateStr;
+	}
+
+	public static Date parseDate(String dateStr, String format) {
+		Date date = null;
+		try {
+			SimpleDateFormat sdf = new SimpleDateFormat(format);
+			date = sdf.parse(dateStr);
+		} catch (Exception e) {
+		}
+		return date;
+	}
+
+	/**
+	 * 鑾峰彇鏃ユ湡褰撳ぉ鐨勬渶灏忔椂闂存棩鏈�,0鐐�
+	 */
+	public static Date getMinTimeDateByDate(Date date) {
+		if (date == null) {
+			date = new Date();
+		}
+		String datestr = toString(date, "yyyyMMdd");
+		return parseDate(datestr, "yyyyMMdd");
+	}
+
+	/**
+	 * 鑾峰彇鏃ユ湡褰撳ぉ鐨勬渶澶ф椂闂存棩鏈�,12鐐规暣
+	 */
+	public static Date getMaxTimeDateByDate(Date date) {
+		if (date == null) {
+			date = new Date();
+		}
+		String datestr = toString(date, "yyyyMMdd");
+		Date d = parseDate(datestr, "yyyyMMdd");
+		return new Date(d.getTime() + 24l * 60l * 60l * 1000l - 1l);
+	}
+
+	public static long subTime(Date startDate, Date endDate) {
+		return endDate.getTime() - startDate.getTime();
+	}
+
+	/**
+	 * 鑾峰彇涓婃湀绗竴澶╂渶鏃╂椂闂�
+	 *
+	 * @return Date
+	 */
+	public static Date getLastMonthFirstDay() {
+		Calendar cal_1 = Calendar.getInstance();// 鑾峰彇褰撳墠鏃ユ湡
+		cal_1.setTime(getMinTimeDateByDate(new Date()));
+		cal_1.add(Calendar.MONTH, -1);
+		cal_1.set(Calendar.DAY_OF_MONTH, 1);
+		return cal_1.getTime();
+	}
+
+	/**
+	 * 鑾峰彇涓婃湀鏈�鍚庝竴澶╂渶鏅氭椂闂�
+	 *
+	 * @return Date
+	 */
+	public static Date getLastMonthLastDay() {
+		Calendar cale = Calendar.getInstance();
+		cale.setTime(getMinTimeDateByDate(new Date()));
+		cale.add(Calendar.MONTH, -1);
+		cale.set(Calendar.DAY_OF_MONTH, cale.getActualMaximum(Calendar.DAY_OF_MONTH));
+		return new Date(cale.getTime().getTime() + 1000l * 60l * 60l * 24l - 1l);
+	}
+
+	/**
+	 * 鑾峰彇鏈湀绗竴澶╂渶鏃╂椂闂�
+	 *
+	 * @return Date
+	 */
+	public static Date getNowMonthFirstDay() {
+		Calendar cal_1 = Calendar.getInstance();// 鑾峰彇褰撳墠鏃ユ湡
+		cal_1.setTime(getMinTimeDateByDate(new Date()));
+		cal_1.add(Calendar.MONTH, 0);
+		cal_1.set(Calendar.DAY_OF_MONTH, 1);
+		return cal_1.getTime();
+	}
+
+	/**
+	 * 鑾峰彇鏈湀鏈�鍚庝竴澶╂渶鏅氭椂闂�
+	 *
+	 * @return Date
+	 */
+	public static Date getNowMonthLastDay() {
+		Calendar cale = Calendar.getInstance();
+		cale.setTime(getMinTimeDateByDate(new Date()));
+		cale.set(Calendar.DAY_OF_MONTH, cale.getActualMaximum(Calendar.DAY_OF_MONTH));
+		return new Date(cale.getTime().getTime() + 1000l * 60l * 60l * 24l - 1l);
+	}
+
+	/**
+	 * 鑾峰彇褰撳墠鏃堕棿鍙橀噺鍐呮湀浠界殑绗竴澶╃殑鏃ユ湡
+	 * 
+	 * @param changeMonthNum
+	 *            鍙樺姩鏈堜唤鏁般�備緥濡� -1锛氬墠涓�涓湀鐨勬渶灏忔棩鏈� 1 锛氬悗涓�涓湀鐨勬渶灏忔棩鏈�
+	 * @return Date
+	 */
+	public static Date getOtherMonthFirstDay(int changeMonthNum) {
+		Calendar cale = Calendar.getInstance();
+		Date nowMonthFirstDay = getNowMonthFirstDay();
+		cale.setTime(nowMonthFirstDay);
+		cale.add(Calendar.MONTH, changeMonthNum);
+		return cale.getTime();
+	}
+
+	/**
+	 * 鑾峰彇鏈湀鏈�鍚庝竴澶�
+	 *
+	 * @return Date
+	 */
+	public static Date getTheMonthLastDay(Date date) {
+		if (date == null) {
+			return null;
+		}
+		Calendar cale = Calendar.getInstance();
+		cale.setTime(date);
+		cale.set(Calendar.DAY_OF_MONTH, cale.getActualMaximum(Calendar.DAY_OF_MONTH));
+		cale.set(Calendar.HOUR, 0);
+		cale.set(Calendar.HOUR_OF_DAY, 0);
+		cale.set(Calendar.MINUTE, 0);
+		cale.set(Calendar.SECOND, 0);
+		cale.set(Calendar.MILLISECOND, 0);
+		return cale.getTime();
+	}
+
+	/**
+	 * 杩斿洖涔嬪墠鏌愪箣鍚庣殑鏌愬ぉ寮�濮嬫棩鏈熴�佺粨鏉熸垨鑰呭綋鍓嶆椂鍒嗙
+	 *
+	 * @param date
+	 *            鏃ユ湡
+	 * @param n
+	 *            姝f暟涔嬪悗鐨勬煇澶╋紝璐熸暟涔嬪墠鏌愬ぉ
+	 * @param type
+	 *            杩斿洖鏁版嵁绫诲瀷锛屽紑濮�00:00:00 缁撴潫 23:59:59鎴栧綋
+	 *
+	 *            ex :
+	 *
+	 *            getOtherDay(new Date(), -1, ""); 鍓嶄竴澶╁綋鍓嶆椂鍒嗙
+	 *
+	 *            getOtherDay(new Date(), -1, "start"); 鍓嶄竴澶╁紑濮嬫椂闂�00:00:00
+	 *
+	 *            getOtherDay(new Date(), -1, "end"); 鍓嶄竴澶╃粨鏉熸椂闂�23:59:59
+	 * @return
+	 */
+	public static Date getOtherDay(Date date, int n, String type) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTime(date);
+		if ("start".equals(type)) {
+			calendar.set(Calendar.HOUR_OF_DAY, 0);
+			calendar.set(Calendar.SECOND, 0);
+			calendar.set(Calendar.MINUTE, 0);
+		} else if ("end".equals(type)) {
+			calendar.set(Calendar.HOUR_OF_DAY, 23);
+			calendar.set(Calendar.SECOND, 59);
+			calendar.set(Calendar.MINUTE, 59);
+		}
+		calendar.add(Calendar.DAY_OF_MONTH, n);
+		date = calendar.getTime();
+		return date;
+	}
+
+	public static void main(String[] args) {
+		Date otherDay = getOtherDay(new Date(), -1, "");
+		System.out.println(otherDay);
+		// System.out.println(toString(getTheMonthLastDay(new Date()),
+		// "yyyy-MM-dd HH:mm:ss"));
+		// System.out.println(toString(getLastMonthLastDay(), "yyyy-MM-dd
+		// HH:mm:ss"));
+		// System.out.println(toString(getNowMonthFirstDay(), "yyyy-MM-dd
+		// HH:mm:ss"));
+		// System.out.println(toString(getNowMonthLastDay(), "yyyy-MM-dd
+		// HH:mm:ss"));
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/GZIPUtils.java b/server/emaysms/src/main/java/cn/emay/sdk/util/GZIPUtils.java
new file mode 100644
index 0000000..a0d53e1
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/GZIPUtils.java
@@ -0,0 +1,105 @@
+package cn.emay.sdk.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * GZIP 鍘嬬缉宸ュ叿
+ * @author Frank
+ *
+ */
+public class GZIPUtils {
+
+	public static void main(String[] args) throws IOException {
+		String sst = "hahahah";
+		System.out.println(sst);
+		System.out.println(System.currentTimeMillis());
+		System.out.println("size:" + sst.length());
+		byte[] bytes = sst.getBytes();
+		System.out.println("length:" + bytes.length);
+		System.out.println(System.currentTimeMillis());
+		byte[] end = compress(bytes);
+		System.out.println(System.currentTimeMillis());
+		System.out.println("length:" + end.length);
+		System.out.println(System.currentTimeMillis());
+		byte[] start = decompress(end);
+		System.out.println(System.currentTimeMillis());
+		System.out.println("length:" + start.length);
+		System.out.println(new String(start));
+	}
+
+	/**
+	 * 鏁版嵁鍘嬬缉浼犺緭
+	 * 
+	 * @param is
+	 * @param os
+	 * @throws Exception
+	 */
+	public static void compressTransfe(byte[] bytes, OutputStream out) throws IOException {
+		GZIPOutputStream gos = null;
+		try {
+			gos = new GZIPOutputStream(out);
+			gos.write(bytes);
+			gos.finish();
+			gos.flush();
+		} finally{
+			if(gos != null){
+				gos.close();
+			}
+		}
+	}
+	
+	/**
+	 * 鏁版嵁鍘嬬缉
+	 * 
+	 * @param is
+	 * @param os
+	 * @throws Exception
+	 */
+	public static byte[] compress(byte[] bytes) throws IOException {
+		ByteArrayOutputStream out = null;
+		GZIPOutputStream gos = null;
+		try {
+			out = new ByteArrayOutputStream();
+			gos = new GZIPOutputStream(out);
+			gos.write(bytes);
+			gos.finish();
+			gos.flush();
+		} finally{
+			if(gos != null){
+				gos.close();
+			}
+			if(out != null){
+				out.close();
+			}
+		}
+		return out.toByteArray();
+	}
+	
+	/**
+	 * 鏁版嵁瑙e帇
+	 * 
+	 * @param in
+	 * @return
+	 * @throws IOException
+	 */
+	public static byte[] decompress(byte[] bytes) throws IOException {
+		ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+		GZIPInputStream gin = new GZIPInputStream(in);
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		int count;
+		byte data[] = new byte[1024];
+		while ((count = gin.read(data, 0, 1024)) != -1) {
+			out.write(data, 0, count);
+		}
+		out.flush();
+		out.close();
+		gin.close();
+		return out.toByteArray();
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/HostUtil.java b/server/emaysms/src/main/java/cn/emay/sdk/util/HostUtil.java
new file mode 100644
index 0000000..bc72336
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/HostUtil.java
@@ -0,0 +1,228 @@
+package cn.emay.sdk.util;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import cn.emay.sdk.common.CommonConstants;
+import cn.emay.sdk.core.dto.cache.UrlDTO;
+import cn.emay.sdk.core.dto.sms.common.ResultModel;
+import cn.emay.sdk.core.dto.sms.request.BalanceRequest;
+import cn.emay.sdk.core.dto.sms.response.BalanceResponse;
+import cn.emay.sdk.util.http.client.EmayHttpClient;
+import cn.emay.sdk.util.http.request.impl.EmayHttpRequestString;
+import cn.emay.sdk.util.http.response.impl.string.EmayHttpResponseString;
+import cn.emay.sdk.util.http.response.impl.string.EmayHttpResponseStringPraser;
+import cn.emay.sdk.util.json.JsonHelper;
+import cn.emay.sdk.util.json.gson.JsonArray;
+import cn.emay.sdk.util.json.gson.JsonElement;
+import cn.emay.sdk.util.json.gson.JsonParser;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+
+public class HostUtil {
+
+	public static String getUrl(String appId, String secretkey) {
+		String host = "";
+		List<String> list = CommonConstants.interList;
+		try {
+			if (list != null) {
+				long minUseTime = -1l;
+				for (String url : list) {
+					long useTime = getBalance(appId, secretkey, url);
+					if (useTime == -1) {
+						continue;
+					} else if (minUseTime == -1 || minUseTime > useTime) {
+						minUseTime = useTime;
+						host = url;
+					}
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return host;
+	}
+
+	public static void getSDKInter() {
+
+		EmayHttpClient client = new EmayHttpClient();
+		EmayHttpRequestString request = null;
+		try {
+			request = new EmayHttpRequestString(CommonConstants.getSdkUrl, "UTF-8", "POST", null, null, null);
+			EmayHttpResponseString res = client.service(request, new EmayHttpResponseStringPraser());
+			if (res != null && res.getResultCode().getCode().equals("SUCCESS")) {
+				String json = res.getResultString();
+				if (json != null && !json.equals("")) {
+					List<String> list = new ArrayList<String>();
+					JsonArray array = new JsonParser().parse(json).getAsJsonObject().getAsJsonArray("result");
+					Iterator<JsonElement> iterator = array.iterator();
+					while (iterator.hasNext()) {
+						JsonElement element = iterator.next();
+						String ip = element.getAsJsonObject().get("ip").getAsString();
+						Integer port = element.getAsJsonObject().get("port").getAsInt();
+						list.add("http://" + ip + ":" + port);
+					}
+					CommonConstants.interList = list;
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+	}
+
+	public static long getBalance(String appId, String secretKey, String host) {
+		long useTime = 0l;
+		try {
+			long time = System.currentTimeMillis();
+			BalanceRequest request = new BalanceRequest();
+			ResultModel<BalanceResponse> result = HttpUtil.request(appId, secretKey, host + "/inter/getBalance", request, BalanceResponse.class);
+			if (result != null && result.getCode().equals("SUCCESS")) {
+				useTime = System.currentTimeMillis() - time;
+			} else {
+				useTime = -1l;
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			useTime = -1l;
+		}
+		return useTime;
+
+	}
+
+	public static List<Entry<String, Integer>> sortMap(Map<String, Integer> map) {
+		List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(map.entrySet());
+		Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
+			public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
+				return (o2.getValue() - o1.getValue());
+			}
+		});
+		return list;
+	}
+
+	public static File creatCacheFile(String filePath) {
+		File file = null;
+		try {
+			if (StringUtil.isEmpty(filePath)) {
+				file = new File(CommonConstants.fileName);
+				if (!file.exists()) {
+					file.createNewFile();
+				}
+				return file;
+			}
+			file = new File(filePath);
+			if (!file.exists()) {
+				File fileParent = file.getParentFile();
+				if (!fileParent.exists()) {
+					fileParent.mkdirs();
+				}
+				file.createNewFile();
+			}
+			if (!file.canWrite() || !file.canRead()) {
+				return null;
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+		return file;
+	}
+
+	public static boolean writeFile(File file, String content) {
+		BufferedOutputStream bos = null;
+		try {
+			bos = new BufferedOutputStream(new FileOutputStream(file));
+			bos.write(content.getBytes("UTF-8"));
+			bos.flush();
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		} finally {
+			try {
+				bos.close();
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		return true;
+	}
+
+	public static String readFile(File file) {
+		long fileSize = file.length();
+		String json = "";
+		if (fileSize > Integer.MAX_VALUE) {
+			return null;
+		}
+		byte[] by = new byte[(int) fileSize];
+		try {
+			FileInputStream in = new FileInputStream(file);
+			int offset = 0;
+			int numRead = 0;
+			while (offset < by.length && (numRead = in.read(by, offset, by.length - offset)) >= 0) {
+				offset += numRead;
+			}
+			json = new String(by, "UTF-8");
+			in.close();
+		} catch (Exception e) {
+			return null;
+		}
+		return json;
+	}
+
+	public static void contrastWrite(File file, String appId, String host) {
+		List<UrlDTO> list = new ArrayList<UrlDTO>();
+		String json = readFile(file);
+		if (StringUtil.isEmpty(json)) {
+			UrlDTO dto = new UrlDTO(appId, host);
+			list.add(dto);
+			json = JsonHelper.toJsonString(list);
+			writeFile(file, json);
+		} else {
+			list = JsonHelper.fromJson(new TypeToken<List<UrlDTO>>() {
+			}, json);
+			Map<String, String> map = new HashMap<String, String>();
+			for (UrlDTO urlDTO : list) {
+				map.put(urlDTO.getAppId(), urlDTO.getUrl());
+				if (urlDTO.getAppId().equals(appId) && !urlDTO.getUrl().equals(host)) {
+					urlDTO.setUrl(host);
+					json = JsonHelper.toJsonString(list);
+					writeFile(file, json);
+					break;
+				}
+
+			}
+			if (!map.containsKey(appId)) {
+				list.add(new UrlDTO(appId, host));
+				writeFile(file, JsonHelper.toJsonString(list));
+			}
+		}
+	}
+
+	public static String getFileUrl(File file, String appId) {
+		String url = "";
+		String json = readFile(file);
+		if (StringUtil.isEmpty(json)) {
+			return url;
+		} else {
+			List<UrlDTO> list = JsonHelper.fromJson(new TypeToken<List<UrlDTO>>() {
+			}, json);
+			for (UrlDTO urlDTO : list) {
+				if (urlDTO.getAppId().equals(appId)) {
+					url = urlDTO.getUrl();
+					break;
+				}
+			}
+		}
+		return url;
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/HttpUtil.java b/server/emaysms/src/main/java/cn/emay/sdk/util/HttpUtil.java
new file mode 100644
index 0000000..986ed31
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/HttpUtil.java
@@ -0,0 +1,103 @@
+package cn.emay.sdk.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import cn.emay.sdk.common.CommonConstants;
+import cn.emay.sdk.core.dto.sms.common.ResultModel;
+import cn.emay.sdk.util.http.client.EmayHttpClient;
+import cn.emay.sdk.util.http.common.EmayHttpResultCode;
+import cn.emay.sdk.util.http.request.impl.EmayHttpRequestBytes;
+import cn.emay.sdk.util.http.request.impl.EmayHttpRequestKV;
+import cn.emay.sdk.util.http.response.impl.string.EmayHttpResponseBytes;
+import cn.emay.sdk.util.http.response.impl.string.EmayHttpResponseBytesPraser;
+import cn.emay.sdk.util.http.response.impl.string.EmayHttpResponseString;
+import cn.emay.sdk.util.http.response.impl.string.EmayHttpResponseStringPraser;
+import cn.emay.sdk.util.json.JsonHelper;
+
+public class HttpUtil {
+
+	private static String remoteSign = "SDK";
+
+	public static <T> ResultModel<T> request(String appId, String secretKey, String url, Object object, Class<T> clazz) {
+		String code = "SYSTEM";// 榛樿绯荤粺寮傚父
+		T result = null;
+		EmayHttpRequestBytes request = null;
+		try {
+			Map<String, String> headers = new HashMap<String, String>();
+			headers.put("appId", appId);
+			headers.put("encode", "UTF-8");
+			headers.put("gzip", "on");
+			headers.put("remoteSign", remoteSign);
+			byte[] bytes = JsonHelper.toJsonString(object).getBytes("UTF-8");
+			bytes = GZIPUtils.compress(bytes);
+			byte[] parambytes = AES.encrypt(bytes, secretKey.getBytes(), CommonConstants.algorithm);
+			request = new EmayHttpRequestBytes(url, "UTF-8", "POST", headers, null, parambytes);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return new ResultModel<T>(code, result);
+		}
+		try {
+			EmayHttpClient client = new EmayHttpClient();
+			EmayHttpResponseBytes res = client.service(request, new EmayHttpResponseBytesPraser());
+			if (res == null) {
+				return new ResultModel<T>(code, result);
+			}
+			if (res.getResultCode().equals(EmayHttpResultCode.SUCCESS)) {
+				if (res.getHttpCode() == 200) {
+					code = res.getHeaders().get("result");
+					if (code.equals("SUCCESS")) {
+						byte[] data = res.getResultBytes();
+						data = AES.decrypt(data, secretKey.getBytes(), CommonConstants.algorithm);
+						data = GZIPUtils.decompress(data);
+						String json = new String(data, "UTF-8");
+						result = JsonHelper.fromJson(clazz, json);
+					}
+				} else {
+					return new ResultModel<T>(code, result);
+				}
+			} else {
+				return new ResultModel<T>(code, result);
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			return new ResultModel<T>(code, result);
+		}
+		return new ResultModel<T>(code, result);
+	}
+
+	public static <T> ResultModel<T> request(Map<String, String> params, String url, String encode, Class<T> clazz) {
+		String code = "SYSTEM";// 榛樿绯荤粺寮傚父
+		T result = null;
+		EmayHttpRequestKV request = new EmayHttpRequestKV(url, encode, "POST", null, null, params);
+		EmayHttpClient client = new EmayHttpClient();
+		String json = null;
+		try {
+			String mapst = "";
+			for (String key : params.keySet()) {
+				String value = params.get(key);
+				mapst += key + "=" + value + "&";
+			}
+			mapst = mapst.substring(0, mapst.length() - 1);
+			EmayHttpResponseString res = client.service(request, new EmayHttpResponseStringPraser());
+			if (res == null) {
+				return new ResultModel<T>(code, result);
+			}
+			if (res.getResultCode().equals(EmayHttpResultCode.SUCCESS)) {
+				if (res.getHttpCode() == 200) {
+					code = res.getResultCode().getCode();
+					json = res.getResultString();
+					result = JsonHelper.fromJson(clazz, json);
+				} else {
+					return new ResultModel<T>(code, result);
+				}
+			} else {
+				return new ResultModel<T>(code, result);
+			}
+		} catch (Exception e) {
+			return new ResultModel<T>(code, result);
+		}
+		return new ResultModel<T>(code, result);
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/Md5.java b/server/emaysms/src/main/java/cn/emay/sdk/util/Md5.java
new file mode 100644
index 0000000..5153bef
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/Md5.java
@@ -0,0 +1,52 @@
+package cn.emay.sdk.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * md5鐗瑰緛鐮佸伐鍏�
+ * 
+ * @author Frank
+ *
+ */
+public class Md5 {
+
+	/**
+	 * MD5
+	 * @param bytes
+	 * @return
+	 */
+	public static String md5(byte[] bytes) {
+		if (bytes == null || bytes.length == 0)
+			return null;
+		String s = null;
+		char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+		try {
+			MessageDigest md = MessageDigest.getInstance("MD5");
+			md.update(bytes);
+			byte tmp[] = md.digest();
+			char str[] = new char[16 * 2];
+			int k = 0;
+			for (int i = 0; i < 16; i++) {
+				byte byte0 = tmp[i];
+				str[k++] = hexDigits[byte0 >>> 4 & 0xf];
+				str[k++] = hexDigits[byte0 & 0xf];
+			}
+			s = new String(str);
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+		}
+		return s;
+	}
+
+	/**
+	 * MD5[16浣峕
+	 * 
+	 * @param bytes
+	 * @return
+	 */
+	public static String md5For16(byte[] bytes) {
+		return md5(bytes).substring(8,24);
+	}
+	
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/OnlyIdGenerator.java b/server/emaysms/src/main/java/cn/emay/sdk/util/OnlyIdGenerator.java
new file mode 100644
index 0000000..a1f9452
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/OnlyIdGenerator.java
@@ -0,0 +1,101 @@
+package cn.emay.sdk.util;
+
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * 鍞竴搴忓垪鐢熸垚鍣�
+ *
+ * @author Frank
+ *
+ */
+public class OnlyIdGenerator {
+
+	/**
+	 * 琛ュ叏瀛楁
+	 */
+	private static String ZERO10 = "0000000000";
+
+	/**
+	 * ID 搴忓垪
+	 */
+	private static int ID_INDEX = 0;
+
+	/**
+	 * ID 搴忓垪璁$畻鏃堕棿
+	 */
+	private static long ID_END_MILLIS = 0l;
+
+	/**
+	 * 鐢熸垚18浣嶇殑鍞竴鏁板瓧ID<br/>
+	 * 褰撳墠姣鏁�(13) + 搴忓垪(2) + 鏈嶅姟鏍囪瘑(3)<br/>
+	 * 绛夊緟鏈哄埗锛氬鏋滃綋鍓嶆绉掓暟鐨勫簭鍒楄秴杩�100锛屽垯绛夊緟绱姞鍒颁笅涓�姣銆傚嵆姣忔绉掓渶澶氱敓鎴�100涓猻msid
+	 *
+	 * @return
+	 */
+	public synchronized static String genOnlyId(String serverCode) {
+		long millis = System.currentTimeMillis();
+		if (millis == ID_END_MILLIS) {
+			ID_INDEX++;
+		} else {
+			ID_INDEX = 0;
+			ID_END_MILLIS = millis;
+		}
+		if (ID_INDEX > 99) {
+			boolean wait = true;
+			while (wait) {
+				millis = System.currentTimeMillis();
+				if (millis != ID_END_MILLIS) {
+					wait = false;
+					ID_END_MILLIS = millis;
+					ID_INDEX = 0;
+				}
+			}
+		}
+		String indexStr = ZERO10 + ID_INDEX;
+		indexStr = indexStr.substring(indexStr.length() - 2, indexStr.length());
+		String smsId = millis + indexStr + serverCode;
+		return smsId;
+	}
+
+	/**
+	 * BID 搴忓垪
+	 */
+	private static int BID_INDEX = 0;
+
+	/**
+	 * BID 搴忓垪璁$畻鏃堕棿
+	 */
+	private static String BID_END_MILLIS = "";
+
+	/**
+	 * 鐢熸垚鍞竴BID缂栫爜<br/>
+	 * yyyyMMddHHmmss(14) + 搴忓垪(7) + 鏈嶅姟鏍囪瘑(3)<br/>
+	 *
+	 */
+	public synchronized static String genOblyBId(String serverCode) {
+		String time = DateUtil.toString(new Date(), "yyyyMMddHHmmss");
+		if (BID_END_MILLIS.equals(time)) {
+			BID_INDEX++;
+		} else {
+			BID_INDEX = 0;
+			BID_END_MILLIS = time;
+		}
+		String indexStr = ZERO10 + BID_INDEX;
+		indexStr = indexStr.substring(indexStr.length() - 7, indexStr.length());
+		String no = time + indexStr + serverCode;
+		return no;
+	}
+
+	public static String paramSmsId(String smsId) {
+		Calendar calendar = Calendar.getInstance();
+		calendar.setTimeInMillis(Long.parseLong(smsId.substring(0, 13)));
+		String name = DateUtil.toString(calendar.getTime(), "yyyyMMdd");
+		return name;
+	}
+
+	public static void main(String[] args) {
+		String genid = genOnlyId("111");
+		System.out.println(genid + "\t" + genid.length());
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/PropertiesUtil.java b/server/emaysms/src/main/java/cn/emay/sdk/util/PropertiesUtil.java
new file mode 100644
index 0000000..05a2632
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/PropertiesUtil.java
@@ -0,0 +1,271 @@
+package cn.emay.sdk.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 閰嶇疆鏂囦欢璇诲彇宸ュ叿
+ * 
+ * @author 涓滄棴
+ *
+ */
+public class PropertiesUtil {
+
+	/**
+	 * 鑾峰彇鍗曚竴鍙傛暟鐨勫��
+	 * 
+	 * @param key
+	 * @param propertiesFilePath
+	 * @return
+	 */
+	public static String getProperty(String key, String propertiesClassPath) {
+		Properties properties = getProperties(propertiesClassPath);
+		if (properties == null)
+			return null;
+		return properties.getProperty(key);
+	}
+
+	/**
+	 * 鑾峰彇鍗曚竴鍙傛暟鐨勬暟鍊�
+	 * 
+	 * @param key
+	 * @param propertiesClassPath
+	 * @param defaultValue
+	 * @return
+	 */
+	public static int getIntProperty(String key, String propertiesClassPath, int defaultValue) {
+		Properties properties = getProperties(propertiesClassPath);
+		if (properties == null || !properties.containsKey(key))
+			return defaultValue;
+		int value = defaultValue;
+		try {
+			value = Integer.valueOf(properties.getProperty(key));
+		} catch (Exception e) {
+		}
+		return value;
+	}
+
+	/**
+	 * 鑾峰彇鍗曚竴鍙傛暟鐨勬暟鍊�
+	 * 
+	 * @param key
+	 * @param propertiesClassPath
+	 * @param defaultValue
+	 * @return
+	 */
+	public static long getLongProperty(String key, String propertiesClassPath, long defaultValue) {
+		Properties properties = getProperties(propertiesClassPath);
+		if (properties == null || !properties.containsKey(key))
+			return defaultValue;
+		long value = defaultValue;
+		try {
+			value = Long.valueOf(properties.getProperty(key));
+		} catch (Exception e) {
+		}
+		return value;
+	}
+
+	/**
+	 * 鑾峰彇鍗曚竴鍙傛暟鐨勫竷灏斿��
+	 * 
+	 * @param key
+	 * @param propertiesClassPath
+	 * @param defaultValue
+	 * @return
+	 */
+	public static boolean getBooleanProperty(String key, String propertiesClassPath, boolean defaultValue) {
+		Properties properties = getProperties(propertiesClassPath);
+		if (properties == null || !properties.containsKey(key))
+			return defaultValue;
+		String valuep = properties.getProperty(key);
+		if (valuep.equalsIgnoreCase("true") || valuep.equalsIgnoreCase("on") || valuep.equalsIgnoreCase("1"))
+			return true;
+		if (valuep.equalsIgnoreCase("false") || valuep.equalsIgnoreCase("off") || valuep.equalsIgnoreCase("0"))
+			return false;
+		return defaultValue;
+	}
+
+	public static float getFloatProperty(String key, String propertiesClassPath, float defaultValue) {
+		Properties properties = getProperties(propertiesClassPath);
+		if (properties == null || !properties.containsKey(key))
+			return defaultValue;
+		float value = defaultValue;
+		try {
+			value = Float.valueOf(properties.getProperty(key));
+		} catch (Exception e) {
+		}
+		return value;
+	}
+
+	public static Date getDateProperty(String key, String propertiesClassPath, String format, Date defaultValue) {
+		Properties properties = getProperties(propertiesClassPath);
+		if (properties == null || !properties.containsKey(key))
+			return defaultValue;
+		Date date = DateUtil.parseDate(properties.getProperty(key), format);
+		if (date == null)
+			date = defaultValue;
+		return date;
+	}
+
+	public static BigDecimal getBigDecimalProperty(String key, String propertiesClassPath, String format, BigDecimal defaultValue) {
+		Properties properties = getProperties(propertiesClassPath);
+		if (properties == null || !properties.containsKey(key))
+			return defaultValue;
+		BigDecimal value = defaultValue;
+		try {
+			value = new BigDecimal(properties.getProperty(key));
+		} catch (Exception e) {
+		}
+		return value;
+	}
+
+	/**
+	 * 鑾峰彇鎵归噺鍙傛暟鐨勫��
+	 * 
+	 * @param propertiesFilePath
+	 * @return
+	 */
+	public static Map<String, String> getPropertys(String propertiesClassPath) {
+		Map<String, String> map = new ConcurrentHashMap<String, String>();
+		Properties properties = getProperties(propertiesClassPath);
+		if (properties == null) {
+			return map;
+		}
+		for (String name : properties.stringPropertyNames()) {
+			map.put(name, properties.getProperty(name));
+		}
+		return map;
+	}
+
+	/**
+	 * 鑾峰彇鍘熺敓鐨凱roperties
+	 * 
+	 * @Title: getProperties
+	 * @Description: 鑾峰彇鍘熺敓鐨凱roperties
+	 * @param propertiesFilePath
+	 * @return Properties
+	 */
+	public static Properties getProperties(String propertiesClassPath) {
+		Properties properties = new Properties();
+		InputStream in = null;
+		try {
+			in = PropertiesUtil.class.getClassLoader().getResourceAsStream(propertiesClassPath);
+			if(in != null){
+				properties.load(in);
+			}
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		} finally {
+			if (in != null) {
+				try {
+					in.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		return properties;
+	}
+
+	/**
+	 * 鑾峰彇鍘熺敓鐨凱roperties
+	 * 
+	 * @Title: getProperties
+	 * @Description: 鑾峰彇鍘熺敓鐨凱roperties
+	 * @param propertiesFilePath
+	 * @return Properties
+	 */
+	public static Properties getPropertiesByFile(File file) {
+		Properties properties = new Properties();
+		FileInputStream fis = null;
+		try {
+			fis = new FileInputStream(file);
+			if(fis != null){
+				properties.load(fis);
+			}
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		} finally {
+			if (fis != null) {
+				try {
+					fis.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		return properties;
+	}
+
+	/**
+	 * 鑾峰彇鍘熺敓鐨凱roperties
+	 * 
+	 * @Title: getProperties
+	 * @Description: 鑾峰彇鍘熺敓鐨凱roperties
+	 * @param propertiesFilePath
+	 * @return Properties
+	 */
+	public static Properties getPropertiesByFile(String propertiesFilePath) {
+		Properties properties = new Properties();
+		FileInputStream fis = null;
+		try {
+			File file = new File(propertiesFilePath);
+			fis = new FileInputStream(file);
+			if(fis != null){
+				properties.load(fis);
+			}
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		} finally {
+			if (fis != null) {
+				try {
+					fis.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		return properties;
+	}
+
+	/**
+	 * 鑾峰彇鍗曚竴鍙傛暟鐨勫��
+	 * 
+	 * @param key
+	 * @param propertiesFilePath
+	 * @return
+	 */
+	public static String getPropertyByFile(String key, String propertiesFilePath) {
+		Properties properties = getPropertiesByFile(propertiesFilePath);
+		if (properties == null) {
+			return null;
+		}
+		return properties.getProperty(key);
+	}
+
+	/**
+	 * 鑾峰彇鎵归噺鍙傛暟鐨勫��
+	 * 
+	 * @param propertiesFilePath
+	 * @return
+	 */
+	public static Map<String, String> getPropertysByFile(String propertiesFilePath) {
+		Map<String, String> map = new ConcurrentHashMap<String, String>();
+		Properties properties = getPropertiesByFile(propertiesFilePath);
+		if (properties == null) {
+			return map;
+		}
+		for (String name : properties.stringPropertyNames()) {
+			map.put(name, properties.getProperty(name));
+		}
+		return map;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/StringUtil.java b/server/emaysms/src/main/java/cn/emay/sdk/util/StringUtil.java
new file mode 100644
index 0000000..1feab63
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/StringUtil.java
@@ -0,0 +1,19 @@
+package cn.emay.sdk.util;
+
+public class StringUtil {
+
+	/**
+	 * 鍒ゆ柇鏄惁涓虹┖
+	 * 
+	 * @param val
+	 * @return
+	 */
+	public static boolean isEmpty(String val) {
+		if (val == null || (val.trim()).length() == 0 || "null".equals(val.trim())) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/exception/SDKParamsException.java b/server/emaysms/src/main/java/cn/emay/sdk/util/exception/SDKParamsException.java
new file mode 100644
index 0000000..925d7ee
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/exception/SDKParamsException.java
@@ -0,0 +1,23 @@
+package cn.emay.sdk.util.exception;
+
+public class SDKParamsException extends Exception {
+
+	private static final long serialVersionUID = 1L;
+
+	public SDKParamsException() {
+		super();
+	}
+
+	public SDKParamsException(String message) {
+		super(message);
+	}
+
+	public SDKParamsException(Throwable throwable) {
+		super(throwable);
+	}
+
+	public SDKParamsException(String message, Throwable throwable) {
+		super(message, throwable);
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/client/EmayHttpClient.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/client/EmayHttpClient.java
new file mode 100644
index 0000000..14d7281
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/client/EmayHttpClient.java
@@ -0,0 +1,349 @@
+package cn.emay.sdk.util.http.client;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import cn.emay.sdk.util.http.common.EmayHttpResultCode;
+import cn.emay.sdk.util.http.request.EmayHttpRequest;
+import cn.emay.sdk.util.http.response.EmayHttpResponsePraser;
+
+/**
+ * EMAY http瀹㈡埛绔�
+ * 
+ * @author Frank
+ *
+ */
+public class EmayHttpClient {
+
+	/**
+	 * 閾炬帴瓒呮椂鏃堕棿(s)
+	 */
+	private int httpConnectionTimeOut = 30;
+
+	/**
+	 * 鏁版嵁浼犺緭瓒呮椂鏃堕棿(s)
+	 */
+	private int httpReadTimeOut = 30;
+
+	public EmayHttpClient() {
+
+	}
+
+	/**
+	 * 
+	 * @param httpConnectionTimeOut
+	 *            閾炬帴瓒呮椂鏃堕棿(s)
+	 * @param httpReadTimeOut
+	 *            鏁版嵁浼犺緭瓒呮椂鏃堕棿(s)
+	 */
+	public EmayHttpClient(int httpConnectionTimeOut, int httpReadTimeOut) {
+		this.httpConnectionTimeOut = httpConnectionTimeOut;
+		this.httpReadTimeOut = httpReadTimeOut;
+	}
+
+	/**
+	 * 鍙戦�丠TTP璇锋眰
+	 * 
+	 * @param request
+	 *            璇锋眰
+	 * @param praser
+	 *            鍝嶅簲瑙f瀽鍣�
+	 * @return T 鍝嶅簲
+	 */
+	public <T> T service(EmayHttpRequest<?> request, EmayHttpResponsePraser<T> praser) {
+		EmayHttpResultCode code = EmayHttpResultCode.SUCCESS;
+		if (request.getUrl() == null || request.getUrl().length() == 0) {
+			code = EmayHttpResultCode.ERROR_URL_NULL;
+			return praser.prase(request.getCharSet(), code, 0, null, null, null);
+		}
+		HttpURLConnection conn = null;
+		int httpCode = 0;
+		Map<String, String> headers = null;
+		List<String> cookies = null;
+		ByteArrayOutputStream outputStream = null;
+		try {
+			conn = this.createConnection(request);
+			this.fillConnection(conn, request);
+			this.request(conn, request);
+			httpCode = conn.getResponseCode();
+			headers = this.getHeaders(conn, request.getCharSet());
+			cookies = this.getCookies(conn, request.getCharSet());
+			outputStream = this.getResultOutputStream(conn);
+		} catch (KeyManagementException e) {
+			code = EmayHttpResultCode.ERROR_HTTPS_SSL;
+			e.printStackTrace();
+		} catch (NoSuchAlgorithmException e) {
+			code = EmayHttpResultCode.ERROR_HTTPS_SSL;
+			e.printStackTrace();
+		} catch (ProtocolException e) {
+			code = EmayHttpResultCode.ERROR_METHOD;
+			e.printStackTrace();
+		} catch (UnsupportedEncodingException e) {
+			code = EmayHttpResultCode.ERROR_CHARSET;
+			e.printStackTrace();
+		} catch (MalformedURLException e) {
+			code = EmayHttpResultCode.ERROR_URL;
+			httpCode = 500;
+			e.printStackTrace();
+		} catch (IOException e) {
+			code = EmayHttpResultCode.ERROR_CONNECT;
+			e.printStackTrace();
+		} finally {
+			if (conn != null) {
+				conn.disconnect();
+			}
+		}
+		T t = null;
+		try {
+			t = praser.prase(request.getCharSet(), code, httpCode, headers, cookies, outputStream);
+		} catch (Exception e) {
+			e.printStackTrace();
+		} finally {
+			if (outputStream != null) {
+				try {
+					outputStream.flush();
+					outputStream.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		return t;
+	}
+
+	/**
+	 * 鑾峰彇HTTP鍝嶅簲澶�
+	 * 
+	 * @param conn
+	 * @param charSet
+	 * @return
+	 * @throws UnsupportedEncodingException
+	 */
+	private Map<String, String> getHeaders(HttpURLConnection conn, String charSet) throws UnsupportedEncodingException {
+		Map<String, String> resultHeaders = new HashMap<String, String>();
+		Map<String, List<String>> header = conn.getHeaderFields();
+		if (header != null && header.size() > 0) {
+			for (Entry<String, List<String>> entry : header.entrySet()) {
+				if (!"Set-Cookie".equalsIgnoreCase(entry.getKey())) {
+					String valuer = "";
+					if (entry.getValue() != null && entry.getValue().size() > 0) {
+						for (String value : entry.getValue()) {
+							valuer += new String(value.getBytes("ISO-8859-1"), charSet) + ",";
+						}
+						valuer = valuer.substring(0, valuer.length() - 1);
+					}
+					resultHeaders.put(entry.getKey(), valuer);
+				}
+			}
+		}
+		return resultHeaders;
+	}
+
+	/**
+	 * 鑾峰彇HTTP鍝嶅簲Cookies
+	 * 
+	 * @param conn
+	 * @param charSet
+	 * @return
+	 * @throws UnsupportedEncodingException
+	 */
+	private List<String> getCookies(HttpURLConnection conn, String charSet) throws UnsupportedEncodingException {
+		List<String> resultC = new ArrayList<String>();
+		List<String> cookies = null;
+		Map<String, List<String>> header = conn.getHeaderFields();
+		if (header != null && header.size() > 0) {
+			cookies = header.get("Set-Cookie");
+		}
+		if (cookies != null) {
+			for (String cookie : cookies) {
+				resultC.add(new String(cookie.getBytes("ISO-8859-1"), charSet));
+			}
+		}
+		return cookies;
+	}
+
+	/**
+	 * 鑾峰彇HTTP鍝嶅簲鏁版嵁娴�
+	 * 
+	 * @param conn
+	 * @return
+	 * @throws IOException
+	 */
+	private ByteArrayOutputStream getResultOutputStream(HttpURLConnection conn) throws IOException {
+		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+		InputStream is = conn.getInputStream();
+		try {
+			if (is != null) {
+				byte[] buffer = new byte[1024];
+				int len = 0;
+				while ((len = is.read(buffer)) != -1) {
+					outStream.write(buffer, 0, len);
+				}
+			}
+		} catch (IOException e) {
+			throw e;
+		} finally {
+			if (is != null) {
+				is.close();
+			}
+		}
+		return outStream;
+	}
+
+	/**
+	 * 鍙戦�丠ttp璇锋眰
+	 * 
+	 * @param conn
+	 * @param request
+	 * @throws IOException
+	 */
+	private void request(HttpURLConnection conn, EmayHttpRequest<?> request) throws IOException {
+		if (request.getMethod().equalsIgnoreCase("POST")) {
+			conn.setDoOutput(true);
+			// conn.connect();
+			if (request.getParams() != null) {
+				DataOutputStream out = new DataOutputStream(conn.getOutputStream());
+				out.write(request.paramsToBytesForPost());
+				out.flush();
+				out.close();
+			}
+		} else {
+			conn.connect();
+		}
+	}
+
+	/**
+	 * 娣诲姞璇锋眰淇℃伅
+	 * 
+	 * @param conn
+	 * @param request
+	 * @throws ProtocolException
+	 */
+	private void fillConnection(HttpURLConnection conn, EmayHttpRequest<?> request) throws ProtocolException {
+		this.fillTimeout(conn);
+		this.filleMethod(conn, request);
+		this.fillHeaders(conn, request);
+		this.fillCookies(conn, request);
+	}
+
+	/**
+	 * 娣诲姞瓒呮椂鏃堕棿
+	 * 
+	 * @param conn
+	 */
+	private void fillTimeout(HttpURLConnection conn) {
+		if (httpConnectionTimeOut != 0) {
+			conn.setConnectTimeout(httpConnectionTimeOut * 1000);
+		}
+		if (httpReadTimeOut != 0) {
+			conn.setReadTimeout(httpReadTimeOut * 1000);
+		}
+	}
+
+	/**
+	 * 鎸囧畾HTTP鏂规硶
+	 * 
+	 * @param conn
+	 * @param request
+	 * @throws ProtocolException
+	 */
+	private void filleMethod(HttpURLConnection conn, EmayHttpRequest<?> request) throws ProtocolException {
+		conn.setRequestMethod(request.getMethod().toUpperCase());
+	}
+
+	/**
+	 * 娣诲姞澶翠俊鎭�
+	 * 
+	 * @param conn
+	 * @param request
+	 */
+	private void fillHeaders(HttpURLConnection conn, EmayHttpRequest<?> request) {
+		if (request.getHeaders() != null) {
+			for (Map.Entry<String, String> entry : request.getHeaders().entrySet()) {
+				conn.setRequestProperty(entry.getKey(), entry.getValue());
+			}
+		}
+	}
+
+	/**
+	 * 娣诲姞Cookies
+	 * 
+	 * @param conn
+	 * @param request
+	 */
+	private void fillCookies(HttpURLConnection conn, EmayHttpRequest<?> request) {
+		if (request.getCookies() != null) {
+			conn.setRequestProperty("Cookie", request.getCookies());
+		}
+	}
+
+	/**
+	 * 鍒涘缓Http閾炬帴
+	 * 
+	 * @param request
+	 * @return
+	 * @throws NoSuchAlgorithmException
+	 * @throws KeyManagementException
+	 * @throws MalformedURLException
+	 * @throws IOException
+	 */
+	private HttpURLConnection createConnection(EmayHttpRequest<?> request) throws NoSuchAlgorithmException, KeyManagementException, MalformedURLException, IOException {
+		URL console = new URL(request.getUrl());
+		HttpURLConnection conn;
+		if (request.isHttps()) {
+			SSLContext sc = SSLContext.getInstance("SSL");
+			sc.init(null, new TrustManager[] { new X509TrustManager() {
+
+				public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+
+				}
+
+				public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+
+				}
+
+				public X509Certificate[] getAcceptedIssuers() {
+					return new X509Certificate[] {};
+				}
+
+			} }, new java.security.SecureRandom());
+			HttpsURLConnection sconn = (HttpsURLConnection) console.openConnection();
+			sconn.setSSLSocketFactory(sc.getSocketFactory());
+			sconn.setHostnameVerifier(new HostnameVerifier() {
+
+				public boolean verify(String hostname, SSLSession session) {
+					return true;
+				}
+
+			});
+			conn = sconn;
+		} else {
+			conn = (HttpURLConnection) console.openConnection();
+		}
+		return conn;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/common/EmayHttpResultCode.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/common/EmayHttpResultCode.java
new file mode 100644
index 0000000..24f583d
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/common/EmayHttpResultCode.java
@@ -0,0 +1,69 @@
+package cn.emay.sdk.util.http.common;
+
+/**
+ * HTTP 璁块棶缁撴灉缂栫爜
+ * 
+ * @author Frank
+ *
+ */
+public enum EmayHttpResultCode {
+
+	SUCCESS("鎴愬姛", "SUCCESS"), //
+	ERROR_URL_NULL("URL涓虹┖", "ERROR-URL-NULL"), //
+	ERROR_URL("URL璁块棶澶辫触", "ERROR-URL"), //
+	ERROR_HTTPS_SSL("HTTPS寮傚父", "ERROR-HTTPS-SSL"), //
+	ERROR_METHOD("HTTP鏂规硶鏃犳硶璇嗗埆", "ERROR-METHOD"), //
+	ERROR_CHARSET("缂栫爜閿欒", "ERROR-CHARSET"), //
+	ERROR_CONNECT("璁块棶澶辫触", "ERROR-CONNECT"), //
+
+	;
+
+	/**
+	 * 鍚嶇О
+	 */
+	private String name;
+	/**
+	 * 缂栫爜
+	 */
+	private String code;
+
+	private EmayHttpResultCode(String name, String code) {
+		this.name = name;
+		this.code = code;
+	}
+
+	public static String findNameByCode(String code) {
+		for (EmayHttpResultCode oc : EmayHttpResultCode.values()) {
+			if (oc.getCode().equals(code)) {
+				return oc.getName();
+			}
+		}
+		return null;
+	}
+
+	public static String findCodeByName(String name) {
+		for (EmayHttpResultCode oc : EmayHttpResultCode.values()) {
+			if (oc.getName().equals(name)) {
+				return oc.getCode();
+			}
+		}
+		return null;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/EmayHttpRequest.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/EmayHttpRequest.java
new file mode 100644
index 0000000..935f74e
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/EmayHttpRequest.java
@@ -0,0 +1,147 @@
+package cn.emay.sdk.util.http.request;
+
+import java.util.Map;
+
+/**
+ * Http璇锋眰瀹炰綋
+ * 
+ * @author Frank
+ *
+ * @param <T>
+ *            浼犺緭鏁版嵁绫诲瀷
+ */
+public abstract class EmayHttpRequest<T extends Object> {
+
+	private String url;// URL
+	private String charSet = "UTF-8";// 缂栫爜
+	private String method = "GET";// Http鏂规硶
+	private Map<String, String> headers;// 澶翠俊鎭�
+	private String cookies;// cookie淇℃伅
+	private T params;// 浼犺緭鏁版嵁
+
+	/**
+	 * 
+	 * @param url
+	 *            URL
+	 * @param charSet
+	 *            缂栫爜
+	 * @param method
+	 *            Http鏂规硶
+	 * @param headers
+	 *            澶翠俊鎭�
+	 * @param cookies
+	 *            cookie淇℃伅
+	 * @param params
+	 *            浼犺緭鏁版嵁
+	 */
+	public EmayHttpRequest(String url, String charSet, String method, Map<String, String> headers, String cookies, T params) {
+		if (url != null) {
+			this.url = url.trim();
+		}
+		if (charSet != null) {
+			this.charSet = charSet;
+		}
+		if (method != null) {
+			this.method = method;
+		}
+		this.headers = headers;
+		this.cookies = cookies;
+		this.params = params;
+		this.fillGetUrl();
+	}
+
+	/**
+	 * 灏嗕紶杈撴暟鎹浆鎹负byte[]绫诲瀷
+	 * 
+	 * @return
+	 */
+	public abstract byte[] paramsToBytesForPost();
+
+	/**
+	 * 灏嗕紶杈撴暟鎹浆鎹负String绫诲瀷
+	 * 
+	 * @return
+	 */
+	public abstract String paramsToStringForGet();
+
+	/**
+	 * 鏄惁https璇锋眰
+	 * 
+	 * @return
+	 */
+	public boolean isHttps() {
+		if (url == null) {
+			return false;
+		}
+		if (url.startsWith("https")) {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * 鏋勫缓GET URL
+	 */
+	private void fillGetUrl() {
+		if (url == null || params == null) {
+			return;
+		}
+		if (this.getMethod().equalsIgnoreCase("GET")) {
+			String getprams = this.paramsToStringForGet();
+			if (url.indexOf("?") > 0) {
+				url = url + "&" + getprams;
+			} else {
+				url = url + "?" + getprams;
+			}
+		}
+	}
+
+	public String getUrl() {
+		return url;
+	}
+
+	public void setUrl(String url) {
+		this.url = url;
+	}
+
+	public String getCharSet() {
+		return charSet;
+	}
+
+	public void setCharSet(String charSet) {
+		this.charSet = charSet;
+	}
+
+	public String getMethod() {
+		return method;
+	}
+
+	public void setMethod(String method) {
+		this.method = method;
+	}
+
+	public Map<String, String> getHeaders() {
+		return headers;
+	}
+
+	public void setHeaders(Map<String, String> headers) {
+		this.headers = headers;
+	}
+
+	public String getCookies() {
+		return cookies;
+	}
+
+	public void setCookies(String cookies) {
+		this.cookies = cookies;
+	}
+
+	public T getParams() {
+		return params;
+	}
+
+	public void setParams(T params) {
+		this.params = params;
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestBytes.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestBytes.java
new file mode 100644
index 0000000..bb19c20
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestBytes.java
@@ -0,0 +1,37 @@
+package cn.emay.sdk.util.http.request.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Map;
+
+import cn.emay.sdk.util.http.request.EmayHttpRequest;
+
+
+/**
+ * 浼犺緭鏁版嵁涓篵yte[]鐨勮姹傚疄浣�
+ * 
+ * @author Frank
+ *
+ */
+public class EmayHttpRequestBytes extends EmayHttpRequest<byte[]>{
+
+	public EmayHttpRequestBytes(String url, String charSet, String method, Map<String, String> headers, String cookies, byte[] params) {
+		super(url, charSet, method, headers, cookies, params);
+	}
+
+	@Override
+	public byte[] paramsToBytesForPost() {
+		return this.getParams();
+	}
+
+	@Override
+	public String paramsToStringForGet() {
+		try {
+			return new String(this.getParams(),this.getCharSet());
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestKV.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestKV.java
new file mode 100644
index 0000000..83a19f1
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestKV.java
@@ -0,0 +1,53 @@
+package cn.emay.sdk.util.http.request.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import cn.emay.sdk.util.http.request.EmayHttpRequest;
+
+/**
+ * 浼犺緭鏁版嵁涓篕ey-Value鐨勮姹傚疄浣�
+ * 
+ * @author Frank
+ *
+ */
+public class EmayHttpRequestKV extends EmayHttpRequest<Map<String, String>> {
+
+	public EmayHttpRequestKV(String url, String charSet, String method, Map<String, String> headers, String cookies, Map<String, String> params) {
+		super(url, charSet, method, headers, cookies, params);
+	}
+
+	@Override
+	public byte[] paramsToBytesForPost() {
+		String paramStr = paramsToStringForGet();
+		if(paramStr == null){
+			return null;
+		}
+		byte[] param = null;
+		try {
+			param = paramStr.getBytes(this.getCharSet());
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		return param;
+	}
+
+	@Override
+	public String paramsToStringForGet() {
+		Map<String, String> params = this.getParams();
+		if (params == null || params.size() == 0) {
+			return null;
+		}
+		StringBuffer buffer = new StringBuffer();
+		for (Entry<String, String> entry : params.entrySet()) {
+			if(entry.getValue() != null){
+				buffer.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
+			}
+		}
+		String param = buffer.toString();
+		param = param.substring(0,param.length() - 1);
+		return param;
+	}
+	
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestString.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestString.java
new file mode 100644
index 0000000..12881a3
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/request/impl/EmayHttpRequestString.java
@@ -0,0 +1,36 @@
+package cn.emay.sdk.util.http.request.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Map;
+
+import cn.emay.sdk.util.http.request.EmayHttpRequest;
+
+/**
+ * 浼犺緭鏁版嵁涓篠tring鐨勮姹傚疄浣�
+ * 
+ * @author Frank
+ *
+ */
+public class EmayHttpRequestString extends EmayHttpRequest<String>{
+
+	public EmayHttpRequestString(String url, String charSet, String method, Map<String, String> headers, String cookies, String params) {
+		super(url, charSet, method, headers, cookies, params);
+	}
+
+	@Override
+	public byte[] paramsToBytesForPost() {
+		try {
+			return this.getParams().getBytes(this.getCharSet());
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+
+	@Override
+	public String paramsToStringForGet() {
+		return this.getParams();
+	}
+
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/EmayHttpResponsePraser.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/EmayHttpResponsePraser.java
new file mode 100644
index 0000000..266c0a6
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/EmayHttpResponsePraser.java
@@ -0,0 +1,39 @@
+package cn.emay.sdk.util.http.response;
+
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.http.common.EmayHttpResultCode;
+
+/**
+ * 
+ * Http鍝嶅簲瑙f瀽鍣�
+ * 
+ * @author Frank
+ *
+ * @param <T>
+ *            http鍝嶅簲鏁版嵁杞崲鍚庡疄浣�
+ */
+public interface EmayHttpResponsePraser<T extends Object> {
+
+	/**
+	 * 瑙f瀽
+	 * 
+	 * @param charSet
+	 *            鍝嶅簲缂栫爜
+	 * @param resultCode
+	 *            HttpClient缁撴灉缂栫爜
+	 * @param httpCode
+	 *            Http鐘舵�佺爜
+	 * @param headers
+	 *            http鍝嶅簲澶�
+	 * @param cookies
+	 *            http鍝嶅簲Cookies
+	 * @param outputStream
+	 *            http鍝嶅簲鏁版嵁
+	 * @return T http鍝嶅簲鏁版嵁杞崲鍚庡疄浣�
+	 */
+	public T prase(String charSet, EmayHttpResultCode resultCode, int httpCode, Map<String, String> headers, List<String> cookies, ByteArrayOutputStream outputStream);
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseBytes.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseBytes.java
new file mode 100644
index 0000000..e48aaf9
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseBytes.java
@@ -0,0 +1,89 @@
+package cn.emay.sdk.util.http.response.impl.string;
+
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.http.common.EmayHttpResultCode;
+
+/**
+ * 銆愯嚜瀹氫箟銆慡tring绫诲瀷Http鍝嶅簲
+ * 
+ * @author Frank
+ *
+ */
+public class EmayHttpResponseBytes {
+
+	private EmayHttpResultCode resultCode; //HttpClient 缁撴灉浠g爜
+	private int httpCode; //Http閾炬帴Code
+	private Map<String, String> headers;//Http鍝嶅簲澶�
+	private List<String> cookies;//http鍝嶅簲Cookies
+	private byte[] resultBytes;//http鍝嶅簲鏁版嵁
+	private String charSet;//http鍝嶅簲缂栫爜
+
+	/**
+	 * 
+	 * @param charSet http鍝嶅簲缂栫爜
+	 * @param resultCode HttpClient缁撴灉浠g爜
+	 * @param httpCode Http閾炬帴Code
+	 * @param headers Http鍝嶅簲澶�
+	 * @param cookies http鍝嶅簲Cookies
+	 * @param resultString http鍝嶅簲鏁版嵁
+	 */
+	public EmayHttpResponseBytes(String charSet,EmayHttpResultCode resultCode, int httpCode, Map<String, String> headers, List<String> cookies, byte[] resultBytes) {
+		this.resultCode = resultCode;
+		this.httpCode = httpCode;
+		this.headers = headers;
+		this.cookies = cookies;
+		this.resultBytes = resultBytes;
+		this.charSet = charSet;
+	}
+
+	public EmayHttpResultCode getResultCode() {
+		return resultCode;
+	}
+
+	public void setResultCode(EmayHttpResultCode resultCode) {
+		this.resultCode = resultCode;
+	}
+
+	public int getHttpCode() {
+		return httpCode;
+	}
+
+	public void setHttpCode(int httpCode) {
+		this.httpCode = httpCode;
+	}
+
+	public Map<String, String> getHeaders() {
+		return headers;
+	}
+
+	public void setHeaders(Map<String, String> headers) {
+		this.headers = headers;
+	}
+
+	public List<String> getCookies() {
+		return cookies;
+	}
+
+	public void setCookies(List<String> cookies) {
+		this.cookies = cookies;
+	}
+
+	public String getCharSet() {
+		return charSet;
+	}
+
+	public void setCharSet(String charSet) {
+		this.charSet = charSet;
+	}
+
+	public byte[] getResultBytes() {
+		return resultBytes;
+	}
+
+	public void setResultBytes(byte[] resultBytes) {
+		this.resultBytes = resultBytes;
+	}
+	
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseBytesPraser.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseBytesPraser.java
new file mode 100644
index 0000000..a11acc3
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseBytesPraser.java
@@ -0,0 +1,24 @@
+package cn.emay.sdk.util.http.response.impl.string;
+
+import java.io.ByteArrayOutputStream;
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.http.common.EmayHttpResultCode;
+import cn.emay.sdk.util.http.response.EmayHttpResponsePraser;
+
+/**
+ * 瑙f瀽鑷畾涔夊搷搴旂殑瑙f瀽鍣�
+ * 
+ * @author Frank
+ *
+ */
+public class EmayHttpResponseBytesPraser implements EmayHttpResponsePraser<EmayHttpResponseBytes>{
+
+	@Override
+	public EmayHttpResponseBytes prase(String charSet,EmayHttpResultCode resultCode,int httpCode, Map<String, String> headers, List<String> cookies, ByteArrayOutputStream outputStream) {
+		return new EmayHttpResponseBytes(charSet, resultCode, httpCode, headers, cookies, outputStream == null ? null : outputStream.toByteArray());
+	}
+
+	
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseString.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseString.java
new file mode 100644
index 0000000..f00af67
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseString.java
@@ -0,0 +1,107 @@
+package cn.emay.sdk.util.http.response.impl.string;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.http.common.EmayHttpResultCode;
+
+/**
+ * 銆愯嚜瀹氫箟銆慡tring绫诲瀷Http鍝嶅簲
+ * 
+ * @author Frank
+ *
+ */
+public class EmayHttpResponseString {
+
+	private EmayHttpResultCode resultCode; //HttpClient 缁撴灉浠g爜
+	private int httpCode; //Http閾炬帴Code
+	private Map<String, String> headers;//Http鍝嶅簲澶�
+	private List<String> cookies;//http鍝嶅簲Cookies
+	private String resultString;//http鍝嶅簲鏁版嵁
+	private String charSet;//http鍝嶅簲缂栫爜
+
+	/**
+	 * 
+	 * @param charSet http鍝嶅簲缂栫爜
+	 * @param resultCode HttpClient缁撴灉浠g爜
+	 * @param httpCode Http閾炬帴Code
+	 * @param headers Http鍝嶅簲澶�
+	 * @param cookies http鍝嶅簲Cookies
+	 * @param resultString http鍝嶅簲鏁版嵁
+	 */
+	public EmayHttpResponseString(String charSet,EmayHttpResultCode resultCode, int httpCode, Map<String, String> headers, List<String> cookies, String resultString) {
+		this.resultCode = resultCode;
+		this.httpCode = httpCode;
+		this.headers = headers;
+		this.cookies = cookies;
+		this.resultString = resultString;
+		this.charSet = charSet;
+	}
+
+	public EmayHttpResultCode getResultCode() {
+		return resultCode;
+	}
+
+	public void setResultCode(EmayHttpResultCode resultCode) {
+		this.resultCode = resultCode;
+	}
+
+	public int getHttpCode() {
+		return httpCode;
+	}
+
+	public void setHttpCode(int httpCode) {
+		this.httpCode = httpCode;
+	}
+
+	public Map<String, String> getHeaders() {
+		return headers;
+	}
+
+	public void setHeaders(Map<String, String> headers) {
+		this.headers = headers;
+	}
+
+	public List<String> getCookies() {
+		return cookies;
+	}
+
+	public void setCookies(List<String> cookies) {
+		this.cookies = cookies;
+	}
+
+	public String getResultString() {
+		return resultString;
+	}
+
+	public void setResultString(String resultString) {
+		this.resultString = resultString;
+	}
+
+	public String toString(){
+		StringBuffer buffer = new StringBuffer();
+		for(Field filed : this.getClass().getDeclaredFields()){
+			Object value = null;
+			try {
+				value = filed.get(this);
+			} catch (IllegalArgumentException e) {
+				e.printStackTrace();
+			} catch (IllegalAccessException e) {
+				e.printStackTrace();
+			}
+			buffer.append("[ ").append(filed.getName()).append(" : ").append(value).append(" ]");
+		}
+		return buffer.toString();
+		
+	}
+
+	public String getCharSet() {
+		return charSet;
+	}
+
+	public void setCharSet(String charSet) {
+		this.charSet = charSet;
+	}
+	
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseStringPraser.java b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseStringPraser.java
new file mode 100644
index 0000000..19a0abf
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/http/response/impl/string/EmayHttpResponseStringPraser.java
@@ -0,0 +1,34 @@
+package cn.emay.sdk.util.http.response.impl.string;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.http.common.EmayHttpResultCode;
+import cn.emay.sdk.util.http.response.EmayHttpResponsePraser;
+
+/**
+ * 瑙f瀽鑷畾涔夊搷搴旂殑瑙f瀽鍣�
+ * 
+ * @author Frank
+ *
+ */
+public class EmayHttpResponseStringPraser implements EmayHttpResponsePraser<EmayHttpResponseString>{
+
+	@Override
+	public EmayHttpResponseString prase(String charSet,EmayHttpResultCode resultCode,int httpCode, Map<String, String> headers, List<String> cookies, ByteArrayOutputStream outputStream) {
+		String st = null;
+		try {
+			if(outputStream != null){
+				byte[] resultBytes = outputStream.toByteArray();
+				st = new String(resultBytes, charSet);
+			}
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		return new EmayHttpResponseString(charSet, resultCode, httpCode, headers, cookies, st);
+	}
+
+	
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/JsonHelper.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/JsonHelper.java
new file mode 100644
index 0000000..b2b9608
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/JsonHelper.java
@@ -0,0 +1,130 @@
+package cn.emay.sdk.util.json;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.GsonBuilder;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+
+/**
+ * json util
+ * 
+ * @author 涓滄棴
+ *
+ */
+public class JsonHelper {
+
+	private static Map<String, Gson> gsons = new HashMap<String, Gson>();
+
+	private static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
+
+	static {
+		gsons.put(DEFAULT_DATE_PATTERN, createGson(DEFAULT_DATE_PATTERN));
+	}
+
+	private static Gson createGson(String datePattern) {
+		return new GsonBuilder().setDateFormat(datePattern).disableHtmlEscaping().serializeNulls().create();
+	}
+
+	public static Gson getGson() {
+		return gsons.get(DEFAULT_DATE_PATTERN);
+	}
+
+	public static Gson getGson(String datePattern) {
+		Gson gson = gsons.get(datePattern);
+		if (gson == null) {
+			gson = createGson(datePattern);
+			gsons.put(datePattern, gson);
+		}
+		return gson;
+	}
+
+	public static GsonBuilder newGsonBuilder() {
+		return new GsonBuilder();
+	}
+
+	/**
+	 * 灏嗗璞¤浆鎹负json涓�
+	 * 
+	 * @param obj
+	 * @return
+	 */
+	public static String toJsonString(Object obj) {
+		if (obj == null) {
+			return null;
+		}
+		return getGson().toJson(obj);
+	}
+
+	/**
+	 * 灏嗗璞¤浆鎹负json涓诧紝鑷畾涔夋棩鏈熻浆鎹㈣鍒�
+	 * 
+	 * @param obj
+	 * @param datePattern
+	 * @return
+	 */
+	public static String toJsonString(Object obj, String datePattern) {
+		if (obj == null) {
+			return null;
+		}
+		return getGson(datePattern).toJson(obj);
+	}
+
+	/**
+	 * 灏唈son涓茶浆鎹负瀵硅薄
+	 * 
+	 * @param clazz
+	 * @param jsonString
+	 * @return
+	 */
+	public static <T> T fromJson(Class<T> clazz, String jsonString) {
+		if (jsonString == null) {
+			return null;
+		}
+		return getGson().fromJson(jsonString, clazz);
+	}
+
+	/**
+	 * 灏唈son涓茶浆鎹负瀵硅薄
+	 * 
+	 * @Type type
+	 * @param jsonString
+	 * @return
+	 */
+	public static <T> T fromJson(TypeToken<T> token, String jsonString) {
+		if (jsonString == null) {
+			return null;
+		}
+		return getGson().fromJson(jsonString, token.getType());
+	}
+
+	/**
+	 * 灏唈son涓茶浆鎹负瀵硅薄
+	 * 
+	 * @Type type
+	 * @param jsonString
+	 * @return
+	 */
+	public static <T> T fromJson(TypeToken<T> token, String jsonString, String datePattern) {
+		if (jsonString == null) {
+			return null;
+		}
+		return getGson(datePattern).fromJson(jsonString, token.getType());
+	}
+
+	/**
+	 * 灏唈son涓茶浆鎹负瀵硅薄
+	 * 
+	 * @param clazz
+	 * @param jsonString
+	 * @return
+	 */
+	public static <T> T fromJson(Class<T> clazz, String jsonString, String datePattern) {
+		if (jsonString == null) {
+			return null;
+		}
+		return getGson(datePattern).fromJson(jsonString, clazz);
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/DefaultDateTypeAdapter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/DefaultDateTypeAdapter.java
new file mode 100644
index 0000000..779ed35
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/DefaultDateTypeAdapter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.lang.reflect.Type;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import cn.emay.sdk.util.json.gson.internal.bind.util.ISO8601Utils;
+
+/**
+ * This type adapter supports three subclasses of date: Date, Timestamp, and
+ * java.sql.Date.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
+
+	// TODO: migrate to streaming adapter
+
+	private final DateFormat enUsFormat;
+	private final DateFormat localFormat;
+
+	DefaultDateTypeAdapter() {
+		this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US), DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
+	}
+
+	DefaultDateTypeAdapter(String datePattern) {
+		this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern));
+	}
+
+	DefaultDateTypeAdapter(int style) {
+		this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style));
+	}
+
+	public DefaultDateTypeAdapter(int dateStyle, int timeStyle) {
+		this(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US), DateFormat.getDateTimeInstance(dateStyle, timeStyle));
+	}
+
+	DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) {
+		this.enUsFormat = enUsFormat;
+		this.localFormat = localFormat;
+	}
+
+	// These methods need to be synchronized since JDK DateFormat classes are not
+	// thread-safe
+	// See issue 162
+	@Override
+	public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
+		synchronized (localFormat) {
+			String dateFormatAsString = enUsFormat.format(src);
+			return new JsonPrimitive(dateFormatAsString);
+		}
+	}
+
+	@Override
+	public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+		if (!(json instanceof JsonPrimitive)) {
+			throw new JsonParseException("The date should be a string value");
+		}
+		Date date = deserializeToDate(json);
+		if (typeOfT == Date.class) {
+			return date;
+		} else if (typeOfT == Timestamp.class) {
+			return new Timestamp(date.getTime());
+		} else if (typeOfT == java.sql.Date.class) {
+			return new java.sql.Date(date.getTime());
+		} else {
+			throw new IllegalArgumentException(getClass() + " cannot deserialize to " + typeOfT);
+		}
+	}
+
+	private Date deserializeToDate(JsonElement json) {
+		synchronized (localFormat) {
+			try {
+				return localFormat.parse(json.getAsString());
+			} catch (ParseException ignored) {
+			}
+			try {
+				return enUsFormat.parse(json.getAsString());
+			} catch (ParseException ignored) {
+			}
+			try {
+				return ISO8601Utils.parse(json.getAsString(), new ParsePosition(0));
+			} catch (ParseException e) {
+				throw new JsonSyntaxException(json.getAsString(), e);
+			}
+		}
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder sb = new StringBuilder();
+		sb.append(DefaultDateTypeAdapter.class.getSimpleName());
+		sb.append('(').append(localFormat.getClass().getSimpleName()).append(')');
+		return sb.toString();
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/ExclusionStrategy.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/ExclusionStrategy.java
new file mode 100644
index 0000000..80d6205
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/ExclusionStrategy.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+/**
+ * A strategy (or policy) definition that is used to decide whether or not a
+ * field or top-level class should be serialized or deserialized as part of the
+ * JSON output/input. For serialization, if the {@link #shouldSkipClass(Class)}
+ * method returns true then that class or field type will not be part of the
+ * JSON output. For deserialization, if {@link #shouldSkipClass(Class)} returns
+ * true, then it will not be set as part of the Java object structure.
+ *
+ * <p>
+ * The following are a few examples that shows how you can use this exclusion
+ * mechanism.
+ *
+ * <p>
+ * <strong>Exclude fields and objects based on a particular class type:</strong>
+ * 
+ * <pre class="code">
+ * private static class SpecificClassExclusionStrategy implements ExclusionStrategy {
+ * 	private final Class&lt;?&gt; excludedThisClass;
+ *
+ * 	public SpecificClassExclusionStrategy(Class&lt;?&gt; excludedThisClass) {
+ * 		this.excludedThisClass = excludedThisClass;
+ * 	}
+ *
+ * 	public boolean shouldSkipClass(Class&lt;?&gt; clazz) {
+ * 		return excludedThisClass.equals(clazz);
+ * 	}
+ *
+ * 	public boolean shouldSkipField(FieldAttributes f) {
+ * 		return excludedThisClass.equals(f.getDeclaredClass());
+ * 	}
+ * }
+ * </pre>
+ *
+ * <p>
+ * <strong>Excludes fields and objects based on a particular
+ * annotation:</strong>
+ * 
+ * <pre class="code">
+ * public &#64interface FooAnnotation {
+ *   // some implementation here
+ * }
+ *
+ * // Excludes any field (or class) that is tagged with an "&#64FooAnnotation"
+ * private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
+ *   public boolean shouldSkipClass(Class&lt;?&gt; clazz) {
+ *     return clazz.getAnnotation(FooAnnotation.class) != null;
+ *   }
+ *
+ *   public boolean shouldSkipField(FieldAttributes f) {
+ *     return f.getAnnotation(FooAnnotation.class) != null;
+ *   }
+ * }
+ * </pre>
+ *
+ * <p>
+ * Now if you want to configure {@code Gson} to use a user defined exclusion
+ * strategy, then the {@code GsonBuilder} is required. The following is an
+ * example of how you can use the {@code GsonBuilder} to configure Gson to use
+ * one of the above sample:
+ * 
+ * <pre class="code">
+ * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
+ * Gson gson = new GsonBuilder().setExclusionStrategies(excludeStrings).create();
+ * </pre>
+ *
+ * <p>
+ * For certain model classes, you may only want to serialize a field, but
+ * exclude it for deserialization. To do that, you can write an
+ * {@code ExclusionStrategy} as per normal; however, you would register it with
+ * the
+ * {@link GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)}
+ * method. For example:
+ * 
+ * <pre class="code">
+ * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
+ * Gson gson = new GsonBuilder().addDeserializationExclusionStrategy(excludeStrings).create();
+ * </pre>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @see GsonBuilder#setExclusionStrategies(ExclusionStrategy...)
+ * @see GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)
+ * @see GsonBuilder#addSerializationExclusionStrategy(ExclusionStrategy)
+ *
+ * @since 1.4
+ */
+public interface ExclusionStrategy {
+
+	/**
+	 * @param f
+	 *            the field object that is under test
+	 * @return true if the field should be ignored; otherwise false
+	 */
+	public boolean shouldSkipField(FieldAttributes f);
+
+	/**
+	 * @param clazz
+	 *            the class object that is under test
+	 * @return true if the class should be ignored; otherwise false
+	 */
+	public boolean shouldSkipClass(Class<?> clazz);
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldAttributes.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldAttributes.java
new file mode 100644
index 0000000..420d4ee
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldAttributes.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collection;
+
+import cn.emay.sdk.util.json.gson.internal.$Gson$Preconditions;
+
+/**
+ * A data object that stores attributes of a field.
+ *
+ * <p>
+ * This class is immutable; therefore, it can be safely shared across threads.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @since 1.4
+ */
+public final class FieldAttributes {
+	private final Field field;
+
+	/**
+	 * Constructs a Field Attributes object from the {@code f}.
+	 *
+	 * @param f
+	 *            the field to pull attributes from
+	 */
+	public FieldAttributes(Field f) {
+		$Gson$Preconditions.checkNotNull(f);
+		this.field = f;
+	}
+
+	/**
+	 * @return the declaring class that contains this field
+	 */
+	public Class<?> getDeclaringClass() {
+		return field.getDeclaringClass();
+	}
+
+	/**
+	 * @return the name of the field
+	 */
+	public String getName() {
+		return field.getName();
+	}
+
+	/**
+	 * <p>
+	 * For example, assume the following class definition:
+	 * 
+	 * <pre class="code">
+	 * public class Foo {
+	 * 	private String bar;
+	 * 	private List&lt;String&gt; red;
+	 * }
+	 *
+	 * Type listParameterizedType = new TypeToken&lt;List&lt;String&gt;&gt;() {
+	 * }.getType();
+	 * </pre>
+	 *
+	 * <p>
+	 * This method would return {@code String.class} for the {@code bar} field and
+	 * {@code listParameterizedType} for the {@code red} field.
+	 *
+	 * @return the specific type declared for this field
+	 */
+	public Type getDeclaredType() {
+		return field.getGenericType();
+	}
+
+	/**
+	 * Returns the {@code Class} object that was declared for this field.
+	 *
+	 * <p>
+	 * For example, assume the following class definition:
+	 * 
+	 * <pre class="code">
+	 * public class Foo {
+	 * 	private String bar;
+	 * 	private List&lt;String&gt; red;
+	 * }
+	 * </pre>
+	 *
+	 * <p>
+	 * This method would return {@code String.class} for the {@code bar} field and
+	 * {@code List.class} for the {@code red} field.
+	 *
+	 * @return the specific class object that was declared for the field
+	 */
+	public Class<?> getDeclaredClass() {
+		return field.getType();
+	}
+
+	/**
+	 * Return the {@code T} annotation object from this field if it exist; otherwise
+	 * returns {@code null}.
+	 *
+	 * @param annotation
+	 *            the class of the annotation that will be retrieved
+	 * @return the annotation instance if it is bound to the field; otherwise
+	 *         {@code null}
+	 */
+	public <T extends Annotation> T getAnnotation(Class<T> annotation) {
+		return field.getAnnotation(annotation);
+	}
+
+	/**
+	 * Return the annotations that are present on this field.
+	 *
+	 * @return an array of all the annotations set on the field
+	 * @since 1.4
+	 */
+	public Collection<Annotation> getAnnotations() {
+		return Arrays.asList(field.getAnnotations());
+	}
+
+	/**
+	 * Returns {@code true} if the field is defined with the {@code modifier}.
+	 *
+	 * <p>
+	 * This method is meant to be called as:
+	 * 
+	 * <pre class="code">
+	 * boolean hasPublicModifier = fieldAttribute.hasModifier(java.lang.reflect.Modifier.PUBLIC);
+	 * </pre>
+	 *
+	 * @see java.lang.reflect.Modifier
+	 */
+	public boolean hasModifier(int modifier) {
+		return (field.getModifiers() & modifier) != 0;
+	}
+
+	/**
+	 * This is exposed internally only for the removing synthetic fields from the
+	 * JSON output.
+	 *
+	 * @return true if the field is synthetic; otherwise false
+	 * @throws IllegalAccessException
+	 * @throws IllegalArgumentException
+	 */
+	Object get(Object instance) throws IllegalAccessException {
+		return field.get(instance);
+	}
+
+	/**
+	 * This is exposed internally only for the removing synthetic fields from the
+	 * JSON output.
+	 *
+	 * @return true if the field is synthetic; otherwise false
+	 */
+	boolean isSynthetic() {
+		return field.isSynthetic();
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldNamingPolicy.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldNamingPolicy.java
new file mode 100644
index 0000000..2dde11c
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldNamingPolicy.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.lang.reflect.Field;
+import java.util.Locale;
+
+/**
+ * An enumeration that defines a few standard naming conventions for JSON field
+ * names. This enumeration should be used in conjunction with
+ * {@link com.google.gson.GsonBuilder} to configure a
+ * {@link com.google.gson.Gson} instance to properly translate Java field names
+ * into the desired JSON field names.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public enum FieldNamingPolicy implements FieldNamingStrategy {
+
+	/**
+	 * Using this naming policy with Gson will ensure that the field name is
+	 * unchanged.
+	 */
+	IDENTITY() {
+		@Override
+		public String translateName(Field f) {
+			return f.getName();
+		}
+	},
+
+	/**
+	 * Using this naming policy with Gson will ensure that the first "letter" of the
+	 * Java field name is capitalized when serialized to its JSON form.
+	 *
+	 * <p>
+	 * Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":
+	 * </p>
+	 * <ul>
+	 * <li>someFieldName ---> SomeFieldName</li>
+	 * <li>_someFieldName ---> _SomeFieldName</li>
+	 * </ul>
+	 */
+	UPPER_CAMEL_CASE() {
+		@Override
+		public String translateName(Field f) {
+			return upperCaseFirstLetter(f.getName());
+		}
+	},
+
+	/**
+	 * Using this naming policy with Gson will ensure that the first "letter" of the
+	 * Java field name is capitalized when serialized to its JSON form and the words
+	 * will be separated by a space.
+	 *
+	 * <p>
+	 * Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":
+	 * </p>
+	 * <ul>
+	 * <li>someFieldName ---> Some Field Name</li>
+	 * <li>_someFieldName ---> _Some Field Name</li>
+	 * </ul>
+	 *
+	 * @since 1.4
+	 */
+	UPPER_CAMEL_CASE_WITH_SPACES() {
+		@Override
+		public String translateName(Field f) {
+			return upperCaseFirstLetter(separateCamelCase(f.getName(), " "));
+		}
+	},
+
+	/**
+	 * Using this naming policy with Gson will modify the Java Field name from its
+	 * camel cased form to a lower case field name where each word is separated by
+	 * an underscore (_).
+	 *
+	 * <p>
+	 * Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":
+	 * </p>
+	 * <ul>
+	 * <li>someFieldName ---> some_field_name</li>
+	 * <li>_someFieldName ---> _some_field_name</li>
+	 * <li>aStringField ---> a_string_field</li>
+	 * <li>aURL ---> a_u_r_l</li>
+	 * </ul>
+	 */
+	LOWER_CASE_WITH_UNDERSCORES() {
+		@Override
+		public String translateName(Field f) {
+			return separateCamelCase(f.getName(), "_").toLowerCase(Locale.ENGLISH);
+		}
+	},
+
+	/**
+	 * Using this naming policy with Gson will modify the Java Field name from its
+	 * camel cased form to a lower case field name where each word is separated by a
+	 * dash (-).
+	 *
+	 * <p>
+	 * Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":
+	 * </p>
+	 * <ul>
+	 * <li>someFieldName ---> some-field-name</li>
+	 * <li>_someFieldName ---> _some-field-name</li>
+	 * <li>aStringField ---> a-string-field</li>
+	 * <li>aURL ---> a-u-r-l</li>
+	 * </ul>
+	 * Using dashes in JavaScript is not recommended since dash is also used for a
+	 * minus sign in expressions. This requires that a field named with dashes is
+	 * always accessed as a quoted property like {@code myobject['my-field']}.
+	 * Accessing it as an object field {@code myobject.my-field} will result in an
+	 * unintended javascript expression.
+	 * 
+	 * @since 1.4
+	 */
+	LOWER_CASE_WITH_DASHES() {
+		@Override
+		public String translateName(Field f) {
+			return separateCamelCase(f.getName(), "-").toLowerCase(Locale.ENGLISH);
+		}
+	};
+
+	/**
+	 * Converts the field name that uses camel-case define word separation into
+	 * separate words that are separated by the provided {@code separatorString}.
+	 */
+	static String separateCamelCase(String name, String separator) {
+		StringBuilder translation = new StringBuilder();
+		for (int i = 0; i < name.length(); i++) {
+			char character = name.charAt(i);
+			if (Character.isUpperCase(character) && translation.length() != 0) {
+				translation.append(separator);
+			}
+			translation.append(character);
+		}
+		return translation.toString();
+	}
+
+	/**
+	 * Ensures the JSON field names begins with an upper case letter.
+	 */
+	static String upperCaseFirstLetter(String name) {
+		StringBuilder fieldNameBuilder = new StringBuilder();
+		int index = 0;
+		char firstCharacter = name.charAt(index);
+
+		while (index < name.length() - 1) {
+			if (Character.isLetter(firstCharacter)) {
+				break;
+			}
+
+			fieldNameBuilder.append(firstCharacter);
+			firstCharacter = name.charAt(++index);
+		}
+
+		if (index == name.length()) {
+			return fieldNameBuilder.toString();
+		}
+
+		if (!Character.isUpperCase(firstCharacter)) {
+			String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), name, ++index);
+			return fieldNameBuilder.append(modifiedTarget).toString();
+		} else {
+			return name;
+		}
+	}
+
+	private static String modifyString(char firstCharacter, String srcString, int indexOfSubstring) {
+		return (indexOfSubstring < srcString.length()) ? firstCharacter + srcString.substring(indexOfSubstring) : String.valueOf(firstCharacter);
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldNamingStrategy.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldNamingStrategy.java
new file mode 100644
index 0000000..1546184
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/FieldNamingStrategy.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.lang.reflect.Field;
+
+/**
+ * A mechanism for providing custom field naming in Gson. This allows the client
+ * code to translate field names into a particular convention that is not
+ * supported as a normal Java field declaration rules. For example, Java does
+ * not support "-" characters in a field name.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.3
+ */
+public interface FieldNamingStrategy {
+
+	/**
+	 * Translates the field name into its JSON field name representation.
+	 *
+	 * @param f
+	 *            the field object that we are translating
+	 * @return the translated field name.
+	 * @since 1.3
+	 */
+	public String translateName(Field f);
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/Gson.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/Gson.java
new file mode 100644
index 0000000..a6296a5
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/Gson.java
@@ -0,0 +1,1132 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicLongArray;
+
+import cn.emay.sdk.util.json.gson.internal.ConstructorConstructor;
+import cn.emay.sdk.util.json.gson.internal.Excluder;
+import cn.emay.sdk.util.json.gson.internal.Primitives;
+import cn.emay.sdk.util.json.gson.internal.Streams;
+import cn.emay.sdk.util.json.gson.internal.bind.ArrayTypeAdapter;
+import cn.emay.sdk.util.json.gson.internal.bind.CollectionTypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.internal.bind.DateTypeAdapter;
+import cn.emay.sdk.util.json.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.internal.bind.JsonTreeReader;
+import cn.emay.sdk.util.json.gson.internal.bind.JsonTreeWriter;
+import cn.emay.sdk.util.json.gson.internal.bind.MapTypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.internal.bind.ObjectTypeAdapter;
+import cn.emay.sdk.util.json.gson.internal.bind.ReflectiveTypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.internal.bind.SqlDateTypeAdapter;
+import cn.emay.sdk.util.json.gson.internal.bind.TimeTypeAdapter;
+import cn.emay.sdk.util.json.gson.internal.bind.TypeAdapters;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+import cn.emay.sdk.util.json.gson.stream.MalformedJsonException;
+
+/**
+ * This is the main class for using Gson. Gson is typically used by first
+ * constructing a Gson instance and then invoking {@link #toJson(Object)} or
+ * {@link #fromJson(String, Class)} methods on it. Gson instances are
+ * Thread-safe so you can reuse them freely across multiple threads.
+ *
+ * <p>
+ * You can create a Gson instance by invoking {@code new Gson()} if the default
+ * configuration is all you need. You can also use {@link GsonBuilder} to build
+ * a Gson instance with various configuration options such as versioning
+ * support, pretty printing, custom {@link JsonSerializer}s,
+ * {@link JsonDeserializer}s, and {@link InstanceCreator}s.
+ * </p>
+ *
+ * <p>
+ * Here is an example of how Gson is used for a simple Class:
+ *
+ * <pre>
+ * Gson gson = new Gson(); // Or use new GsonBuilder().create();
+ * MyType target = new MyType();
+ * String json = gson.toJson(target); // serializes target to Json
+ * MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * If the object that your are serializing/deserializing is a
+ * {@code ParameterizedType} (i.e. contains at least one type parameter and may
+ * be an array) then you must use the {@link #toJson(Object, Type)} or
+ * {@link #fromJson(String, Type)} method. Here is an example for serializing
+ * and deserializing a {@code ParameterizedType}:
+ *
+ * <pre>
+ * Type listType = new TypeToken&lt;List&lt;String&gt;&gt;() {
+ * }.getType();
+ * List&lt;String&gt; target = new LinkedList&lt;String&gt;();
+ * target.add("blah");
+ *
+ * Gson gson = new Gson();
+ * String json = gson.toJson(target, listType);
+ * List&lt;String&gt; target2 = gson.fromJson(json, listType);
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * See the <a href="https://sites.google.com/site/gson/gson-user-guide">Gson
+ * User Guide</a> for a more complete set of examples.
+ * </p>
+ *
+ * @see com.google.gson.reflect.TypeToken
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+public final class Gson {
+	static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
+	static final boolean DEFAULT_LENIENT = false;
+	static final boolean DEFAULT_PRETTY_PRINT = false;
+	static final boolean DEFAULT_ESCAPE_HTML = true;
+	static final boolean DEFAULT_SERIALIZE_NULLS = false;
+	static final boolean DEFAULT_COMPLEX_MAP_KEYS = false;
+	static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false;
+
+	private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
+
+	/**
+	 * This thread local guards against reentrant calls to getAdapter(). In certain
+	 * object graphs, creating an adapter for a type may recursively require an
+	 * adapter for the same type! Without intervention, the recursive lookup would
+	 * stack overflow. We cheat by returning a proxy type adapter. The proxy is
+	 * wired up once the initial adapter has been created.
+	 */
+	private final ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls = new ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>>();
+
+	private final Map<TypeToken<?>, TypeAdapter<?>> typeTokenCache = Collections.synchronizedMap(new HashMap<TypeToken<?>, TypeAdapter<?>>());
+
+	private final List<TypeAdapterFactory> factories;
+	private final ConstructorConstructor constructorConstructor;
+
+	private final boolean serializeNulls;
+	private final boolean htmlSafe;
+	private final boolean generateNonExecutableJson;
+	private final boolean prettyPrinting;
+	private final boolean lenient;
+
+	final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() {
+		@SuppressWarnings("unchecked")
+		@Override
+		public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
+			return (T) fromJson(json, typeOfT);
+		}
+	};
+
+	final JsonSerializationContext serializationContext = new JsonSerializationContext() {
+		@Override
+		public JsonElement serialize(Object src) {
+			return toJsonTree(src);
+		}
+
+		@Override
+		public JsonElement serialize(Object src, Type typeOfSrc) {
+			return toJsonTree(src, typeOfSrc);
+		}
+	};
+
+	/**
+	 * Constructs a Gson object with default configuration. The default
+	 * configuration has the following settings:
+	 * <ul>
+	 * <li>The JSON generated by <code>toJson</code> methods is in compact
+	 * representation. This means that all the unneeded white-space is removed. You
+	 * can change this behavior with {@link GsonBuilder#setPrettyPrinting()}.</li>
+	 * <li>The generated JSON omits all the fields that are null. Note that nulls in
+	 * arrays are kept as is since an array is an ordered list. Moreover, if a field
+	 * is not null, but its generated JSON is empty, the field is kept. You can
+	 * configure Gson to serialize null values by setting
+	 * {@link GsonBuilder#serializeNulls()}.</li>
+	 * <li>Gson provides default serialization and deserialization for Enums,
+	 * {@link Map}, {@link java.net.URL}, {@link java.net.URI},
+	 * {@link java.util.Locale}, {@link java.util.Date},
+	 * {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If
+	 * you would prefer to change the default representation, you can do so by
+	 * registering a type adapter through
+	 * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.</li>
+	 * <li>The default Date format is same as {@link java.text.DateFormat#DEFAULT}.
+	 * This format ignores the millisecond portion of the date during serialization.
+	 * You can change this by invoking {@link GsonBuilder#setDateFormat(int)} or
+	 * {@link GsonBuilder#setDateFormat(String)}.</li>
+	 * <li>By default, Gson ignores the {@link com.google.gson.annotations.Expose}
+	 * annotation. You can enable Gson to serialize/deserialize only those fields
+	 * marked with this annotation through
+	 * {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}.</li>
+	 * <li>By default, Gson ignores the {@link com.google.gson.annotations.Since}
+	 * annotation. You can enable Gson to use this annotation through
+	 * {@link GsonBuilder#setVersion(double)}.</li>
+	 * <li>The default field naming policy for the output Json is same as in Java.
+	 * So, a Java class field <code>versionNumber</code> will be output as
+	 * <code>&quot;versionNumber&quot;</code> in Json. The same rules are applied
+	 * for mapping incoming Json to the Java classes. You can change this policy
+	 * through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.</li>
+	 * <li>By default, Gson excludes <code>transient</code> or <code>static</code>
+	 * fields from consideration for serialization and deserialization. You can
+	 * change this behavior through
+	 * {@link GsonBuilder#excludeFieldsWithModifiers(int...)}.</li>
+	 * </ul>
+	 */
+	public Gson() {
+		this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY, Collections.<Type, InstanceCreator<?>>emptyMap(), DEFAULT_SERIALIZE_NULLS, DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE,
+				DEFAULT_ESCAPE_HTML, DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, LongSerializationPolicy.DEFAULT, Collections.<TypeAdapterFactory>emptyList());
+	}
+
+	Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy, final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls, boolean complexMapKeySerialization,
+			boolean generateNonExecutableGson, boolean htmlSafe, boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy,
+			List<TypeAdapterFactory> typeAdapterFactories) {
+		this.constructorConstructor = new ConstructorConstructor(instanceCreators);
+		this.serializeNulls = serializeNulls;
+		this.generateNonExecutableJson = generateNonExecutableGson;
+		this.htmlSafe = htmlSafe;
+		this.prettyPrinting = prettyPrinting;
+		this.lenient = lenient;
+
+		List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
+
+		// built-in type adapters that cannot be overridden
+		factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
+		factories.add(ObjectTypeAdapter.FACTORY);
+
+		// the excluder must precede all adapters that handle user-defined types
+		factories.add(excluder);
+
+		// user's type adapters
+		factories.addAll(typeAdapterFactories);
+
+		// type adapters for basic platform types
+		factories.add(TypeAdapters.STRING_FACTORY);
+		factories.add(TypeAdapters.INTEGER_FACTORY);
+		factories.add(TypeAdapters.BOOLEAN_FACTORY);
+		factories.add(TypeAdapters.BYTE_FACTORY);
+		factories.add(TypeAdapters.SHORT_FACTORY);
+		TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
+		factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
+		factories.add(TypeAdapters.newFactory(double.class, Double.class, doubleAdapter(serializeSpecialFloatingPointValues)));
+		factories.add(TypeAdapters.newFactory(float.class, Float.class, floatAdapter(serializeSpecialFloatingPointValues)));
+		factories.add(TypeAdapters.NUMBER_FACTORY);
+		factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
+		factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
+		factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
+		factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
+		factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
+		factories.add(TypeAdapters.CHARACTER_FACTORY);
+		factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
+		factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
+		factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
+		factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
+		factories.add(TypeAdapters.URL_FACTORY);
+		factories.add(TypeAdapters.URI_FACTORY);
+		factories.add(TypeAdapters.UUID_FACTORY);
+		factories.add(TypeAdapters.CURRENCY_FACTORY);
+		factories.add(TypeAdapters.LOCALE_FACTORY);
+		factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
+		factories.add(TypeAdapters.BIT_SET_FACTORY);
+		factories.add(DateTypeAdapter.FACTORY);
+		factories.add(TypeAdapters.CALENDAR_FACTORY);
+		factories.add(TimeTypeAdapter.FACTORY);
+		factories.add(SqlDateTypeAdapter.FACTORY);
+		factories.add(TypeAdapters.TIMESTAMP_FACTORY);
+		factories.add(ArrayTypeAdapter.FACTORY);
+		factories.add(TypeAdapters.CLASS_FACTORY);
+
+		// type adapters for composite and user-defined types
+		factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
+		factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
+		factories.add(new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor));
+		factories.add(TypeAdapters.ENUM_FACTORY);
+		factories.add(new ReflectiveTypeAdapterFactory(constructorConstructor, fieldNamingPolicy, excluder));
+
+		this.factories = Collections.unmodifiableList(factories);
+	}
+
+	private TypeAdapter<Number> doubleAdapter(boolean serializeSpecialFloatingPointValues) {
+		if (serializeSpecialFloatingPointValues) {
+			return TypeAdapters.DOUBLE;
+		}
+		return new TypeAdapter<Number>() {
+			@Override
+			public Double read(JsonReader in) throws IOException {
+				if (in.peek() == JsonToken.NULL) {
+					in.nextNull();
+					return null;
+				}
+				return in.nextDouble();
+			}
+
+			@Override
+			public void write(JsonWriter out, Number value) throws IOException {
+				if (value == null) {
+					out.nullValue();
+					return;
+				}
+				double doubleValue = value.doubleValue();
+				checkValidFloatingPoint(doubleValue);
+				out.value(value);
+			}
+		};
+	}
+
+	private TypeAdapter<Number> floatAdapter(boolean serializeSpecialFloatingPointValues) {
+		if (serializeSpecialFloatingPointValues) {
+			return TypeAdapters.FLOAT;
+		}
+		return new TypeAdapter<Number>() {
+			@Override
+			public Float read(JsonReader in) throws IOException {
+				if (in.peek() == JsonToken.NULL) {
+					in.nextNull();
+					return null;
+				}
+				return (float) in.nextDouble();
+			}
+
+			@Override
+			public void write(JsonWriter out, Number value) throws IOException {
+				if (value == null) {
+					out.nullValue();
+					return;
+				}
+				float floatValue = value.floatValue();
+				checkValidFloatingPoint(floatValue);
+				out.value(value);
+			}
+		};
+	}
+
+	static void checkValidFloatingPoint(double value) {
+		if (Double.isNaN(value) || Double.isInfinite(value)) {
+			throw new IllegalArgumentException(
+					value + " is not a valid double value as per JSON specification. To override this" + " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.");
+		}
+	}
+
+	private static TypeAdapter<Number> longAdapter(LongSerializationPolicy longSerializationPolicy) {
+		if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) {
+			return TypeAdapters.LONG;
+		}
+		return new TypeAdapter<Number>() {
+			@Override
+			public Number read(JsonReader in) throws IOException {
+				if (in.peek() == JsonToken.NULL) {
+					in.nextNull();
+					return null;
+				}
+				return in.nextLong();
+			}
+
+			@Override
+			public void write(JsonWriter out, Number value) throws IOException {
+				if (value == null) {
+					out.nullValue();
+					return;
+				}
+				out.value(value.toString());
+			}
+		};
+	}
+
+	private static TypeAdapter<AtomicLong> atomicLongAdapter(final TypeAdapter<Number> longAdapter) {
+		return new TypeAdapter<AtomicLong>() {
+			@Override
+			public void write(JsonWriter out, AtomicLong value) throws IOException {
+				longAdapter.write(out, value.get());
+			}
+
+			@Override
+			public AtomicLong read(JsonReader in) throws IOException {
+				Number value = longAdapter.read(in);
+				return new AtomicLong(value.longValue());
+			}
+		}.nullSafe();
+	}
+
+	private static TypeAdapter<AtomicLongArray> atomicLongArrayAdapter(final TypeAdapter<Number> longAdapter) {
+		return new TypeAdapter<AtomicLongArray>() {
+			@Override
+			public void write(JsonWriter out, AtomicLongArray value) throws IOException {
+				out.beginArray();
+				for (int i = 0, length = value.length(); i < length; i++) {
+					longAdapter.write(out, value.get(i));
+				}
+				out.endArray();
+			}
+
+			@Override
+			public AtomicLongArray read(JsonReader in) throws IOException {
+				List<Long> list = new ArrayList<Long>();
+				in.beginArray();
+				while (in.hasNext()) {
+					long value = longAdapter.read(in).longValue();
+					list.add(value);
+				}
+				in.endArray();
+				int length = list.size();
+				AtomicLongArray array = new AtomicLongArray(length);
+				for (int i = 0; i < length; ++i) {
+					array.set(i, list.get(i));
+				}
+				return array;
+			}
+		}.nullSafe();
+	}
+
+	/**
+	 * Returns the type adapter for {@code} type.
+	 *
+	 * @throws IllegalArgumentException
+	 *             if this GSON cannot serialize and deserialize {@code type}.
+	 */
+	@SuppressWarnings("unchecked")
+	public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
+		TypeAdapter<?> cached = typeTokenCache.get(type);
+		if (cached != null) {
+			return (TypeAdapter<T>) cached;
+		}
+
+		Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
+		boolean requiresThreadLocalCleanup = false;
+		if (threadCalls == null) {
+			threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
+			calls.set(threadCalls);
+			requiresThreadLocalCleanup = true;
+		}
+
+		// the key and value type parameters always agree
+		FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
+		if (ongoingCall != null) {
+			return ongoingCall;
+		}
+
+		try {
+			FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
+			threadCalls.put(type, call);
+
+			for (TypeAdapterFactory factory : factories) {
+				TypeAdapter<T> candidate = factory.create(this, type);
+				if (candidate != null) {
+					call.setDelegate(candidate);
+					typeTokenCache.put(type, candidate);
+					return candidate;
+				}
+			}
+			throw new IllegalArgumentException("GSON cannot handle " + type);
+		} finally {
+			threadCalls.remove(type);
+
+			if (requiresThreadLocalCleanup) {
+				calls.remove();
+			}
+		}
+	}
+
+	/**
+	 * This method is used to get an alternate type adapter for the specified type.
+	 * This is used to access a type adapter that is overridden by a
+	 * {@link TypeAdapterFactory} that you may have registered. This features is
+	 * typically used when you want to register a type adapter that does a little
+	 * bit of work but then delegates further processing to the Gson default type
+	 * adapter. Here is an example:
+	 * <p>
+	 * Let's say we want to write a type adapter that counts the number of objects
+	 * being read from or written to JSON. We can achieve this by writing a type
+	 * adapter factory that uses the <code>getDelegateAdapter</code> method:
+	 * 
+	 * <pre>
+	 * {
+	 * 	&#64;code
+	 * 	class StatsTypeAdapterFactory implements TypeAdapterFactory {
+	 * 		public int numReads = 0;
+	 * 		public int numWrites = 0;
+	 * 
+	 * 		public &lt;T&gt; TypeAdapter&lt;T&gt; create(Gson gson, TypeToken&lt;T&gt; type) {
+	 * 			final TypeAdapter&lt;T&gt; delegate = gson.getDelegateAdapter(this, type);
+	 * 			return new TypeAdapter&lt;T&gt;() {
+	 * 				public void write(JsonWriter out, T value) throws IOException {
+	 * 					++numWrites;
+	 * 					delegate.write(out, value);
+	 * 				}
+	 * 
+	 * 				public T read(JsonReader in) throws IOException {
+	 * 					++numReads;
+	 * 					return delegate.read(in);
+	 * 				}
+	 * 			};
+	 * 		}
+	 * 	}
+	 * }
+	 * </pre>
+	 * 
+	 * This factory can now be used like this:
+	 * 
+	 * <pre>
+	 * {
+	 * 	&#64;code
+	 * 	StatsTypeAdapterFactory stats = new StatsTypeAdapterFactory();
+	 * 	Gson gson = new GsonBuilder().registerTypeAdapterFactory(stats).create();
+	 * 	// Call gson.toJson() and fromJson methods on objects
+	 * 	System.out.println("Num JSON reads" + stats.numReads);
+	 * 	System.out.println("Num JSON writes" + stats.numWrites);
+	 * }
+	 * </pre>
+	 * 
+	 * Note that this call will skip all factories registered before
+	 * {@code skipPast}. In case of multiple TypeAdapterFactories registered it is
+	 * up to the caller of this function to insure that the order of registration
+	 * does not prevent this method from reaching a factory they would expect to
+	 * reply from this call. Note that since you can not override type adapter
+	 * factories for String and Java primitive types, our stats factory will not
+	 * count the number of String or primitives that will be read or written.
+	 * 
+	 * @param skipPast
+	 *            The type adapter factory that needs to be skipped while searching
+	 *            for a matching type adapter. In most cases, you should just pass
+	 *            <i>this</i> (the type adapter factory from where
+	 *            {@link #getDelegateAdapter} method is being invoked).
+	 * @param type
+	 *            Type for which the delegate adapter is being searched for.
+	 *
+	 * @since 2.2
+	 */
+	public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type) {
+		boolean skipPastFound = false;
+		// Skip past if and only if the specified factory is present in the factories.
+		// This is useful because the factories created through JsonAdapter annotations
+		// are not
+		// registered in this list.
+		if (!factories.contains(skipPast))
+			skipPastFound = true;
+
+		for (TypeAdapterFactory factory : factories) {
+			if (!skipPastFound) {
+				if (factory == skipPast) {
+					skipPastFound = true;
+				}
+				continue;
+			}
+
+			TypeAdapter<T> candidate = factory.create(this, type);
+			if (candidate != null) {
+				return candidate;
+			}
+		}
+		throw new IllegalArgumentException("GSON cannot serialize " + type);
+	}
+
+	/**
+	 * Returns the type adapter for {@code} type.
+	 *
+	 * @throws IllegalArgumentException
+	 *             if this GSON cannot serialize and deserialize {@code type}.
+	 */
+	public <T> TypeAdapter<T> getAdapter(Class<T> type) {
+		return getAdapter(TypeToken.get(type));
+	}
+
+	/**
+	 * This method serializes the specified object into its equivalent
+	 * representation as a tree of {@link JsonElement}s. This method should be used
+	 * when the specified object is not a generic type. This method uses
+	 * {@link Class#getClass()} to get the type for the specified object, but the
+	 * {@code getClass()} loses the generic type information because of the Type
+	 * Erasure feature of Java. Note that this method works fine if the any of the
+	 * object fields are of generic type, just the object itself should not be of a
+	 * generic type. If the object is of generic type, use
+	 * {@link #toJsonTree(Object, Type)} instead.
+	 *
+	 * @param src
+	 *            the object for which Json representation is to be created setting
+	 *            for Gson
+	 * @return Json representation of {@code src}.
+	 * @since 1.4
+	 */
+	public JsonElement toJsonTree(Object src) {
+		if (src == null) {
+			return JsonNull.INSTANCE;
+		}
+		return toJsonTree(src, src.getClass());
+	}
+
+	/**
+	 * This method serializes the specified object, including those of generic
+	 * types, into its equivalent representation as a tree of {@link JsonElement}s.
+	 * This method must be used if the specified object is a generic type. For
+	 * non-generic objects, use {@link #toJsonTree(Object)} instead.
+	 *
+	 * @param src
+	 *            the object for which JSON representation is to be created
+	 * @param typeOfSrc
+	 *            The specific genericized type of src. You can obtain this type by
+	 *            using the {@link com.google.gson.reflect.TypeToken} class. For
+	 *            example, to get the type for {@code Collection<Foo>}, you should
+	 *            use:
+	 * 
+	 *            <pre>
+	 *            Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;() {
+	 *            }.getType();
+	 *            </pre>
+	 * 
+	 * @return Json representation of {@code src}
+	 * @since 1.4
+	 */
+	public JsonElement toJsonTree(Object src, Type typeOfSrc) {
+		JsonTreeWriter writer = new JsonTreeWriter();
+		toJson(src, typeOfSrc, writer);
+		return writer.get();
+	}
+
+	/**
+	 * This method serializes the specified object into its equivalent Json
+	 * representation. This method should be used when the specified object is not a
+	 * generic type. This method uses {@link Class#getClass()} to get the type for
+	 * the specified object, but the {@code getClass()} loses the generic type
+	 * information because of the Type Erasure feature of Java. Note that this
+	 * method works fine if the any of the object fields are of generic type, just
+	 * the object itself should not be of a generic type. If the object is of
+	 * generic type, use {@link #toJson(Object, Type)} instead. If you want to write
+	 * out the object to a {@link Writer}, use {@link #toJson(Object, Appendable)}
+	 * instead.
+	 *
+	 * @param src
+	 *            the object for which Json representation is to be created setting
+	 *            for Gson
+	 * @return Json representation of {@code src}.
+	 */
+	public String toJson(Object src) {
+		if (src == null) {
+			return toJson(JsonNull.INSTANCE);
+		}
+		return toJson(src, src.getClass());
+	}
+
+	/**
+	 * This method serializes the specified object, including those of generic
+	 * types, into its equivalent Json representation. This method must be used if
+	 * the specified object is a generic type. For non-generic objects, use
+	 * {@link #toJson(Object)} instead. If you want to write out the object to a
+	 * {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead.
+	 *
+	 * @param src
+	 *            the object for which JSON representation is to be created
+	 * @param typeOfSrc
+	 *            The specific genericized type of src. You can obtain this type by
+	 *            using the {@link com.google.gson.reflect.TypeToken} class. For
+	 *            example, to get the type for {@code Collection<Foo>}, you should
+	 *            use:
+	 * 
+	 *            <pre>
+	 *            Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;() {
+	 *            }.getType();
+	 *            </pre>
+	 * 
+	 * @return Json representation of {@code src}
+	 */
+	public String toJson(Object src, Type typeOfSrc) {
+		StringWriter writer = new StringWriter();
+		toJson(src, typeOfSrc, writer);
+		return writer.toString();
+	}
+
+	/**
+	 * This method serializes the specified object into its equivalent Json
+	 * representation. This method should be used when the specified object is not a
+	 * generic type. This method uses {@link Class#getClass()} to get the type for
+	 * the specified object, but the {@code getClass()} loses the generic type
+	 * information because of the Type Erasure feature of Java. Note that this
+	 * method works fine if the any of the object fields are of generic type, just
+	 * the object itself should not be of a generic type. If the object is of
+	 * generic type, use {@link #toJson(Object, Type, Appendable)} instead.
+	 *
+	 * @param src
+	 *            the object for which Json representation is to be created setting
+	 *            for Gson
+	 * @param writer
+	 *            Writer to which the Json representation needs to be written
+	 * @throws JsonIOException
+	 *             if there was a problem writing to the writer
+	 * @since 1.2
+	 */
+	public void toJson(Object src, Appendable writer) throws JsonIOException {
+		if (src != null) {
+			toJson(src, src.getClass(), writer);
+		} else {
+			toJson(JsonNull.INSTANCE, writer);
+		}
+	}
+
+	/**
+	 * This method serializes the specified object, including those of generic
+	 * types, into its equivalent Json representation. This method must be used if
+	 * the specified object is a generic type. For non-generic objects, use
+	 * {@link #toJson(Object, Appendable)} instead.
+	 *
+	 * @param src
+	 *            the object for which JSON representation is to be created
+	 * @param typeOfSrc
+	 *            The specific genericized type of src. You can obtain this type by
+	 *            using the {@link com.google.gson.reflect.TypeToken} class. For
+	 *            example, to get the type for {@code Collection<Foo>}, you should
+	 *            use:
+	 * 
+	 *            <pre>
+	 *            Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;() {
+	 *            }.getType();
+	 *            </pre>
+	 * 
+	 * @param writer
+	 *            Writer to which the Json representation of src needs to be
+	 *            written.
+	 * @throws JsonIOException
+	 *             if there was a problem writing to the writer
+	 * @since 1.2
+	 */
+	public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException {
+		try {
+			JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));
+			toJson(src, typeOfSrc, jsonWriter);
+		} catch (IOException e) {
+			throw new JsonIOException(e);
+		}
+	}
+
+	/**
+	 * Writes the JSON representation of {@code src} of type {@code typeOfSrc} to
+	 * {@code writer}.
+	 * 
+	 * @throws JsonIOException
+	 *             if there was a problem writing to the writer
+	 */
+	@SuppressWarnings("unchecked")
+	public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
+		TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
+		boolean oldLenient = writer.isLenient();
+		writer.setLenient(true);
+		boolean oldHtmlSafe = writer.isHtmlSafe();
+		writer.setHtmlSafe(htmlSafe);
+		boolean oldSerializeNulls = writer.getSerializeNulls();
+		writer.setSerializeNulls(serializeNulls);
+		try {
+			((TypeAdapter<Object>) adapter).write(writer, src);
+		} catch (IOException e) {
+			throw new JsonIOException(e);
+		} finally {
+			writer.setLenient(oldLenient);
+			writer.setHtmlSafe(oldHtmlSafe);
+			writer.setSerializeNulls(oldSerializeNulls);
+		}
+	}
+
+	/**
+	 * Converts a tree of {@link JsonElement}s into its equivalent JSON
+	 * representation.
+	 *
+	 * @param jsonElement
+	 *            root of a tree of {@link JsonElement}s
+	 * @return JSON String representation of the tree
+	 * @since 1.4
+	 */
+	public String toJson(JsonElement jsonElement) {
+		StringWriter writer = new StringWriter();
+		toJson(jsonElement, writer);
+		return writer.toString();
+	}
+
+	/**
+	 * Writes out the equivalent JSON for a tree of {@link JsonElement}s.
+	 *
+	 * @param jsonElement
+	 *            root of a tree of {@link JsonElement}s
+	 * @param writer
+	 *            Writer to which the Json representation needs to be written
+	 * @throws JsonIOException
+	 *             if there was a problem writing to the writer
+	 * @since 1.4
+	 */
+	public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOException {
+		try {
+			JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));
+			toJson(jsonElement, jsonWriter);
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	/**
+	 * Returns a new JSON writer configured for the settings on this Gson instance.
+	 */
+	public JsonWriter newJsonWriter(Writer writer) throws IOException {
+		if (generateNonExecutableJson) {
+			writer.write(JSON_NON_EXECUTABLE_PREFIX);
+		}
+		JsonWriter jsonWriter = new JsonWriter(writer);
+		if (prettyPrinting) {
+			jsonWriter.setIndent("  ");
+		}
+		jsonWriter.setSerializeNulls(serializeNulls);
+		return jsonWriter;
+	}
+
+	/**
+	 * Returns a new JSON writer configured for the settings on this Gson instance.
+	 */
+	public JsonReader newJsonReader(Reader reader) {
+		JsonReader jsonReader = new JsonReader(reader);
+		jsonReader.setLenient(lenient);
+		return jsonReader;
+	}
+
+	/**
+	 * Writes the JSON for {@code jsonElement} to {@code writer}.
+	 * 
+	 * @throws JsonIOException
+	 *             if there was a problem writing to the writer
+	 */
+	public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException {
+		boolean oldLenient = writer.isLenient();
+		writer.setLenient(true);
+		boolean oldHtmlSafe = writer.isHtmlSafe();
+		writer.setHtmlSafe(htmlSafe);
+		boolean oldSerializeNulls = writer.getSerializeNulls();
+		writer.setSerializeNulls(serializeNulls);
+		try {
+			Streams.write(jsonElement, writer);
+		} catch (IOException e) {
+			throw new JsonIOException(e);
+		} finally {
+			writer.setLenient(oldLenient);
+			writer.setHtmlSafe(oldHtmlSafe);
+			writer.setSerializeNulls(oldSerializeNulls);
+		}
+	}
+
+	/**
+	 * This method deserializes the specified Json into an object of the specified
+	 * class. It is not suitable to use if the specified class is a generic type
+	 * since it will not have the generic type information because of the Type
+	 * Erasure feature of Java. Therefore, this method should not be used if the
+	 * desired type is a generic type. Note that this method works fine if the any
+	 * of the fields of the specified object are generics, just the object itself
+	 * should not be a generic type. For the cases when the object is of generic
+	 * type, invoke {@link #fromJson(String, Type)}. If you have the Json in a
+	 * {@link Reader} instead of a String, use {@link #fromJson(Reader, Class)}
+	 * instead.
+	 *
+	 * @param <T>
+	 *            the type of the desired object
+	 * @param json
+	 *            the string from which the object is to be deserialized
+	 * @param classOfT
+	 *            the class of T
+	 * @return an object of type T from the string. Returns {@code null} if
+	 *         {@code json} is {@code null}.
+	 * @throws JsonSyntaxException
+	 *             if json is not a valid representation for an object of type
+	 *             classOfT
+	 */
+	public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
+		Object object = fromJson(json, (Type) classOfT);
+		return Primitives.wrap(classOfT).cast(object);
+	}
+
+	/**
+	 * This method deserializes the specified Json into an object of the specified
+	 * type. This method is useful if the specified object is a generic type. For
+	 * non-generic objects, use {@link #fromJson(String, Class)} instead. If you
+	 * have the Json in a {@link Reader} instead of a String, use
+	 * {@link #fromJson(Reader, Type)} instead.
+	 *
+	 * @param <T>
+	 *            the type of the desired object
+	 * @param json
+	 *            the string from which the object is to be deserialized
+	 * @param typeOfT
+	 *            The specific genericized type of src. You can obtain this type by
+	 *            using the {@link com.google.gson.reflect.TypeToken} class. For
+	 *            example, to get the type for {@code Collection<Foo>}, you should
+	 *            use:
+	 * 
+	 *            <pre>
+	 *            Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;() {
+	 *            }.getType();
+	 *            </pre>
+	 * 
+	 * @return an object of type T from the string. Returns {@code null} if
+	 *         {@code json} is {@code null}.
+	 * @throws JsonParseException
+	 *             if json is not a valid representation for an object of type
+	 *             typeOfT
+	 * @throws JsonSyntaxException
+	 *             if json is not a valid representation for an object of type
+	 */
+	@SuppressWarnings("unchecked")
+	public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
+		if (json == null) {
+			return null;
+		}
+		StringReader reader = new StringReader(json);
+		T target = (T) fromJson(reader, typeOfT);
+		return target;
+	}
+
+	/**
+	 * This method deserializes the Json read from the specified reader into an
+	 * object of the specified class. It is not suitable to use if the specified
+	 * class is a generic type since it will not have the generic type information
+	 * because of the Type Erasure feature of Java. Therefore, this method should
+	 * not be used if the desired type is a generic type. Note that this method
+	 * works fine if the any of the fields of the specified object are generics,
+	 * just the object itself should not be a generic type. For the cases when the
+	 * object is of generic type, invoke {@link #fromJson(Reader, Type)}. If you
+	 * have the Json in a String form instead of a {@link Reader}, use
+	 * {@link #fromJson(String, Class)} instead.
+	 *
+	 * @param <T>
+	 *            the type of the desired object
+	 * @param json
+	 *            the reader producing the Json from which the object is to be
+	 *            deserialized.
+	 * @param classOfT
+	 *            the class of T
+	 * @return an object of type T from the string. Returns {@code null} if
+	 *         {@code json} is at EOF.
+	 * @throws JsonIOException
+	 *             if there was a problem reading from the Reader
+	 * @throws JsonSyntaxException
+	 *             if json is not a valid representation for an object of type
+	 * @since 1.2
+	 */
+	public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
+		JsonReader jsonReader = newJsonReader(json);
+		Object object = fromJson(jsonReader, classOfT);
+		assertFullConsumption(object, jsonReader);
+		return Primitives.wrap(classOfT).cast(object);
+	}
+
+	/**
+	 * This method deserializes the Json read from the specified reader into an
+	 * object of the specified type. This method is useful if the specified object
+	 * is a generic type. For non-generic objects, use
+	 * {@link #fromJson(Reader, Class)} instead. If you have the Json in a String
+	 * form instead of a {@link Reader}, use {@link #fromJson(String, Type)}
+	 * instead.
+	 *
+	 * @param <T>
+	 *            the type of the desired object
+	 * @param json
+	 *            the reader producing Json from which the object is to be
+	 *            deserialized
+	 * @param typeOfT
+	 *            The specific genericized type of src. You can obtain this type by
+	 *            using the {@link com.google.gson.reflect.TypeToken} class. For
+	 *            example, to get the type for {@code Collection<Foo>}, you should
+	 *            use:
+	 * 
+	 *            <pre>
+	 *            Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;() {
+	 *            }.getType();
+	 *            </pre>
+	 * 
+	 * @return an object of type T from the json. Returns {@code null} if
+	 *         {@code json} is at EOF.
+	 * @throws JsonIOException
+	 *             if there was a problem reading from the Reader
+	 * @throws JsonSyntaxException
+	 *             if json is not a valid representation for an object of type
+	 * @since 1.2
+	 */
+	@SuppressWarnings("unchecked")
+	public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
+		JsonReader jsonReader = newJsonReader(json);
+		T object = (T) fromJson(jsonReader, typeOfT);
+		assertFullConsumption(object, jsonReader);
+		return object;
+	}
+
+	private static void assertFullConsumption(Object obj, JsonReader reader) {
+		try {
+			if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) {
+				throw new JsonIOException("JSON document was not fully consumed.");
+			}
+		} catch (MalformedJsonException e) {
+			throw new JsonSyntaxException(e);
+		} catch (IOException e) {
+			throw new JsonIOException(e);
+		}
+	}
+
+	/**
+	 * Reads the next JSON value from {@code reader} and convert it to an object of
+	 * type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF.
+	 * Since Type is not parameterized by T, this method is type unsafe and should
+	 * be used carefully
+	 *
+	 * @throws JsonIOException
+	 *             if there was a problem writing to the Reader
+	 * @throws JsonSyntaxException
+	 *             if json is not a valid representation for an object of type
+	 */
+	@SuppressWarnings("unchecked")
+	public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
+		boolean isEmpty = true;
+		boolean oldLenient = reader.isLenient();
+		reader.setLenient(true);
+		try {
+			reader.peek();
+			isEmpty = false;
+			TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
+			TypeAdapter<T> typeAdapter = getAdapter(typeToken);
+			T object = typeAdapter.read(reader);
+			return object;
+		} catch (EOFException e) {
+			/*
+			 * For compatibility with JSON 1.5 and earlier, we return null for empty
+			 * documents instead of throwing.
+			 */
+			if (isEmpty) {
+				return null;
+			}
+			throw new JsonSyntaxException(e);
+		} catch (IllegalStateException e) {
+			throw new JsonSyntaxException(e);
+		} catch (IOException e) {
+			// TODO(inder): Figure out whether it is indeed right to rethrow this as
+			// JsonSyntaxException
+			throw new JsonSyntaxException(e);
+		} finally {
+			reader.setLenient(oldLenient);
+		}
+	}
+
+	/**
+	 * This method deserializes the Json read from the specified parse tree into an
+	 * object of the specified type. It is not suitable to use if the specified
+	 * class is a generic type since it will not have the generic type information
+	 * because of the Type Erasure feature of Java. Therefore, this method should
+	 * not be used if the desired type is a generic type. Note that this method
+	 * works fine if the any of the fields of the specified object are generics,
+	 * just the object itself should not be a generic type. For the cases when the
+	 * object is of generic type, invoke {@link #fromJson(JsonElement, Type)}.
+	 * 
+	 * @param <T>
+	 *            the type of the desired object
+	 * @param json
+	 *            the root of the parse tree of {@link JsonElement}s from which the
+	 *            object is to be deserialized
+	 * @param classOfT
+	 *            The class of T
+	 * @return an object of type T from the json. Returns {@code null} if
+	 *         {@code json} is {@code null}.
+	 * @throws JsonSyntaxException
+	 *             if json is not a valid representation for an object of type
+	 *             typeOfT
+	 * @since 1.3
+	 */
+	public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
+		Object object = fromJson(json, (Type) classOfT);
+		return Primitives.wrap(classOfT).cast(object);
+	}
+
+	/**
+	 * This method deserializes the Json read from the specified parse tree into an
+	 * object of the specified type. This method is useful if the specified object
+	 * is a generic type. For non-generic objects, use
+	 * {@link #fromJson(JsonElement, Class)} instead.
+	 *
+	 * @param <T>
+	 *            the type of the desired object
+	 * @param json
+	 *            the root of the parse tree of {@link JsonElement}s from which the
+	 *            object is to be deserialized
+	 * @param typeOfT
+	 *            The specific genericized type of src. You can obtain this type by
+	 *            using the {@link com.google.gson.reflect.TypeToken} class. For
+	 *            example, to get the type for {@code Collection<Foo>}, you should
+	 *            use:
+	 * 
+	 *            <pre>
+	 *            Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;() {
+	 *            }.getType();
+	 *            </pre>
+	 * 
+	 * @return an object of type T from the json. Returns {@code null} if
+	 *         {@code json} is {@code null}.
+	 * @throws JsonSyntaxException
+	 *             if json is not a valid representation for an object of type
+	 *             typeOfT
+	 * @since 1.3
+	 */
+	@SuppressWarnings("unchecked")
+	public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
+		if (json == null) {
+			return null;
+		}
+		return (T) fromJson(new JsonTreeReader(json), typeOfT);
+	}
+
+	static class FutureTypeAdapter<T> extends TypeAdapter<T> {
+		private TypeAdapter<T> delegate;
+
+		public void setDelegate(TypeAdapter<T> typeAdapter) {
+			if (delegate != null) {
+				throw new AssertionError();
+			}
+			delegate = typeAdapter;
+		}
+
+		@Override
+		public T read(JsonReader in) throws IOException {
+			if (delegate == null) {
+				throw new IllegalStateException();
+			}
+			return delegate.read(in);
+		}
+
+		@Override
+		public void write(JsonWriter out, T value) throws IOException {
+			if (delegate == null) {
+				throw new IllegalStateException();
+			}
+			delegate.write(out, value);
+		}
+	}
+
+	@Override
+	public String toString() {
+		return new StringBuilder("{serializeNulls:").append(serializeNulls).append("factories:").append(factories).append(",instanceCreators:").append(constructorConstructor).append("}").toString();
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/GsonBuilder.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/GsonBuilder.java
new file mode 100644
index 0000000..a4536e4
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/GsonBuilder.java
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import static cn.emay.sdk.util.json.gson.Gson.DEFAULT_COMPLEX_MAP_KEYS;
+import static cn.emay.sdk.util.json.gson.Gson.DEFAULT_ESCAPE_HTML;
+import static cn.emay.sdk.util.json.gson.Gson.DEFAULT_JSON_NON_EXECUTABLE;
+import static cn.emay.sdk.util.json.gson.Gson.DEFAULT_LENIENT;
+import static cn.emay.sdk.util.json.gson.Gson.DEFAULT_PRETTY_PRINT;
+import static cn.emay.sdk.util.json.gson.Gson.DEFAULT_SERIALIZE_NULLS;
+import static cn.emay.sdk.util.json.gson.Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES;
+
+import java.lang.reflect.Type;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.json.gson.internal.$Gson$Preconditions;
+import cn.emay.sdk.util.json.gson.internal.Excluder;
+import cn.emay.sdk.util.json.gson.internal.bind.TypeAdapters;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+
+/**
+ * <p>
+ * Use this builder to construct a {@link Gson} instance when you need to set
+ * configuration options other than the default. For {@link Gson} with default
+ * configuration, it is simpler to use {@code new Gson()}. {@code GsonBuilder}
+ * is best used by creating it, and then invoking its various configuration
+ * methods, and finally calling create.
+ * </p>
+ *
+ * <p>
+ * The following is an example shows how to use the {@code GsonBuilder} to
+ * construct a Gson instance:
+ *
+ * <pre>
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdTypeAdapter()).enableComplexMapKeySerialization().serializeNulls().setDateFormat(DateFormat.LONG)
+ * 		.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().setVersion(1.0).create();
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * NOTES:
+ * <ul>
+ * <li>the order of invocation of configuration methods does not matter.</li>
+ * <li>The default serialization of {@link Date} and its subclasses in Gson does
+ * not contain time-zone information. So, if you are using date/time instances,
+ * use {@code GsonBuilder} and its {@code setDateFormat} methods.</li>
+ * </ul>
+ * </p>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+public final class GsonBuilder {
+	private Excluder excluder = Excluder.DEFAULT;
+	private LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT;
+	private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY;
+	private final Map<Type, InstanceCreator<?>> instanceCreators = new HashMap<Type, InstanceCreator<?>>();
+	private final List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
+	/**
+	 * tree-style hierarchy factories. These come after factories for backwards
+	 * compatibility.
+	 */
+	private final List<TypeAdapterFactory> hierarchyFactories = new ArrayList<TypeAdapterFactory>();
+	private boolean serializeNulls = DEFAULT_SERIALIZE_NULLS;
+	private String datePattern;
+	private int dateStyle = DateFormat.DEFAULT;
+	private int timeStyle = DateFormat.DEFAULT;
+	private boolean complexMapKeySerialization = DEFAULT_COMPLEX_MAP_KEYS;
+	private boolean serializeSpecialFloatingPointValues = DEFAULT_SPECIALIZE_FLOAT_VALUES;
+	private boolean escapeHtmlChars = DEFAULT_ESCAPE_HTML;
+	private boolean prettyPrinting = DEFAULT_PRETTY_PRINT;
+	private boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE;
+	private boolean lenient = DEFAULT_LENIENT;
+
+	/**
+	 * Creates a GsonBuilder instance that can be used to build Gson with various
+	 * configuration settings. GsonBuilder follows the builder pattern, and it is
+	 * typically used by first invoking various configuration methods to set desired
+	 * options, and finally calling {@link #create()}.
+	 */
+	public GsonBuilder() {
+	}
+
+	/**
+	 * Configures Gson to enable versioning support.
+	 *
+	 * @param ignoreVersionsAfter
+	 *            any field or type marked with a version higher than this value are
+	 *            ignored during serialization or deserialization.
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 */
+	public GsonBuilder setVersion(double ignoreVersionsAfter) {
+		excluder = excluder.withVersion(ignoreVersionsAfter);
+		return this;
+	}
+
+	/**
+	 * Configures Gson to excludes all class fields that have the specified
+	 * modifiers. By default, Gson will exclude all fields marked transient or
+	 * static. This method will override that behavior.
+	 *
+	 * @param modifiers
+	 *            the field modifiers. You must use the modifiers specified in the
+	 *            {@link java.lang.reflect.Modifier} class. For example,
+	 *            {@link java.lang.reflect.Modifier#TRANSIENT},
+	 *            {@link java.lang.reflect.Modifier#STATIC}.
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 */
+	public GsonBuilder excludeFieldsWithModifiers(int... modifiers) {
+		excluder = excluder.withModifiers(modifiers);
+		return this;
+	}
+
+	/**
+	 * Makes the output JSON non-executable in Javascript by prefixing the generated
+	 * JSON with some special text. This prevents attacks from third-party sites
+	 * through script sourcing. See
+	 * <a href="http://code.google.com/p/google-gson/issues/detail?id=42">Gson Issue
+	 * 42</a> for details.
+	 *
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.3
+	 */
+	public GsonBuilder generateNonExecutableJson() {
+		this.generateNonExecutableJson = true;
+		return this;
+	}
+
+	/**
+	 * Configures Gson to exclude all fields from consideration for serialization or
+	 * deserialization that do not have the
+	 * {@link com.google.gson.annotations.Expose} annotation.
+	 *
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 */
+	public GsonBuilder excludeFieldsWithoutExposeAnnotation() {
+		excluder = excluder.excludeFieldsWithoutExposeAnnotation();
+		return this;
+	}
+
+	/**
+	 * Configure Gson to serialize null fields. By default, Gson omits all fields
+	 * that are null during serialization.
+	 *
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.2
+	 */
+	public GsonBuilder serializeNulls() {
+		this.serializeNulls = true;
+		return this;
+	}
+
+	/**
+	 * Enabling this feature will only change the serialized form if the map key is
+	 * a complex type (i.e. non-primitive) in its <strong>serialized</strong> JSON
+	 * form. The default implementation of map serialization uses {@code toString()}
+	 * on the key; however, when this is called then one of the following cases
+	 * apply:
+	 *
+	 * <h3>Maps as JSON objects</h3> For this case, assume that a type adapter is
+	 * registered to serialize and deserialize some {@code Point} class, which
+	 * contains an x and y coordinate, to/from the JSON Primitive string value
+	 * {@code "(x,y)"}. The Java map would then be serialized as a
+	 * {@link JsonObject}.
+	 *
+	 * <p>
+	 * Below is an example:
+	 * 
+	 * <pre>
+	 * {
+	 * 	&#64;code
+	 * 	Gson gson = new GsonBuilder().register(Point.class, new MyPointTypeAdapter()).enableComplexMapKeySerialization().create();
+	 *
+	 * 	Map<Point, String> original = new LinkedHashMap<Point, String>();
+	 * 	original.put(new Point(5, 6), "a");
+	 * 	original.put(new Point(8, 8), "b");
+	 * 	System.out.println(gson.toJson(original, type));
+	 * }
+	 * </pre>
+	 * 
+	 * The above code prints this JSON object:
+	 * 
+	 * <pre>
+	 *   {@code
+	 *   {
+	 *     "(5,6)": "a",
+	 *     "(8,8)": "b"
+	 *   }
+	 * }
+	 * </pre>
+	 *
+	 * <h3>Maps as JSON arrays</h3> For this case, assume that a type adapter was
+	 * NOT registered for some {@code Point} class, but rather the default Gson
+	 * serialization is applied. In this case, some {@code new Point(2,3)} would
+	 * serialize as {@code {"x":2,"y":5}}.
+	 *
+	 * <p>
+	 * Given the assumption above, a {@code Map<Point, String>} will be serialize as
+	 * an array of arrays (can be viewed as an entry set of pairs).
+	 *
+	 * <p>
+	 * Below is an example of serializing complex types as JSON arrays:
+	 * 
+	 * <pre>
+	 *  {@code
+	 *   Gson gson = new GsonBuilder()
+	 *       .enableComplexMapKeySerialization()
+	 *       .create();
+	 *
+	 *   Map<Point, String> original = new LinkedHashMap<Point, String>();
+	 *   original.put(new Point(5, 6), "a");
+	 *   original.put(new Point(8, 8), "b");
+	 *   System.out.println(gson.toJson(original, type));
+	 * }
+	 *
+	 * The JSON output would look as follows:
+	 * <pre>   {@code
+	 *   [
+	 *     [
+	 *       {
+	 *         "x": 5,
+	 *         "y": 6
+	 *       },
+	 *       "a"
+	 *     ],
+	 *     [
+	 *       {
+	 *         "x": 8,
+	 *         "y": 8
+	 *       },
+	 *       "b"
+	 *     ]
+	 *   ]
+	 * }
+	 * </pre>
+	 *
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.7
+	 */
+	public GsonBuilder enableComplexMapKeySerialization() {
+		complexMapKeySerialization = true;
+		return this;
+	}
+
+	/**
+	 * Configures Gson to exclude inner classes during serialization.
+	 *
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.3
+	 */
+	public GsonBuilder disableInnerClassSerialization() {
+		excluder = excluder.disableInnerClassSerialization();
+		return this;
+	}
+
+	/**
+	 * Configures Gson to apply a specific serialization policy for {@code Long} and
+	 * {@code long} objects.
+	 *
+	 * @param serializationPolicy
+	 *            the particular policy to use for serializing longs.
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.3
+	 */
+	public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) {
+		this.longSerializationPolicy = serializationPolicy;
+		return this;
+	}
+
+	/**
+	 * Configures Gson to apply a specific naming policy to an object's field during
+	 * serialization and deserialization.
+	 *
+	 * @param namingConvention
+	 *            the JSON field naming convention to use for serialization and
+	 *            deserialization.
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 */
+	public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) {
+		this.fieldNamingPolicy = namingConvention;
+		return this;
+	}
+
+	/**
+	 * Configures Gson to apply a specific naming policy strategy to an object's
+	 * field during serialization and deserialization.
+	 *
+	 * @param fieldNamingStrategy
+	 *            the actual naming strategy to apply to the fields
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.3
+	 */
+	public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
+		this.fieldNamingPolicy = fieldNamingStrategy;
+		return this;
+	}
+
+	/**
+	 * Configures Gson to apply a set of exclusion strategies during both
+	 * serialization and deserialization. Each of the {@code strategies} will be
+	 * applied as a disjunction rule. This means that if one of the
+	 * {@code strategies} suggests that a field (or class) should be skipped then
+	 * that field (or object) is skipped during serialization/deserialization.
+	 *
+	 * @param strategies
+	 *            the set of strategy object to apply during object
+	 *            (de)serialization.
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.4
+	 */
+	public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) {
+		for (ExclusionStrategy strategy : strategies) {
+			excluder = excluder.withExclusionStrategy(strategy, true, true);
+		}
+		return this;
+	}
+
+	/**
+	 * Configures Gson to apply the passed in exclusion strategy during
+	 * serialization. If this method is invoked numerous times with different
+	 * exclusion strategy objects then the exclusion strategies that were added will
+	 * be applied as a disjunction rule. This means that if one of the added
+	 * exclusion strategies suggests that a field (or class) should be skipped then
+	 * that field (or object) is skipped during its serialization.
+	 *
+	 * @param strategy
+	 *            an exclusion strategy to apply during serialization.
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.7
+	 */
+	public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy) {
+		excluder = excluder.withExclusionStrategy(strategy, true, false);
+		return this;
+	}
+
+	/**
+	 * Configures Gson to apply the passed in exclusion strategy during
+	 * deserialization. If this method is invoked numerous times with different
+	 * exclusion strategy objects then the exclusion strategies that were added will
+	 * be applied as a disjunction rule. This means that if one of the added
+	 * exclusion strategies suggests that a field (or class) should be skipped then
+	 * that field (or object) is skipped during its deserialization.
+	 *
+	 * @param strategy
+	 *            an exclusion strategy to apply during deserialization.
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.7
+	 */
+	public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strategy) {
+		excluder = excluder.withExclusionStrategy(strategy, false, true);
+		return this;
+	}
+
+	/**
+	 * Configures Gson to output Json that fits in a page for pretty printing. This
+	 * option only affects Json serialization.
+	 *
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 */
+	public GsonBuilder setPrettyPrinting() {
+		prettyPrinting = true;
+		return this;
+	}
+
+	/**
+	 * By default, Gson is strict and only accepts JSON as specified by
+	 * <a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. This option makes
+	 * the parser liberal in what it accepts.
+	 *
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @see JsonReader#setLenient(boolean)
+	 */
+	public GsonBuilder setLenient() {
+		lenient = true;
+		return this;
+	}
+
+	/**
+	 * By default, Gson escapes HTML characters such as &lt; &gt; etc. Use this
+	 * option to configure Gson to pass-through HTML characters as is.
+	 *
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.3
+	 */
+	public GsonBuilder disableHtmlEscaping() {
+		this.escapeHtmlChars = false;
+		return this;
+	}
+
+	/**
+	 * Configures Gson to serialize {@code Date} objects according to the pattern
+	 * provided. You can call this method or {@link #setDateFormat(int)} multiple
+	 * times, but only the last invocation will be used to decide the serialization
+	 * format.
+	 *
+	 * <p>
+	 * The date format will be used to serialize and deserialize
+	 * {@link java.util.Date}, {@link java.sql.Timestamp} and {@link java.sql.Date}.
+	 *
+	 * <p>
+	 * Note that this pattern must abide by the convention provided by
+	 * {@code SimpleDateFormat} class. See the documentation in
+	 * {@link java.text.SimpleDateFormat} for more information on valid date and
+	 * time patterns.
+	 * </p>
+	 *
+	 * @param pattern
+	 *            the pattern that dates will be serialized/deserialized to/from
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.2
+	 */
+	public GsonBuilder setDateFormat(String pattern) {
+		// TODO(Joel): Make this fail fast if it is an invalid date format
+		this.datePattern = pattern;
+		return this;
+	}
+
+	/**
+	 * Configures Gson to to serialize {@code Date} objects according to the style
+	 * value provided. You can call this method or {@link #setDateFormat(String)}
+	 * multiple times, but only the last invocation will be used to decide the
+	 * serialization format.
+	 *
+	 * <p>
+	 * Note that this style value should be one of the predefined constants in the
+	 * {@code DateFormat} class. See the documentation in
+	 * {@link java.text.DateFormat} for more information on the valid style
+	 * constants.
+	 * </p>
+	 *
+	 * @param style
+	 *            the predefined date style that date objects will be
+	 *            serialized/deserialized to/from
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.2
+	 */
+	public GsonBuilder setDateFormat(int style) {
+		this.dateStyle = style;
+		this.datePattern = null;
+		return this;
+	}
+
+	/**
+	 * Configures Gson to to serialize {@code Date} objects according to the style
+	 * value provided. You can call this method or {@link #setDateFormat(String)}
+	 * multiple times, but only the last invocation will be used to decide the
+	 * serialization format.
+	 *
+	 * <p>
+	 * Note that this style value should be one of the predefined constants in the
+	 * {@code DateFormat} class. See the documentation in
+	 * {@link java.text.DateFormat} for more information on the valid style
+	 * constants.
+	 * </p>
+	 *
+	 * @param dateStyle
+	 *            the predefined date style that date objects will be
+	 *            serialized/deserialized to/from
+	 * @param timeStyle
+	 *            the predefined style for the time portion of the date objects
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.2
+	 */
+	public GsonBuilder setDateFormat(int dateStyle, int timeStyle) {
+		this.dateStyle = dateStyle;
+		this.timeStyle = timeStyle;
+		this.datePattern = null;
+		return this;
+	}
+
+	/**
+	 * Configures Gson for custom serialization or deserialization. This method
+	 * combines the registration of an {@link TypeAdapter}, {@link InstanceCreator},
+	 * {@link JsonSerializer}, and a {@link JsonDeserializer}. It is best used when
+	 * a single object {@code typeAdapter} implements all the required interfaces
+	 * for custom serialization with Gson. If a type adapter was previously
+	 * registered for the specified {@code type}, it is overwritten.
+	 *
+	 * <p>
+	 * This registers the type specified and no other types: you must manually
+	 * register related types! For example, applications registering
+	 * {@code boolean.class} should also register {@code
+	 * Boolean.class}.
+	 *
+	 * @param type
+	 *            the type definition for the type adapter being registered
+	 * @param typeAdapter
+	 *            This object must implement at least one of the
+	 *            {@link TypeAdapter}, {@link InstanceCreator},
+	 *            {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
+		$Gson$Preconditions.checkArgument(
+				typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?> || typeAdapter instanceof InstanceCreator<?> || typeAdapter instanceof TypeAdapter<?>);
+		if (typeAdapter instanceof InstanceCreator<?>) {
+			instanceCreators.put(type, (InstanceCreator) typeAdapter);
+		}
+		if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
+			TypeToken<?> typeToken = TypeToken.get(type);
+			factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
+		}
+		if (typeAdapter instanceof TypeAdapter<?>) {
+			factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter) typeAdapter));
+		}
+		return this;
+	}
+
+	/**
+	 * Register a factory for type adapters. Registering a factory is useful when
+	 * the type adapter needs to be configured based on the type of the field being
+	 * processed. Gson is designed to handle a large number of factories, so you
+	 * should consider registering them to be at par with registering an individual
+	 * type adapter.
+	 *
+	 * @since 2.1
+	 */
+	public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
+		factories.add(factory);
+		return this;
+	}
+
+	/**
+	 * Configures Gson for custom serialization or deserialization for an
+	 * inheritance type hierarchy. This method combines the registration of a
+	 * {@link TypeAdapter}, {@link JsonSerializer} and a {@link JsonDeserializer}.
+	 * If a type adapter was previously registered for the specified type hierarchy,
+	 * it is overridden. If a type adapter is registered for a specific type in the
+	 * type hierarchy, it will be invoked instead of the one registered for the type
+	 * hierarchy.
+	 *
+	 * @param baseType
+	 *            the class definition for the type adapter being registered for the
+	 *            base class or interface
+	 * @param typeAdapter
+	 *            This object must implement at least one of {@link TypeAdapter},
+	 *            {@link JsonSerializer} or {@link JsonDeserializer} interfaces.
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.7
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	public GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) {
+		$Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?> || typeAdapter instanceof TypeAdapter<?>);
+		if (typeAdapter instanceof JsonDeserializer || typeAdapter instanceof JsonSerializer) {
+			hierarchyFactories.add(0, TreeTypeAdapter.newTypeHierarchyFactory(baseType, typeAdapter));
+		}
+		if (typeAdapter instanceof TypeAdapter<?>) {
+			factories.add(TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter) typeAdapter));
+		}
+		return this;
+	}
+
+	/**
+	 * Section 2.4 of <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON
+	 * specification</a> disallows special double values (NaN, Infinity, -Infinity).
+	 * However, <a href=
+	 * "http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">Javascript
+	 * specification</a> (see section 4.3.20, 4.3.22, 4.3.23) allows these values as
+	 * valid Javascript values. Moreover, most JavaScript engines will accept these
+	 * special values in JSON without problem. So, at a practical level, it makes
+	 * sense to accept these values as valid JSON even though JSON specification
+	 * disallows them.
+	 *
+	 * <p>
+	 * Gson always accepts these special values during deserialization. However, it
+	 * outputs strictly compliant JSON. Hence, if it encounters a float value
+	 * {@link Float#NaN}, {@link Float#POSITIVE_INFINITY},
+	 * {@link Float#NEGATIVE_INFINITY}, or a double value {@link Double#NaN},
+	 * {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it will
+	 * throw an {@link IllegalArgumentException}. This method provides a way to
+	 * override the default behavior when you know that the JSON receiver will be
+	 * able to handle these special values.
+	 *
+	 * @return a reference to this {@code GsonBuilder} object to fulfill the
+	 *         "Builder" pattern
+	 * @since 1.3
+	 */
+	public GsonBuilder serializeSpecialFloatingPointValues() {
+		this.serializeSpecialFloatingPointValues = true;
+		return this;
+	}
+
+	/**
+	 * Creates a {@link Gson} instance based on the current configuration. This
+	 * method is free of side-effects to this {@code GsonBuilder} instance and hence
+	 * can be called multiple times.
+	 *
+	 * @return an instance of Gson configured with the options currently set in this
+	 *         builder
+	 */
+	public Gson create() {
+		List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
+		factories.addAll(this.factories);
+		Collections.reverse(factories);
+		factories.addAll(this.hierarchyFactories);
+		addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories);
+
+		return new Gson(excluder, fieldNamingPolicy, instanceCreators, serializeNulls, complexMapKeySerialization, generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient,
+				serializeSpecialFloatingPointValues, longSerializationPolicy, factories);
+	}
+
+	private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, List<TypeAdapterFactory> factories) {
+		DefaultDateTypeAdapter dateTypeAdapter;
+		if (datePattern != null && !"".equals(datePattern.trim())) {
+			dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
+		} else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
+			dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
+		} else {
+			return;
+		}
+
+		factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Date.class), dateTypeAdapter));
+		factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Timestamp.class), dateTypeAdapter));
+		factories.add(TreeTypeAdapter.newFactory(TypeToken.get(java.sql.Date.class), dateTypeAdapter));
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/InstanceCreator.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/InstanceCreator.java
new file mode 100644
index 0000000..a6734f6
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/InstanceCreator.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * This interface is implemented to create instances of a class that does not
+ * define a no-args constructor. If you can modify the class, you should instead
+ * add a private, or public no-args constructor. However, that is not possible
+ * for library classes, such as JDK classes, or a third-party library that you
+ * do not have source-code of. In such cases, you should define an instance
+ * creator for the class. Implementations of this interface should be registered
+ * with {@link GsonBuilder#registerTypeAdapter(Type, Object)} method before Gson
+ * will be able to use them.
+ * <p>
+ * Let us look at an example where defining an InstanceCreator might be useful.
+ * The {@code Id} class defined below does not have a default no-args
+ * constructor.
+ * </p>
+ *
+ * <pre>
+ * public class Id&lt;T&gt; {
+ * 	private final Class&lt;T&gt; clazz;
+ * 	private final long value;
+ * 
+ * 	public Id(Class&lt;T&gt; clazz, long value) {
+ * 		this.clazz = clazz;
+ * 		this.value = value;
+ * 	}
+ * }
+ * </pre>
+ *
+ * <p>
+ * If Gson encounters an object of type {@code Id} during deserialization, it
+ * will throw an exception. The easiest way to solve this problem will be to add
+ * a (public or private) no-args constructor as follows:
+ * </p>
+ *
+ * <pre>
+ * private Id() {
+ * 	this(Object.class, 0L);
+ * }
+ * </pre>
+ *
+ * <p>
+ * However, let us assume that the developer does not have access to the
+ * source-code of the {@code Id} class, or does not want to define a no-args
+ * constructor for it. The developer can solve this problem by defining an
+ * {@code InstanceCreator} for {@code Id}:
+ * </p>
+ *
+ * <pre>
+ * class IdInstanceCreator implements InstanceCreator&lt;Id&gt; {
+ * 	public Id createInstance(Type type) {
+ * 		return new Id(Object.class, 0L);
+ * 	}
+ * }
+ * </pre>
+ *
+ * <p>
+ * Note that it does not matter what the fields of the created instance contain
+ * since Gson will overwrite them with the deserialized values specified in
+ * Json. You should also ensure that a <i>new</i> object is returned, not a
+ * common object since its fields will be overwritten. The developer will need
+ * to register {@code IdInstanceCreator} with Gson as follows:
+ * </p>
+ *
+ * <pre>
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdInstanceCreator()).create();
+ * </pre>
+ *
+ * @param <T>
+ *            the type of object that will be created by this implementation.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface InstanceCreator<T> {
+
+	/**
+	 * Gson invokes this call-back method during deserialization to create an
+	 * instance of the specified type. The fields of the returned instance are
+	 * overwritten with the data present in the Json. Since the prior contents of
+	 * the object are destroyed and overwritten, do not return an instance that is
+	 * useful elsewhere. In particular, do not return a common instance, always use
+	 * {@code new} to create a new instance.
+	 *
+	 * @param type
+	 *            the parameterized T represented as a {@link Type}.
+	 * @return a default object instance of type T.
+	 */
+	public T createInstance(Type type);
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonArray.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonArray.java
new file mode 100644
index 0000000..f42512f
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonArray.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A class representing an array type in Json. An array is a list of
+ * {@link JsonElement}s each of which can be of a different type. This is an
+ * ordered list, meaning that the order in which elements are added is
+ * preserved.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonArray extends JsonElement implements Iterable<JsonElement> {
+	private final List<JsonElement> elements;
+
+	/**
+	 * Creates an empty JsonArray.
+	 */
+	public JsonArray() {
+		elements = new ArrayList<JsonElement>();
+	}
+
+	@Override
+	JsonArray deepCopy() {
+		JsonArray result = new JsonArray();
+		for (JsonElement element : elements) {
+			result.add(element.deepCopy());
+		}
+		return result;
+	}
+
+	/**
+	 * Adds the specified boolean to self.
+	 *
+	 * @param bool
+	 *            the boolean that needs to be added to the array.
+	 */
+	public void add(Boolean bool) {
+		elements.add(bool == null ? JsonNull.INSTANCE : new JsonPrimitive(bool));
+	}
+
+	/**
+	 * Adds the specified character to self.
+	 *
+	 * @param character
+	 *            the character that needs to be added to the array.
+	 */
+	public void add(Character character) {
+		elements.add(character == null ? JsonNull.INSTANCE : new JsonPrimitive(character));
+	}
+
+	/**
+	 * Adds the specified number to self.
+	 *
+	 * @param number
+	 *            the number that needs to be added to the array.
+	 */
+	public void add(Number number) {
+		elements.add(number == null ? JsonNull.INSTANCE : new JsonPrimitive(number));
+	}
+
+	/**
+	 * Adds the specified string to self.
+	 *
+	 * @param string
+	 *            the string that needs to be added to the array.
+	 */
+	public void add(String string) {
+		elements.add(string == null ? JsonNull.INSTANCE : new JsonPrimitive(string));
+	}
+
+	/**
+	 * Adds the specified element to self.
+	 *
+	 * @param element
+	 *            the element that needs to be added to the array.
+	 */
+	public void add(JsonElement element) {
+		if (element == null) {
+			element = JsonNull.INSTANCE;
+		}
+		elements.add(element);
+	}
+
+	/**
+	 * Adds all the elements of the specified array to self.
+	 *
+	 * @param array
+	 *            the array whose elements need to be added to the array.
+	 */
+	public void addAll(JsonArray array) {
+		elements.addAll(array.elements);
+	}
+
+	/**
+	 * Replaces the element at the specified position in this array with the
+	 * specified element. Element can be null.
+	 * 
+	 * @param index
+	 *            index of the element to replace
+	 * @param element
+	 *            element to be stored at the specified position
+	 * @return the element previously at the specified position
+	 * @throws IndexOutOfBoundsException
+	 *             if the specified index is outside the array bounds
+	 */
+	public JsonElement set(int index, JsonElement element) {
+		return elements.set(index, element);
+	}
+
+	/**
+	 * Removes the first occurrence of the specified element from this array, if it
+	 * is present. If the array does not contain the element, it is unchanged.
+	 * 
+	 * @param element
+	 *            element to be removed from this array, if present
+	 * @return true if this array contained the specified element, false otherwise
+	 * @since 2.3
+	 */
+	public boolean remove(JsonElement element) {
+		return elements.remove(element);
+	}
+
+	/**
+	 * Removes the element at the specified position in this array. Shifts any
+	 * subsequent elements to the left (subtracts one from their indices). Returns
+	 * the element that was removed from the array.
+	 * 
+	 * @param index
+	 *            index the index of the element to be removed
+	 * @return the element previously at the specified position
+	 * @throws IndexOutOfBoundsException
+	 *             if the specified index is outside the array bounds
+	 * @since 2.3
+	 */
+	public JsonElement remove(int index) {
+		return elements.remove(index);
+	}
+
+	/**
+	 * Returns true if this array contains the specified element.
+	 * 
+	 * @return true if this array contains the specified element.
+	 * @param element
+	 *            whose presence in this array is to be tested
+	 * @since 2.3
+	 */
+	public boolean contains(JsonElement element) {
+		return elements.contains(element);
+	}
+
+	/**
+	 * Returns the number of elements in the array.
+	 *
+	 * @return the number of elements in the array.
+	 */
+	public int size() {
+		return elements.size();
+	}
+
+	/**
+	 * Returns an iterator to navigate the elements of the array. Since the array is
+	 * an ordered list, the iterator navigates the elements in the order they were
+	 * inserted.
+	 *
+	 * @return an iterator to navigate the elements of the array.
+	 */
+	public Iterator<JsonElement> iterator() {
+		return elements.iterator();
+	}
+
+	/**
+	 * Returns the ith element of the array.
+	 *
+	 * @param i
+	 *            the index of the element that is being sought.
+	 * @return the element present at the ith index.
+	 * @throws IndexOutOfBoundsException
+	 *             if i is negative or greater than or equal to the {@link #size()}
+	 *             of the array.
+	 */
+	public JsonElement get(int i) {
+		return elements.get(i);
+	}
+
+	/**
+	 * convenience method to get this array as a {@link Number} if it contains a
+	 * single element.
+	 *
+	 * @return get this element as a number if it is single element array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive} and
+	 *             is not a valid Number.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 */
+	@Override
+	public Number getAsNumber() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsNumber();
+		}
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * convenience method to get this array as a {@link String} if it contains a
+	 * single element.
+	 *
+	 * @return get this element as a String if it is single element array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive} and
+	 *             is not a valid String.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 */
+	@Override
+	public String getAsString() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsString();
+		}
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * convenience method to get this array as a double if it contains a single
+	 * element.
+	 *
+	 * @return get this element as a double if it is single element array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive} and
+	 *             is not a valid double.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 */
+	@Override
+	public double getAsDouble() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsDouble();
+		}
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * convenience method to get this array as a {@link BigDecimal} if it contains a
+	 * single element.
+	 *
+	 * @return get this element as a {@link BigDecimal} if it is single element
+	 *         array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive}.
+	 * @throws NumberFormatException
+	 *             if the element at index 0 is not a valid {@link BigDecimal}.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 * @since 1.2
+	 */
+	@Override
+	public BigDecimal getAsBigDecimal() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsBigDecimal();
+		}
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * convenience method to get this array as a {@link BigInteger} if it contains a
+	 * single element.
+	 *
+	 * @return get this element as a {@link BigInteger} if it is single element
+	 *         array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive}.
+	 * @throws NumberFormatException
+	 *             if the element at index 0 is not a valid {@link BigInteger}.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 * @since 1.2
+	 */
+	@Override
+	public BigInteger getAsBigInteger() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsBigInteger();
+		}
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * convenience method to get this array as a float if it contains a single
+	 * element.
+	 *
+	 * @return get this element as a float if it is single element array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive} and
+	 *             is not a valid float.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 */
+	@Override
+	public float getAsFloat() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsFloat();
+		}
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * convenience method to get this array as a long if it contains a single
+	 * element.
+	 *
+	 * @return get this element as a long if it is single element array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive} and
+	 *             is not a valid long.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 */
+	@Override
+	public long getAsLong() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsLong();
+		}
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * convenience method to get this array as an integer if it contains a single
+	 * element.
+	 *
+	 * @return get this element as an integer if it is single element array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive} and
+	 *             is not a valid integer.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 */
+	@Override
+	public int getAsInt() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsInt();
+		}
+		throw new IllegalStateException();
+	}
+
+	@Override
+	public byte getAsByte() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsByte();
+		}
+		throw new IllegalStateException();
+	}
+
+	@Override
+	public char getAsCharacter() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsCharacter();
+		}
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * convenience method to get this array as a primitive short if it contains a
+	 * single element.
+	 *
+	 * @return get this element as a primitive short if it is single element array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive} and
+	 *             is not a valid short.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 */
+	@Override
+	public short getAsShort() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsShort();
+		}
+		throw new IllegalStateException();
+	}
+
+	/**
+	 * convenience method to get this array as a boolean if it contains a single
+	 * element.
+	 *
+	 * @return get this element as a boolean if it is single element array.
+	 * @throws ClassCastException
+	 *             if the element in the array is of not a {@link JsonPrimitive} and
+	 *             is not a valid boolean.
+	 * @throws IllegalStateException
+	 *             if the array has more than one element.
+	 */
+	@Override
+	public boolean getAsBoolean() {
+		if (elements.size() == 1) {
+			return elements.get(0).getAsBoolean();
+		}
+		throw new IllegalStateException();
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		return (o == this) || (o instanceof JsonArray && ((JsonArray) o).elements.equals(elements));
+	}
+
+	@Override
+	public int hashCode() {
+		return elements.hashCode();
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonDeserializationContext.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonDeserializationContext.java
new file mode 100644
index 0000000..29c85a4
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonDeserializationContext.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Context for deserialization that is passed to a custom deserializer during
+ * invocation of its
+ * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)}
+ * method.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface JsonDeserializationContext {
+
+	/**
+	 * Invokes default deserialization on the specified object. It should never be
+	 * invoked on the element received as a parameter of the
+	 * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)}
+	 * method. Doing so will result in an infinite loop since Gson will in-turn call
+	 * the custom deserializer again.
+	 *
+	 * @param json
+	 *            the parse tree.
+	 * @param typeOfT
+	 *            type of the expected return value.
+	 * @param <T>
+	 *            The type of the deserialized object.
+	 * @return An object of type typeOfT.
+	 * @throws JsonParseException
+	 *             if the parse tree does not contain expected data.
+	 */
+	public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException;
+}
\ No newline at end of file
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonDeserializer.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonDeserializer.java
new file mode 100644
index 0000000..84a50cc
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonDeserializer.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * <p>
+ * Interface representing a custom deserializer for Json. You should write a
+ * custom deserializer, if you are not happy with the default deserialization
+ * done by Gson. You will also need to register this deserializer through
+ * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.
+ * </p>
+ *
+ * <p>
+ * Let us look at example where defining a deserializer will be useful. The
+ * {@code Id} class defined below has two fields: {@code clazz} and
+ * {@code value}.
+ * </p>
+ *
+ * <pre>
+ * public class Id&lt;T&gt; {
+ * 	private final Class&lt;T&gt; clazz;
+ * 	private final long value;
+ * 
+ * 	public Id(Class&lt;T&gt; clazz, long value) {
+ * 		this.clazz = clazz;
+ * 		this.value = value;
+ * 	}
+ * 
+ * 	public long getValue() {
+ * 		return value;
+ * 	}
+ * }
+ * </pre>
+ *
+ * <p>
+ * The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will
+ * require the Json string to be
+ * <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you already know
+ * the type of the field that the {@code Id} will be deserialized into, and
+ * hence just want to deserialize it from a Json string {@code 20}. You can
+ * achieve that by writing a custom deserializer:
+ * </p>
+ *
+ * <pre>
+ * class IdDeserializer implements JsonDeserializer&lt;Id&gt;() {
+ *   public Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ *       throws JsonParseException {
+ *     return new Id((Class)typeOfT, id.getValue());
+ *   }
+ * </pre>
+ *
+ * <p>
+ * You will also need to register {@code IdDeserializer} with Gson as follows:
+ * </p>
+ *
+ * <pre>
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdDeserializer()).create();
+ * </pre>
+ *
+ * <p>
+ * New applications should prefer {@link TypeAdapter}, whose streaming API is
+ * more efficient than this interface's tree API.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @param <T>
+ *            type for which the deserializer is being registered. It is
+ *            possible that a deserializer may be asked to deserialize a
+ *            specific generic type of the T.
+ */
+public interface JsonDeserializer<T> {
+
+	/**
+	 * Gson invokes this call-back method during deserialization when it encounters
+	 * a field of the specified type.
+	 * <p>
+	 * In the implementation of this call-back method, you should consider invoking
+	 * {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to
+	 * create objects for any non-trivial field of the returned object. However, you
+	 * should never invoke it on the the same type passing {@code json} since that
+	 * will cause an infinite loop (Gson will call your call-back method again).
+	 *
+	 * @param json
+	 *            The Json data being deserialized
+	 * @param typeOfT
+	 *            The type of the Object to deserialize to
+	 * @return a deserialized object of the specified type typeOfT which is a
+	 *         subclass of {@code T}
+	 * @throws JsonParseException
+	 *             if json is not in the expected format of {@code typeofT}
+	 */
+	public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException;
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonElement.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonElement.java
new file mode 100644
index 0000000..c3568e7
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonElement.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import cn.emay.sdk.util.json.gson.internal.Streams;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * A class representing an element of Json. It could either be a
+ * {@link JsonObject}, a {@link JsonArray}, a {@link JsonPrimitive} or a
+ * {@link JsonNull}.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public abstract class JsonElement {
+	/**
+	 * Returns a deep copy of this element. Immutable elements like primitives and
+	 * nulls are not copied.
+	 */
+	abstract JsonElement deepCopy();
+
+	/**
+	 * provides check for verifying if this element is an array or not.
+	 *
+	 * @return true if this element is of type {@link JsonArray}, false otherwise.
+	 */
+	public boolean isJsonArray() {
+		return this instanceof JsonArray;
+	}
+
+	/**
+	 * provides check for verifying if this element is a Json object or not.
+	 *
+	 * @return true if this element is of type {@link JsonObject}, false otherwise.
+	 */
+	public boolean isJsonObject() {
+		return this instanceof JsonObject;
+	}
+
+	/**
+	 * provides check for verifying if this element is a primitive or not.
+	 *
+	 * @return true if this element is of type {@link JsonPrimitive}, false
+	 *         otherwise.
+	 */
+	public boolean isJsonPrimitive() {
+		return this instanceof JsonPrimitive;
+	}
+
+	/**
+	 * provides check for verifying if this element represents a null value or not.
+	 *
+	 * @return true if this element is of type {@link JsonNull}, false otherwise.
+	 * @since 1.2
+	 */
+	public boolean isJsonNull() {
+		return this instanceof JsonNull;
+	}
+
+	/**
+	 * convenience method to get this element as a {@link JsonObject}. If the
+	 * element is of some other type, a {@link IllegalStateException} will result.
+	 * Hence it is best to use this method after ensuring that this element is of
+	 * the desired type by calling {@link #isJsonObject()} first.
+	 *
+	 * @return get this element as a {@link JsonObject}.
+	 * @throws IllegalStateException
+	 *             if the element is of another type.
+	 */
+	public JsonObject getAsJsonObject() {
+		if (isJsonObject()) {
+			return (JsonObject) this;
+		}
+		throw new IllegalStateException("Not a JSON Object: " + this);
+	}
+
+	/**
+	 * convenience method to get this element as a {@link JsonArray}. If the element
+	 * is of some other type, a {@link IllegalStateException} will result. Hence it
+	 * is best to use this method after ensuring that this element is of the desired
+	 * type by calling {@link #isJsonArray()} first.
+	 *
+	 * @return get this element as a {@link JsonArray}.
+	 * @throws IllegalStateException
+	 *             if the element is of another type.
+	 */
+	public JsonArray getAsJsonArray() {
+		if (isJsonArray()) {
+			return (JsonArray) this;
+		}
+		throw new IllegalStateException("This is not a JSON Array.");
+	}
+
+	/**
+	 * convenience method to get this element as a {@link JsonPrimitive}. If the
+	 * element is of some other type, a {@link IllegalStateException} will result.
+	 * Hence it is best to use this method after ensuring that this element is of
+	 * the desired type by calling {@link #isJsonPrimitive()} first.
+	 *
+	 * @return get this element as a {@link JsonPrimitive}.
+	 * @throws IllegalStateException
+	 *             if the element is of another type.
+	 */
+	public JsonPrimitive getAsJsonPrimitive() {
+		if (isJsonPrimitive()) {
+			return (JsonPrimitive) this;
+		}
+		throw new IllegalStateException("This is not a JSON Primitive.");
+	}
+
+	/**
+	 * convenience method to get this element as a {@link JsonNull}. If the element
+	 * is of some other type, a {@link IllegalStateException} will result. Hence it
+	 * is best to use this method after ensuring that this element is of the desired
+	 * type by calling {@link #isJsonNull()} first.
+	 *
+	 * @return get this element as a {@link JsonNull}.
+	 * @throws IllegalStateException
+	 *             if the element is of another type.
+	 * @since 1.2
+	 */
+	public JsonNull getAsJsonNull() {
+		if (isJsonNull()) {
+			return (JsonNull) this;
+		}
+		throw new IllegalStateException("This is not a JSON Null.");
+	}
+
+	/**
+	 * convenience method to get this element as a boolean value.
+	 *
+	 * @return get this element as a primitive boolean value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid boolean value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 */
+	public boolean getAsBoolean() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a {@link Boolean} value.
+	 *
+	 * @return get this element as a {@link Boolean} value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid boolean value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 */
+	Boolean getAsBooleanWrapper() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a {@link Number}.
+	 *
+	 * @return get this element as a {@link Number}.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid number.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 */
+	public Number getAsNumber() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a string value.
+	 *
+	 * @return get this element as a string value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid string value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 */
+	public String getAsString() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive double value.
+	 *
+	 * @return get this element as a primitive double value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid double value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 */
+	public double getAsDouble() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive float value.
+	 *
+	 * @return get this element as a primitive float value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid float value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 */
+	public float getAsFloat() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive long value.
+	 *
+	 * @return get this element as a primitive long value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid long value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 */
+	public long getAsLong() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive integer value.
+	 *
+	 * @return get this element as a primitive integer value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid integer value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 */
+	public int getAsInt() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive byte value.
+	 *
+	 * @return get this element as a primitive byte value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid byte value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 * @since 1.3
+	 */
+	public byte getAsByte() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive character value.
+	 *
+	 * @return get this element as a primitive char value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid char value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 * @since 1.3
+	 */
+	public char getAsCharacter() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a {@link BigDecimal}.
+	 *
+	 * @return get this element as a {@link BigDecimal}.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive}. * @throws
+	 *             NumberFormatException if the element is not a valid
+	 *             {@link BigDecimal}.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 * @since 1.2
+	 */
+	public BigDecimal getAsBigDecimal() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a {@link BigInteger}.
+	 *
+	 * @return get this element as a {@link BigInteger}.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive}.
+	 * @throws NumberFormatException
+	 *             if the element is not a valid {@link BigInteger}.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 * @since 1.2
+	 */
+	public BigInteger getAsBigInteger() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive short value.
+	 *
+	 * @return get this element as a primitive short value.
+	 * @throws ClassCastException
+	 *             if the element is of not a {@link JsonPrimitive} and is not a
+	 *             valid short value.
+	 * @throws IllegalStateException
+	 *             if the element is of the type {@link JsonArray} but contains more
+	 *             than a single element.
+	 */
+	public short getAsShort() {
+		throw new UnsupportedOperationException(getClass().getSimpleName());
+	}
+
+	/**
+	 * Returns a String representation of this element.
+	 */
+	@Override
+	public String toString() {
+		try {
+			StringWriter stringWriter = new StringWriter();
+			JsonWriter jsonWriter = new JsonWriter(stringWriter);
+			jsonWriter.setLenient(true);
+			Streams.write(this, jsonWriter);
+			return stringWriter.toString();
+		} catch (IOException e) {
+			throw new AssertionError(e);
+		}
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonIOException.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonIOException.java
new file mode 100644
index 0000000..f5907ce
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonIOException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+/**
+ * This exception is raised when Gson was unable to read an input stream or
+ * write to one.
+ * 
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonIOException extends JsonParseException {
+	private static final long serialVersionUID = 1L;
+
+	public JsonIOException(String msg) {
+		super(msg);
+	}
+
+	public JsonIOException(String msg, Throwable cause) {
+		super(msg, cause);
+	}
+
+	/**
+	 * Creates exception with the specified cause. Consider using
+	 * {@link #JsonIOException(String, Throwable)} instead if you can describe what
+	 * happened.
+	 *
+	 * @param cause
+	 *            root exception that caused this exception to be thrown.
+	 */
+	public JsonIOException(Throwable cause) {
+		super(cause);
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonNull.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonNull.java
new file mode 100644
index 0000000..7cb645a
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonNull.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+/**
+ * A class representing a Json {@code null} value.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.2
+ */
+public final class JsonNull extends JsonElement {
+	/**
+	 * singleton for JsonNull
+	 *
+	 * @since 1.8
+	 */
+	public static final JsonNull INSTANCE = new JsonNull();
+
+	/**
+	 * Creates a new JsonNull object. Deprecated since Gson version 1.8. Use
+	 * {@link #INSTANCE} instead
+	 */
+	@Deprecated
+	public JsonNull() {
+		// Do nothing
+	}
+
+	@Override
+	JsonNull deepCopy() {
+		return INSTANCE;
+	}
+
+	/**
+	 * All instances of JsonNull have the same hash code since they are
+	 * indistinguishable
+	 */
+	@Override
+	public int hashCode() {
+		return JsonNull.class.hashCode();
+	}
+
+	/**
+	 * All instances of JsonNull are the same
+	 */
+	@Override
+	public boolean equals(Object other) {
+		return this == other || other instanceof JsonNull;
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonObject.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonObject.java
new file mode 100644
index 0000000..cdee646
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonObject.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.util.Map;
+import java.util.Set;
+
+import cn.emay.sdk.util.json.gson.internal.LinkedTreeMap;
+
+/**
+ * A class representing an object type in Json. An object consists of name-value
+ * pairs where names are strings, and values are any other type of
+ * {@link JsonElement}. This allows for a creating a tree of JsonElements. The
+ * member elements of this object are maintained in order they were added.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonObject extends JsonElement {
+	private final LinkedTreeMap<String, JsonElement> members = new LinkedTreeMap<String, JsonElement>();
+
+	@Override
+	JsonObject deepCopy() {
+		JsonObject result = new JsonObject();
+		for (Map.Entry<String, JsonElement> entry : members.entrySet()) {
+			result.add(entry.getKey(), entry.getValue().deepCopy());
+		}
+		return result;
+	}
+
+	/**
+	 * Adds a member, which is a name-value pair, to self. The name must be a
+	 * String, but the value can be an arbitrary JsonElement, thereby allowing you
+	 * to build a full tree of JsonElements rooted at this node.
+	 *
+	 * @param property
+	 *            name of the member.
+	 * @param value
+	 *            the member object.
+	 */
+	public void add(String property, JsonElement value) {
+		if (value == null) {
+			value = JsonNull.INSTANCE;
+		}
+		members.put(property, value);
+	}
+
+	/**
+	 * Removes the {@code property} from this {@link JsonObject}.
+	 *
+	 * @param property
+	 *            name of the member that should be removed.
+	 * @return the {@link JsonElement} object that is being removed.
+	 * @since 1.3
+	 */
+	public JsonElement remove(String property) {
+		return members.remove(property);
+	}
+
+	/**
+	 * Convenience method to add a primitive member. The specified value is
+	 * converted to a JsonPrimitive of String.
+	 *
+	 * @param property
+	 *            name of the member.
+	 * @param value
+	 *            the string value associated with the member.
+	 */
+	public void addProperty(String property, String value) {
+		add(property, createJsonElement(value));
+	}
+
+	/**
+	 * Convenience method to add a primitive member. The specified value is
+	 * converted to a JsonPrimitive of Number.
+	 *
+	 * @param property
+	 *            name of the member.
+	 * @param value
+	 *            the number value associated with the member.
+	 */
+	public void addProperty(String property, Number value) {
+		add(property, createJsonElement(value));
+	}
+
+	/**
+	 * Convenience method to add a boolean member. The specified value is converted
+	 * to a JsonPrimitive of Boolean.
+	 *
+	 * @param property
+	 *            name of the member.
+	 * @param value
+	 *            the number value associated with the member.
+	 */
+	public void addProperty(String property, Boolean value) {
+		add(property, createJsonElement(value));
+	}
+
+	/**
+	 * Convenience method to add a char member. The specified value is converted to
+	 * a JsonPrimitive of Character.
+	 *
+	 * @param property
+	 *            name of the member.
+	 * @param value
+	 *            the number value associated with the member.
+	 */
+	public void addProperty(String property, Character value) {
+		add(property, createJsonElement(value));
+	}
+
+	/**
+	 * Creates the proper {@link JsonElement} object from the given {@code value}
+	 * object.
+	 *
+	 * @param value
+	 *            the object to generate the {@link JsonElement} for
+	 * @return a {@link JsonPrimitive} if the {@code value} is not null, otherwise a
+	 *         {@link JsonNull}
+	 */
+	private JsonElement createJsonElement(Object value) {
+		return value == null ? JsonNull.INSTANCE : new JsonPrimitive(value);
+	}
+
+	/**
+	 * Returns a set of members of this object. The set is ordered, and the order is
+	 * in which the elements were added.
+	 *
+	 * @return a set of members of this object.
+	 */
+	public Set<Map.Entry<String, JsonElement>> entrySet() {
+		return members.entrySet();
+	}
+
+	/**
+	 * Convenience method to check if a member with the specified name is present in
+	 * this object.
+	 *
+	 * @param memberName
+	 *            name of the member that is being checked for presence.
+	 * @return true if there is a member with the specified name, false otherwise.
+	 */
+	public boolean has(String memberName) {
+		return members.containsKey(memberName);
+	}
+
+	/**
+	 * Returns the member with the specified name.
+	 *
+	 * @param memberName
+	 *            name of the member that is being requested.
+	 * @return the member matching the name. Null if no such member exists.
+	 */
+	public JsonElement get(String memberName) {
+		return members.get(memberName);
+	}
+
+	/**
+	 * Convenience method to get the specified member as a JsonPrimitive element.
+	 *
+	 * @param memberName
+	 *            name of the member being requested.
+	 * @return the JsonPrimitive corresponding to the specified member.
+	 */
+	public JsonPrimitive getAsJsonPrimitive(String memberName) {
+		return (JsonPrimitive) members.get(memberName);
+	}
+
+	/**
+	 * Convenience method to get the specified member as a JsonArray.
+	 *
+	 * @param memberName
+	 *            name of the member being requested.
+	 * @return the JsonArray corresponding to the specified member.
+	 */
+	public JsonArray getAsJsonArray(String memberName) {
+		return (JsonArray) members.get(memberName);
+	}
+
+	/**
+	 * Convenience method to get the specified member as a JsonObject.
+	 *
+	 * @param memberName
+	 *            name of the member being requested.
+	 * @return the JsonObject corresponding to the specified member.
+	 */
+	public JsonObject getAsJsonObject(String memberName) {
+		return (JsonObject) members.get(memberName);
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		return (o == this) || (o instanceof JsonObject && ((JsonObject) o).members.equals(members));
+	}
+
+	@Override
+	public int hashCode() {
+		return members.hashCode();
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonParseException.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonParseException.java
new file mode 100644
index 0000000..3784bc9
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonParseException.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+/**
+ * This exception is raised if there is a serious issue that occurs during
+ * parsing of a Json string. One of the main usages for this class is for the
+ * Gson infrastructure. If the incoming Json is bad/malicious, an instance of
+ * this exception is raised.
+ *
+ * <p>
+ * This exception is a {@link RuntimeException} because it is exposed to the
+ * client. Using a {@link RuntimeException} avoids bad coding practices on the
+ * client side where they catch the exception and do nothing. It is often the
+ * case that you want to blow up if there is a parsing error (i.e. often clients
+ * do not know how to recover from a {@link JsonParseException}.
+ * </p>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class JsonParseException extends RuntimeException {
+	static final long serialVersionUID = -4086729973971783390L;
+
+	/**
+	 * Creates exception with the specified message. If you are wrapping another
+	 * exception, consider using {@link #JsonParseException(String, Throwable)}
+	 * instead.
+	 *
+	 * @param msg
+	 *            error message describing a possible cause of this exception.
+	 */
+	public JsonParseException(String msg) {
+		super(msg);
+	}
+
+	/**
+	 * Creates exception with the specified message and cause.
+	 *
+	 * @param msg
+	 *            error message describing what happened.
+	 * @param cause
+	 *            root exception that caused this exception to be thrown.
+	 */
+	public JsonParseException(String msg, Throwable cause) {
+		super(msg, cause);
+	}
+
+	/**
+	 * Creates exception with the specified cause. Consider using
+	 * {@link #JsonParseException(String, Throwable)} instead if you can describe
+	 * what happened.
+	 *
+	 * @param cause
+	 *            root exception that caused this exception to be thrown.
+	 */
+	public JsonParseException(Throwable cause) {
+		super(cause);
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonParser.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonParser.java
new file mode 100644
index 0000000..5b0709c
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonParser.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import cn.emay.sdk.util.json.gson.internal.Streams;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.MalformedJsonException;
+
+/**
+ * A parser to parse Json into a parse tree of {@link JsonElement}s
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.3
+ */
+public final class JsonParser {
+
+	/**
+	 * Parses the specified JSON string into a parse tree
+	 *
+	 * @param json
+	 *            JSON text
+	 * @return a parse tree of {@link JsonElement}s corresponding to the specified
+	 *         JSON
+	 * @throws JsonParseException
+	 *             if the specified text is not valid JSON
+	 * @since 1.3
+	 */
+	public JsonElement parse(String json) throws JsonSyntaxException {
+		return parse(new StringReader(json));
+	}
+
+	/**
+	 * Parses the specified JSON string into a parse tree
+	 *
+	 * @param json
+	 *            JSON text
+	 * @return a parse tree of {@link JsonElement}s corresponding to the specified
+	 *         JSON
+	 * @throws JsonParseException
+	 *             if the specified text is not valid JSON
+	 * @since 1.3
+	 */
+	public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {
+		try {
+			JsonReader jsonReader = new JsonReader(json);
+			JsonElement element = parse(jsonReader);
+			if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {
+				throw new JsonSyntaxException("Did not consume the entire document.");
+			}
+			return element;
+		} catch (MalformedJsonException e) {
+			throw new JsonSyntaxException(e);
+		} catch (IOException e) {
+			throw new JsonIOException(e);
+		} catch (NumberFormatException e) {
+			throw new JsonSyntaxException(e);
+		}
+	}
+
+	/**
+	 * Returns the next value from the JSON stream as a parse tree.
+	 *
+	 * @throws JsonParseException
+	 *             if there is an IOException or if the specified text is not valid
+	 *             JSON
+	 * @since 1.6
+	 */
+	public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {
+		boolean lenient = json.isLenient();
+		json.setLenient(true);
+		try {
+			return Streams.parse(json);
+		} catch (StackOverflowError e) {
+			throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
+		} catch (OutOfMemoryError e) {
+			throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);
+		} finally {
+			json.setLenient(lenient);
+		}
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonPrimitive.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonPrimitive.java
new file mode 100644
index 0000000..f68d2a1
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonPrimitive.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import cn.emay.sdk.util.json.gson.internal.$Gson$Preconditions;
+import cn.emay.sdk.util.json.gson.internal.LazilyParsedNumber;
+
+/**
+ * A class representing a Json primitive value. A primitive value is either a
+ * String, a Java primitive, or a Java primitive wrapper type.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonPrimitive extends JsonElement {
+
+	private static final Class<?>[] PRIMITIVE_TYPES = { int.class, long.class, short.class, float.class, double.class, byte.class, boolean.class, char.class, Integer.class, Long.class, Short.class,
+			Float.class, Double.class, Byte.class, Boolean.class, Character.class };
+
+	private Object value;
+
+	/**
+	 * Create a primitive containing a boolean value.
+	 *
+	 * @param bool
+	 *            the value to create the primitive with.
+	 */
+	public JsonPrimitive(Boolean bool) {
+		setValue(bool);
+	}
+
+	/**
+	 * Create a primitive containing a {@link Number}.
+	 *
+	 * @param number
+	 *            the value to create the primitive with.
+	 */
+	public JsonPrimitive(Number number) {
+		setValue(number);
+	}
+
+	/**
+	 * Create a primitive containing a String value.
+	 *
+	 * @param string
+	 *            the value to create the primitive with.
+	 */
+	public JsonPrimitive(String string) {
+		setValue(string);
+	}
+
+	/**
+	 * Create a primitive containing a character. The character is turned into a one
+	 * character String since Json only supports String.
+	 *
+	 * @param c
+	 *            the value to create the primitive with.
+	 */
+	public JsonPrimitive(Character c) {
+		setValue(c);
+	}
+
+	/**
+	 * Create a primitive using the specified Object. It must be an instance of
+	 * {@link Number}, a Java primitive type, or a String.
+	 *
+	 * @param primitive
+	 *            the value to create the primitive with.
+	 */
+	JsonPrimitive(Object primitive) {
+		setValue(primitive);
+	}
+
+	@Override
+	JsonPrimitive deepCopy() {
+		return this;
+	}
+
+	void setValue(Object primitive) {
+		if (primitive instanceof Character) {
+			// convert characters to strings since in JSON, characters are represented as a
+			// single
+			// character string
+			char c = ((Character) primitive).charValue();
+			this.value = String.valueOf(c);
+		} else {
+			$Gson$Preconditions.checkArgument(primitive instanceof Number || isPrimitiveOrString(primitive));
+			this.value = primitive;
+		}
+	}
+
+	/**
+	 * Check whether this primitive contains a boolean value.
+	 *
+	 * @return true if this primitive contains a boolean value, false otherwise.
+	 */
+	public boolean isBoolean() {
+		return value instanceof Boolean;
+	}
+
+	/**
+	 * convenience method to get this element as a {@link Boolean}.
+	 *
+	 * @return get this element as a {@link Boolean}.
+	 */
+	@Override
+	Boolean getAsBooleanWrapper() {
+		return (Boolean) value;
+	}
+
+	/**
+	 * convenience method to get this element as a boolean value.
+	 *
+	 * @return get this element as a primitive boolean value.
+	 */
+	@Override
+	public boolean getAsBoolean() {
+		if (isBoolean()) {
+			return getAsBooleanWrapper().booleanValue();
+		} else {
+			// Check to see if the value as a String is "true" in any case.
+			return Boolean.parseBoolean(getAsString());
+		}
+	}
+
+	/**
+	 * Check whether this primitive contains a Number.
+	 *
+	 * @return true if this primitive contains a Number, false otherwise.
+	 */
+	public boolean isNumber() {
+		return value instanceof Number;
+	}
+
+	/**
+	 * convenience method to get this element as a Number.
+	 *
+	 * @return get this element as a Number.
+	 * @throws NumberFormatException
+	 *             if the value contained is not a valid Number.
+	 */
+	@Override
+	public Number getAsNumber() {
+		return value instanceof String ? new LazilyParsedNumber((String) value) : (Number) value;
+	}
+
+	/**
+	 * Check whether this primitive contains a String value.
+	 *
+	 * @return true if this primitive contains a String value, false otherwise.
+	 */
+	public boolean isString() {
+		return value instanceof String;
+	}
+
+	/**
+	 * convenience method to get this element as a String.
+	 *
+	 * @return get this element as a String.
+	 */
+	@Override
+	public String getAsString() {
+		if (isNumber()) {
+			return getAsNumber().toString();
+		} else if (isBoolean()) {
+			return getAsBooleanWrapper().toString();
+		} else {
+			return (String) value;
+		}
+	}
+
+	/**
+	 * convenience method to get this element as a primitive double.
+	 *
+	 * @return get this element as a primitive double.
+	 * @throws NumberFormatException
+	 *             if the value contained is not a valid double.
+	 */
+	@Override
+	public double getAsDouble() {
+		return isNumber() ? getAsNumber().doubleValue() : Double.parseDouble(getAsString());
+	}
+
+	/**
+	 * convenience method to get this element as a {@link BigDecimal}.
+	 *
+	 * @return get this element as a {@link BigDecimal}.
+	 * @throws NumberFormatException
+	 *             if the value contained is not a valid {@link BigDecimal}.
+	 */
+	@Override
+	public BigDecimal getAsBigDecimal() {
+		return value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(value.toString());
+	}
+
+	/**
+	 * convenience method to get this element as a {@link BigInteger}.
+	 *
+	 * @return get this element as a {@link BigInteger}.
+	 * @throws NumberFormatException
+	 *             if the value contained is not a valid {@link BigInteger}.
+	 */
+	@Override
+	public BigInteger getAsBigInteger() {
+		return value instanceof BigInteger ? (BigInteger) value : new BigInteger(value.toString());
+	}
+
+	/**
+	 * convenience method to get this element as a float.
+	 *
+	 * @return get this element as a float.
+	 * @throws NumberFormatException
+	 *             if the value contained is not a valid float.
+	 */
+	@Override
+	public float getAsFloat() {
+		return isNumber() ? getAsNumber().floatValue() : Float.parseFloat(getAsString());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive long.
+	 *
+	 * @return get this element as a primitive long.
+	 * @throws NumberFormatException
+	 *             if the value contained is not a valid long.
+	 */
+	@Override
+	public long getAsLong() {
+		return isNumber() ? getAsNumber().longValue() : Long.parseLong(getAsString());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive short.
+	 *
+	 * @return get this element as a primitive short.
+	 * @throws NumberFormatException
+	 *             if the value contained is not a valid short value.
+	 */
+	@Override
+	public short getAsShort() {
+		return isNumber() ? getAsNumber().shortValue() : Short.parseShort(getAsString());
+	}
+
+	/**
+	 * convenience method to get this element as a primitive integer.
+	 *
+	 * @return get this element as a primitive integer.
+	 * @throws NumberFormatException
+	 *             if the value contained is not a valid integer.
+	 */
+	@Override
+	public int getAsInt() {
+		return isNumber() ? getAsNumber().intValue() : Integer.parseInt(getAsString());
+	}
+
+	@Override
+	public byte getAsByte() {
+		return isNumber() ? getAsNumber().byteValue() : Byte.parseByte(getAsString());
+	}
+
+	@Override
+	public char getAsCharacter() {
+		return getAsString().charAt(0);
+	}
+
+	private static boolean isPrimitiveOrString(Object target) {
+		if (target instanceof String) {
+			return true;
+		}
+
+		Class<?> classOfPrimitive = target.getClass();
+		for (Class<?> standardPrimitive : PRIMITIVE_TYPES) {
+			if (standardPrimitive.isAssignableFrom(classOfPrimitive)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		if (value == null) {
+			return 31;
+		}
+		// Using recommended hashing algorithm from Effective Java for longs and doubles
+		if (isIntegral(this)) {
+			long value = getAsNumber().longValue();
+			return (int) (value ^ (value >>> 32));
+		}
+		if (value instanceof Number) {
+			long value = Double.doubleToLongBits(getAsNumber().doubleValue());
+			return (int) (value ^ (value >>> 32));
+		}
+		return value.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj == null || getClass() != obj.getClass()) {
+			return false;
+		}
+		JsonPrimitive other = (JsonPrimitive) obj;
+		if (value == null) {
+			return other.value == null;
+		}
+		if (isIntegral(this) && isIntegral(other)) {
+			return getAsNumber().longValue() == other.getAsNumber().longValue();
+		}
+		if (value instanceof Number && other.value instanceof Number) {
+			double a = getAsNumber().doubleValue();
+			// Java standard types other than double return true for two NaN. So, need
+			// special handling for double.
+			double b = other.getAsNumber().doubleValue();
+			return a == b || (Double.isNaN(a) && Double.isNaN(b));
+		}
+		return value.equals(other.value);
+	}
+
+	/**
+	 * Returns true if the specified number is an integral type (Long, Integer,
+	 * Short, Byte, BigInteger)
+	 */
+	private static boolean isIntegral(JsonPrimitive primitive) {
+		if (primitive.value instanceof Number) {
+			Number number = (Number) primitive.value;
+			return number instanceof BigInteger || number instanceof Long || number instanceof Integer || number instanceof Short || number instanceof Byte;
+		}
+		return false;
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSerializationContext.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSerializationContext.java
new file mode 100644
index 0000000..3518108
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSerializationContext.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Context for serialization that is passed to a custom serializer during
+ * invocation of its
+ * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)}
+ * method.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface JsonSerializationContext {
+
+	/**
+	 * Invokes default serialization on the specified object.
+	 *
+	 * @param src
+	 *            the object that needs to be serialized.
+	 * @return a tree of {@link JsonElement}s corresponding to the serialized form
+	 *         of {@code src}.
+	 */
+	public JsonElement serialize(Object src);
+
+	/**
+	 * Invokes default serialization on the specified object passing the specific
+	 * type information. It should never be invoked on the element received as a
+	 * parameter of the
+	 * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)}
+	 * method. Doing so will result in an infinite loop since Gson will in-turn call
+	 * the custom serializer again.
+	 *
+	 * @param src
+	 *            the object that needs to be serialized.
+	 * @param typeOfSrc
+	 *            the actual genericized type of src object.
+	 * @return a tree of {@link JsonElement}s corresponding to the serialized form
+	 *         of {@code src}.
+	 */
+	public JsonElement serialize(Object src, Type typeOfSrc);
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSerializer.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSerializer.java
new file mode 100644
index 0000000..f53fd40
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSerializer.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Interface representing a custom serializer for Json. You should write a
+ * custom serializer, if you are not happy with the default serialization done
+ * by Gson. You will also need to register this serializer through
+ * {@link com.google.gson.GsonBuilder#registerTypeAdapter(Type, Object)}.
+ *
+ * <p>
+ * Let us look at example where defining a serializer will be useful. The
+ * {@code Id} class defined below has two fields: {@code clazz} and
+ * {@code value}.
+ * </p>
+ *
+ * <p>
+ * 
+ * <pre>
+ * public class Id&lt;T&gt; {
+ * 	private final Class&lt;T&gt; clazz;
+ * 	private final long value;
+ *
+ * 	public Id(Class&lt;T&gt; clazz, long value) {
+ * 		this.clazz = clazz;
+ * 		this.value = value;
+ * 	}
+ *
+ * 	public long getValue() {
+ * 		return value;
+ * 	}
+ * }
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be
+ * <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you just want
+ * the output to be the value instead, which is {@code 20} in this case. You can
+ * achieve that by writing a custom serializer:
+ * </p>
+ *
+ * <p>
+ * 
+ * <pre>
+ * class IdSerializer implements JsonSerializer&lt;Id&gt;() {
+ *   public JsonElement serialize(Id id, Type typeOfId, JsonSerializationContext context) {
+ *     return new JsonPrimitive(id.getValue());
+ *   }
+ * }
+ * </pre>
+ * </p>
+ *
+ * <p>
+ * You will also need to register {@code IdSerializer} with Gson as follows:
+ * </p>
+ * 
+ * <pre>
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdSerializer()).create();
+ * </pre>
+ *
+ * <p>
+ * New applications should prefer {@link TypeAdapter}, whose streaming API is
+ * more efficient than this interface's tree API.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @param <T>
+ *            type for which the serializer is being registered. It is possible
+ *            that a serializer may be asked to serialize a specific generic
+ *            type of the T.
+ */
+public interface JsonSerializer<T> {
+
+	/**
+	 * Gson invokes this call-back method during serialization when it encounters a
+	 * field of the specified type.
+	 *
+	 * <p>
+	 * In the implementation of this call-back method, you should consider invoking
+	 * {@link JsonSerializationContext#serialize(Object, Type)} method to create
+	 * JsonElements for any non-trivial field of the {@code src} object. However,
+	 * you should never invoke it on the {@code src} object itself since that will
+	 * cause an infinite loop (Gson will call your call-back method again).
+	 * </p>
+	 *
+	 * @param src
+	 *            the object that needs to be converted to Json.
+	 * @param typeOfSrc
+	 *            the actual type (fully genericized version) of the source object.
+	 * @return a JsonElement corresponding to the specified object.
+	 */
+	public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonStreamParser.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonStreamParser.java
new file mode 100644
index 0000000..5dc81ea
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonStreamParser.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import cn.emay.sdk.util.json.gson.internal.Streams;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.MalformedJsonException;
+
+/**
+ * A streaming parser that allows reading of multiple {@link JsonElement}s from
+ * the specified reader asynchronously.
+ * 
+ * <p>
+ * This class is conditionally thread-safe (see Item 70, Effective Java second
+ * edition). To properly use this class across multiple threads, you will need
+ * to add some external synchronization. For example:
+ * 
+ * <pre>
+ * JsonStreamParser parser = new JsonStreamParser("['first'] {'second':10} 'third'");
+ * JsonElement element;
+ * synchronized (parser) { // synchronize on an object shared by threads
+ * 	if (parser.hasNext()) {
+ * 		element = parser.next();
+ * 	}
+ * }
+ * </pre>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.4
+ */
+public final class JsonStreamParser implements Iterator<JsonElement> {
+	private final JsonReader parser;
+	private final Object lock;
+
+	/**
+	 * @param json
+	 *            The string containing JSON elements concatenated to each other.
+	 * @since 1.4
+	 */
+	public JsonStreamParser(String json) {
+		this(new StringReader(json));
+	}
+
+	/**
+	 * @param reader
+	 *            The data stream containing JSON elements concatenated to each
+	 *            other.
+	 * @since 1.4
+	 */
+	public JsonStreamParser(Reader reader) {
+		parser = new JsonReader(reader);
+		parser.setLenient(true);
+		lock = new Object();
+	}
+
+	/**
+	 * Returns the next available {@link JsonElement} on the reader. Null if none
+	 * available.
+	 * 
+	 * @return the next available {@link JsonElement} on the reader. Null if none
+	 *         available.
+	 * @throws JsonParseException
+	 *             if the incoming stream is malformed JSON.
+	 * @since 1.4
+	 */
+	public JsonElement next() throws JsonParseException {
+		if (!hasNext()) {
+			throw new NoSuchElementException();
+		}
+
+		try {
+			return Streams.parse(parser);
+		} catch (StackOverflowError e) {
+			throw new JsonParseException("Failed parsing JSON source to Json", e);
+		} catch (OutOfMemoryError e) {
+			throw new JsonParseException("Failed parsing JSON source to Json", e);
+		} catch (JsonParseException e) {
+			throw e.getCause() instanceof EOFException ? new NoSuchElementException() : e;
+		}
+	}
+
+	/**
+	 * Returns true if a {@link JsonElement} is available on the input for
+	 * consumption
+	 * 
+	 * @return true if a {@link JsonElement} is available on the input, false
+	 *         otherwise
+	 * @since 1.4
+	 */
+	public boolean hasNext() {
+		synchronized (lock) {
+			try {
+				return parser.peek() != JsonToken.END_DOCUMENT;
+			} catch (MalformedJsonException e) {
+				throw new JsonSyntaxException(e);
+			} catch (IOException e) {
+				throw new JsonIOException(e);
+			}
+		}
+	}
+
+	/**
+	 * This optional {@link Iterator} method is not relevant for stream parsing and
+	 * hence is not implemented.
+	 * 
+	 * @since 1.4
+	 */
+	public void remove() {
+		throw new UnsupportedOperationException();
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSyntaxException.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSyntaxException.java
new file mode 100644
index 0000000..edd12dd
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/JsonSyntaxException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+/**
+ * This exception is raised when Gson attempts to read (or write) a malformed
+ * JSON element.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonSyntaxException extends JsonParseException {
+
+	private static final long serialVersionUID = 1L;
+
+	public JsonSyntaxException(String msg) {
+		super(msg);
+	}
+
+	public JsonSyntaxException(String msg, Throwable cause) {
+		super(msg, cause);
+	}
+
+	/**
+	 * Creates exception with the specified cause. Consider using
+	 * {@link #JsonSyntaxException(String, Throwable)} instead if you can describe
+	 * what actually happened.
+	 *
+	 * @param cause
+	 *            root exception that caused this exception to be thrown.
+	 */
+	public JsonSyntaxException(Throwable cause) {
+		super(cause);
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/LongSerializationPolicy.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/LongSerializationPolicy.java
new file mode 100644
index 0000000..7ac604c
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/LongSerializationPolicy.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+/**
+ * Defines the expected format for a {@code long} or {@code Long} type when its
+ * serialized.
+ *
+ * @since 1.3
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public enum LongSerializationPolicy {
+	/**
+	 * This is the "default" serialization policy that will output a {@code long}
+	 * object as a JSON number. For example, assume an object has a long field named
+	 * "f" then the serialized output would be: {@code {"f":123}}.
+	 */
+	DEFAULT() {
+		@Override
+		public JsonElement serialize(Long value) {
+			return new JsonPrimitive(value);
+		}
+	},
+
+	/**
+	 * Serializes a long value as a quoted string. For example, assume an object has
+	 * a long field named "f" then the serialized output would be:
+	 * {@code {"f":"123"}}.
+	 */
+	STRING() {
+		@Override
+		public JsonElement serialize(Long value) {
+			return new JsonPrimitive(String.valueOf(value));
+		}
+	};
+
+	/**
+	 * Serialize this {@code value} using this serialization policy.
+	 *
+	 * @param value
+	 *            the long value to be serialized into a {@link JsonElement}
+	 * @return the serialized version of {@code value}
+	 */
+	public abstract JsonElement serialize(Long value);
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TreeTypeAdapter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TreeTypeAdapter.java
new file mode 100644
index 0000000..05f7bc6
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TreeTypeAdapter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.io.IOException;
+
+import cn.emay.sdk.util.json.gson.internal.$Gson$Preconditions;
+import cn.emay.sdk.util.json.gson.internal.Streams;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the
+ * tree adapter may be serialization-only or deserialization-only, this class
+ * has a facility to lookup a delegate type adapter on demand.
+ */
+final class TreeTypeAdapter<T> extends TypeAdapter<T> {
+	private final JsonSerializer<T> serializer;
+	private final JsonDeserializer<T> deserializer;
+	private final Gson gson;
+	private final TypeToken<T> typeToken;
+	private final TypeAdapterFactory skipPast;
+
+	/**
+	 * The delegate is lazily created because it may not be needed, and creating it
+	 * may fail.
+	 */
+	private TypeAdapter<T> delegate;
+
+	TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer, Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast) {
+		this.serializer = serializer;
+		this.deserializer = deserializer;
+		this.gson = gson;
+		this.typeToken = typeToken;
+		this.skipPast = skipPast;
+	}
+
+	@Override
+	public T read(JsonReader in) throws IOException {
+		if (deserializer == null) {
+			return delegate().read(in);
+		}
+		JsonElement value = Streams.parse(in);
+		if (value.isJsonNull()) {
+			return null;
+		}
+		return deserializer.deserialize(value, typeToken.getType(), gson.deserializationContext);
+	}
+
+	@Override
+	public void write(JsonWriter out, T value) throws IOException {
+		if (serializer == null) {
+			delegate().write(out, value);
+			return;
+		}
+		if (value == null) {
+			out.nullValue();
+			return;
+		}
+		JsonElement tree = serializer.serialize(value, typeToken.getType(), gson.serializationContext);
+		Streams.write(tree, out);
+	}
+
+	private TypeAdapter<T> delegate() {
+		TypeAdapter<T> d = delegate;
+		return d != null ? d : (delegate = gson.getDelegateAdapter(skipPast, typeToken));
+	}
+
+	/**
+	 * Returns a new factory that will match each type against {@code exactType}.
+	 */
+	public static TypeAdapterFactory newFactory(TypeToken<?> exactType, Object typeAdapter) {
+		return new SingleTypeFactory(typeAdapter, exactType, false, null);
+	}
+
+	/**
+	 * Returns a new factory that will match each type and its raw type against
+	 * {@code exactType}.
+	 */
+	public static TypeAdapterFactory newFactoryWithMatchRawType(TypeToken<?> exactType, Object typeAdapter) {
+		// only bother matching raw types if exact type is a raw type
+		boolean matchRawType = exactType.getType() == exactType.getRawType();
+		return new SingleTypeFactory(typeAdapter, exactType, matchRawType, null);
+	}
+
+	/**
+	 * Returns a new factory that will match each type's raw type for assignability
+	 * to {@code hierarchyType}.
+	 */
+	public static TypeAdapterFactory newTypeHierarchyFactory(Class<?> hierarchyType, Object typeAdapter) {
+		return new SingleTypeFactory(typeAdapter, null, false, hierarchyType);
+	}
+
+	private static class SingleTypeFactory implements TypeAdapterFactory {
+		private final TypeToken<?> exactType;
+		private final boolean matchRawType;
+		private final Class<?> hierarchyType;
+		private final JsonSerializer<?> serializer;
+		private final JsonDeserializer<?> deserializer;
+
+		SingleTypeFactory(Object typeAdapter, TypeToken<?> exactType, boolean matchRawType, Class<?> hierarchyType) {
+			serializer = typeAdapter instanceof JsonSerializer ? (JsonSerializer<?>) typeAdapter : null;
+			deserializer = typeAdapter instanceof JsonDeserializer ? (JsonDeserializer<?>) typeAdapter : null;
+			$Gson$Preconditions.checkArgument(serializer != null || deserializer != null);
+			this.exactType = exactType;
+			this.matchRawType = matchRawType;
+			this.hierarchyType = hierarchyType;
+		}
+
+		@SuppressWarnings("unchecked") // guarded by typeToken.equals() call
+		@Override
+		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+			boolean matches = exactType != null ? exactType.equals(type) || matchRawType && exactType.getType() == type.getRawType() : hierarchyType.isAssignableFrom(type.getRawType());
+			return matches ? new TreeTypeAdapter<T>((JsonSerializer<T>) serializer, (JsonDeserializer<T>) deserializer, gson, type, this) : null;
+		}
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TypeAdapter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TypeAdapter.java
new file mode 100644
index 0000000..9469d9d
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TypeAdapter.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import cn.emay.sdk.util.json.gson.internal.bind.JsonTreeReader;
+import cn.emay.sdk.util.json.gson.internal.bind.JsonTreeWriter;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Converts Java objects to and from JSON.
+ *
+ * <h3>Defining a type's JSON form</h3> By default Gson converts application
+ * classes to JSON using its built-in type adapters. If Gson's default JSON
+ * conversion isn't appropriate for a type, extend this class to customize the
+ * conversion. Here's an example of a type adapter for an (X,Y) coordinate
+ * point:
+ * 
+ * <pre>
+ * {
+ * 	&#64;code
+ *
+ * 	public class PointAdapter extends TypeAdapter<Point> {
+ * 		public Point read(JsonReader reader) throws IOException {
+ * 			if (reader.peek() == JsonToken.NULL) {
+ * 				reader.nextNull();
+ * 				return null;
+ * 			}
+ * 			String xy = reader.nextString();
+ * 			String[] parts = xy.split(",");
+ * 			int x = Integer.parseInt(parts[0]);
+ * 			int y = Integer.parseInt(parts[1]);
+ * 			return new Point(x, y);
+ * 		}
+ * 
+ * 		public void write(JsonWriter writer, Point value) throws IOException {
+ * 			if (value == null) {
+ * 				writer.nullValue();
+ * 				return;
+ * 			}
+ * 			String xy = value.getX() + "," + value.getY();
+ * 			writer.value(xy);
+ * 		}
+ * 	}
+ * }
+ * </pre>
+ * 
+ * With this type adapter installed, Gson will convert {@code Points} to JSON as
+ * strings like {@code "5,8"} rather than objects like {@code {"x":5,"y":8}}. In
+ * this case the type adapter binds a rich Java class to a compact JSON value.
+ *
+ * <p>
+ * The {@link #read(JsonReader) read()} method must read exactly one value and
+ * {@link #write(JsonWriter,Object) write()} must write exactly one value. For
+ * primitive types this is means readers should make exactly one call to
+ * {@code nextBoolean()}, {@code nextDouble()}, {@code nextInt()}, {@code
+ * nextLong()}, {@code nextString()} or {@code nextNull()}. Writers should make
+ * exactly one call to one of <code>value()</code> or <code>nullValue()</code>.
+ * For arrays, type adapters should start with a call to {@code beginArray()},
+ * convert all elements, and finish with a call to {@code endArray()}. For
+ * objects, they should start with {@code beginObject()}, convert the object,
+ * and finish with {@code endObject()}. Failing to convert a value or converting
+ * too many values may cause the application to crash.
+ *
+ * <p>
+ * Type adapters should be prepared to read null from the stream and write it to
+ * the stream. Alternatively, they should use {@link #nullSafe()} method while
+ * registering the type adapter with Gson. If your {@code Gson} instance has
+ * been configured to {@link GsonBuilder#serializeNulls()}, these nulls will be
+ * written to the final document. Otherwise the value (and the corresponding
+ * name when writing to a JSON object) will be omitted automatically. In either
+ * case your type adapter must handle null.
+ *
+ * <p>
+ * To use a custom type adapter with Gson, you must <i>register</i> it with a
+ * {@link GsonBuilder}:
+ * 
+ * <pre>
+ *    {@code
+ *
+ *   GsonBuilder builder = new GsonBuilder();
+ *   builder.registerTypeAdapter(Point.class, new PointAdapter());
+ *   // if PointAdapter didn't check for nulls in its read/write methods, you should instead use
+ *   // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe());
+ *   ...
+ *   Gson gson = builder.create();
+ * }
+ * </pre>
+ *
+ * @since 2.1
+ */
+// non-Javadoc:
+//
+// <h3>JSON Conversion</h3>
+// <p>A type adapter registered with Gson is automatically invoked while
+// serializing
+// or deserializing JSON. However, you can also use type adapters directly to
+// serialize
+// and deserialize JSON. Here is an example for deserialization: <pre> {@code
+//
+// String json = "{'origin':'0,0','points':['1,2','3,4']}";
+// TypeAdapter<Graph> graphAdapter = gson.getAdapter(Graph.class);
+// Graph graph = graphAdapter.fromJson(json);
+// }</pre>
+// And an example for serialization: <pre> {@code
+//
+// Graph graph = new Graph(...);
+// TypeAdapter<Graph> graphAdapter = gson.getAdapter(Graph.class);
+// String json = graphAdapter.toJson(graph);
+// }</pre>
+//
+// <p>Type adapters are <strong>type-specific</strong>. For example, a {@code
+// TypeAdapter<Date>} can convert {@code Date} instances to JSON and JSON to
+// instances of {@code Date}, but cannot convert any other types.
+//
+public abstract class TypeAdapter<T> {
+
+	/**
+	 * Writes one JSON value (an array, object, string, number, boolean or null) for
+	 * {@code value}.
+	 *
+	 * @param value
+	 *            the Java object to write. May be null.
+	 */
+	public abstract void write(JsonWriter out, T value) throws IOException;
+
+	/**
+	 * Converts {@code value} to a JSON document and writes it to {@code out}.
+	 * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson}
+	 * method, this write is strict. Create a {@link JsonWriter#setLenient(boolean)
+	 * lenient} {@code JsonWriter} and call
+	 * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
+	 * writing.
+	 *
+	 * @param value
+	 *            the Java object to convert. May be null.
+	 * @since 2.2
+	 */
+	public final void toJson(Writer out, T value) throws IOException {
+		JsonWriter writer = new JsonWriter(out);
+		write(writer, value);
+	}
+
+	/**
+	 * This wrapper method is used to make a type adapter null tolerant. In general,
+	 * a type adapter is required to handle nulls in write and read methods. Here is
+	 * how this is typically done:<br>
+	 * 
+	 * <pre>
+	 * {
+	 * 	&#64;code
+	 *
+	 * 	Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, new TypeAdapter<Foo>() {
+	 * 		public Foo read(JsonReader in) throws IOException {
+	 * 			if (in.peek() == JsonToken.NULL) {
+	 * 				in.nextNull();
+	 * 				return null;
+	 * 			}
+	 * 			// read a Foo from in and return it
+	 * 		}
+	 * 
+	 * 		public void write(JsonWriter out, Foo src) throws IOException {
+	 * 			if (src == null) {
+	 * 				out.nullValue();
+	 * 				return;
+	 * 			}
+	 * 			// write src as JSON to out
+	 * 		}
+	 * 	}).create();
+	 * }
+	 * </pre>
+	 * 
+	 * You can avoid this boilerplate handling of nulls by wrapping your type
+	 * adapter with this method. Here is how we will rewrite the above example:
+	 * 
+	 * <pre>
+	 * {
+	 * 	&#64;code
+	 *
+	 * 	Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, new TypeAdapter<Foo>() {
+	 * 		public Foo read(JsonReader in) throws IOException {
+	 * 			// read a Foo from in and return it
+	 * 		}
+	 * 
+	 * 		public void write(JsonWriter out, Foo src) throws IOException {
+	 * 			// write src as JSON to out
+	 * 		}
+	 * 	}.nullSafe()).create();
+	 * }
+	 * </pre>
+	 * 
+	 * Note that we didn't need to check for nulls in our type adapter after we used
+	 * nullSafe.
+	 */
+	public final TypeAdapter<T> nullSafe() {
+		return new TypeAdapter<T>() {
+			@Override
+			public void write(JsonWriter out, T value) throws IOException {
+				if (value == null) {
+					out.nullValue();
+				} else {
+					TypeAdapter.this.write(out, value);
+				}
+			}
+
+			@Override
+			public T read(JsonReader reader) throws IOException {
+				if (reader.peek() == JsonToken.NULL) {
+					reader.nextNull();
+					return null;
+				}
+				return TypeAdapter.this.read(reader);
+			}
+		};
+	}
+
+	/**
+	 * Converts {@code value} to a JSON document. Unlike Gson's similar
+	 * {@link Gson#toJson(Object) toJson} method, this write is strict. Create a
+	 * {@link JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call
+	 * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
+	 * writing.
+	 *
+	 * @param value
+	 *            the Java object to convert. May be null.
+	 * @since 2.2
+	 */
+	public final String toJson(T value) {
+		StringWriter stringWriter = new StringWriter();
+		try {
+			toJson(stringWriter, value);
+		} catch (IOException e) {
+			throw new AssertionError(e); // No I/O writing to a StringWriter.
+		}
+		return stringWriter.toString();
+	}
+
+	/**
+	 * Converts {@code value} to a JSON tree.
+	 *
+	 * @param value
+	 *            the Java object to convert. May be null.
+	 * @return the converted JSON tree. May be {@link JsonNull}.
+	 * @since 2.2
+	 */
+	public final JsonElement toJsonTree(T value) {
+		try {
+			JsonTreeWriter jsonWriter = new JsonTreeWriter();
+			write(jsonWriter, value);
+			return jsonWriter.get();
+		} catch (IOException e) {
+			throw new JsonIOException(e);
+		}
+	}
+
+	/**
+	 * Reads one JSON value (an array, object, string, number, boolean or null) and
+	 * converts it to a Java object. Returns the converted object.
+	 *
+	 * @return the converted Java object. May be null.
+	 */
+	public abstract T read(JsonReader in) throws IOException;
+
+	/**
+	 * Converts the JSON document in {@code in} to a Java object. Unlike Gson's
+	 * similar {@link Gson#fromJson(java.io.Reader, Class) fromJson} method, this
+	 * read is strict. Create a {@link JsonReader#setLenient(boolean) lenient}
+	 * {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading.
+	 *
+	 * @return the converted Java object. May be null.
+	 * @since 2.2
+	 */
+	public final T fromJson(Reader in) throws IOException {
+		JsonReader reader = new JsonReader(in);
+		return read(reader);
+	}
+
+	/**
+	 * Converts the JSON document in {@code json} to a Java object. Unlike Gson's
+	 * similar {@link Gson#fromJson(String, Class) fromJson} method, this read is
+	 * strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code
+	 * JsonReader} and call {@link #read(JsonReader)} for lenient reading.
+	 *
+	 * @return the converted Java object. May be null.
+	 * @since 2.2
+	 */
+	public final T fromJson(String json) throws IOException {
+		return fromJson(new StringReader(json));
+	}
+
+	/**
+	 * Converts {@code jsonTree} to a Java object.
+	 *
+	 * @param jsonTree
+	 *            the Java object to convert. May be {@link JsonNull}.
+	 * @since 2.2
+	 */
+	public final T fromJsonTree(JsonElement jsonTree) {
+		try {
+			JsonReader jsonReader = new JsonTreeReader(jsonTree);
+			return read(jsonReader);
+		} catch (IOException e) {
+			throw new JsonIOException(e);
+		}
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TypeAdapterFactory.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TypeAdapterFactory.java
new file mode 100644
index 0000000..0f2f8c9
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/TypeAdapterFactory.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson;
+
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+
+/**
+ * Creates type adapters for set of related types. Type adapter factories are
+ * most useful when several types share similar structure in their JSON form.
+ *
+ * <h3>Example: Converting enums to lowercase</h3> In this example, we implement
+ * a factory that creates type adapters for all enums. The type adapters will
+ * write enums in lowercase, despite the fact that they're defined in
+ * {@code CONSTANT_CASE} in the corresponding Java model:
+ * 
+ * <pre>
+ * {
+ * 	&#64;code
+ *
+ * 	public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory {
+ * 		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+ * 			Class<T> rawType = (Class<T>) type.getRawType();
+ * 			if (!rawType.isEnum()) {
+ * 				return null;
+ * 			}
+ *
+ * 			final Map<String, T> lowercaseToConstant = new HashMap<String, T>();
+ * 			for (T constant : rawType.getEnumConstants()) {
+ * 				lowercaseToConstant.put(toLowercase(constant), constant);
+ * 			}
+ *
+ * 			return new TypeAdapter<T>() {
+ * 				public void write(JsonWriter out, T value) throws IOException {
+ * 					if (value == null) {
+ * 						out.nullValue();
+ * 					} else {
+ * 						out.value(toLowercase(value));
+ * 					}
+ * 				}
+ *
+ * 				public T read(JsonReader reader) throws IOException {
+ * 					if (reader.peek() == JsonToken.NULL) {
+ * 						reader.nextNull();
+ * 						return null;
+ * 					} else {
+ * 						return lowercaseToConstant.get(reader.nextString());
+ * 					}
+ * 				}
+ * 			};
+ * 		}
+ *
+ * 		private String toLowercase(Object o) {
+ * 			return o.toString().toLowerCase(Locale.US);
+ * 		}
+ * 	}
+ * }
+ * </pre>
+ *
+ * <p>
+ * Type adapter factories select which types they provide type adapters for. If
+ * a factory cannot support a given type, it must return null when that type is
+ * passed to {@link #create}. Factories should expect {@code
+ * create()} to be called on them for many types and should return null for most
+ * of those types. In the above example the factory returns null for calls to
+ * {@code create()} where {@code type} is not an enum.
+ *
+ * <p>
+ * A factory is typically called once per type, but the returned type adapter
+ * may be used many times. It is most efficient to do expensive work like
+ * reflection in {@code create()} so that the type adapter's {@code
+ * read()} and {@code write()} methods can be very fast. In this example the
+ * mapping from lowercase name to enum value is computed eagerly.
+ *
+ * <p>
+ * As with type adapters, factories must be <i>registered</i> with a
+ * {@link com.google.gson.GsonBuilder} for them to take effect:
+ * 
+ * <pre>
+ *    {@code
+ *
+ *  GsonBuilder builder = new GsonBuilder();
+ *  builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
+ *  ...
+ *  Gson gson = builder.create();
+ * }
+ * </pre>
+ * 
+ * If multiple factories support the same type, the factory registered earlier
+ * takes precedence.
+ *
+ * <h3>Example: composing other type adapters</h3> In this example we implement
+ * a factory for Guava's {@code Multiset} collection type. The factory can be
+ * used to create type adapters for multisets of any element type: the type
+ * adapter for {@code
+ * Multiset<String>} is different from the type adapter for {@code
+ * Multiset<URL>}.
+ *
+ * <p>
+ * The type adapter <i>delegates</i> to another type adapter for the multiset
+ * elements. It figures out the element type by reflecting on the multiset's
+ * type token. A {@code Gson} is passed in to {@code create} for just this
+ * purpose:
+ * 
+ * <pre>
+ * {
+ * 	&#64;code
+ *
+ * 	public class MultisetTypeAdapterFactory implements TypeAdapterFactory {
+ * 		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+ * 			Type type = typeToken.getType();
+ * 			if (typeToken.getRawType() != Multiset.class || !(type instanceof ParameterizedType)) {
+ * 				return null;
+ * 			}
+ *
+ * 			Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
+ * 			TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
+ * 			return (TypeAdapter<T>) newMultisetAdapter(elementAdapter);
+ * 		}
+ *
+ * 		private <E> TypeAdapter<Multiset<E>> newMultisetAdapter(final TypeAdapter<E> elementAdapter) {
+ * 			return new TypeAdapter<Multiset<E>>() {
+ * 				public void write(JsonWriter out, Multiset<E> value) throws IOException {
+ * 					if (value == null) {
+ * 						out.nullValue();
+ * 						return;
+ * 					}
+ *
+ * 					out.beginArray();
+ * 					for (Multiset.Entry<E> entry : value.entrySet()) {
+ * 						out.value(entry.getCount());
+ * 						elementAdapter.write(out, entry.getElement());
+ * 					}
+ * 					out.endArray();
+ * 				}
+ *
+ * 				public Multiset<E> read(JsonReader in) throws IOException {
+ * 					if (in.peek() == JsonToken.NULL) {
+ * 						in.nextNull();
+ * 						return null;
+ * 					}
+ *
+ * 					Multiset<E> result = LinkedHashMultiset.create();
+ * 					in.beginArray();
+ * 					while (in.hasNext()) {
+ * 						int count = in.nextInt();
+ * 						E element = elementAdapter.read(in);
+ * 						result.add(element, count);
+ * 					}
+ * 					in.endArray();
+ * 					return result;
+ * 				}
+ * 			};
+ * 		}
+ * 	}
+ * }
+ * </pre>
+ * 
+ * Delegating from one type adapter to another is extremely powerful; it's the
+ * foundation of how Gson converts Java objects and collections. Whenever
+ * possible your factory should retrieve its delegate type adapter in the
+ * {@code create()} method; this ensures potentially-expensive type adapter
+ * creation happens only once.
+ *
+ * @since 2.1
+ */
+public interface TypeAdapterFactory {
+
+	/**
+	 * Returns a type adapter for {@code type}, or null if this factory doesn't
+	 * support {@code type}.
+	 */
+	<T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Expose.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Expose.java
new file mode 100644
index 0000000..2139ca0
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Expose.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates this member should be exposed for JSON
+ * serialization or deserialization.
+ *
+ * <p>
+ * This annotation has no effect unless you build {@link com.google.gson.Gson}
+ * with a {@link com.google.gson.GsonBuilder} and invoke
+ * {@link com.google.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()}
+ * method.
+ * </p>
+ *
+ * <p>
+ * Here is an example of how this annotation is meant to be used:
+ * <p>
+ * 
+ * <pre>
+ * public class User {
+ *   &#64Expose private String firstName;
+ *   &#64Expose(serialize = false) private String lastName;
+ *   &#64Expose (serialize = false, deserialize = false) private String emailAddress;
+ *   private String password;
+ * }
+ * </pre>
+ * </p>
+ * If you created Gson with {@code new Gson()}, the {@code toJson()} and
+ * {@code fromJson()} methods will use the {@code password} field along-with
+ * {@code firstName}, {@code lastName}, and {@code emailAddress} for
+ * serialization and deserialization. However, if you created Gson with
+ * {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()}
+ * then the {@code toJson()} and {@code fromJson()} methods of Gson will exclude
+ * the {@code password} field. This is because the {@code password} field is not
+ * marked with the {@code @Expose} annotation. Gson will also exclude
+ * {@code lastName} and {@code emailAddress} from serialization since
+ * {@code serialize} is set to {@code false}. Similarly, Gson will exclude
+ * {@code emailAddress} from deserialization since {@code deserialize} is set to
+ * false.
+ *
+ * <p>
+ * Note that another way to achieve the same effect would have been to just mark
+ * the {@code password} field as {@code transient}, and Gson would have excluded
+ * it even with default settings. The {@code @Expose} annotation is useful in a
+ * style of programming where you want to explicitly specify all fields that
+ * should get considered for serialization or deserialization.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Expose {
+
+	/**
+	 * If {@code true}, the field marked with this annotation is written out in the
+	 * JSON while serializing. If {@code false}, the field marked with this
+	 * annotation is skipped from the serialized output. Defaults to {@code true}.
+	 * 
+	 * @since 1.4
+	 */
+	public boolean serialize() default true;
+
+	/**
+	 * If {@code true}, the field marked with this annotation is deserialized from
+	 * the JSON. If {@code false}, the field marked with this annotation is skipped
+	 * during deserialization. Defaults to {@code true}.
+	 * 
+	 * @since 1.4
+	 */
+	public boolean deserialize() default true;
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/JsonAdapter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/JsonAdapter.java
new file mode 100644
index 0000000..24e4d4a
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/JsonAdapter.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+
+/**
+ * An annotation that indicates the Gson {@link TypeAdapter} to use with a class
+ * or field.
+ *
+ * <p>
+ * Here is an example of how this annotation is used:
+ * </p>
+ * 
+ * <pre>
+ * &#64JsonAdapter(UserJsonAdapter.class)
+ * public class User {
+ *   public final String firstName, lastName;
+ *   private User(String firstName, String lastName) {
+ *     this.firstName = firstName;
+ *     this.lastName = lastName;
+ *   }
+ * }
+ * public class UserJsonAdapter extends TypeAdapter&lt;User&gt; {
+ *   &#64Override public void write(JsonWriter out, User user) throws IOException {
+ *     // implement write: combine firstName and lastName into name
+ *     out.beginObject();
+ *     out.name("name");
+ *     out.value(user.firstName + " " + user.lastName);
+ *     out.endObject();
+ *     // implement the write method
+ *   }
+ *   &#64Override public User read(JsonReader in) throws IOException {
+ *     // implement read: split name into firstName and lastName
+ *     in.beginObject();
+ *     in.nextName();
+ *     String[] nameParts = in.nextString().split(" ");
+ *     in.endObject();
+ *     return new User(nameParts[0], nameParts[1]);
+ *   }
+ * }
+ * </pre>
+ *
+ * Since User class specified UserJsonAdapter.class in &#64JsonAdapter
+ * annotation, it will automatically be invoked to serialize/deserialize User
+ * instances. <br>
+ *
+ * <p>
+ * Here is an example of how to apply this annotation to a field.
+ * 
+ * <pre>
+ * private static final class Gadget {
+ *   &#64JsonAdapter(UserJsonAdapter2.class)
+ *   final User user;
+ *   Gadget(User user) {
+ *     this.user = user;
+ *   }
+ * }
+ * </pre>
+ *
+ * It's possible to specify different type adapters on a field, that field's
+ * type, and in the {@link com.google.gson.GsonBuilder}. Field annotations take
+ * precedence over {@code GsonBuilder}-registered type adapters, which in turn
+ * take precedence over annotated types.
+ *
+ * <p>
+ * The class referenced by this annotation must be either a {@link TypeAdapter}
+ * or a {@link TypeAdapterFactory}. Using the factory interface makes it
+ * possible to delegate to the enclosing {@code Gson} instance.
+ *
+ * @since 2.3
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+// Note that the above example is taken from AdaptAnnotationTest.
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.TYPE, ElementType.FIELD })
+public @interface JsonAdapter {
+
+	/** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}. */
+	Class<?> value();
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/SerializedName.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/SerializedName.java
new file mode 100644
index 0000000..75329d4
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/SerializedName.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates this member should be serialized to JSON with
+ * the provided name value as its field name.
+ *
+ * <p>
+ * This annotation will override any {@link com.google.gson.FieldNamingPolicy},
+ * including the default field naming policy, that may have been set on the
+ * {@link com.google.gson.Gson} instance. A different naming policy can set
+ * using the {@code GsonBuilder} class. See
+ * {@link com.google.gson.GsonBuilder#setFieldNamingPolicy(com.google.gson.FieldNamingPolicy)}
+ * for more information.
+ * </p>
+ *
+ * <p>
+ * Here is an example of how this annotation is meant to be used:
+ * </p>
+ * 
+ * <pre>
+ * public class MyClass {
+ *   &#64SerializedName("name") String a;
+ *   &#64SerializedName(value="name1", alternate={"name2", "name3"}) String b;
+ *   String c;
+ *
+ *   public MyClass(String a, String b, String c) {
+ *     this.a = a;
+ *     this.b = b;
+ *     this.c = c;
+ *   }
+ * }
+ * </pre>
+ *
+ * <p>
+ * The following shows the output that is generated when serializing an instance
+ * of the above example class:
+ * </p>
+ * 
+ * <pre>
+ * MyClass target = new MyClass("v1", "v2", "v3");
+ * Gson gson = new Gson();
+ * String json = gson.toJson(target);
+ * System.out.println(json);
+ *
+ * ===== OUTPUT =====
+ * {"name":"v1","name1":"v2","c":"v3"}
+ * </pre>
+ *
+ * <p>
+ * NOTE: The value you specify in this annotation must be a valid JSON field
+ * name.
+ * </p>
+ * While deserializing, all values specified in the annotation will be
+ * deserialized into the field. For example:
+ * 
+ * <pre>
+ * MyClass target = gson.fromJson("{'name1':'v1'}", MyClass.class);
+ * assertEquals("v1", target.b);
+ * target = gson.fromJson("{'name2':'v2'}", MyClass.class);
+ * assertEquals("v2", target.b);
+ * target = gson.fromJson("{'name3':'v3'}", MyClass.class);
+ * assertEquals("v3", target.b);
+ * </pre>
+ * 
+ * Note that MyClass.b is now deserialized from either name1, name2 or name3.
+ *
+ * @see com.google.gson.FieldNamingPolicy
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD, ElementType.METHOD })
+public @interface SerializedName {
+
+	/**
+	 * @return the desired name of the field when it is serialized or deserialized
+	 */
+	String value();
+
+	/**
+	 * @return the alternative names of the field when it is deserialized
+	 */
+	String[] alternate() default {};
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Since.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Since.java
new file mode 100644
index 0000000..58a1ef6
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Since.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates the version number since a member or a type has
+ * been present. This annotation is useful to manage versioning of your Json
+ * classes for a web-service.
+ *
+ * <p>
+ * This annotation has no effect unless you build {@link com.google.gson.Gson}
+ * with a {@link com.google.gson.GsonBuilder} and invoke
+ * {@link com.google.gson.GsonBuilder#setVersion(double)} method.
+ *
+ * <p>
+ * Here is an example of how this annotation is meant to be used:
+ * </p>
+ * 
+ * <pre>
+ * public class User {
+ *   private String firstName;
+ *   private String lastName;
+ *   &#64Since(1.0) private String emailAddress;
+ *   &#64Since(1.0) private String password;
+ *   &#64Since(1.1) private Address address;
+ * }
+ * </pre>
+ *
+ * <p>
+ * If you created Gson with {@code new Gson()}, the {@code toJson()} and
+ * {@code fromJson()} methods will use all the fields for serialization and
+ * deserialization. However, if you created Gson with
+ * {@code Gson gson = new GsonBuilder().setVersion(1.0).create()} then the
+ * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the
+ * {@code address} field since it's version number is set to {@code 1.1}.
+ * </p>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD, ElementType.TYPE })
+public @interface Since {
+	/**
+	 * the value indicating a version number since this member or type has been
+	 * present.
+	 */
+	double value();
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Until.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Until.java
new file mode 100644
index 0000000..793e3fc
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/Until.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates the version number until a member or a type
+ * should be present. Basically, if Gson is created with a version number that
+ * exceeds the value stored in the {@code Until} annotation then the field will
+ * be ignored from the JSON output. This annotation is useful to manage
+ * versioning of your JSON classes for a web-service.
+ *
+ * <p>
+ * This annotation has no effect unless you build {@link com.google.gson.Gson}
+ * with a {@link com.google.gson.GsonBuilder} and invoke
+ * {@link com.google.gson.GsonBuilder#setVersion(double)} method.
+ *
+ * <p>
+ * Here is an example of how this annotation is meant to be used:
+ * </p>
+ * 
+ * <pre>
+ * public class User {
+ *   private String firstName;
+ *   private String lastName;
+ *   &#64Until(1.1) private String emailAddress;
+ *   &#64Until(1.1) private String password;
+ * }
+ * </pre>
+ *
+ * <p>
+ * If you created Gson with {@code new Gson()}, the {@code toJson()} and
+ * {@code fromJson()} methods will use all the fields for serialization and
+ * deserialization. However, if you created Gson with
+ * {@code Gson gson = new GsonBuilder().setVersion(1.2).create()} then the
+ * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the
+ * {@code emailAddress} and {@code password} fields from the example above,
+ * because the version number passed to the GsonBuilder, {@code 1.2}, exceeds
+ * the version number set on the {@code Until} annotation, {@code 1.1}, for
+ * those fields.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.3
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD, ElementType.TYPE })
+public @interface Until {
+
+	/**
+	 * the value indicating a version number until this member or type should be
+	 * ignored.
+	 */
+	double value();
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/package-info.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/package-info.java
new file mode 100644
index 0000000..f639a55
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/annotations/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * This package provides annotations that can be used with
+ * {@link com.google.gson.Gson}.
+ * 
+ * @author Inderjeet Singh, Joel Leitch
+ */
+package cn.emay.sdk.util.json.gson.annotations;
\ No newline at end of file
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/$Gson$Preconditions.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/$Gson$Preconditions.java
new file mode 100644
index 0000000..1f7835c
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/$Gson$Preconditions.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+/**
+ * A simple utility class used to check method Preconditions.
+ *
+ * <pre>
+ * public long divideBy(long value) {
+ * 	Preconditions.checkArgument(value != 0);
+ * 	return this.value / value;
+ * }
+ * </pre>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class $Gson$Preconditions {
+	private $Gson$Preconditions() {
+		throw new UnsupportedOperationException();
+	}
+
+	public static <T> T checkNotNull(T obj) {
+		if (obj == null) {
+			throw new NullPointerException();
+		}
+		return obj;
+	}
+
+	public static void checkArgument(boolean condition) {
+		if (!condition) {
+			throw new IllegalArgumentException();
+		}
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/$Gson$Types.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/$Gson$Types.java
new file mode 100644
index 0000000..0589ff1
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/$Gson$Types.java
@@ -0,0 +1,584 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import static cn.emay.sdk.util.json.gson.internal.$Gson$Preconditions.checkArgument;
+import static cn.emay.sdk.util.json.gson.internal.$Gson$Preconditions.checkNotNull;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
+/**
+ * Static methods for working with types.
+ *
+ * @author Bob Lee
+ * @author Jesse Wilson
+ */
+public final class $Gson$Types {
+	static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
+
+	private $Gson$Types() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Returns a new parameterized type, applying {@code typeArguments} to
+	 * {@code rawType} and enclosed by {@code ownerType}.
+	 *
+	 * @return a {@link java.io.Serializable serializable} parameterized type.
+	 */
+	public static ParameterizedType newParameterizedTypeWithOwner(Type ownerType, Type rawType, Type... typeArguments) {
+		return new ParameterizedTypeImpl(ownerType, rawType, typeArguments);
+	}
+
+	/**
+	 * Returns an array type whose elements are all instances of
+	 * {@code componentType}.
+	 *
+	 * @return a {@link java.io.Serializable serializable} generic array type.
+	 */
+	public static GenericArrayType arrayOf(Type componentType) {
+		return new GenericArrayTypeImpl(componentType);
+	}
+
+	/**
+	 * Returns a type that represents an unknown type that extends {@code bound}.
+	 * For example, if {@code bound} is {@code CharSequence.class}, this returns
+	 * {@code ? extends CharSequence}. If {@code bound} is {@code Object.class},
+	 * this returns {@code ?}, which is shorthand for {@code ? extends Object}.
+	 */
+	public static WildcardType subtypeOf(Type bound) {
+		return new WildcardTypeImpl(new Type[] { bound }, EMPTY_TYPE_ARRAY);
+	}
+
+	/**
+	 * Returns a type that represents an unknown supertype of {@code bound}. For
+	 * example, if {@code bound} is {@code String.class}, this returns {@code ?
+	 * super String}.
+	 */
+	public static WildcardType supertypeOf(Type bound) {
+		return new WildcardTypeImpl(new Type[] { Object.class }, new Type[] { bound });
+	}
+
+	/**
+	 * Returns a type that is functionally equal but not necessarily equal according
+	 * to {@link Object#equals(Object) Object.equals()}. The returned type is
+	 * {@link java.io.Serializable}.
+	 */
+	public static Type canonicalize(Type type) {
+		if (type instanceof Class) {
+			Class<?> c = (Class<?>) type;
+			return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
+
+		} else if (type instanceof ParameterizedType) {
+			ParameterizedType p = (ParameterizedType) type;
+			return new ParameterizedTypeImpl(p.getOwnerType(), p.getRawType(), p.getActualTypeArguments());
+
+		} else if (type instanceof GenericArrayType) {
+			GenericArrayType g = (GenericArrayType) type;
+			return new GenericArrayTypeImpl(g.getGenericComponentType());
+
+		} else if (type instanceof WildcardType) {
+			WildcardType w = (WildcardType) type;
+			return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
+
+		} else {
+			// type is either serializable as-is or unsupported
+			return type;
+		}
+	}
+
+	public static Class<?> getRawType(Type type) {
+		if (type instanceof Class<?>) {
+			// type is a normal class.
+			return (Class<?>) type;
+
+		} else if (type instanceof ParameterizedType) {
+			ParameterizedType parameterizedType = (ParameterizedType) type;
+
+			// I'm not exactly sure why getRawType() returns Type instead of Class.
+			// Neal isn't either but suspects some pathological case related
+			// to nested classes exists.
+			Type rawType = parameterizedType.getRawType();
+			checkArgument(rawType instanceof Class);
+			return (Class<?>) rawType;
+
+		} else if (type instanceof GenericArrayType) {
+			Type componentType = ((GenericArrayType) type).getGenericComponentType();
+			return Array.newInstance(getRawType(componentType), 0).getClass();
+
+		} else if (type instanceof TypeVariable) {
+			// we could use the variable's bounds, but that won't work if there are
+			// multiple.
+			// having a raw type that's more general than necessary is okay
+			return Object.class;
+
+		} else if (type instanceof WildcardType) {
+			return getRawType(((WildcardType) type).getUpperBounds()[0]);
+
+		} else {
+			String className = type == null ? "null" : type.getClass().getName();
+			throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " + "GenericArrayType, but <" + type + "> is of type " + className);
+		}
+	}
+
+	static boolean equal(Object a, Object b) {
+		return a == b || (a != null && a.equals(b));
+	}
+
+	/**
+	 * Returns true if {@code a} and {@code b} are equal.
+	 */
+	public static boolean equals(Type a, Type b) {
+		if (a == b) {
+			// also handles (a == null && b == null)
+			return true;
+
+		} else if (a instanceof Class) {
+			// Class already specifies equals().
+			return a.equals(b);
+
+		} else if (a instanceof ParameterizedType) {
+			if (!(b instanceof ParameterizedType)) {
+				return false;
+			}
+
+			// TODO: save a .clone() call
+			ParameterizedType pa = (ParameterizedType) a;
+			ParameterizedType pb = (ParameterizedType) b;
+			return equal(pa.getOwnerType(), pb.getOwnerType()) && pa.getRawType().equals(pb.getRawType()) && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
+
+		} else if (a instanceof GenericArrayType) {
+			if (!(b instanceof GenericArrayType)) {
+				return false;
+			}
+
+			GenericArrayType ga = (GenericArrayType) a;
+			GenericArrayType gb = (GenericArrayType) b;
+			return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
+
+		} else if (a instanceof WildcardType) {
+			if (!(b instanceof WildcardType)) {
+				return false;
+			}
+
+			WildcardType wa = (WildcardType) a;
+			WildcardType wb = (WildcardType) b;
+			return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds()) && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
+
+		} else if (a instanceof TypeVariable) {
+			if (!(b instanceof TypeVariable)) {
+				return false;
+			}
+			TypeVariable<?> va = (TypeVariable<?>) a;
+			TypeVariable<?> vb = (TypeVariable<?>) b;
+			return va.getGenericDeclaration() == vb.getGenericDeclaration() && va.getName().equals(vb.getName());
+
+		} else {
+			// This isn't a type we support. Could be a generic array type, wildcard type,
+			// etc.
+			return false;
+		}
+	}
+
+	static int hashCodeOrZero(Object o) {
+		return o != null ? o.hashCode() : 0;
+	}
+
+	public static String typeToString(Type type) {
+		return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
+	}
+
+	/**
+	 * Returns the generic supertype for {@code supertype}. For example, given a
+	 * class {@code
+	 * IntegerSet}, the result for when supertype is {@code Set.class} is
+	 * {@code Set<Integer>} and the result when the supertype is
+	 * {@code Collection.class} is {@code Collection<Integer>}.
+	 */
+	static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve) {
+		if (toResolve == rawType) {
+			return context;
+		}
+
+		// we skip searching through interfaces if unknown is an interface
+		if (toResolve.isInterface()) {
+			Class<?>[] interfaces = rawType.getInterfaces();
+			for (int i = 0, length = interfaces.length; i < length; i++) {
+				if (interfaces[i] == toResolve) {
+					return rawType.getGenericInterfaces()[i];
+				} else if (toResolve.isAssignableFrom(interfaces[i])) {
+					return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
+				}
+			}
+		}
+
+		// check our supertypes
+		if (!rawType.isInterface()) {
+			while (rawType != Object.class) {
+				Class<?> rawSupertype = rawType.getSuperclass();
+				if (rawSupertype == toResolve) {
+					return rawType.getGenericSuperclass();
+				} else if (toResolve.isAssignableFrom(rawSupertype)) {
+					return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
+				}
+				rawType = rawSupertype;
+			}
+		}
+
+		// we can't resolve this further
+		return toResolve;
+	}
+
+	/**
+	 * Returns the generic form of {@code supertype}. For example, if this is {@code
+	 * ArrayList<String>}, this returns {@code Iterable<String>} given the input
+	 * {@code
+	 * Iterable.class}.
+	 *
+	 * @param supertype
+	 *            a superclass of, or interface implemented by, this.
+	 */
+	static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype) {
+		checkArgument(supertype.isAssignableFrom(contextRawType));
+		return resolve(context, contextRawType, $Gson$Types.getGenericSupertype(context, contextRawType, supertype));
+	}
+
+	/**
+	 * Returns the component type of this array type.
+	 * 
+	 * @throws ClassCastException
+	 *             if this type is not an array.
+	 */
+	public static Type getArrayComponentType(Type array) {
+		return array instanceof GenericArrayType ? ((GenericArrayType) array).getGenericComponentType() : ((Class<?>) array).getComponentType();
+	}
+
+	/**
+	 * Returns the element type of this collection type.
+	 * 
+	 * @throws IllegalArgumentException
+	 *             if this type is not a collection.
+	 */
+	public static Type getCollectionElementType(Type context, Class<?> contextRawType) {
+		Type collectionType = getSupertype(context, contextRawType, Collection.class);
+
+		if (collectionType instanceof WildcardType) {
+			collectionType = ((WildcardType) collectionType).getUpperBounds()[0];
+		}
+		if (collectionType instanceof ParameterizedType) {
+			return ((ParameterizedType) collectionType).getActualTypeArguments()[0];
+		}
+		return Object.class;
+	}
+
+	/**
+	 * Returns a two element array containing this map's key and value types in
+	 * positions 0 and 1 respectively.
+	 */
+	public static Type[] getMapKeyAndValueTypes(Type context, Class<?> contextRawType) {
+		/*
+		 * Work around a problem with the declaration of java.util.Properties. That
+		 * class should extend Hashtable<String, String>, but it's declared to extend
+		 * Hashtable<Object, Object>.
+		 */
+		if (context == Properties.class) {
+			return new Type[] { String.class, String.class }; // TODO: test subclasses of Properties!
+		}
+
+		Type mapType = getSupertype(context, contextRawType, Map.class);
+		// TODO: strip wildcards?
+		if (mapType instanceof ParameterizedType) {
+			ParameterizedType mapParameterizedType = (ParameterizedType) mapType;
+			return mapParameterizedType.getActualTypeArguments();
+		}
+		return new Type[] { Object.class, Object.class };
+	}
+
+	public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
+		// this implementation is made a little more complicated in an attempt to avoid
+		// object-creation
+		while (true) {
+			if (toResolve instanceof TypeVariable) {
+				TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
+				toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
+				if (toResolve == typeVariable) {
+					return toResolve;
+				}
+
+			} else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) {
+				Class<?> original = (Class<?>) toResolve;
+				Type componentType = original.getComponentType();
+				Type newComponentType = resolve(context, contextRawType, componentType);
+				return componentType == newComponentType ? original : arrayOf(newComponentType);
+
+			} else if (toResolve instanceof GenericArrayType) {
+				GenericArrayType original = (GenericArrayType) toResolve;
+				Type componentType = original.getGenericComponentType();
+				Type newComponentType = resolve(context, contextRawType, componentType);
+				return componentType == newComponentType ? original : arrayOf(newComponentType);
+
+			} else if (toResolve instanceof ParameterizedType) {
+				ParameterizedType original = (ParameterizedType) toResolve;
+				Type ownerType = original.getOwnerType();
+				Type newOwnerType = resolve(context, contextRawType, ownerType);
+				boolean changed = newOwnerType != ownerType;
+
+				Type[] args = original.getActualTypeArguments();
+				for (int t = 0, length = args.length; t < length; t++) {
+					Type resolvedTypeArgument = resolve(context, contextRawType, args[t]);
+					if (resolvedTypeArgument != args[t]) {
+						if (!changed) {
+							args = args.clone();
+							changed = true;
+						}
+						args[t] = resolvedTypeArgument;
+					}
+				}
+
+				return changed ? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args) : original;
+
+			} else if (toResolve instanceof WildcardType) {
+				WildcardType original = (WildcardType) toResolve;
+				Type[] originalLowerBound = original.getLowerBounds();
+				Type[] originalUpperBound = original.getUpperBounds();
+
+				if (originalLowerBound.length == 1) {
+					Type lowerBound = resolve(context, contextRawType, originalLowerBound[0]);
+					if (lowerBound != originalLowerBound[0]) {
+						return supertypeOf(lowerBound);
+					}
+				} else if (originalUpperBound.length == 1) {
+					Type upperBound = resolve(context, contextRawType, originalUpperBound[0]);
+					if (upperBound != originalUpperBound[0]) {
+						return subtypeOf(upperBound);
+					}
+				}
+				return original;
+
+			} else {
+				return toResolve;
+			}
+		}
+	}
+
+	static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
+		Class<?> declaredByRaw = declaringClassOf(unknown);
+
+		// we can't reduce this further
+		if (declaredByRaw == null) {
+			return unknown;
+		}
+
+		Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw);
+		if (declaredBy instanceof ParameterizedType) {
+			int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
+			return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
+		}
+
+		return unknown;
+	}
+
+	private static int indexOf(Object[] array, Object toFind) {
+		for (int i = 0; i < array.length; i++) {
+			if (toFind.equals(array[i])) {
+				return i;
+			}
+		}
+		throw new NoSuchElementException();
+	}
+
+	/**
+	 * Returns the declaring class of {@code typeVariable}, or {@code null} if it
+	 * was not declared by a class.
+	 */
+	private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
+		GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
+		return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
+	}
+
+	static void checkNotPrimitive(Type type) {
+		checkArgument(!(type instanceof Class<?>) || !((Class<?>) type).isPrimitive());
+	}
+
+	private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable {
+		private final Type ownerType;
+		private final Type rawType;
+		private final Type[] typeArguments;
+
+		public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) {
+			// require an owner type if the raw type needs it
+			if (rawType instanceof Class<?>) {
+				Class<?> rawTypeAsClass = (Class<?>) rawType;
+				boolean isStaticOrTopLevelClass = Modifier.isStatic(rawTypeAsClass.getModifiers()) || rawTypeAsClass.getEnclosingClass() == null;
+				checkArgument(ownerType != null || isStaticOrTopLevelClass);
+			}
+
+			this.ownerType = ownerType == null ? null : canonicalize(ownerType);
+			this.rawType = canonicalize(rawType);
+			this.typeArguments = typeArguments.clone();
+			for (int t = 0; t < this.typeArguments.length; t++) {
+				checkNotNull(this.typeArguments[t]);
+				checkNotPrimitive(this.typeArguments[t]);
+				this.typeArguments[t] = canonicalize(this.typeArguments[t]);
+			}
+		}
+
+		public Type[] getActualTypeArguments() {
+			return typeArguments.clone();
+		}
+
+		public Type getRawType() {
+			return rawType;
+		}
+
+		public Type getOwnerType() {
+			return ownerType;
+		}
+
+		@Override
+		public boolean equals(Object other) {
+			return other instanceof ParameterizedType && $Gson$Types.equals(this, (ParameterizedType) other);
+		}
+
+		@Override
+		public int hashCode() {
+			return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
+		}
+
+		@Override
+		public String toString() {
+			StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
+			stringBuilder.append(typeToString(rawType));
+
+			if (typeArguments.length == 0) {
+				return stringBuilder.toString();
+			}
+
+			stringBuilder.append("<").append(typeToString(typeArguments[0]));
+			for (int i = 1; i < typeArguments.length; i++) {
+				stringBuilder.append(", ").append(typeToString(typeArguments[i]));
+			}
+			return stringBuilder.append(">").toString();
+		}
+
+		private static final long serialVersionUID = 0;
+	}
+
+	private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable {
+		private final Type componentType;
+
+		public GenericArrayTypeImpl(Type componentType) {
+			this.componentType = canonicalize(componentType);
+		}
+
+		public Type getGenericComponentType() {
+			return componentType;
+		}
+
+		@Override
+		public boolean equals(Object o) {
+			return o instanceof GenericArrayType && $Gson$Types.equals(this, (GenericArrayType) o);
+		}
+
+		@Override
+		public int hashCode() {
+			return componentType.hashCode();
+		}
+
+		@Override
+		public String toString() {
+			return typeToString(componentType) + "[]";
+		}
+
+		private static final long serialVersionUID = 0;
+	}
+
+	/**
+	 * The WildcardType interface supports multiple upper bounds and multiple lower
+	 * bounds. We only support what the Java 6 language needs - at most one bound.
+	 * If a lower bound is set, the upper bound must be Object.class.
+	 */
+	private static final class WildcardTypeImpl implements WildcardType, Serializable {
+		private final Type upperBound;
+		private final Type lowerBound;
+
+		public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
+			checkArgument(lowerBounds.length <= 1);
+			checkArgument(upperBounds.length == 1);
+
+			if (lowerBounds.length == 1) {
+				checkNotNull(lowerBounds[0]);
+				checkNotPrimitive(lowerBounds[0]);
+				checkArgument(upperBounds[0] == Object.class);
+				this.lowerBound = canonicalize(lowerBounds[0]);
+				this.upperBound = Object.class;
+
+			} else {
+				checkNotNull(upperBounds[0]);
+				checkNotPrimitive(upperBounds[0]);
+				this.lowerBound = null;
+				this.upperBound = canonicalize(upperBounds[0]);
+			}
+		}
+
+		public Type[] getUpperBounds() {
+			return new Type[] { upperBound };
+		}
+
+		public Type[] getLowerBounds() {
+			return lowerBound != null ? new Type[] { lowerBound } : EMPTY_TYPE_ARRAY;
+		}
+
+		@Override
+		public boolean equals(Object other) {
+			return other instanceof WildcardType && $Gson$Types.equals(this, (WildcardType) other);
+		}
+
+		@Override
+		public int hashCode() {
+			// this equals Arrays.hashCode(getLowerBounds()) ^
+			// Arrays.hashCode(getUpperBounds());
+			return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
+		}
+
+		@Override
+		public String toString() {
+			if (lowerBound != null) {
+				return "? super " + typeToString(lowerBound);
+			} else if (upperBound == Object.class) {
+				return "?";
+			} else {
+				return "? extends " + typeToString(upperBound);
+			}
+		}
+
+		private static final long serialVersionUID = 0;
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/ConstructorConstructor.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/ConstructorConstructor.java
new file mode 100644
index 0000000..b438c54
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/ConstructorConstructor.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import cn.emay.sdk.util.json.gson.InstanceCreator;
+import cn.emay.sdk.util.json.gson.JsonIOException;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+
+/**
+ * Returns a function that can construct an instance of a requested type.
+ */
+public final class ConstructorConstructor {
+	private final Map<Type, InstanceCreator<?>> instanceCreators;
+
+	public ConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators) {
+		this.instanceCreators = instanceCreators;
+	}
+
+	public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
+		final Type type = typeToken.getType();
+		final Class<? super T> rawType = typeToken.getRawType();
+
+		// first try an instance creator
+
+		@SuppressWarnings("unchecked") // types must agree
+		final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
+		if (typeCreator != null) {
+			return new ObjectConstructor<T>() {
+				@Override
+				public T construct() {
+					return typeCreator.createInstance(type);
+				}
+			};
+		}
+
+		// Next try raw type match for instance creators
+		@SuppressWarnings("unchecked") // types must agree
+		final InstanceCreator<T> rawTypeCreator = (InstanceCreator<T>) instanceCreators.get(rawType);
+		if (rawTypeCreator != null) {
+			return new ObjectConstructor<T>() {
+				@Override
+				public T construct() {
+					return rawTypeCreator.createInstance(type);
+				}
+			};
+		}
+
+		ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
+		if (defaultConstructor != null) {
+			return defaultConstructor;
+		}
+
+		ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
+		if (defaultImplementation != null) {
+			return defaultImplementation;
+		}
+
+		// finally try unsafe
+		return newUnsafeAllocator(type, rawType);
+	}
+
+	private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
+		try {
+			final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
+			if (!constructor.isAccessible()) {
+				constructor.setAccessible(true);
+			}
+			return new ObjectConstructor<T>() {
+				@SuppressWarnings("unchecked") // T is the same raw type as is requested
+				@Override
+				public T construct() {
+					try {
+						Object[] args = null;
+						return (T) constructor.newInstance(args);
+					} catch (InstantiationException e) {
+						// TODO: JsonParseException ?
+						throw new RuntimeException("Failed to invoke " + constructor + " with no args", e);
+					} catch (InvocationTargetException e) {
+						// TODO: don't wrap if cause is unchecked!
+						// TODO: JsonParseException ?
+						throw new RuntimeException("Failed to invoke " + constructor + " with no args", e.getTargetException());
+					} catch (IllegalAccessException e) {
+						throw new AssertionError(e);
+					}
+				}
+			};
+		} catch (NoSuchMethodException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * Constructors for common interface types like Map and List and their subtypes.
+	 */
+	@SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is
+	private <T> ObjectConstructor<T> newDefaultImplementationConstructor(final Type type, Class<? super T> rawType) {
+		if (Collection.class.isAssignableFrom(rawType)) {
+			if (SortedSet.class.isAssignableFrom(rawType)) {
+				return new ObjectConstructor<T>() {
+					@Override
+					public T construct() {
+						return (T) new TreeSet<Object>();
+					}
+				};
+			} else if (EnumSet.class.isAssignableFrom(rawType)) {
+				return new ObjectConstructor<T>() {
+					@SuppressWarnings("rawtypes")
+					@Override
+					public T construct() {
+						if (type instanceof ParameterizedType) {
+							Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
+							if (elementType instanceof Class) {
+								return (T) EnumSet.noneOf((Class) elementType);
+							} else {
+								throw new JsonIOException("Invalid EnumSet type: " + type.toString());
+							}
+						} else {
+							throw new JsonIOException("Invalid EnumSet type: " + type.toString());
+						}
+					}
+				};
+			} else if (Set.class.isAssignableFrom(rawType)) {
+				return new ObjectConstructor<T>() {
+					@Override
+					public T construct() {
+						return (T) new LinkedHashSet<Object>();
+					}
+				};
+			} else if (Queue.class.isAssignableFrom(rawType)) {
+				return new ObjectConstructor<T>() {
+					@Override
+					public T construct() {
+						return (T) new LinkedList<Object>();
+					}
+				};
+			} else {
+				return new ObjectConstructor<T>() {
+					@Override
+					public T construct() {
+						return (T) new ArrayList<Object>();
+					}
+				};
+			}
+		}
+
+		if (Map.class.isAssignableFrom(rawType)) {
+			if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) {
+				return new ObjectConstructor<T>() {
+					@Override
+					public T construct() {
+						return (T) new ConcurrentSkipListMap<Object, Object>();
+					}
+				};
+			} else if (ConcurrentMap.class.isAssignableFrom(rawType)) {
+				return new ObjectConstructor<T>() {
+					@Override
+					public T construct() {
+						return (T) new ConcurrentHashMap<Object, Object>();
+					}
+				};
+			} else if (SortedMap.class.isAssignableFrom(rawType)) {
+				return new ObjectConstructor<T>() {
+					@Override
+					public T construct() {
+						return (T) new TreeMap<Object, Object>();
+					}
+				};
+			} else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom(TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) {
+				return new ObjectConstructor<T>() {
+					@Override
+					public T construct() {
+						return (T) new LinkedHashMap<Object, Object>();
+					}
+				};
+			} else {
+				return new ObjectConstructor<T>() {
+					@Override
+					public T construct() {
+						return (T) new LinkedTreeMap<String, Object>();
+					}
+				};
+			}
+		}
+
+		return null;
+	}
+
+	private <T> ObjectConstructor<T> newUnsafeAllocator(final Type type, final Class<? super T> rawType) {
+		return new ObjectConstructor<T>() {
+			private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
+
+			@SuppressWarnings("unchecked")
+			@Override
+			public T construct() {
+				try {
+					Object newInstance = unsafeAllocator.newInstance(rawType);
+					return (T) newInstance;
+				} catch (Exception e) {
+					throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". " + "Register an InstanceCreator with Gson for this type may fix this problem."), e);
+				}
+			}
+		};
+	}
+
+	@Override
+	public String toString() {
+		return instanceCreators.toString();
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Excluder.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Excluder.java
new file mode 100644
index 0000000..4ef0808
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Excluder.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import cn.emay.sdk.util.json.gson.ExclusionStrategy;
+import cn.emay.sdk.util.json.gson.FieldAttributes;
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.annotations.Expose;
+import cn.emay.sdk.util.json.gson.annotations.Since;
+import cn.emay.sdk.util.json.gson.annotations.Until;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * This class selects which fields and types to omit. It is configurable,
+ * supporting version attributes {@link Since} and {@link Until}, modifiers,
+ * synthetic fields, anonymous and local classes, inner classes, and fields with
+ * the {@link Expose} annotation.
+ *
+ * <p>
+ * This class is a type adapter factory; types that are excluded will be adapted
+ * to null. It may delegate to another type adapter if only one direction is
+ * excluded.
+ *
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+public final class Excluder implements TypeAdapterFactory, Cloneable {
+	private static final double IGNORE_VERSIONS = -1.0d;
+	public static final Excluder DEFAULT = new Excluder();
+
+	private double version = IGNORE_VERSIONS;
+	private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
+	private boolean serializeInnerClasses = true;
+	private boolean requireExpose;
+	private List<ExclusionStrategy> serializationStrategies = Collections.emptyList();
+	private List<ExclusionStrategy> deserializationStrategies = Collections.emptyList();
+
+	@Override
+	protected Excluder clone() {
+		try {
+			return (Excluder) super.clone();
+		} catch (CloneNotSupportedException e) {
+			throw new AssertionError(e);
+		}
+	}
+
+	public Excluder withVersion(double ignoreVersionsAfter) {
+		Excluder result = clone();
+		result.version = ignoreVersionsAfter;
+		return result;
+	}
+
+	public Excluder withModifiers(int... modifiers) {
+		Excluder result = clone();
+		result.modifiers = 0;
+		for (int modifier : modifiers) {
+			result.modifiers |= modifier;
+		}
+		return result;
+	}
+
+	public Excluder disableInnerClassSerialization() {
+		Excluder result = clone();
+		result.serializeInnerClasses = false;
+		return result;
+	}
+
+	public Excluder excludeFieldsWithoutExposeAnnotation() {
+		Excluder result = clone();
+		result.requireExpose = true;
+		return result;
+	}
+
+	public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy, boolean serialization, boolean deserialization) {
+		Excluder result = clone();
+		if (serialization) {
+			result.serializationStrategies = new ArrayList<ExclusionStrategy>(serializationStrategies);
+			result.serializationStrategies.add(exclusionStrategy);
+		}
+		if (deserialization) {
+			result.deserializationStrategies = new ArrayList<ExclusionStrategy>(deserializationStrategies);
+			result.deserializationStrategies.add(exclusionStrategy);
+		}
+		return result;
+	}
+
+	public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
+		Class<?> rawType = type.getRawType();
+		final boolean skipSerialize = excludeClass(rawType, true);
+		final boolean skipDeserialize = excludeClass(rawType, false);
+
+		if (!skipSerialize && !skipDeserialize) {
+			return null;
+		}
+
+		return new TypeAdapter<T>() {
+			/**
+			 * The delegate is lazily created because it may not be needed, and creating it
+			 * may fail.
+			 */
+			private TypeAdapter<T> delegate;
+
+			@Override
+			public T read(JsonReader in) throws IOException {
+				if (skipDeserialize) {
+					in.skipValue();
+					return null;
+				}
+				return delegate().read(in);
+			}
+
+			@Override
+			public void write(JsonWriter out, T value) throws IOException {
+				if (skipSerialize) {
+					out.nullValue();
+					return;
+				}
+				delegate().write(out, value);
+			}
+
+			private TypeAdapter<T> delegate() {
+				TypeAdapter<T> d = delegate;
+				return d != null ? d : (delegate = gson.getDelegateAdapter(Excluder.this, type));
+			}
+		};
+	}
+
+	public boolean excludeField(Field field, boolean serialize) {
+		if ((modifiers & field.getModifiers()) != 0) {
+			return true;
+		}
+
+		if (version != Excluder.IGNORE_VERSIONS && !isValidVersion(field.getAnnotation(Since.class), field.getAnnotation(Until.class))) {
+			return true;
+		}
+
+		if (field.isSynthetic()) {
+			return true;
+		}
+
+		if (requireExpose) {
+			Expose annotation = field.getAnnotation(Expose.class);
+			if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) {
+				return true;
+			}
+		}
+
+		if (!serializeInnerClasses && isInnerClass(field.getType())) {
+			return true;
+		}
+
+		if (isAnonymousOrLocal(field.getType())) {
+			return true;
+		}
+
+		List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
+		if (!list.isEmpty()) {
+			FieldAttributes fieldAttributes = new FieldAttributes(field);
+			for (ExclusionStrategy exclusionStrategy : list) {
+				if (exclusionStrategy.shouldSkipField(fieldAttributes)) {
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+
+	public boolean excludeClass(Class<?> clazz, boolean serialize) {
+		if (version != Excluder.IGNORE_VERSIONS && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {
+			return true;
+		}
+
+		if (!serializeInnerClasses && isInnerClass(clazz)) {
+			return true;
+		}
+
+		if (isAnonymousOrLocal(clazz)) {
+			return true;
+		}
+
+		List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
+		for (ExclusionStrategy exclusionStrategy : list) {
+			if (exclusionStrategy.shouldSkipClass(clazz)) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	private boolean isAnonymousOrLocal(Class<?> clazz) {
+		return !Enum.class.isAssignableFrom(clazz) && (clazz.isAnonymousClass() || clazz.isLocalClass());
+	}
+
+	private boolean isInnerClass(Class<?> clazz) {
+		return clazz.isMemberClass() && !isStatic(clazz);
+	}
+
+	private boolean isStatic(Class<?> clazz) {
+		return (clazz.getModifiers() & Modifier.STATIC) != 0;
+	}
+
+	private boolean isValidVersion(Since since, Until until) {
+		return isValidSince(since) && isValidUntil(until);
+	}
+
+	private boolean isValidSince(Since annotation) {
+		if (annotation != null) {
+			double annotationVersion = annotation.value();
+			if (annotationVersion > version) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	private boolean isValidUntil(Until annotation) {
+		if (annotation != null) {
+			double annotationVersion = annotation.value();
+			if (annotationVersion <= version) {
+				return false;
+			}
+		}
+		return true;
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/JsonReaderInternalAccess.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/JsonReaderInternalAccess.java
new file mode 100644
index 0000000..b2b41c1
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/JsonReaderInternalAccess.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import java.io.IOException;
+
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+
+/**
+ * Internal-only APIs of JsonReader available only to other classes in Gson.
+ */
+public abstract class JsonReaderInternalAccess {
+	public static JsonReaderInternalAccess INSTANCE;
+
+	/**
+	 * Changes the type of the current property name token to a string value.
+	 */
+	public abstract void promoteNameToValue(JsonReader reader) throws IOException;
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LazilyParsedNumber.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LazilyParsedNumber.java
new file mode 100644
index 0000000..6743929
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LazilyParsedNumber.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import java.io.ObjectStreamException;
+import java.math.BigDecimal;
+
+/**
+ * This class holds a number value that is lazily converted to a specific number
+ * type
+ *
+ * @author Inderjeet Singh
+ */
+public final class LazilyParsedNumber extends Number {
+	private final String value;
+
+	/**
+	 * @param value
+	 *            must not be null
+	 */
+	public LazilyParsedNumber(String value) {
+		this.value = value;
+	}
+
+	@Override
+	public int intValue() {
+		try {
+			return Integer.parseInt(value);
+		} catch (NumberFormatException e) {
+			try {
+				return (int) Long.parseLong(value);
+			} catch (NumberFormatException nfe) {
+				return new BigDecimal(value).intValue();
+			}
+		}
+	}
+
+	@Override
+	public long longValue() {
+		try {
+			return Long.parseLong(value);
+		} catch (NumberFormatException e) {
+			return new BigDecimal(value).longValue();
+		}
+	}
+
+	@Override
+	public float floatValue() {
+		return Float.parseFloat(value);
+	}
+
+	@Override
+	public double doubleValue() {
+		return Double.parseDouble(value);
+	}
+
+	@Override
+	public String toString() {
+		return value;
+	}
+
+	/**
+	 * If somebody is unlucky enough to have to serialize one of these, serialize it
+	 * as a BigDecimal so that they won't need Gson on the other side to deserialize
+	 * it.
+	 */
+	private Object writeReplace() throws ObjectStreamException {
+		return new BigDecimal(value);
+	}
+
+	@Override
+	public int hashCode() {
+		return value.hashCode();
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj) {
+			return true;
+		}
+		if (obj instanceof LazilyParsedNumber) {
+			LazilyParsedNumber other = (LazilyParsedNumber) obj;
+			return value == other.value || value.equals(other.value);
+		}
+		return false;
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LinkedHashTreeMap.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LinkedHashTreeMap.java
new file mode 100644
index 0000000..b4dac99
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LinkedHashTreeMap.java
@@ -0,0 +1,880 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2012 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
+ * insertion order for iteration order. Comparison order is only used as an
+ * optimization for efficient insertion and removal.
+ *
+ * <p>
+ * This implementation was derived from Android 4.1's TreeMap and LinkedHashMap
+ * classes.
+ */
+public final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Serializable {
+	@SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable<Comparable<Comparable<...>>>
+	private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() {
+		public int compare(Comparable a, Comparable b) {
+			return a.compareTo(b);
+		}
+	};
+
+	Comparator<? super K> comparator;
+	Node<K, V>[] table;
+	final Node<K, V> header;
+	int size = 0;
+	int modCount = 0;
+	int threshold;
+
+	/**
+	 * Create a natural order, empty tree map whose keys must be mutually comparable
+	 * and non-null.
+	 */
+	@SuppressWarnings("unchecked") // unsafe! this assumes K is comparable
+	public LinkedHashTreeMap() {
+		this((Comparator<? super K>) NATURAL_ORDER);
+	}
+
+	/**
+	 * Create a tree map ordered by {@code comparator}. This map's keys may only be
+	 * null if {@code comparator} permits.
+	 *
+	 * @param comparator
+	 *            the comparator to order elements with, or {@code null} to use the
+	 *            natural ordering.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable
+	public LinkedHashTreeMap(Comparator<? super K> comparator) {
+		this.comparator = comparator != null ? comparator : (Comparator) NATURAL_ORDER;
+		this.header = new Node<K, V>();
+		this.table = new Node[16]; // TODO: sizing/resizing policies
+		this.threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity
+	}
+
+	@Override
+	public int size() {
+		return size;
+	}
+
+	@Override
+	public V get(Object key) {
+		Node<K, V> node = findByObject(key);
+		return node != null ? node.value : null;
+	}
+
+	@Override
+	public boolean containsKey(Object key) {
+		return findByObject(key) != null;
+	}
+
+	@Override
+	public V put(K key, V value) {
+		if (key == null) {
+			throw new NullPointerException("key == null");
+		}
+		Node<K, V> created = find(key, true);
+		V result = created.value;
+		created.value = value;
+		return result;
+	}
+
+	@Override
+	public void clear() {
+		Arrays.fill(table, null);
+		size = 0;
+		modCount++;
+
+		// Clear all links to help GC
+		Node<K, V> header = this.header;
+		for (Node<K, V> e = header.next; e != header;) {
+			Node<K, V> next = e.next;
+			e.next = e.prev = null;
+			e = next;
+		}
+
+		header.next = header.prev = header;
+	}
+
+	@Override
+	public V remove(Object key) {
+		Node<K, V> node = removeInternalByKey(key);
+		return node != null ? node.value : null;
+	}
+
+	/**
+	 * Returns the node at or adjacent to the given key, creating it if requested.
+	 *
+	 * @throws ClassCastException
+	 *             if {@code key} and the tree's keys aren't mutually comparable.
+	 */
+	Node<K, V> find(K key, boolean create) {
+		Comparator<? super K> comparator = this.comparator;
+		Node<K, V>[] table = this.table;
+		int hash = secondaryHash(key.hashCode());
+		int index = hash & (table.length - 1);
+		Node<K, V> nearest = table[index];
+		int comparison = 0;
+
+		if (nearest != null) {
+			// Micro-optimization: avoid polymorphic calls to Comparator.compare().
+			@SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble.
+			Comparable<Object> comparableKey = (comparator == NATURAL_ORDER) ? (Comparable<Object>) key : null;
+
+			while (true) {
+				comparison = (comparableKey != null) ? comparableKey.compareTo(nearest.key) : comparator.compare(key, nearest.key);
+
+				// We found the requested key.
+				if (comparison == 0) {
+					return nearest;
+				}
+
+				// If it exists, the key is in a subtree. Go deeper.
+				Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right;
+				if (child == null) {
+					break;
+				}
+
+				nearest = child;
+			}
+		}
+
+		// The key doesn't exist in this tree.
+		if (!create) {
+			return null;
+		}
+
+		// Create the node and add it to the tree or the table.
+		Node<K, V> header = this.header;
+		Node<K, V> created;
+		if (nearest == null) {
+			// Check that the value is comparable if we didn't do any comparisons.
+			if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
+				throw new ClassCastException(key.getClass().getName() + " is not Comparable");
+			}
+			created = new Node<K, V>(nearest, key, hash, header, header.prev);
+			table[index] = created;
+		} else {
+			created = new Node<K, V>(nearest, key, hash, header, header.prev);
+			if (comparison < 0) { // nearest.key is higher
+				nearest.left = created;
+			} else { // comparison > 0, nearest.key is lower
+				nearest.right = created;
+			}
+			rebalance(nearest, true);
+		}
+
+		if (size++ > threshold) {
+			doubleCapacity();
+		}
+		modCount++;
+
+		return created;
+	}
+
+	@SuppressWarnings("unchecked")
+	Node<K, V> findByObject(Object key) {
+		try {
+			return key != null ? find((K) key, false) : null;
+		} catch (ClassCastException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * Returns this map's entry that has the same key and value as {@code
+	 * entry}, or null if this map has no such entry.
+	 *
+	 * <p>
+	 * This method uses the comparator for key equality rather than {@code
+	 * equals}. If this map's comparator isn't consistent with equals (such as
+	 * {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code
+	 * contains()} will violate the collections API.
+	 */
+	Node<K, V> findByEntry(Entry<?, ?> entry) {
+		Node<K, V> mine = findByObject(entry.getKey());
+		boolean valuesEqual = mine != null && equal(mine.value, entry.getValue());
+		return valuesEqual ? mine : null;
+	}
+
+	private boolean equal(Object a, Object b) {
+		return a == b || (a != null && a.equals(b));
+	}
+
+	/**
+	 * Applies a supplemental hash function to a given hashCode, which defends
+	 * against poor quality hash functions. This is critical because HashMap uses
+	 * power-of-two length hash tables, that otherwise encounter collisions for
+	 * hashCodes that do not differ in lower or upper bits.
+	 */
+	private static int secondaryHash(int h) {
+		// Doug Lea's supplemental hash function
+		h ^= (h >>> 20) ^ (h >>> 12);
+		return h ^ (h >>> 7) ^ (h >>> 4);
+	}
+
+	/**
+	 * Removes {@code node} from this tree, rearranging the tree's structure as
+	 * necessary.
+	 *
+	 * @param unlink
+	 *            true to also unlink this node from the iteration linked list.
+	 */
+	void removeInternal(Node<K, V> node, boolean unlink) {
+		if (unlink) {
+			node.prev.next = node.next;
+			node.next.prev = node.prev;
+			node.next = node.prev = null; // Help the GC (for performance)
+		}
+
+		Node<K, V> left = node.left;
+		Node<K, V> right = node.right;
+		Node<K, V> originalParent = node.parent;
+		if (left != null && right != null) {
+
+			/*
+			 * To remove a node with both left and right subtrees, move an adjacent node
+			 * from one of those subtrees into this node's place.
+			 *
+			 * Removing the adjacent node may change this node's subtrees. This node may no
+			 * longer have two subtrees once the adjacent node is gone!
+			 */
+
+			Node<K, V> adjacent = (left.height > right.height) ? left.last() : right.first();
+			removeInternal(adjacent, false); // takes care of rebalance and size--
+
+			int leftHeight = 0;
+			left = node.left;
+			if (left != null) {
+				leftHeight = left.height;
+				adjacent.left = left;
+				left.parent = adjacent;
+				node.left = null;
+			}
+			int rightHeight = 0;
+			right = node.right;
+			if (right != null) {
+				rightHeight = right.height;
+				adjacent.right = right;
+				right.parent = adjacent;
+				node.right = null;
+			}
+			adjacent.height = Math.max(leftHeight, rightHeight) + 1;
+			replaceInParent(node, adjacent);
+			return;
+		} else if (left != null) {
+			replaceInParent(node, left);
+			node.left = null;
+		} else if (right != null) {
+			replaceInParent(node, right);
+			node.right = null;
+		} else {
+			replaceInParent(node, null);
+		}
+
+		rebalance(originalParent, false);
+		size--;
+		modCount++;
+	}
+
+	Node<K, V> removeInternalByKey(Object key) {
+		Node<K, V> node = findByObject(key);
+		if (node != null) {
+			removeInternal(node, true);
+		}
+		return node;
+	}
+
+	private void replaceInParent(Node<K, V> node, Node<K, V> replacement) {
+		Node<K, V> parent = node.parent;
+		node.parent = null;
+		if (replacement != null) {
+			replacement.parent = parent;
+		}
+
+		if (parent != null) {
+			if (parent.left == node) {
+				parent.left = replacement;
+			} else {
+				assert (parent.right == node);
+				parent.right = replacement;
+			}
+		} else {
+			int index = node.hash & (table.length - 1);
+			table[index] = replacement;
+		}
+	}
+
+	/**
+	 * Rebalances the tree by making any AVL rotations necessary between the
+	 * newly-unbalanced node and the tree's root.
+	 *
+	 * @param insert
+	 *            true if the node was unbalanced by an insert; false if it was by a
+	 *            removal.
+	 */
+	private void rebalance(Node<K, V> unbalanced, boolean insert) {
+		for (Node<K, V> node = unbalanced; node != null; node = node.parent) {
+			Node<K, V> left = node.left;
+			Node<K, V> right = node.right;
+			int leftHeight = left != null ? left.height : 0;
+			int rightHeight = right != null ? right.height : 0;
+
+			int delta = leftHeight - rightHeight;
+			if (delta == -2) {
+				Node<K, V> rightLeft = right.left;
+				Node<K, V> rightRight = right.right;
+				int rightRightHeight = rightRight != null ? rightRight.height : 0;
+				int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;
+
+				int rightDelta = rightLeftHeight - rightRightHeight;
+				if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
+					rotateLeft(node); // AVL right right
+				} else {
+					assert (rightDelta == 1);
+					rotateRight(right); // AVL right left
+					rotateLeft(node);
+				}
+				if (insert) {
+					break; // no further rotations will be necessary
+				}
+
+			} else if (delta == 2) {
+				Node<K, V> leftLeft = left.left;
+				Node<K, V> leftRight = left.right;
+				int leftRightHeight = leftRight != null ? leftRight.height : 0;
+				int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;
+
+				int leftDelta = leftLeftHeight - leftRightHeight;
+				if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
+					rotateRight(node); // AVL left left
+				} else {
+					assert (leftDelta == -1);
+					rotateLeft(left); // AVL left right
+					rotateRight(node);
+				}
+				if (insert) {
+					break; // no further rotations will be necessary
+				}
+
+			} else if (delta == 0) {
+				node.height = leftHeight + 1; // leftHeight == rightHeight
+				if (insert) {
+					break; // the insert caused balance, so rebalancing is done!
+				}
+
+			} else {
+				assert (delta == -1 || delta == 1);
+				node.height = Math.max(leftHeight, rightHeight) + 1;
+				if (!insert) {
+					break; // the height hasn't changed, so rebalancing is done!
+				}
+			}
+		}
+	}
+
+	/**
+	 * Rotates the subtree so that its root's right child is the new root.
+	 */
+	private void rotateLeft(Node<K, V> root) {
+		Node<K, V> left = root.left;
+		Node<K, V> pivot = root.right;
+		Node<K, V> pivotLeft = pivot.left;
+		Node<K, V> pivotRight = pivot.right;
+
+		// move the pivot's left child to the root's right
+		root.right = pivotLeft;
+		if (pivotLeft != null) {
+			pivotLeft.parent = root;
+		}
+
+		replaceInParent(root, pivot);
+
+		// move the root to the pivot's left
+		pivot.left = root;
+		root.parent = pivot;
+
+		// fix heights
+		root.height = Math.max(left != null ? left.height : 0, pivotLeft != null ? pivotLeft.height : 0) + 1;
+		pivot.height = Math.max(root.height, pivotRight != null ? pivotRight.height : 0) + 1;
+	}
+
+	/**
+	 * Rotates the subtree so that its root's left child is the new root.
+	 */
+	private void rotateRight(Node<K, V> root) {
+		Node<K, V> pivot = root.left;
+		Node<K, V> right = root.right;
+		Node<K, V> pivotLeft = pivot.left;
+		Node<K, V> pivotRight = pivot.right;
+
+		// move the pivot's right child to the root's left
+		root.left = pivotRight;
+		if (pivotRight != null) {
+			pivotRight.parent = root;
+		}
+
+		replaceInParent(root, pivot);
+
+		// move the root to the pivot's right
+		pivot.right = root;
+		root.parent = pivot;
+
+		// fixup heights
+		root.height = Math.max(right != null ? right.height : 0, pivotRight != null ? pivotRight.height : 0) + 1;
+		pivot.height = Math.max(root.height, pivotLeft != null ? pivotLeft.height : 0) + 1;
+	}
+
+	private EntrySet entrySet;
+	private KeySet keySet;
+
+	@Override
+	public Set<Entry<K, V>> entrySet() {
+		EntrySet result = entrySet;
+		return result != null ? result : (entrySet = new EntrySet());
+	}
+
+	@Override
+	public Set<K> keySet() {
+		KeySet result = keySet;
+		return result != null ? result : (keySet = new KeySet());
+	}
+
+	static final class Node<K, V> implements Entry<K, V> {
+		Node<K, V> parent;
+		Node<K, V> left;
+		Node<K, V> right;
+		Node<K, V> next;
+		Node<K, V> prev;
+		final K key;
+		final int hash;
+		V value;
+		int height;
+
+		/** Create the header entry */
+		Node() {
+			key = null;
+			hash = -1;
+			next = prev = this;
+		}
+
+		/** Create a regular entry */
+		Node(Node<K, V> parent, K key, int hash, Node<K, V> next, Node<K, V> prev) {
+			this.parent = parent;
+			this.key = key;
+			this.hash = hash;
+			this.height = 1;
+			this.next = next;
+			this.prev = prev;
+			prev.next = this;
+			next.prev = this;
+		}
+
+		public K getKey() {
+			return key;
+		}
+
+		public V getValue() {
+			return value;
+		}
+
+		public V setValue(V value) {
+			V oldValue = this.value;
+			this.value = value;
+			return oldValue;
+		}
+
+		@SuppressWarnings("rawtypes")
+		@Override
+		public boolean equals(Object o) {
+			if (o instanceof Entry) {
+				Entry other = (Entry) o;
+				return (key == null ? other.getKey() == null : key.equals(other.getKey())) && (value == null ? other.getValue() == null : value.equals(other.getValue()));
+			}
+			return false;
+		}
+
+		@Override
+		public int hashCode() {
+			return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
+		}
+
+		@Override
+		public String toString() {
+			return key + "=" + value;
+		}
+
+		/**
+		 * Returns the first node in this subtree.
+		 */
+		public Node<K, V> first() {
+			Node<K, V> node = this;
+			Node<K, V> child = node.left;
+			while (child != null) {
+				node = child;
+				child = node.left;
+			}
+			return node;
+		}
+
+		/**
+		 * Returns the last node in this subtree.
+		 */
+		public Node<K, V> last() {
+			Node<K, V> node = this;
+			Node<K, V> child = node.right;
+			while (child != null) {
+				node = child;
+				child = node.right;
+			}
+			return node;
+		}
+	}
+
+	private void doubleCapacity() {
+		table = doubleCapacity(table);
+		threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity
+	}
+
+	/**
+	 * Returns a new array containing the same nodes as {@code oldTable}, but with
+	 * twice as many trees, each of (approximately) half the previous size.
+	 */
+	static <K, V> Node<K, V>[] doubleCapacity(Node<K, V>[] oldTable) {
+		// TODO: don't do anything if we're already at MAX_CAPACITY
+		int oldCapacity = oldTable.length;
+		@SuppressWarnings("unchecked") // Arrays and generics don't get along.
+		Node<K, V>[] newTable = new Node[oldCapacity * 2];
+		AvlIterator<K, V> iterator = new AvlIterator<K, V>();
+		AvlBuilder<K, V> leftBuilder = new AvlBuilder<K, V>();
+		AvlBuilder<K, V> rightBuilder = new AvlBuilder<K, V>();
+
+		// Split each tree into two trees.
+		for (int i = 0; i < oldCapacity; i++) {
+			Node<K, V> root = oldTable[i];
+			if (root == null) {
+				continue;
+			}
+
+			// Compute the sizes of the left and right trees.
+			iterator.reset(root);
+			int leftSize = 0;
+			int rightSize = 0;
+			for (Node<K, V> node; (node = iterator.next()) != null;) {
+				if ((node.hash & oldCapacity) == 0) {
+					leftSize++;
+				} else {
+					rightSize++;
+				}
+			}
+
+			// Split the tree into two.
+			leftBuilder.reset(leftSize);
+			rightBuilder.reset(rightSize);
+			iterator.reset(root);
+			for (Node<K, V> node; (node = iterator.next()) != null;) {
+				if ((node.hash & oldCapacity) == 0) {
+					leftBuilder.add(node);
+				} else {
+					rightBuilder.add(node);
+				}
+			}
+
+			// Populate the enlarged array with these new roots.
+			newTable[i] = leftSize > 0 ? leftBuilder.root() : null;
+			newTable[i + oldCapacity] = rightSize > 0 ? rightBuilder.root() : null;
+		}
+		return newTable;
+	}
+
+	/**
+	 * Walks an AVL tree in iteration order. Once a node has been returned, its
+	 * left, right and parent links are <strong>no longer used</strong>. For this
+	 * reason it is safe to transform these links as you walk a tree.
+	 *
+	 * <p>
+	 * <strong>Warning:</strong> this iterator is destructive. It clears the parent
+	 * node of all nodes in the tree. It is an error to make a partial iteration of
+	 * a tree.
+	 */
+	static class AvlIterator<K, V> {
+		/** This stack is a singly linked list, linked by the 'parent' field. */
+		private Node<K, V> stackTop;
+
+		void reset(Node<K, V> root) {
+			Node<K, V> stackTop = null;
+			for (Node<K, V> n = root; n != null; n = n.left) {
+				n.parent = stackTop;
+				stackTop = n; // Stack push.
+			}
+			this.stackTop = stackTop;
+		}
+
+		public Node<K, V> next() {
+			Node<K, V> stackTop = this.stackTop;
+			if (stackTop == null) {
+				return null;
+			}
+			Node<K, V> result = stackTop;
+			stackTop = result.parent;
+			result.parent = null;
+			for (Node<K, V> n = result.right; n != null; n = n.left) {
+				n.parent = stackTop;
+				stackTop = n; // Stack push.
+			}
+			this.stackTop = stackTop;
+			return result;
+		}
+	}
+
+	/**
+	 * Builds AVL trees of a predetermined size by accepting nodes of increasing
+	 * value. To use:
+	 * <ol>
+	 * <li>Call {@link #reset} to initialize the target size <i>size</i>.
+	 * <li>Call {@link #add} <i>size</i> times with increasing values.
+	 * <li>Call {@link #root} to get the root of the balanced tree.
+	 * </ol>
+	 *
+	 * <p>
+	 * The returned tree will satisfy the AVL constraint: for every node <i>N</i>,
+	 * the height of <i>N.left</i> and <i>N.right</i> is different by at most 1. It
+	 * accomplishes this by omitting deepest-level leaf nodes when building trees
+	 * whose size isn't a power of 2 minus 1.
+	 *
+	 * <p>
+	 * Unlike rebuilding a tree from scratch, this approach requires no value
+	 * comparisons. Using this class to create a tree of size <i>S</i> is
+	 * {@code O(S)}.
+	 */
+	final static class AvlBuilder<K, V> {
+		/** This stack is a singly linked list, linked by the 'parent' field. */
+		private Node<K, V> stack;
+		private int leavesToSkip;
+		private int leavesSkipped;
+		private int size;
+
+		void reset(int targetSize) {
+			// compute the target tree size. This is a power of 2 minus one, like 15 or 31.
+			int treeCapacity = Integer.highestOneBit(targetSize) * 2 - 1;
+			leavesToSkip = treeCapacity - targetSize;
+			size = 0;
+			leavesSkipped = 0;
+			stack = null;
+		}
+
+		void add(Node<K, V> node) {
+			node.left = node.parent = node.right = null;
+			node.height = 1;
+
+			// Skip a leaf if necessary.
+			if (leavesToSkip > 0 && (size & 1) == 0) {
+				size++;
+				leavesToSkip--;
+				leavesSkipped++;
+			}
+
+			node.parent = stack;
+			stack = node; // Stack push.
+			size++;
+
+			// Skip a leaf if necessary.
+			if (leavesToSkip > 0 && (size & 1) == 0) {
+				size++;
+				leavesToSkip--;
+				leavesSkipped++;
+			}
+
+			/*
+			 * Combine 3 nodes into subtrees whenever the size is one less than a multiple
+			 * of 4. For example we combine the nodes A, B, C into a 3-element tree with B
+			 * as the root.
+			 *
+			 * Combine two subtrees and a spare single value whenever the size is one less
+			 * than a multiple of 8. For example at 8 we may combine subtrees (A B C) and (E
+			 * F G) with D as the root to form ((A B C) D (E F G)).
+			 *
+			 * Just as we combine single nodes when size nears a multiple of 4, and
+			 * 3-element trees when size nears a multiple of 8, we combine subtrees of size
+			 * (N-1) whenever the total size is 2N-1 whenever N is a power of 2.
+			 */
+			for (int scale = 4; (size & scale - 1) == scale - 1; scale *= 2) {
+				if (leavesSkipped == 0) {
+					// Pop right, center and left, then make center the top of the stack.
+					Node<K, V> right = stack;
+					Node<K, V> center = right.parent;
+					Node<K, V> left = center.parent;
+					center.parent = left.parent;
+					stack = center;
+					// Construct a tree.
+					center.left = left;
+					center.right = right;
+					center.height = right.height + 1;
+					left.parent = center;
+					right.parent = center;
+				} else if (leavesSkipped == 1) {
+					// Pop right and center, then make center the top of the stack.
+					Node<K, V> right = stack;
+					Node<K, V> center = right.parent;
+					stack = center;
+					// Construct a tree with no left child.
+					center.right = right;
+					center.height = right.height + 1;
+					right.parent = center;
+					leavesSkipped = 0;
+				} else if (leavesSkipped == 2) {
+					leavesSkipped = 0;
+				}
+			}
+		}
+
+		Node<K, V> root() {
+			Node<K, V> stackTop = this.stack;
+			if (stackTop.parent != null) {
+				throw new IllegalStateException();
+			}
+			return stackTop;
+		}
+	}
+
+	private abstract class LinkedTreeMapIterator<T> implements Iterator<T> {
+		Node<K, V> next = header.next;
+		Node<K, V> lastReturned = null;
+		int expectedModCount = modCount;
+
+		LinkedTreeMapIterator() {
+		}
+
+		public final boolean hasNext() {
+			return next != header;
+		}
+
+		final Node<K, V> nextNode() {
+			Node<K, V> e = next;
+			if (e == header) {
+				throw new NoSuchElementException();
+			}
+			if (modCount != expectedModCount) {
+				throw new ConcurrentModificationException();
+			}
+			next = e.next;
+			return lastReturned = e;
+		}
+
+		public final void remove() {
+			if (lastReturned == null) {
+				throw new IllegalStateException();
+			}
+			removeInternal(lastReturned, true);
+			lastReturned = null;
+			expectedModCount = modCount;
+		}
+	}
+
+	final class EntrySet extends AbstractSet<Entry<K, V>> {
+		@Override
+		public int size() {
+			return size;
+		}
+
+		@Override
+		public Iterator<Entry<K, V>> iterator() {
+			return new LinkedTreeMapIterator<Entry<K, V>>() {
+				public Entry<K, V> next() {
+					return nextNode();
+				}
+			};
+		}
+
+		@Override
+		public boolean contains(Object o) {
+			return o instanceof Entry && findByEntry((Entry<?, ?>) o) != null;
+		}
+
+		@Override
+		public boolean remove(Object o) {
+			if (!(o instanceof Entry)) {
+				return false;
+			}
+
+			Node<K, V> node = findByEntry((Entry<?, ?>) o);
+			if (node == null) {
+				return false;
+			}
+			removeInternal(node, true);
+			return true;
+		}
+
+		@Override
+		public void clear() {
+			LinkedHashTreeMap.this.clear();
+		}
+	}
+
+	final class KeySet extends AbstractSet<K> {
+		@Override
+		public int size() {
+			return size;
+		}
+
+		@Override
+		public Iterator<K> iterator() {
+			return new LinkedTreeMapIterator<K>() {
+				public K next() {
+					return nextNode().key;
+				}
+			};
+		}
+
+		@Override
+		public boolean contains(Object o) {
+			return containsKey(o);
+		}
+
+		@Override
+		public boolean remove(Object key) {
+			return removeInternalByKey(key) != null;
+		}
+
+		@Override
+		public void clear() {
+			LinkedHashTreeMap.this.clear();
+		}
+	}
+
+	/**
+	 * If somebody is unlucky enough to have to serialize one of these, serialize it
+	 * as a LinkedHashMap so that they won't need Gson on the other side to
+	 * deserialize it. Using serialization defeats our DoS defence, so most apps
+	 * shouldn't use it.
+	 */
+	private Object writeReplace() throws ObjectStreamException {
+		return new LinkedHashMap<K, V>(this);
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LinkedTreeMap.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LinkedTreeMap.java
new file mode 100644
index 0000000..de72fc1
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/LinkedTreeMap.java
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2012 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
+ * insertion order for iteration order. Comparison order is only used as an
+ * optimization for efficient insertion and removal.
+ *
+ * <p>
+ * This implementation was derived from Android 4.1's TreeMap class.
+ */
+public final class LinkedTreeMap<K, V> extends AbstractMap<K, V> implements Serializable {
+	@SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable<Comparable<Comparable<...>>>
+	private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() {
+		public int compare(Comparable a, Comparable b) {
+			return a.compareTo(b);
+		}
+	};
+
+	Comparator<? super K> comparator;
+	Node<K, V> root;
+	int size = 0;
+	int modCount = 0;
+
+	// Used to preserve iteration order
+	final Node<K, V> header = new Node<K, V>();
+
+	/**
+	 * Create a natural order, empty tree map whose keys must be mutually comparable
+	 * and non-null.
+	 */
+	@SuppressWarnings("unchecked") // unsafe! this assumes K is comparable
+	public LinkedTreeMap() {
+		this((Comparator<? super K>) NATURAL_ORDER);
+	}
+
+	/**
+	 * Create a tree map ordered by {@code comparator}. This map's keys may only be
+	 * null if {@code comparator} permits.
+	 *
+	 * @param comparator
+	 *            the comparator to order elements with, or {@code null} to use the
+	 *            natural ordering.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable
+	public LinkedTreeMap(Comparator<? super K> comparator) {
+		this.comparator = comparator != null ? comparator : (Comparator) NATURAL_ORDER;
+	}
+
+	@Override
+	public int size() {
+		return size;
+	}
+
+	@Override
+	public V get(Object key) {
+		Node<K, V> node = findByObject(key);
+		return node != null ? node.value : null;
+	}
+
+	@Override
+	public boolean containsKey(Object key) {
+		return findByObject(key) != null;
+	}
+
+	@Override
+	public V put(K key, V value) {
+		if (key == null) {
+			throw new NullPointerException("key == null");
+		}
+		Node<K, V> created = find(key, true);
+		V result = created.value;
+		created.value = value;
+		return result;
+	}
+
+	@Override
+	public void clear() {
+		root = null;
+		size = 0;
+		modCount++;
+
+		// Clear iteration order
+		Node<K, V> header = this.header;
+		header.next = header.prev = header;
+	}
+
+	@Override
+	public V remove(Object key) {
+		Node<K, V> node = removeInternalByKey(key);
+		return node != null ? node.value : null;
+	}
+
+	/**
+	 * Returns the node at or adjacent to the given key, creating it if requested.
+	 *
+	 * @throws ClassCastException
+	 *             if {@code key} and the tree's keys aren't mutually comparable.
+	 */
+	Node<K, V> find(K key, boolean create) {
+		Comparator<? super K> comparator = this.comparator;
+		Node<K, V> nearest = root;
+		int comparison = 0;
+
+		if (nearest != null) {
+			// Micro-optimization: avoid polymorphic calls to Comparator.compare().
+			@SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble.
+			Comparable<Object> comparableKey = (comparator == NATURAL_ORDER) ? (Comparable<Object>) key : null;
+
+			while (true) {
+				comparison = (comparableKey != null) ? comparableKey.compareTo(nearest.key) : comparator.compare(key, nearest.key);
+
+				// We found the requested key.
+				if (comparison == 0) {
+					return nearest;
+				}
+
+				// If it exists, the key is in a subtree. Go deeper.
+				Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right;
+				if (child == null) {
+					break;
+				}
+
+				nearest = child;
+			}
+		}
+
+		// The key doesn't exist in this tree.
+		if (!create) {
+			return null;
+		}
+
+		// Create the node and add it to the tree or the table.
+		Node<K, V> header = this.header;
+		Node<K, V> created;
+		if (nearest == null) {
+			// Check that the value is comparable if we didn't do any comparisons.
+			if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
+				throw new ClassCastException(key.getClass().getName() + " is not Comparable");
+			}
+			created = new Node<K, V>(nearest, key, header, header.prev);
+			root = created;
+		} else {
+			created = new Node<K, V>(nearest, key, header, header.prev);
+			if (comparison < 0) { // nearest.key is higher
+				nearest.left = created;
+			} else { // comparison > 0, nearest.key is lower
+				nearest.right = created;
+			}
+			rebalance(nearest, true);
+		}
+		size++;
+		modCount++;
+
+		return created;
+	}
+
+	@SuppressWarnings("unchecked")
+	Node<K, V> findByObject(Object key) {
+		try {
+			return key != null ? find((K) key, false) : null;
+		} catch (ClassCastException e) {
+			return null;
+		}
+	}
+
+	/**
+	 * Returns this map's entry that has the same key and value as {@code
+	 * entry}, or null if this map has no such entry.
+	 *
+	 * <p>
+	 * This method uses the comparator for key equality rather than {@code
+	 * equals}. If this map's comparator isn't consistent with equals (such as
+	 * {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code
+	 * contains()} will violate the collections API.
+	 */
+	Node<K, V> findByEntry(Entry<?, ?> entry) {
+		Node<K, V> mine = findByObject(entry.getKey());
+		boolean valuesEqual = mine != null && equal(mine.value, entry.getValue());
+		return valuesEqual ? mine : null;
+	}
+
+	private boolean equal(Object a, Object b) {
+		return a == b || (a != null && a.equals(b));
+	}
+
+	/**
+	 * Removes {@code node} from this tree, rearranging the tree's structure as
+	 * necessary.
+	 *
+	 * @param unlink
+	 *            true to also unlink this node from the iteration linked list.
+	 */
+	void removeInternal(Node<K, V> node, boolean unlink) {
+		if (unlink) {
+			node.prev.next = node.next;
+			node.next.prev = node.prev;
+		}
+
+		Node<K, V> left = node.left;
+		Node<K, V> right = node.right;
+		Node<K, V> originalParent = node.parent;
+		if (left != null && right != null) {
+
+			/*
+			 * To remove a node with both left and right subtrees, move an adjacent node
+			 * from one of those subtrees into this node's place.
+			 *
+			 * Removing the adjacent node may change this node's subtrees. This node may no
+			 * longer have two subtrees once the adjacent node is gone!
+			 */
+
+			Node<K, V> adjacent = (left.height > right.height) ? left.last() : right.first();
+			removeInternal(adjacent, false); // takes care of rebalance and size--
+
+			int leftHeight = 0;
+			left = node.left;
+			if (left != null) {
+				leftHeight = left.height;
+				adjacent.left = left;
+				left.parent = adjacent;
+				node.left = null;
+			}
+
+			int rightHeight = 0;
+			right = node.right;
+			if (right != null) {
+				rightHeight = right.height;
+				adjacent.right = right;
+				right.parent = adjacent;
+				node.right = null;
+			}
+
+			adjacent.height = Math.max(leftHeight, rightHeight) + 1;
+			replaceInParent(node, adjacent);
+			return;
+		} else if (left != null) {
+			replaceInParent(node, left);
+			node.left = null;
+		} else if (right != null) {
+			replaceInParent(node, right);
+			node.right = null;
+		} else {
+			replaceInParent(node, null);
+		}
+
+		rebalance(originalParent, false);
+		size--;
+		modCount++;
+	}
+
+	Node<K, V> removeInternalByKey(Object key) {
+		Node<K, V> node = findByObject(key);
+		if (node != null) {
+			removeInternal(node, true);
+		}
+		return node;
+	}
+
+	private void replaceInParent(Node<K, V> node, Node<K, V> replacement) {
+		Node<K, V> parent = node.parent;
+		node.parent = null;
+		if (replacement != null) {
+			replacement.parent = parent;
+		}
+
+		if (parent != null) {
+			if (parent.left == node) {
+				parent.left = replacement;
+			} else {
+				assert (parent.right == node);
+				parent.right = replacement;
+			}
+		} else {
+			root = replacement;
+		}
+	}
+
+	/**
+	 * Rebalances the tree by making any AVL rotations necessary between the
+	 * newly-unbalanced node and the tree's root.
+	 *
+	 * @param insert
+	 *            true if the node was unbalanced by an insert; false if it was by a
+	 *            removal.
+	 */
+	private void rebalance(Node<K, V> unbalanced, boolean insert) {
+		for (Node<K, V> node = unbalanced; node != null; node = node.parent) {
+			Node<K, V> left = node.left;
+			Node<K, V> right = node.right;
+			int leftHeight = left != null ? left.height : 0;
+			int rightHeight = right != null ? right.height : 0;
+
+			int delta = leftHeight - rightHeight;
+			if (delta == -2) {
+				Node<K, V> rightLeft = right.left;
+				Node<K, V> rightRight = right.right;
+				int rightRightHeight = rightRight != null ? rightRight.height : 0;
+				int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;
+
+				int rightDelta = rightLeftHeight - rightRightHeight;
+				if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
+					rotateLeft(node); // AVL right right
+				} else {
+					assert (rightDelta == 1);
+					rotateRight(right); // AVL right left
+					rotateLeft(node);
+				}
+				if (insert) {
+					break; // no further rotations will be necessary
+				}
+
+			} else if (delta == 2) {
+				Node<K, V> leftLeft = left.left;
+				Node<K, V> leftRight = left.right;
+				int leftRightHeight = leftRight != null ? leftRight.height : 0;
+				int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;
+
+				int leftDelta = leftLeftHeight - leftRightHeight;
+				if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
+					rotateRight(node); // AVL left left
+				} else {
+					assert (leftDelta == -1);
+					rotateLeft(left); // AVL left right
+					rotateRight(node);
+				}
+				if (insert) {
+					break; // no further rotations will be necessary
+				}
+
+			} else if (delta == 0) {
+				node.height = leftHeight + 1; // leftHeight == rightHeight
+				if (insert) {
+					break; // the insert caused balance, so rebalancing is done!
+				}
+
+			} else {
+				assert (delta == -1 || delta == 1);
+				node.height = Math.max(leftHeight, rightHeight) + 1;
+				if (!insert) {
+					break; // the height hasn't changed, so rebalancing is done!
+				}
+			}
+		}
+	}
+
+	/**
+	 * Rotates the subtree so that its root's right child is the new root.
+	 */
+	private void rotateLeft(Node<K, V> root) {
+		Node<K, V> left = root.left;
+		Node<K, V> pivot = root.right;
+		Node<K, V> pivotLeft = pivot.left;
+		Node<K, V> pivotRight = pivot.right;
+
+		// move the pivot's left child to the root's right
+		root.right = pivotLeft;
+		if (pivotLeft != null) {
+			pivotLeft.parent = root;
+		}
+
+		replaceInParent(root, pivot);
+
+		// move the root to the pivot's left
+		pivot.left = root;
+		root.parent = pivot;
+
+		// fix heights
+		root.height = Math.max(left != null ? left.height : 0, pivotLeft != null ? pivotLeft.height : 0) + 1;
+		pivot.height = Math.max(root.height, pivotRight != null ? pivotRight.height : 0) + 1;
+	}
+
+	/**
+	 * Rotates the subtree so that its root's left child is the new root.
+	 */
+	private void rotateRight(Node<K, V> root) {
+		Node<K, V> pivot = root.left;
+		Node<K, V> right = root.right;
+		Node<K, V> pivotLeft = pivot.left;
+		Node<K, V> pivotRight = pivot.right;
+
+		// move the pivot's right child to the root's left
+		root.left = pivotRight;
+		if (pivotRight != null) {
+			pivotRight.parent = root;
+		}
+
+		replaceInParent(root, pivot);
+
+		// move the root to the pivot's right
+		pivot.right = root;
+		root.parent = pivot;
+
+		// fixup heights
+		root.height = Math.max(right != null ? right.height : 0, pivotRight != null ? pivotRight.height : 0) + 1;
+		pivot.height = Math.max(root.height, pivotLeft != null ? pivotLeft.height : 0) + 1;
+	}
+
+	private EntrySet entrySet;
+	private KeySet keySet;
+
+	@Override
+	public Set<Entry<K, V>> entrySet() {
+		EntrySet result = entrySet;
+		return result != null ? result : (entrySet = new EntrySet());
+	}
+
+	@Override
+	public Set<K> keySet() {
+		KeySet result = keySet;
+		return result != null ? result : (keySet = new KeySet());
+	}
+
+	static final class Node<K, V> implements Entry<K, V> {
+		Node<K, V> parent;
+		Node<K, V> left;
+		Node<K, V> right;
+		Node<K, V> next;
+		Node<K, V> prev;
+		final K key;
+		V value;
+		int height;
+
+		/** Create the header entry */
+		Node() {
+			key = null;
+			next = prev = this;
+		}
+
+		/** Create a regular entry */
+		Node(Node<K, V> parent, K key, Node<K, V> next, Node<K, V> prev) {
+			this.parent = parent;
+			this.key = key;
+			this.height = 1;
+			this.next = next;
+			this.prev = prev;
+			prev.next = this;
+			next.prev = this;
+		}
+
+		public K getKey() {
+			return key;
+		}
+
+		public V getValue() {
+			return value;
+		}
+
+		public V setValue(V value) {
+			V oldValue = this.value;
+			this.value = value;
+			return oldValue;
+		}
+
+		@SuppressWarnings("rawtypes")
+		@Override
+		public boolean equals(Object o) {
+			if (o instanceof Entry) {
+				Entry other = (Entry) o;
+				return (key == null ? other.getKey() == null : key.equals(other.getKey())) && (value == null ? other.getValue() == null : value.equals(other.getValue()));
+			}
+			return false;
+		}
+
+		@Override
+		public int hashCode() {
+			return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
+		}
+
+		@Override
+		public String toString() {
+			return key + "=" + value;
+		}
+
+		/**
+		 * Returns the first node in this subtree.
+		 */
+		public Node<K, V> first() {
+			Node<K, V> node = this;
+			Node<K, V> child = node.left;
+			while (child != null) {
+				node = child;
+				child = node.left;
+			}
+			return node;
+		}
+
+		/**
+		 * Returns the last node in this subtree.
+		 */
+		public Node<K, V> last() {
+			Node<K, V> node = this;
+			Node<K, V> child = node.right;
+			while (child != null) {
+				node = child;
+				child = node.right;
+			}
+			return node;
+		}
+	}
+
+	private abstract class LinkedTreeMapIterator<T> implements Iterator<T> {
+		Node<K, V> next = header.next;
+		Node<K, V> lastReturned = null;
+		int expectedModCount = modCount;
+
+		LinkedTreeMapIterator() {
+		}
+
+		public final boolean hasNext() {
+			return next != header;
+		}
+
+		final Node<K, V> nextNode() {
+			Node<K, V> e = next;
+			if (e == header) {
+				throw new NoSuchElementException();
+			}
+			if (modCount != expectedModCount) {
+				throw new ConcurrentModificationException();
+			}
+			next = e.next;
+			return lastReturned = e;
+		}
+
+		public final void remove() {
+			if (lastReturned == null) {
+				throw new IllegalStateException();
+			}
+			removeInternal(lastReturned, true);
+			lastReturned = null;
+			expectedModCount = modCount;
+		}
+	}
+
+	class EntrySet extends AbstractSet<Entry<K, V>> {
+		@Override
+		public int size() {
+			return size;
+		}
+
+		@Override
+		public Iterator<Entry<K, V>> iterator() {
+			return new LinkedTreeMapIterator<Entry<K, V>>() {
+				public Entry<K, V> next() {
+					return nextNode();
+				}
+			};
+		}
+
+		@Override
+		public boolean contains(Object o) {
+			return o instanceof Entry && findByEntry((Entry<?, ?>) o) != null;
+		}
+
+		@Override
+		public boolean remove(Object o) {
+			if (!(o instanceof Entry)) {
+				return false;
+			}
+
+			Node<K, V> node = findByEntry((Entry<?, ?>) o);
+			if (node == null) {
+				return false;
+			}
+			removeInternal(node, true);
+			return true;
+		}
+
+		@Override
+		public void clear() {
+			LinkedTreeMap.this.clear();
+		}
+	}
+
+	final class KeySet extends AbstractSet<K> {
+		@Override
+		public int size() {
+			return size;
+		}
+
+		@Override
+		public Iterator<K> iterator() {
+			return new LinkedTreeMapIterator<K>() {
+				public K next() {
+					return nextNode().key;
+				}
+			};
+		}
+
+		@Override
+		public boolean contains(Object o) {
+			return containsKey(o);
+		}
+
+		@Override
+		public boolean remove(Object key) {
+			return removeInternalByKey(key) != null;
+		}
+
+		@Override
+		public void clear() {
+			LinkedTreeMap.this.clear();
+		}
+	}
+
+	/**
+	 * If somebody is unlucky enough to have to serialize one of these, serialize it
+	 * as a LinkedHashMap so that they won't need Gson on the other side to
+	 * deserialize it. Using serialization defeats our DoS defence, so most apps
+	 * shouldn't use it.
+	 */
+	private Object writeReplace() throws ObjectStreamException {
+		return new LinkedHashMap<K, V>(this);
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/ObjectConstructor.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/ObjectConstructor.java
new file mode 100644
index 0000000..6bda7d0
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/ObjectConstructor.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+/**
+ * Defines a generic object construction factory. The purpose of this class is
+ * to construct a default instance of a class that can be used for object
+ * navigation while deserialization from its JSON representation.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface ObjectConstructor<T> {
+
+	/**
+	 * Returns a new instance.
+	 */
+	public T construct();
+}
\ No newline at end of file
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Primitives.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Primitives.java
new file mode 100644
index 0000000..73b9d2a
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Primitives.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import java.lang.reflect.Type;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Contains static utility methods pertaining to primitive types and their
+ * corresponding wrapper types.
+ *
+ * @author Kevin Bourrillion
+ */
+public final class Primitives {
+	private Primitives() {
+		throw new UnsupportedOperationException();
+	}
+
+	/** A map from primitive types to their corresponding wrapper types. */
+	private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE;
+
+	/** A map from wrapper types to their corresponding primitive types. */
+	private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPE;
+
+	// Sad that we can't use a BiMap. :(
+
+	static {
+		Map<Class<?>, Class<?>> primToWrap = new HashMap<Class<?>, Class<?>>(16);
+		Map<Class<?>, Class<?>> wrapToPrim = new HashMap<Class<?>, Class<?>>(16);
+
+		add(primToWrap, wrapToPrim, boolean.class, Boolean.class);
+		add(primToWrap, wrapToPrim, byte.class, Byte.class);
+		add(primToWrap, wrapToPrim, char.class, Character.class);
+		add(primToWrap, wrapToPrim, double.class, Double.class);
+		add(primToWrap, wrapToPrim, float.class, Float.class);
+		add(primToWrap, wrapToPrim, int.class, Integer.class);
+		add(primToWrap, wrapToPrim, long.class, Long.class);
+		add(primToWrap, wrapToPrim, short.class, Short.class);
+		add(primToWrap, wrapToPrim, void.class, Void.class);
+
+		PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap);
+		WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim);
+	}
+
+	private static void add(Map<Class<?>, Class<?>> forward, Map<Class<?>, Class<?>> backward, Class<?> key, Class<?> value) {
+		forward.put(key, value);
+		backward.put(value, key);
+	}
+
+	/**
+	 * Returns true if this type is a primitive.
+	 */
+	public static boolean isPrimitive(Type type) {
+		return PRIMITIVE_TO_WRAPPER_TYPE.containsKey(type);
+	}
+
+	/**
+	 * Returns {@code true} if {@code type} is one of the nine primitive-wrapper
+	 * types, such as {@link Integer}.
+	 *
+	 * @see Class#isPrimitive
+	 */
+	public static boolean isWrapperType(Type type) {
+		return WRAPPER_TO_PRIMITIVE_TYPE.containsKey($Gson$Preconditions.checkNotNull(type));
+	}
+
+	/**
+	 * Returns the corresponding wrapper type of {@code type} if it is a primitive
+	 * type; otherwise returns {@code type} itself. Idempotent.
+	 * 
+	 * <pre>
+	 *     wrap(int.class) == Integer.class
+	 *     wrap(Integer.class) == Integer.class
+	 *     wrap(String.class) == String.class
+	 * </pre>
+	 */
+	public static <T> Class<T> wrap(Class<T> type) {
+		// cast is safe: long.class and Long.class are both of type Class<Long>
+		@SuppressWarnings("unchecked")
+		Class<T> wrapped = (Class<T>) PRIMITIVE_TO_WRAPPER_TYPE.get($Gson$Preconditions.checkNotNull(type));
+		return (wrapped == null) ? type : wrapped;
+	}
+
+	/**
+	 * Returns the corresponding primitive type of {@code type} if it is a wrapper
+	 * type; otherwise returns {@code type} itself. Idempotent.
+	 * 
+	 * <pre>
+	 *     unwrap(Integer.class) == int.class
+	 *     unwrap(int.class) == int.class
+	 *     unwrap(String.class) == String.class
+	 * </pre>
+	 */
+	public static <T> Class<T> unwrap(Class<T> type) {
+		// cast is safe: long.class and Long.class are both of type Class<Long>
+		@SuppressWarnings("unchecked")
+		Class<T> unwrapped = (Class<T>) WRAPPER_TO_PRIMITIVE_TYPE.get($Gson$Preconditions.checkNotNull(type));
+		return (unwrapped == null) ? type : unwrapped;
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Streams.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Streams.java
new file mode 100644
index 0000000..49d9e3b
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/Streams.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Writer;
+
+import cn.emay.sdk.util.json.gson.JsonElement;
+import cn.emay.sdk.util.json.gson.JsonIOException;
+import cn.emay.sdk.util.json.gson.JsonNull;
+import cn.emay.sdk.util.json.gson.JsonParseException;
+import cn.emay.sdk.util.json.gson.JsonSyntaxException;
+import cn.emay.sdk.util.json.gson.internal.bind.TypeAdapters;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+import cn.emay.sdk.util.json.gson.stream.MalformedJsonException;
+
+/**
+ * Reads and writes GSON parse trees over streams.
+ */
+public final class Streams {
+	private Streams() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * Takes a reader in any state and returns the next value as a JsonElement.
+	 */
+	public static JsonElement parse(JsonReader reader) throws JsonParseException {
+		boolean isEmpty = true;
+		try {
+			reader.peek();
+			isEmpty = false;
+			return TypeAdapters.JSON_ELEMENT.read(reader);
+		} catch (EOFException e) {
+			/*
+			 * For compatibility with JSON 1.5 and earlier, we return a JsonNull for empty
+			 * documents instead of throwing.
+			 */
+			if (isEmpty) {
+				return JsonNull.INSTANCE;
+			}
+			// The stream ended prematurely so it is likely a syntax error.
+			throw new JsonSyntaxException(e);
+		} catch (MalformedJsonException e) {
+			throw new JsonSyntaxException(e);
+		} catch (IOException e) {
+			throw new JsonIOException(e);
+		} catch (NumberFormatException e) {
+			throw new JsonSyntaxException(e);
+		}
+	}
+
+	/**
+	 * Writes the JSON element to the writer, recursively.
+	 */
+	public static void write(JsonElement element, JsonWriter writer) throws IOException {
+		TypeAdapters.JSON_ELEMENT.write(writer, element);
+	}
+
+	@SuppressWarnings("resource")
+	public static Writer writerForAppendable(Appendable appendable) {
+		return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
+	}
+
+	/**
+	 * Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer} is
+	 * used.
+	 */
+	private static final class AppendableWriter extends Writer {
+		private final Appendable appendable;
+		private final CurrentWrite currentWrite = new CurrentWrite();
+
+		AppendableWriter(Appendable appendable) {
+			this.appendable = appendable;
+		}
+
+		@Override
+		public void write(char[] chars, int offset, int length) throws IOException {
+			currentWrite.chars = chars;
+			appendable.append(currentWrite, offset, offset + length);
+		}
+
+		@Override
+		public void write(int i) throws IOException {
+			appendable.append((char) i);
+		}
+
+		@Override
+		public void flush() {
+		}
+
+		@Override
+		public void close() {
+		}
+
+		/**
+		 * A mutable char sequence pointing at a single char[].
+		 */
+		static class CurrentWrite implements CharSequence {
+			char[] chars;
+
+			public int length() {
+				return chars.length;
+			}
+
+			public char charAt(int i) {
+				return chars[i];
+			}
+
+			public CharSequence subSequence(int start, int end) {
+				return new String(chars, start, end - start);
+			}
+		}
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/UnsafeAllocator.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/UnsafeAllocator.java
new file mode 100644
index 0000000..7f1a33b
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/UnsafeAllocator.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * Do sneaky things to allocate objects without invoking their constructors.
+ *
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+public abstract class UnsafeAllocator {
+	public abstract <T> T newInstance(Class<T> c) throws Exception;
+
+	public static UnsafeAllocator create() {
+		// try JVM
+		// public class Unsafe {
+		// public Object allocateInstance(Class<?> type);
+		// }
+		try {
+			Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+			Field f = unsafeClass.getDeclaredField("theUnsafe");
+			f.setAccessible(true);
+			final Object unsafe = f.get(null);
+			final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
+			return new UnsafeAllocator() {
+				@Override
+				@SuppressWarnings("unchecked")
+				public <T> T newInstance(Class<T> c) throws Exception {
+					return (T) allocateInstance.invoke(unsafe, c);
+				}
+			};
+		} catch (Exception ignored) {
+		}
+
+		// try dalvikvm, post-gingerbread
+		// public class ObjectStreamClass {
+		// private static native int getConstructorId(Class<?> c);
+		// private static native Object newInstance(Class<?> instantiationClass, int
+		// methodId);
+		// }
+		try {
+			Method getConstructorId = ObjectStreamClass.class.getDeclaredMethod("getConstructorId", Class.class);
+			getConstructorId.setAccessible(true);
+			final int constructorId = (Integer) getConstructorId.invoke(null, Object.class);
+			final Method newInstance = ObjectStreamClass.class.getDeclaredMethod("newInstance", Class.class, int.class);
+			newInstance.setAccessible(true);
+			return new UnsafeAllocator() {
+				@Override
+				@SuppressWarnings("unchecked")
+				public <T> T newInstance(Class<T> c) throws Exception {
+					return (T) newInstance.invoke(null, c, constructorId);
+				}
+			};
+		} catch (Exception ignored) {
+		}
+
+		// try dalvikvm, pre-gingerbread
+		// public class ObjectInputStream {
+		// private static native Object newInstance(
+		// Class<?> instantiationClass, Class<?> constructorClass);
+		// }
+		try {
+			final Method newInstance = ObjectInputStream.class.getDeclaredMethod("newInstance", Class.class, Class.class);
+			newInstance.setAccessible(true);
+			return new UnsafeAllocator() {
+				@Override
+				@SuppressWarnings("unchecked")
+				public <T> T newInstance(Class<T> c) throws Exception {
+					return (T) newInstance.invoke(null, c, Object.class);
+				}
+			};
+		} catch (Exception ignored) {
+		}
+
+		// give up
+		return new UnsafeAllocator() {
+			@Override
+			public <T> T newInstance(Class<T> c) {
+				throw new UnsupportedOperationException("Cannot allocate " + c);
+			}
+		};
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ArrayTypeAdapter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ArrayTypeAdapter.java
new file mode 100644
index 0000000..7aaab89
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ArrayTypeAdapter.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.internal.$Gson$Types;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Adapt an array of objects.
+ */
+public final class ArrayTypeAdapter<E> extends TypeAdapter<Object> {
+	public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+		@SuppressWarnings({ "unchecked", "rawtypes" })
+		@Override
+		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+			Type type = typeToken.getType();
+			if (!(type instanceof GenericArrayType || type instanceof Class && ((Class<?>) type).isArray())) {
+				return null;
+			}
+
+			Type componentType = $Gson$Types.getArrayComponentType(type);
+			TypeAdapter<?> componentTypeAdapter = gson.getAdapter(TypeToken.get(componentType));
+			return new ArrayTypeAdapter(gson, componentTypeAdapter, $Gson$Types.getRawType(componentType));
+		}
+	};
+
+	private final Class<E> componentType;
+	private final TypeAdapter<E> componentTypeAdapter;
+
+	public ArrayTypeAdapter(Gson context, TypeAdapter<E> componentTypeAdapter, Class<E> componentType) {
+		this.componentTypeAdapter = new TypeAdapterRuntimeTypeWrapper<E>(context, componentTypeAdapter, componentType);
+		this.componentType = componentType;
+	}
+
+	@Override
+	public Object read(JsonReader in) throws IOException {
+		if (in.peek() == JsonToken.NULL) {
+			in.nextNull();
+			return null;
+		}
+
+		List<E> list = new ArrayList<E>();
+		in.beginArray();
+		while (in.hasNext()) {
+			E instance = componentTypeAdapter.read(in);
+			list.add(instance);
+		}
+		in.endArray();
+		Object array = Array.newInstance(componentType, list.size());
+		for (int i = 0; i < list.size(); i++) {
+			Array.set(array, i, list.get(i));
+		}
+		return array;
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public void write(JsonWriter out, Object array) throws IOException {
+		if (array == null) {
+			out.nullValue();
+			return;
+		}
+
+		out.beginArray();
+		for (int i = 0, length = Array.getLength(array); i < length; i++) {
+			E value = (E) Array.get(array, i);
+			componentTypeAdapter.write(out, value);
+		}
+		out.endArray();
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/CollectionTypeAdapterFactory.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/CollectionTypeAdapterFactory.java
new file mode 100644
index 0000000..b6f2499
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/CollectionTypeAdapterFactory.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.internal.$Gson$Types;
+import cn.emay.sdk.util.json.gson.internal.ConstructorConstructor;
+import cn.emay.sdk.util.json.gson.internal.ObjectConstructor;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Adapt a homogeneous collection of objects.
+ */
+public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
+	private final ConstructorConstructor constructorConstructor;
+
+	public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
+		this.constructorConstructor = constructorConstructor;
+	}
+
+	@Override
+	public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+		Type type = typeToken.getType();
+
+		Class<? super T> rawType = typeToken.getRawType();
+		if (!Collection.class.isAssignableFrom(rawType)) {
+			return null;
+		}
+
+		Type elementType = $Gson$Types.getCollectionElementType(type, rawType);
+		TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
+		ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
+
+		@SuppressWarnings({ "unchecked", "rawtypes" }) // create() doesn't define a type parameter
+		TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
+		return result;
+	}
+
+	private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
+		private final TypeAdapter<E> elementTypeAdapter;
+		private final ObjectConstructor<? extends Collection<E>> constructor;
+
+		public Adapter(Gson context, Type elementType, TypeAdapter<E> elementTypeAdapter, ObjectConstructor<? extends Collection<E>> constructor) {
+			this.elementTypeAdapter = new TypeAdapterRuntimeTypeWrapper<E>(context, elementTypeAdapter, elementType);
+			this.constructor = constructor;
+		}
+
+		@Override
+		public Collection<E> read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+
+			Collection<E> collection = constructor.construct();
+			in.beginArray();
+			while (in.hasNext()) {
+				E instance = elementTypeAdapter.read(in);
+				collection.add(instance);
+			}
+			in.endArray();
+			return collection;
+		}
+
+		@Override
+		public void write(JsonWriter out, Collection<E> collection) throws IOException {
+			if (collection == null) {
+				out.nullValue();
+				return;
+			}
+
+			out.beginArray();
+			for (E element : collection) {
+				elementTypeAdapter.write(out, element);
+			}
+			out.endArray();
+		}
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/DateTypeAdapter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/DateTypeAdapter.java
new file mode 100644
index 0000000..6ec5243
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/DateTypeAdapter.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Date;
+import java.util.Locale;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.JsonSyntaxException;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.internal.bind.util.ISO8601Utils;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Adapter for Date. Although this class appears stateless, it is not.
+ * DateFormat captures its time zone and locale when it is created, which gives
+ * this class state. DateFormat isn't thread safe either, so this class has to
+ * synchronize its read and write methods.
+ */
+public final class DateTypeAdapter extends TypeAdapter<Date> {
+	public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+		@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+		@Override
+		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+			return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
+		}
+	};
+
+	private final DateFormat enUsFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
+	private final DateFormat localFormat = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);
+
+	@Override
+	public Date read(JsonReader in) throws IOException {
+		if (in.peek() == JsonToken.NULL) {
+			in.nextNull();
+			return null;
+		}
+		return deserializeToDate(in.nextString());
+	}
+
+	private synchronized Date deserializeToDate(String json) {
+		try {
+			return localFormat.parse(json);
+		} catch (ParseException ignored) {
+		}
+		try {
+			return enUsFormat.parse(json);
+		} catch (ParseException ignored) {
+		}
+		try {
+			return ISO8601Utils.parse(json, new ParsePosition(0));
+		} catch (ParseException e) {
+			throw new JsonSyntaxException(json, e);
+		}
+	}
+
+	@Override
+	public synchronized void write(JsonWriter out, Date value) throws IOException {
+		if (value == null) {
+			out.nullValue();
+			return;
+		}
+		String dateFormatAsString = enUsFormat.format(value);
+		out.value(dateFormatAsString);
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
new file mode 100644
index 0000000..4de0730
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.annotations.JsonAdapter;
+import cn.emay.sdk.util.json.gson.internal.ConstructorConstructor;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+
+/**
+ * Given a type T, looks for the annotation {@link JsonAdapter} and uses an
+ * instance of the specified class as the default type adapter.
+ *
+ * @since 2.3
+ */
+public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
+
+	private final ConstructorConstructor constructorConstructor;
+
+	public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
+		this.constructorConstructor = constructorConstructor;
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
+		JsonAdapter annotation = targetType.getRawType().getAnnotation(JsonAdapter.class);
+		if (annotation == null) {
+			return null;
+		}
+		return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation);
+	}
+
+	@SuppressWarnings("unchecked") // Casts guarded by conditionals.
+	static TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson, TypeToken<?> fieldType, JsonAdapter annotation) {
+		Class<?> value = annotation.value();
+		TypeAdapter<?> typeAdapter;
+		if (TypeAdapter.class.isAssignableFrom(value)) {
+			Class<TypeAdapter<?>> typeAdapterClass = (Class<TypeAdapter<?>>) value;
+			typeAdapter = constructorConstructor.get(TypeToken.get(typeAdapterClass)).construct();
+		} else if (TypeAdapterFactory.class.isAssignableFrom(value)) {
+			Class<TypeAdapterFactory> typeAdapterFactory = (Class<TypeAdapterFactory>) value;
+			typeAdapter = constructorConstructor.get(TypeToken.get(typeAdapterFactory)).construct().create(gson, fieldType);
+		} else {
+			throw new IllegalArgumentException("@JsonAdapter value must be TypeAdapter or TypeAdapterFactory reference.");
+		}
+		if (typeAdapter != null) {
+			typeAdapter = typeAdapter.nullSafe();
+		}
+		return typeAdapter;
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonTreeReader.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonTreeReader.java
new file mode 100644
index 0000000..94f794d
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonTreeReader.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.json.gson.JsonArray;
+import cn.emay.sdk.util.json.gson.JsonElement;
+import cn.emay.sdk.util.json.gson.JsonNull;
+import cn.emay.sdk.util.json.gson.JsonObject;
+import cn.emay.sdk.util.json.gson.JsonPrimitive;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+
+/**
+ * This reader walks the elements of a JsonElement as if it was coming from a
+ * character stream.
+ *
+ * @author Jesse Wilson
+ */
+public final class JsonTreeReader extends JsonReader {
+	private static final Reader UNREADABLE_READER = new Reader() {
+		@Override
+		public int read(char[] buffer, int offset, int count) throws IOException {
+			throw new AssertionError();
+		}
+
+		@Override
+		public void close() throws IOException {
+			throw new AssertionError();
+		}
+	};
+	private static final Object SENTINEL_CLOSED = new Object();
+
+	private final List<Object> stack = new ArrayList<Object>();
+
+	public JsonTreeReader(JsonElement element) {
+		super(UNREADABLE_READER);
+		stack.add(element);
+	}
+
+	@Override
+	public void beginArray() throws IOException {
+		expect(JsonToken.BEGIN_ARRAY);
+		JsonArray array = (JsonArray) peekStack();
+		stack.add(array.iterator());
+	}
+
+	@Override
+	public void endArray() throws IOException {
+		expect(JsonToken.END_ARRAY);
+		popStack(); // empty iterator
+		popStack(); // array
+	}
+
+	@Override
+	public void beginObject() throws IOException {
+		expect(JsonToken.BEGIN_OBJECT);
+		JsonObject object = (JsonObject) peekStack();
+		stack.add(object.entrySet().iterator());
+	}
+
+	@Override
+	public void endObject() throws IOException {
+		expect(JsonToken.END_OBJECT);
+		popStack(); // empty iterator
+		popStack(); // object
+	}
+
+	@Override
+	public boolean hasNext() throws IOException {
+		JsonToken token = peek();
+		return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY;
+	}
+
+	@Override
+	public JsonToken peek() throws IOException {
+		if (stack.isEmpty()) {
+			return JsonToken.END_DOCUMENT;
+		}
+
+		Object o = peekStack();
+		if (o instanceof Iterator) {
+			boolean isObject = stack.get(stack.size() - 2) instanceof JsonObject;
+			Iterator<?> iterator = (Iterator<?>) o;
+			if (iterator.hasNext()) {
+				if (isObject) {
+					return JsonToken.NAME;
+				} else {
+					stack.add(iterator.next());
+					return peek();
+				}
+			} else {
+				return isObject ? JsonToken.END_OBJECT : JsonToken.END_ARRAY;
+			}
+		} else if (o instanceof JsonObject) {
+			return JsonToken.BEGIN_OBJECT;
+		} else if (o instanceof JsonArray) {
+			return JsonToken.BEGIN_ARRAY;
+		} else if (o instanceof JsonPrimitive) {
+			JsonPrimitive primitive = (JsonPrimitive) o;
+			if (primitive.isString()) {
+				return JsonToken.STRING;
+			} else if (primitive.isBoolean()) {
+				return JsonToken.BOOLEAN;
+			} else if (primitive.isNumber()) {
+				return JsonToken.NUMBER;
+			} else {
+				throw new AssertionError();
+			}
+		} else if (o instanceof JsonNull) {
+			return JsonToken.NULL;
+		} else if (o == SENTINEL_CLOSED) {
+			throw new IllegalStateException("JsonReader is closed");
+		} else {
+			throw new AssertionError();
+		}
+	}
+
+	private Object peekStack() {
+		return stack.get(stack.size() - 1);
+	}
+
+	private Object popStack() {
+		return stack.remove(stack.size() - 1);
+	}
+
+	private void expect(JsonToken expected) throws IOException {
+		if (peek() != expected) {
+			throw new IllegalStateException("Expected " + expected + " but was " + peek());
+		}
+	}
+
+	@Override
+	public String nextName() throws IOException {
+		expect(JsonToken.NAME);
+		Iterator<?> i = (Iterator<?>) peekStack();
+		Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
+		stack.add(entry.getValue());
+		return (String) entry.getKey();
+	}
+
+	@Override
+	public String nextString() throws IOException {
+		JsonToken token = peek();
+		if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
+			throw new IllegalStateException("Expected " + JsonToken.STRING + " but was " + token);
+		}
+		return ((JsonPrimitive) popStack()).getAsString();
+	}
+
+	@Override
+	public boolean nextBoolean() throws IOException {
+		expect(JsonToken.BOOLEAN);
+		return ((JsonPrimitive) popStack()).getAsBoolean();
+	}
+
+	@Override
+	public void nextNull() throws IOException {
+		expect(JsonToken.NULL);
+		popStack();
+	}
+
+	@Override
+	public double nextDouble() throws IOException {
+		JsonToken token = peek();
+		if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
+			throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
+		}
+		double result = ((JsonPrimitive) peekStack()).getAsDouble();
+		if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) {
+			throw new NumberFormatException("JSON forbids NaN and infinities: " + result);
+		}
+		popStack();
+		return result;
+	}
+
+	@Override
+	public long nextLong() throws IOException {
+		JsonToken token = peek();
+		if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
+			throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
+		}
+		long result = ((JsonPrimitive) peekStack()).getAsLong();
+		popStack();
+		return result;
+	}
+
+	@Override
+	public int nextInt() throws IOException {
+		JsonToken token = peek();
+		if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
+			throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
+		}
+		int result = ((JsonPrimitive) peekStack()).getAsInt();
+		popStack();
+		return result;
+	}
+
+	@Override
+	public void close() throws IOException {
+		stack.clear();
+		stack.add(SENTINEL_CLOSED);
+	}
+
+	@Override
+	public void skipValue() throws IOException {
+		if (peek() == JsonToken.NAME) {
+			nextName();
+		} else {
+			popStack();
+		}
+	}
+
+	@Override
+	public String toString() {
+		return getClass().getSimpleName();
+	}
+
+	public void promoteNameToValue() throws IOException {
+		expect(JsonToken.NAME);
+		Iterator<?> i = (Iterator<?>) peekStack();
+		Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
+		stack.add(entry.getValue());
+		stack.add(new JsonPrimitive((String) entry.getKey()));
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonTreeWriter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonTreeWriter.java
new file mode 100644
index 0000000..f00584e
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/JsonTreeWriter.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.emay.sdk.util.json.gson.JsonArray;
+import cn.emay.sdk.util.json.gson.JsonElement;
+import cn.emay.sdk.util.json.gson.JsonNull;
+import cn.emay.sdk.util.json.gson.JsonObject;
+import cn.emay.sdk.util.json.gson.JsonPrimitive;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * This writer creates a JsonElement.
+ */
+public final class JsonTreeWriter extends JsonWriter {
+	private static final Writer UNWRITABLE_WRITER = new Writer() {
+		@Override
+		public void write(char[] buffer, int offset, int counter) {
+			throw new AssertionError();
+		}
+
+		@Override
+		public void flush() throws IOException {
+			throw new AssertionError();
+		}
+
+		@Override
+		public void close() throws IOException {
+			throw new AssertionError();
+		}
+	};
+	/**
+	 * Added to the top of the stack when this writer is closed to cause following
+	 * ops to fail.
+	 */
+	private static final JsonPrimitive SENTINEL_CLOSED = new JsonPrimitive("closed");
+
+	/**
+	 * The JsonElements and JsonArrays under modification, outermost to innermost.
+	 */
+	private final List<JsonElement> stack = new ArrayList<JsonElement>();
+
+	/**
+	 * The name for the next JSON object value. If non-null, the top of the stack is
+	 * a JsonObject.
+	 */
+	private String pendingName;
+
+	/** the JSON element constructed by this writer. */
+	private JsonElement product = JsonNull.INSTANCE; // TODO: is this really what we want?;
+
+	public JsonTreeWriter() {
+		super(UNWRITABLE_WRITER);
+	}
+
+	/**
+	 * Returns the top level object produced by this writer.
+	 */
+	public JsonElement get() {
+		if (!stack.isEmpty()) {
+			throw new IllegalStateException("Expected one JSON element but was " + stack);
+		}
+		return product;
+	}
+
+	private JsonElement peek() {
+		return stack.get(stack.size() - 1);
+	}
+
+	private void put(JsonElement value) {
+		if (pendingName != null) {
+			if (!value.isJsonNull() || getSerializeNulls()) {
+				JsonObject object = (JsonObject) peek();
+				object.add(pendingName, value);
+			}
+			pendingName = null;
+		} else if (stack.isEmpty()) {
+			product = value;
+		} else {
+			JsonElement element = peek();
+			if (element instanceof JsonArray) {
+				((JsonArray) element).add(value);
+			} else {
+				throw new IllegalStateException();
+			}
+		}
+	}
+
+	@Override
+	public JsonWriter beginArray() throws IOException {
+		JsonArray array = new JsonArray();
+		put(array);
+		stack.add(array);
+		return this;
+	}
+
+	@Override
+	public JsonWriter endArray() throws IOException {
+		if (stack.isEmpty() || pendingName != null) {
+			throw new IllegalStateException();
+		}
+		JsonElement element = peek();
+		if (element instanceof JsonArray) {
+			stack.remove(stack.size() - 1);
+			return this;
+		}
+		throw new IllegalStateException();
+	}
+
+	@Override
+	public JsonWriter beginObject() throws IOException {
+		JsonObject object = new JsonObject();
+		put(object);
+		stack.add(object);
+		return this;
+	}
+
+	@Override
+	public JsonWriter endObject() throws IOException {
+		if (stack.isEmpty() || pendingName != null) {
+			throw new IllegalStateException();
+		}
+		JsonElement element = peek();
+		if (element instanceof JsonObject) {
+			stack.remove(stack.size() - 1);
+			return this;
+		}
+		throw new IllegalStateException();
+	}
+
+	@Override
+	public JsonWriter name(String name) throws IOException {
+		if (stack.isEmpty() || pendingName != null) {
+			throw new IllegalStateException();
+		}
+		JsonElement element = peek();
+		if (element instanceof JsonObject) {
+			pendingName = name;
+			return this;
+		}
+		throw new IllegalStateException();
+	}
+
+	@Override
+	public JsonWriter value(String value) throws IOException {
+		if (value == null) {
+			return nullValue();
+		}
+		put(new JsonPrimitive(value));
+		return this;
+	}
+
+	@Override
+	public JsonWriter nullValue() throws IOException {
+		put(JsonNull.INSTANCE);
+		return this;
+	}
+
+	@Override
+	public JsonWriter value(boolean value) throws IOException {
+		put(new JsonPrimitive(value));
+		return this;
+	}
+
+	@Override
+	public JsonWriter value(double value) throws IOException {
+		if (!isLenient() && (Double.isNaN(value) || Double.isInfinite(value))) {
+			throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
+		}
+		put(new JsonPrimitive(value));
+		return this;
+	}
+
+	@Override
+	public JsonWriter value(long value) throws IOException {
+		put(new JsonPrimitive(value));
+		return this;
+	}
+
+	@Override
+	public JsonWriter value(Number value) throws IOException {
+		if (value == null) {
+			return nullValue();
+		}
+
+		if (!isLenient()) {
+			double d = value.doubleValue();
+			if (Double.isNaN(d) || Double.isInfinite(d)) {
+				throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
+			}
+		}
+
+		put(new JsonPrimitive(value));
+		return this;
+	}
+
+	@Override
+	public void flush() throws IOException {
+	}
+
+	@Override
+	public void close() throws IOException {
+		if (!stack.isEmpty()) {
+			throw new IOException("Incomplete document");
+		}
+		stack.add(SENTINEL_CLOSED);
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/MapTypeAdapterFactory.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/MapTypeAdapterFactory.java
new file mode 100644
index 0000000..f270266
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/MapTypeAdapterFactory.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.JsonElement;
+import cn.emay.sdk.util.json.gson.JsonPrimitive;
+import cn.emay.sdk.util.json.gson.JsonSyntaxException;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.internal.$Gson$Types;
+import cn.emay.sdk.util.json.gson.internal.ConstructorConstructor;
+import cn.emay.sdk.util.json.gson.internal.JsonReaderInternalAccess;
+import cn.emay.sdk.util.json.gson.internal.ObjectConstructor;
+import cn.emay.sdk.util.json.gson.internal.Streams;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Adapts maps to either JSON objects or JSON arrays.
+ *
+ * <h3>Maps as JSON objects</h3> For primitive keys or when complex map key
+ * serialization is not enabled, this converts Java {@link Map Maps} to JSON
+ * Objects. This requires that map keys can be serialized as strings; this is
+ * insufficient for some key types. For example, consider a map whose keys are
+ * points on a grid. The default JSON form encodes reasonably:
+ * 
+ * <pre>
+ * {
+ * 	&#64;code
+ * 	Map<Point, String> original = new LinkedHashMap<Point, String>();
+ * 	original.put(new Point(5, 6), "a");
+ * 	original.put(new Point(8, 8), "b");
+ * 	System.out.println(gson.toJson(original, type));
+ * }
+ * </pre>
+ * 
+ * The above code prints this JSON object:
+ * 
+ * <pre>
+ *    {@code
+ *   {
+ *     "(5,6)": "a",
+ *     "(8,8)": "b"
+ *   }
+ * }
+ * </pre>
+ * 
+ * But GSON is unable to deserialize this value because the JSON string name is
+ * just the {@link Object#toString() toString()} of the map key. Attempting to
+ * convert the above JSON to an object fails with a parse exception:
+ * 
+ * <pre>
+ * com.google.gson.JsonParseException: Expecting object found: "(5,6)"
+ *   at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
+ *   at com.google.gson.ObjectNavigator.navigateClassFields
+ *   ...
+ * </pre>
+ *
+ * <h3>Maps as JSON arrays</h3> An alternative approach taken by this type
+ * adapter when it is required and complex map key serialization is enabled is
+ * to encode maps as arrays of map entries. Each map entry is a two element
+ * array containing a key and a value. This approach is more flexible because
+ * any type can be used as the map's key; not just strings. But it's also less
+ * portable because the receiver of such JSON must be aware of the map entry
+ * convention.
+ *
+ * <p>
+ * Register this adapter when you are creating your GSON instance.
+ * 
+ * <pre>
+ * {
+ * 	&#64;code
+ * 	Gson gson = new GsonBuilder().registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter()).create();
+ * }
+ * </pre>
+ * 
+ * This will change the structure of the JSON emitted by the code above. Now we
+ * get an array. In this case the arrays elements are map entries:
+ * 
+ * <pre>
+ *    {@code
+ *   [
+ *     [
+ *       {
+ *         "x": 5,
+ *         "y": 6
+ *       },
+ *       "a",
+ *     ],
+ *     [
+ *       {
+ *         "x": 8,
+ *         "y": 8
+ *       },
+ *       "b"
+ *     ]
+ *   ]
+ * }
+ * </pre>
+ * 
+ * This format will serialize and deserialize just fine as long as this adapter
+ * is registered.
+ */
+public final class MapTypeAdapterFactory implements TypeAdapterFactory {
+	private final ConstructorConstructor constructorConstructor;
+	final boolean complexMapKeySerialization;
+
+	public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor, boolean complexMapKeySerialization) {
+		this.constructorConstructor = constructorConstructor;
+		this.complexMapKeySerialization = complexMapKeySerialization;
+	}
+
+	@Override
+	public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+		Type type = typeToken.getType();
+
+		Class<? super T> rawType = typeToken.getRawType();
+		if (!Map.class.isAssignableFrom(rawType)) {
+			return null;
+		}
+
+		Class<?> rawTypeOfSrc = $Gson$Types.getRawType(type);
+		Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc);
+		TypeAdapter<?> keyAdapter = getKeyAdapter(gson, keyAndValueTypes[0]);
+		TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(keyAndValueTypes[1]));
+		ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
+
+		@SuppressWarnings({ "unchecked", "rawtypes" })
+		// we don't define a type parameter for the key or value types
+		TypeAdapter<T> result = new Adapter(gson, keyAndValueTypes[0], keyAdapter, keyAndValueTypes[1], valueAdapter, constructor);
+		return result;
+	}
+
+	/**
+	 * Returns a type adapter that writes the value as a string.
+	 */
+	private TypeAdapter<?> getKeyAdapter(Gson context, Type keyType) {
+		return (keyType == boolean.class || keyType == Boolean.class) ? TypeAdapters.BOOLEAN_AS_STRING : context.getAdapter(TypeToken.get(keyType));
+	}
+
+	private final class Adapter<K, V> extends TypeAdapter<Map<K, V>> {
+		private final TypeAdapter<K> keyTypeAdapter;
+		private final TypeAdapter<V> valueTypeAdapter;
+		private final ObjectConstructor<? extends Map<K, V>> constructor;
+
+		public Adapter(Gson context, Type keyType, TypeAdapter<K> keyTypeAdapter, Type valueType, TypeAdapter<V> valueTypeAdapter, ObjectConstructor<? extends Map<K, V>> constructor) {
+			this.keyTypeAdapter = new TypeAdapterRuntimeTypeWrapper<K>(context, keyTypeAdapter, keyType);
+			this.valueTypeAdapter = new TypeAdapterRuntimeTypeWrapper<V>(context, valueTypeAdapter, valueType);
+			this.constructor = constructor;
+		}
+
+		@Override
+		public Map<K, V> read(JsonReader in) throws IOException {
+			JsonToken peek = in.peek();
+			if (peek == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+
+			Map<K, V> map = constructor.construct();
+
+			if (peek == JsonToken.BEGIN_ARRAY) {
+				in.beginArray();
+				while (in.hasNext()) {
+					in.beginArray(); // entry array
+					K key = keyTypeAdapter.read(in);
+					V value = valueTypeAdapter.read(in);
+					V replaced = map.put(key, value);
+					if (replaced != null) {
+						throw new JsonSyntaxException("duplicate key: " + key);
+					}
+					in.endArray();
+				}
+				in.endArray();
+			} else {
+				in.beginObject();
+				while (in.hasNext()) {
+					JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
+					K key = keyTypeAdapter.read(in);
+					V value = valueTypeAdapter.read(in);
+					V replaced = map.put(key, value);
+					if (replaced != null) {
+						throw new JsonSyntaxException("duplicate key: " + key);
+					}
+				}
+				in.endObject();
+			}
+			return map;
+		}
+
+		@Override
+		public void write(JsonWriter out, Map<K, V> map) throws IOException {
+			if (map == null) {
+				out.nullValue();
+				return;
+			}
+
+			if (!complexMapKeySerialization) {
+				out.beginObject();
+				for (Map.Entry<K, V> entry : map.entrySet()) {
+					out.name(String.valueOf(entry.getKey()));
+					valueTypeAdapter.write(out, entry.getValue());
+				}
+				out.endObject();
+				return;
+			}
+
+			boolean hasComplexKeys = false;
+			List<JsonElement> keys = new ArrayList<JsonElement>(map.size());
+
+			List<V> values = new ArrayList<V>(map.size());
+			for (Map.Entry<K, V> entry : map.entrySet()) {
+				JsonElement keyElement = keyTypeAdapter.toJsonTree(entry.getKey());
+				keys.add(keyElement);
+				values.add(entry.getValue());
+				hasComplexKeys |= keyElement.isJsonArray() || keyElement.isJsonObject();
+			}
+
+			if (hasComplexKeys) {
+				out.beginArray();
+				for (int i = 0; i < keys.size(); i++) {
+					out.beginArray(); // entry array
+					Streams.write(keys.get(i), out);
+					valueTypeAdapter.write(out, values.get(i));
+					out.endArray();
+				}
+				out.endArray();
+			} else {
+				out.beginObject();
+				for (int i = 0; i < keys.size(); i++) {
+					JsonElement keyElement = keys.get(i);
+					out.name(keyToString(keyElement));
+					valueTypeAdapter.write(out, values.get(i));
+				}
+				out.endObject();
+			}
+		}
+
+		private String keyToString(JsonElement keyElement) {
+			if (keyElement.isJsonPrimitive()) {
+				JsonPrimitive primitive = keyElement.getAsJsonPrimitive();
+				if (primitive.isNumber()) {
+					return String.valueOf(primitive.getAsNumber());
+				} else if (primitive.isBoolean()) {
+					return Boolean.toString(primitive.getAsBoolean());
+				} else if (primitive.isString()) {
+					return primitive.getAsString();
+				} else {
+					throw new AssertionError();
+				}
+			} else if (keyElement.isJsonNull()) {
+				return "null";
+			} else {
+				throw new AssertionError();
+			}
+		}
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ObjectTypeAdapter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ObjectTypeAdapter.java
new file mode 100644
index 0000000..a8a3eaa
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ObjectTypeAdapter.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.internal.LinkedTreeMap;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Adapts types whose static type is only 'Object'. Uses getClass() on
+ * serialization and a primitive/Map/List on deserialization.
+ */
+public final class ObjectTypeAdapter extends TypeAdapter<Object> {
+	public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+		@SuppressWarnings("unchecked")
+		@Override
+		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+			if (type.getRawType() == Object.class) {
+				return (TypeAdapter<T>) new ObjectTypeAdapter(gson);
+			}
+			return null;
+		}
+	};
+
+	private final Gson gson;
+
+	ObjectTypeAdapter(Gson gson) {
+		this.gson = gson;
+	}
+
+	@Override
+	public Object read(JsonReader in) throws IOException {
+		JsonToken token = in.peek();
+		switch (token) {
+		case BEGIN_ARRAY:
+			List<Object> list = new ArrayList<Object>();
+			in.beginArray();
+			while (in.hasNext()) {
+				list.add(read(in));
+			}
+			in.endArray();
+			return list;
+
+		case BEGIN_OBJECT:
+			Map<String, Object> map = new LinkedTreeMap<String, Object>();
+			in.beginObject();
+			while (in.hasNext()) {
+				map.put(in.nextName(), read(in));
+			}
+			in.endObject();
+			return map;
+
+		case STRING:
+			return in.nextString();
+
+		case NUMBER:
+			return in.nextDouble();
+
+		case BOOLEAN:
+			return in.nextBoolean();
+
+		case NULL:
+			in.nextNull();
+			return null;
+
+		default:
+			throw new IllegalStateException();
+		}
+	}
+
+	@SuppressWarnings("unchecked")
+	@Override
+	public void write(JsonWriter out, Object value) throws IOException {
+		if (value == null) {
+			out.nullValue();
+			return;
+		}
+
+		TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
+		if (typeAdapter instanceof ObjectTypeAdapter) {
+			out.beginObject();
+			out.endObject();
+			return;
+		}
+
+		typeAdapter.write(out, value);
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ReflectiveTypeAdapterFactory.java
new file mode 100644
index 0000000..6867fde
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/ReflectiveTypeAdapterFactory.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import static cn.emay.sdk.util.json.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory.getTypeAdapter;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import cn.emay.sdk.util.json.gson.FieldNamingStrategy;
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.JsonSyntaxException;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.annotations.JsonAdapter;
+import cn.emay.sdk.util.json.gson.annotations.SerializedName;
+import cn.emay.sdk.util.json.gson.internal.$Gson$Types;
+import cn.emay.sdk.util.json.gson.internal.ConstructorConstructor;
+import cn.emay.sdk.util.json.gson.internal.Excluder;
+import cn.emay.sdk.util.json.gson.internal.ObjectConstructor;
+import cn.emay.sdk.util.json.gson.internal.Primitives;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Type adapter that reflects over the fields and methods of a class.
+ */
+public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
+	private final ConstructorConstructor constructorConstructor;
+	private final FieldNamingStrategy fieldNamingPolicy;
+	private final Excluder excluder;
+
+	public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor, FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
+		this.constructorConstructor = constructorConstructor;
+		this.fieldNamingPolicy = fieldNamingPolicy;
+		this.excluder = excluder;
+	}
+
+	public boolean excludeField(Field f, boolean serialize) {
+		return excludeField(f, serialize, excluder);
+	}
+
+	static boolean excludeField(Field f, boolean serialize, Excluder excluder) {
+		return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
+	}
+
+	/** first element holds the default name */
+	private List<String> getFieldNames(Field f) {
+		return getFieldName(fieldNamingPolicy, f);
+	}
+
+	/** first element holds the default name */
+	static List<String> getFieldName(FieldNamingStrategy fieldNamingPolicy, Field f) {
+		SerializedName serializedName = f.getAnnotation(SerializedName.class);
+		List<String> fieldNames = new LinkedList<String>();
+		if (serializedName == null) {
+			fieldNames.add(fieldNamingPolicy.translateName(f));
+		} else {
+			fieldNames.add(serializedName.value());
+			for (String alternate : serializedName.alternate()) {
+				fieldNames.add(alternate);
+			}
+		}
+		return fieldNames;
+	}
+
+	@Override
+	public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
+		Class<? super T> raw = type.getRawType();
+
+		if (!Object.class.isAssignableFrom(raw)) {
+			return null; // it's a primitive!
+		}
+
+		ObjectConstructor<T> constructor = constructorConstructor.get(type);
+		return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
+	}
+
+	private ReflectiveTypeAdapterFactory.BoundField createBoundField(final Gson context, final Field field, final String name, final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
+		final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
+		// special casing primitives here saves ~5% on Android...
+		return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
+			final TypeAdapter<?> typeAdapter = getFieldAdapter(context, field, fieldType);
+
+			@SuppressWarnings({ "unchecked", "rawtypes" }) // the type adapter and field type always agree
+			@Override
+			void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException {
+				Object fieldValue = field.get(value);
+				TypeAdapter t = new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType());
+				t.write(writer, fieldValue);
+			}
+
+			@Override
+			void read(JsonReader reader, Object value) throws IOException, IllegalAccessException {
+				Object fieldValue = typeAdapter.read(reader);
+				if (fieldValue != null || !isPrimitive) {
+					field.set(value, fieldValue);
+				}
+			}
+
+			@Override
+			public boolean writeField(Object value) throws IOException, IllegalAccessException {
+				if (!serialized)
+					return false;
+				Object fieldValue = field.get(value);
+				return fieldValue != value; // avoid recursion for example for Throwable.cause
+			}
+		};
+	}
+
+	TypeAdapter<?> getFieldAdapter(Gson gson, Field field, TypeToken<?> fieldType) {
+		JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
+		if (annotation != null) {
+			TypeAdapter<?> adapter = getTypeAdapter(constructorConstructor, gson, fieldType, annotation);
+			if (adapter != null)
+				return adapter;
+		}
+		return gson.getAdapter(fieldType);
+	}
+
+	private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
+		Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
+		if (raw.isInterface()) {
+			return result;
+		}
+
+		Type declaredType = type.getType();
+		while (raw != Object.class) {
+			Field[] fields = raw.getDeclaredFields();
+			for (Field field : fields) {
+				boolean serialize = excludeField(field, true);
+				boolean deserialize = excludeField(field, false);
+				if (!serialize && !deserialize) {
+					continue;
+				}
+				field.setAccessible(true);
+				Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
+				List<String> fieldNames = getFieldNames(field);
+				BoundField previous = null;
+				for (int i = 0; i < fieldNames.size(); ++i) {
+					String name = fieldNames.get(i);
+					if (i != 0)
+						serialize = false; // only serialize the default name
+					BoundField boundField = createBoundField(context, field, name, TypeToken.get(fieldType), serialize, deserialize);
+					BoundField replaced = result.put(name, boundField);
+					if (previous == null)
+						previous = replaced;
+				}
+				if (previous != null) {
+					throw new IllegalArgumentException(declaredType + " declares multiple JSON fields named " + previous.name);
+				}
+			}
+			type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
+			raw = type.getRawType();
+		}
+		return result;
+	}
+
+	static abstract class BoundField {
+		final String name;
+		final boolean serialized;
+		final boolean deserialized;
+
+		protected BoundField(String name, boolean serialized, boolean deserialized) {
+			this.name = name;
+			this.serialized = serialized;
+			this.deserialized = deserialized;
+		}
+
+		abstract boolean writeField(Object value) throws IOException, IllegalAccessException;
+
+		abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;
+
+		abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException;
+	}
+
+	public static final class Adapter<T> extends TypeAdapter<T> {
+		private final ObjectConstructor<T> constructor;
+		private final Map<String, BoundField> boundFields;
+
+		Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
+			this.constructor = constructor;
+			this.boundFields = boundFields;
+		}
+
+		@Override
+		public T read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+
+			T instance = constructor.construct();
+
+			try {
+				in.beginObject();
+				while (in.hasNext()) {
+					String name = in.nextName();
+					BoundField field = boundFields.get(name);
+					if (field == null || !field.deserialized) {
+						in.skipValue();
+					} else {
+						field.read(in, instance);
+					}
+				}
+			} catch (IllegalStateException e) {
+				throw new JsonSyntaxException(e);
+			} catch (IllegalAccessException e) {
+				throw new AssertionError(e);
+			}
+			in.endObject();
+			return instance;
+		}
+
+		@Override
+		public void write(JsonWriter out, T value) throws IOException {
+			if (value == null) {
+				out.nullValue();
+				return;
+			}
+
+			out.beginObject();
+			try {
+				for (BoundField boundField : boundFields.values()) {
+					if (boundField.writeField(value)) {
+						out.name(boundField.name);
+						boundField.write(out, value);
+					}
+				}
+			} catch (IllegalAccessException e) {
+				throw new AssertionError(e);
+			}
+			out.endObject();
+		}
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/SqlDateTypeAdapter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/SqlDateTypeAdapter.java
new file mode 100644
index 0000000..8645ac7
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/SqlDateTypeAdapter.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.JsonSyntaxException;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Adapter for java.sql.Date. Although this class appears stateless, it is not.
+ * DateFormat captures its time zone and locale when it is created, which gives
+ * this class state. DateFormat isn't thread safe either, so this class has to
+ * synchronize its read and write methods.
+ */
+public final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {
+	public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+		@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+		@Override
+		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+			return typeToken.getRawType() == java.sql.Date.class ? (TypeAdapter<T>) new SqlDateTypeAdapter() : null;
+		}
+	};
+
+	private final DateFormat format = new SimpleDateFormat("MMM d, yyyy");
+
+	@Override
+	public synchronized java.sql.Date read(JsonReader in) throws IOException {
+		if (in.peek() == JsonToken.NULL) {
+			in.nextNull();
+			return null;
+		}
+		try {
+			final long utilDate = format.parse(in.nextString()).getTime();
+			return new java.sql.Date(utilDate);
+		} catch (ParseException e) {
+			throw new JsonSyntaxException(e);
+		}
+	}
+
+	@Override
+	public synchronized void write(JsonWriter out, java.sql.Date value) throws IOException {
+		out.value(value == null ? null : format.format(value));
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TimeTypeAdapter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TimeTypeAdapter.java
new file mode 100644
index 0000000..25f47e7
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TimeTypeAdapter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.sql.Time;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.JsonSyntaxException;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Adapter for Time. Although this class appears stateless, it is not.
+ * DateFormat captures its time zone and locale when it is created, which gives
+ * this class state. DateFormat isn't thread safe either, so this class has to
+ * synchronize its read and write methods.
+ */
+public final class TimeTypeAdapter extends TypeAdapter<Time> {
+	public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+		@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+		@Override
+		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+			return typeToken.getRawType() == Time.class ? (TypeAdapter<T>) new TimeTypeAdapter() : null;
+		}
+	};
+
+	private final DateFormat format = new SimpleDateFormat("hh:mm:ss a");
+
+	@Override
+	public synchronized Time read(JsonReader in) throws IOException {
+		if (in.peek() == JsonToken.NULL) {
+			in.nextNull();
+			return null;
+		}
+		try {
+			Date date = format.parse(in.nextString());
+			return new Time(date.getTime());
+		} catch (ParseException e) {
+			throw new JsonSyntaxException(e);
+		}
+	}
+
+	@Override
+	public synchronized void write(JsonWriter out, Time value) throws IOException {
+		out.value(value == null ? null : format.format(value));
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java
new file mode 100644
index 0000000..b20ec8c
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
+	private final Gson context;
+	private final TypeAdapter<T> delegate;
+	private final Type type;
+
+	TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {
+		this.context = context;
+		this.delegate = delegate;
+		this.type = type;
+	}
+
+	@Override
+	public T read(JsonReader in) throws IOException {
+		return delegate.read(in);
+	}
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	@Override
+	public void write(JsonWriter out, T value) throws IOException {
+		// Order of preference for choosing type adapters
+		// First preference: a type adapter registered for the runtime type
+		// Second preference: a type adapter registered for the declared type
+		// Third preference: reflective type adapter for the runtime type (if it is a
+		// sub class of the declared type)
+		// Fourth preference: reflective type adapter for the declared type
+
+		TypeAdapter chosen = delegate;
+		Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);
+		if (runtimeType != type) {
+			TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));
+			if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
+				// The user registered a type adapter for the runtime type, so we will use that
+				chosen = runtimeTypeAdapter;
+			} else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {
+				// The user registered a type adapter for Base class, so we prefer it over the
+				// reflective type adapter for the runtime type
+				chosen = delegate;
+			} else {
+				// Use the type adapter for runtime type
+				chosen = runtimeTypeAdapter;
+			}
+		}
+		chosen.write(out, value);
+	}
+
+	/**
+	 * Finds a compatible runtime type if it is more specific
+	 */
+	private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
+		if (value != null && (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {
+			type = value.getClass();
+		}
+		return type;
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TypeAdapters.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TypeAdapters.java
new file mode 100644
index 0000000..a33beaf
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/TypeAdapters.java
@@ -0,0 +1,965 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.internal.bind;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.Currency;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+import cn.emay.sdk.util.json.gson.Gson;
+import cn.emay.sdk.util.json.gson.JsonArray;
+import cn.emay.sdk.util.json.gson.JsonElement;
+import cn.emay.sdk.util.json.gson.JsonIOException;
+import cn.emay.sdk.util.json.gson.JsonNull;
+import cn.emay.sdk.util.json.gson.JsonObject;
+import cn.emay.sdk.util.json.gson.JsonPrimitive;
+import cn.emay.sdk.util.json.gson.JsonSyntaxException;
+import cn.emay.sdk.util.json.gson.TypeAdapter;
+import cn.emay.sdk.util.json.gson.TypeAdapterFactory;
+import cn.emay.sdk.util.json.gson.annotations.SerializedName;
+import cn.emay.sdk.util.json.gson.internal.LazilyParsedNumber;
+import cn.emay.sdk.util.json.gson.reflect.TypeToken;
+import cn.emay.sdk.util.json.gson.stream.JsonReader;
+import cn.emay.sdk.util.json.gson.stream.JsonToken;
+import cn.emay.sdk.util.json.gson.stream.JsonWriter;
+
+/**
+ * Type adapters for basic types.
+ */
+public final class TypeAdapters {
+	private TypeAdapters() {
+		throw new UnsupportedOperationException();
+	}
+
+	@SuppressWarnings("rawtypes")
+	public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
+		@Override
+		public void write(JsonWriter out, Class value) throws IOException {
+			if (value == null) {
+				out.nullValue();
+			} else {
+				throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: " + value.getName() + ". Forgot to register a type adapter?");
+			}
+		}
+
+		@Override
+		public Class read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			} else {
+				throw new UnsupportedOperationException("Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?");
+			}
+		}
+	};
+	public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS);
+
+	public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() {
+		@Override
+		public BitSet read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+
+			BitSet bitset = new BitSet();
+			in.beginArray();
+			int i = 0;
+			JsonToken tokenType = in.peek();
+			while (tokenType != JsonToken.END_ARRAY) {
+				boolean set;
+				switch (tokenType) {
+				case NUMBER:
+					set = in.nextInt() != 0;
+					break;
+				case BOOLEAN:
+					set = in.nextBoolean();
+					break;
+				case STRING:
+					String stringValue = in.nextString();
+					try {
+						set = Integer.parseInt(stringValue) != 0;
+					} catch (NumberFormatException e) {
+						throw new JsonSyntaxException("Error: Expecting: bitset number value (1, 0), Found: " + stringValue);
+					}
+					break;
+				default:
+					throw new JsonSyntaxException("Invalid bitset value type: " + tokenType);
+				}
+				if (set) {
+					bitset.set(i);
+				}
+				++i;
+				tokenType = in.peek();
+			}
+			in.endArray();
+			return bitset;
+		}
+
+		@Override
+		public void write(JsonWriter out, BitSet src) throws IOException {
+			if (src == null) {
+				out.nullValue();
+				return;
+			}
+
+			out.beginArray();
+			for (int i = 0; i < src.length(); i++) {
+				int value = (src.get(i)) ? 1 : 0;
+				out.value(value);
+			}
+			out.endArray();
+		}
+	};
+
+	public static final TypeAdapterFactory BIT_SET_FACTORY = newFactory(BitSet.class, BIT_SET);
+
+	public static final TypeAdapter<Boolean> BOOLEAN = new TypeAdapter<Boolean>() {
+		@Override
+		public Boolean read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			} else if (in.peek() == JsonToken.STRING) {
+				// support strings for compatibility with GSON 1.7
+				return Boolean.parseBoolean(in.nextString());
+			}
+			return in.nextBoolean();
+		}
+
+		@Override
+		public void write(JsonWriter out, Boolean value) throws IOException {
+			if (value == null) {
+				out.nullValue();
+				return;
+			}
+			out.value(value);
+		}
+	};
+
+	/**
+	 * Writes a boolean as a string. Useful for map keys, where booleans aren't
+	 * otherwise permitted.
+	 */
+	public static final TypeAdapter<Boolean> BOOLEAN_AS_STRING = new TypeAdapter<Boolean>() {
+		@Override
+		public Boolean read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			return Boolean.valueOf(in.nextString());
+		}
+
+		@Override
+		public void write(JsonWriter out, Boolean value) throws IOException {
+			out.value(value == null ? "null" : value.toString());
+		}
+	};
+
+	public static final TypeAdapterFactory BOOLEAN_FACTORY = newFactory(boolean.class, Boolean.class, BOOLEAN);
+
+	public static final TypeAdapter<Number> BYTE = new TypeAdapter<Number>() {
+		@Override
+		public Number read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			try {
+				int intValue = in.nextInt();
+				return (byte) intValue;
+			} catch (NumberFormatException e) {
+				throw new JsonSyntaxException(e);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, Number value) throws IOException {
+			out.value(value);
+		}
+	};
+
+	public static final TypeAdapterFactory BYTE_FACTORY = newFactory(byte.class, Byte.class, BYTE);
+
+	public static final TypeAdapter<Number> SHORT = new TypeAdapter<Number>() {
+		@Override
+		public Number read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			try {
+				return (short) in.nextInt();
+			} catch (NumberFormatException e) {
+				throw new JsonSyntaxException(e);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, Number value) throws IOException {
+			out.value(value);
+		}
+	};
+
+	public static final TypeAdapterFactory SHORT_FACTORY = newFactory(short.class, Short.class, SHORT);
+
+	public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() {
+		@Override
+		public Number read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			try {
+				return in.nextInt();
+			} catch (NumberFormatException e) {
+				throw new JsonSyntaxException(e);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, Number value) throws IOException {
+			out.value(value);
+		}
+	};
+	public static final TypeAdapterFactory INTEGER_FACTORY = newFactory(int.class, Integer.class, INTEGER);
+
+	public static final TypeAdapter<AtomicInteger> ATOMIC_INTEGER = new TypeAdapter<AtomicInteger>() {
+		@Override
+		public AtomicInteger read(JsonReader in) throws IOException {
+			try {
+				return new AtomicInteger(in.nextInt());
+			} catch (NumberFormatException e) {
+				throw new JsonSyntaxException(e);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, AtomicInteger value) throws IOException {
+			out.value(value.get());
+		}
+	}.nullSafe();
+	public static final TypeAdapterFactory ATOMIC_INTEGER_FACTORY = newFactory(AtomicInteger.class, TypeAdapters.ATOMIC_INTEGER);
+
+	public static final TypeAdapter<AtomicBoolean> ATOMIC_BOOLEAN = new TypeAdapter<AtomicBoolean>() {
+		@Override
+		public AtomicBoolean read(JsonReader in) throws IOException {
+			return new AtomicBoolean(in.nextBoolean());
+		}
+
+		@Override
+		public void write(JsonWriter out, AtomicBoolean value) throws IOException {
+			out.value(value.get());
+		}
+	}.nullSafe();
+	public static final TypeAdapterFactory ATOMIC_BOOLEAN_FACTORY = newFactory(AtomicBoolean.class, TypeAdapters.ATOMIC_BOOLEAN);
+
+	public static final TypeAdapter<AtomicIntegerArray> ATOMIC_INTEGER_ARRAY = new TypeAdapter<AtomicIntegerArray>() {
+		@Override
+		public AtomicIntegerArray read(JsonReader in) throws IOException {
+			List<Integer> list = new ArrayList<Integer>();
+			in.beginArray();
+			while (in.hasNext()) {
+				try {
+					int integer = in.nextInt();
+					list.add(integer);
+				} catch (NumberFormatException e) {
+					throw new JsonSyntaxException(e);
+				}
+			}
+			in.endArray();
+			int length = list.size();
+			AtomicIntegerArray array = new AtomicIntegerArray(length);
+			for (int i = 0; i < length; ++i) {
+				array.set(i, list.get(i));
+			}
+			return array;
+		}
+
+		@Override
+		public void write(JsonWriter out, AtomicIntegerArray value) throws IOException {
+			out.beginArray();
+			for (int i = 0, length = value.length(); i < length; i++) {
+				out.value(value.get(i));
+			}
+			out.endArray();
+		}
+	}.nullSafe();
+	public static final TypeAdapterFactory ATOMIC_INTEGER_ARRAY_FACTORY = newFactory(AtomicIntegerArray.class, TypeAdapters.ATOMIC_INTEGER_ARRAY);
+
+	public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() {
+		@Override
+		public Number read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			try {
+				return in.nextLong();
+			} catch (NumberFormatException e) {
+				throw new JsonSyntaxException(e);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, Number value) throws IOException {
+			out.value(value);
+		}
+	};
+
+	public static final TypeAdapter<Number> FLOAT = new TypeAdapter<Number>() {
+		@Override
+		public Number read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			return (float) in.nextDouble();
+		}
+
+		@Override
+		public void write(JsonWriter out, Number value) throws IOException {
+			out.value(value);
+		}
+	};
+
+	public static final TypeAdapter<Number> DOUBLE = new TypeAdapter<Number>() {
+		@Override
+		public Number read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			return in.nextDouble();
+		}
+
+		@Override
+		public void write(JsonWriter out, Number value) throws IOException {
+			out.value(value);
+		}
+	};
+
+	public static final TypeAdapter<Number> NUMBER = new TypeAdapter<Number>() {
+		@Override
+		public Number read(JsonReader in) throws IOException {
+			JsonToken jsonToken = in.peek();
+			switch (jsonToken) {
+			case NULL:
+				in.nextNull();
+				return null;
+			case NUMBER:
+				return new LazilyParsedNumber(in.nextString());
+			default:
+				throw new JsonSyntaxException("Expecting number, got: " + jsonToken);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, Number value) throws IOException {
+			out.value(value);
+		}
+	};
+
+	public static final TypeAdapterFactory NUMBER_FACTORY = newFactory(Number.class, NUMBER);
+
+	public static final TypeAdapter<Character> CHARACTER = new TypeAdapter<Character>() {
+		@Override
+		public Character read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			String str = in.nextString();
+			if (str.length() != 1) {
+				throw new JsonSyntaxException("Expecting character, got: " + str);
+			}
+			return str.charAt(0);
+		}
+
+		@Override
+		public void write(JsonWriter out, Character value) throws IOException {
+			out.value(value == null ? null : String.valueOf(value));
+		}
+	};
+
+	public static final TypeAdapterFactory CHARACTER_FACTORY = newFactory(char.class, Character.class, CHARACTER);
+
+	public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
+		@Override
+		public String read(JsonReader in) throws IOException {
+			JsonToken peek = in.peek();
+			if (peek == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			/* coerce booleans to strings for backwards compatibility */
+			if (peek == JsonToken.BOOLEAN) {
+				return Boolean.toString(in.nextBoolean());
+			}
+			return in.nextString();
+		}
+
+		@Override
+		public void write(JsonWriter out, String value) throws IOException {
+			out.value(value);
+		}
+	};
+
+	public static final TypeAdapter<BigDecimal> BIG_DECIMAL = new TypeAdapter<BigDecimal>() {
+		@Override
+		public BigDecimal read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			try {
+				return new BigDecimal(in.nextString());
+			} catch (NumberFormatException e) {
+				throw new JsonSyntaxException(e);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, BigDecimal value) throws IOException {
+			out.value(value);
+		}
+	};
+
+	public static final TypeAdapter<BigInteger> BIG_INTEGER = new TypeAdapter<BigInteger>() {
+		@Override
+		public BigInteger read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			try {
+				return new BigInteger(in.nextString());
+			} catch (NumberFormatException e) {
+				throw new JsonSyntaxException(e);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, BigInteger value) throws IOException {
+			out.value(value);
+		}
+	};
+
+	public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);
+
+	public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
+		@Override
+		public StringBuilder read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			return new StringBuilder(in.nextString());
+		}
+
+		@Override
+		public void write(JsonWriter out, StringBuilder value) throws IOException {
+			out.value(value == null ? null : value.toString());
+		}
+	};
+
+	public static final TypeAdapterFactory STRING_BUILDER_FACTORY = newFactory(StringBuilder.class, STRING_BUILDER);
+
+	public static final TypeAdapter<StringBuffer> STRING_BUFFER = new TypeAdapter<StringBuffer>() {
+		@Override
+		public StringBuffer read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			return new StringBuffer(in.nextString());
+		}
+
+		@Override
+		public void write(JsonWriter out, StringBuffer value) throws IOException {
+			out.value(value == null ? null : value.toString());
+		}
+	};
+
+	public static final TypeAdapterFactory STRING_BUFFER_FACTORY = newFactory(StringBuffer.class, STRING_BUFFER);
+
+	public static final TypeAdapter<URL> URL = new TypeAdapter<URL>() {
+		@Override
+		public URL read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			String nextString = in.nextString();
+			return "null".equals(nextString) ? null : new URL(nextString);
+		}
+
+		@Override
+		public void write(JsonWriter out, URL value) throws IOException {
+			out.value(value == null ? null : value.toExternalForm());
+		}
+	};
+
+	public static final TypeAdapterFactory URL_FACTORY = newFactory(URL.class, URL);
+
+	public static final TypeAdapter<URI> URI = new TypeAdapter<URI>() {
+		@Override
+		public URI read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			try {
+				String nextString = in.nextString();
+				return "null".equals(nextString) ? null : new URI(nextString);
+			} catch (URISyntaxException e) {
+				throw new JsonIOException(e);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, URI value) throws IOException {
+			out.value(value == null ? null : value.toASCIIString());
+		}
+	};
+
+	public static final TypeAdapterFactory URI_FACTORY = newFactory(URI.class, URI);
+
+	public static final TypeAdapter<InetAddress> INET_ADDRESS = new TypeAdapter<InetAddress>() {
+		@Override
+		public InetAddress read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			// regrettably, this should have included both the host name and the host
+			// address
+			return InetAddress.getByName(in.nextString());
+		}
+
+		@Override
+		public void write(JsonWriter out, InetAddress value) throws IOException {
+			out.value(value == null ? null : value.getHostAddress());
+		}
+	};
+
+	public static final TypeAdapterFactory INET_ADDRESS_FACTORY = newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS);
+
+	public static final TypeAdapter<UUID> UUID = new TypeAdapter<UUID>() {
+		@Override
+		public UUID read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			return java.util.UUID.fromString(in.nextString());
+		}
+
+		@Override
+		public void write(JsonWriter out, UUID value) throws IOException {
+			out.value(value == null ? null : value.toString());
+		}
+	};
+
+	public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID);
+
+	public static final TypeAdapter<Currency> CURRENCY = new TypeAdapter<Currency>() {
+		@Override
+		public Currency read(JsonReader in) throws IOException {
+			return Currency.getInstance(in.nextString());
+		}
+
+		@Override
+		public void write(JsonWriter out, Currency value) throws IOException {
+			out.value(value.getCurrencyCode());
+		}
+	}.nullSafe();
+	public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY);
+
+	public static final TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() {
+		@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+		@Override
+		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+			if (typeToken.getRawType() != Timestamp.class) {
+				return null;
+			}
+
+			final TypeAdapter<Date> dateTypeAdapter = gson.getAdapter(Date.class);
+			return (TypeAdapter<T>) new TypeAdapter<Timestamp>() {
+				@Override
+				public Timestamp read(JsonReader in) throws IOException {
+					Date date = dateTypeAdapter.read(in);
+					return date != null ? new Timestamp(date.getTime()) : null;
+				}
+
+				@Override
+				public void write(JsonWriter out, Timestamp value) throws IOException {
+					dateTypeAdapter.write(out, value);
+				}
+			};
+		}
+	};
+
+	public static final TypeAdapter<Calendar> CALENDAR = new TypeAdapter<Calendar>() {
+		private static final String YEAR = "year";
+		private static final String MONTH = "month";
+		private static final String DAY_OF_MONTH = "dayOfMonth";
+		private static final String HOUR_OF_DAY = "hourOfDay";
+		private static final String MINUTE = "minute";
+		private static final String SECOND = "second";
+
+		@Override
+		public Calendar read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			in.beginObject();
+			int year = 0;
+			int month = 0;
+			int dayOfMonth = 0;
+			int hourOfDay = 0;
+			int minute = 0;
+			int second = 0;
+			while (in.peek() != JsonToken.END_OBJECT) {
+				String name = in.nextName();
+				int value = in.nextInt();
+				if (YEAR.equals(name)) {
+					year = value;
+				} else if (MONTH.equals(name)) {
+					month = value;
+				} else if (DAY_OF_MONTH.equals(name)) {
+					dayOfMonth = value;
+				} else if (HOUR_OF_DAY.equals(name)) {
+					hourOfDay = value;
+				} else if (MINUTE.equals(name)) {
+					minute = value;
+				} else if (SECOND.equals(name)) {
+					second = value;
+				}
+			}
+			in.endObject();
+			return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
+		}
+
+		@Override
+		public void write(JsonWriter out, Calendar value) throws IOException {
+			if (value == null) {
+				out.nullValue();
+				return;
+			}
+			out.beginObject();
+			out.name(YEAR);
+			out.value(value.get(Calendar.YEAR));
+			out.name(MONTH);
+			out.value(value.get(Calendar.MONTH));
+			out.name(DAY_OF_MONTH);
+			out.value(value.get(Calendar.DAY_OF_MONTH));
+			out.name(HOUR_OF_DAY);
+			out.value(value.get(Calendar.HOUR_OF_DAY));
+			out.name(MINUTE);
+			out.value(value.get(Calendar.MINUTE));
+			out.name(SECOND);
+			out.value(value.get(Calendar.SECOND));
+			out.endObject();
+		}
+	};
+
+	public static final TypeAdapterFactory CALENDAR_FACTORY = newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR);
+
+	public static final TypeAdapter<Locale> LOCALE = new TypeAdapter<Locale>() {
+		@Override
+		public Locale read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			String locale = in.nextString();
+			StringTokenizer tokenizer = new StringTokenizer(locale, "_");
+			String language = null;
+			String country = null;
+			String variant = null;
+			if (tokenizer.hasMoreElements()) {
+				language = tokenizer.nextToken();
+			}
+			if (tokenizer.hasMoreElements()) {
+				country = tokenizer.nextToken();
+			}
+			if (tokenizer.hasMoreElements()) {
+				variant = tokenizer.nextToken();
+			}
+			if (country == null && variant == null) {
+				return new Locale(language);
+			} else if (variant == null) {
+				return new Locale(language, country);
+			} else {
+				return new Locale(language, country, variant);
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, Locale value) throws IOException {
+			out.value(value == null ? null : value.toString());
+		}
+	};
+
+	public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE);
+
+	public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {
+		@Override
+		public JsonElement read(JsonReader in) throws IOException {
+			switch (in.peek()) {
+			case STRING:
+				return new JsonPrimitive(in.nextString());
+			case NUMBER:
+				String number = in.nextString();
+				return new JsonPrimitive(new LazilyParsedNumber(number));
+			case BOOLEAN:
+				return new JsonPrimitive(in.nextBoolean());
+			case NULL:
+				in.nextNull();
+				return JsonNull.INSTANCE;
+			case BEGIN_ARRAY:
+				JsonArray array = new JsonArray();
+				in.beginArray();
+				while (in.hasNext()) {
+					array.add(read(in));
+				}
+				in.endArray();
+				return array;
+			case BEGIN_OBJECT:
+				JsonObject object = new JsonObject();
+				in.beginObject();
+				while (in.hasNext()) {
+					object.add(in.nextName(), read(in));
+				}
+				in.endObject();
+				return object;
+			case END_DOCUMENT:
+			case NAME:
+			case END_OBJECT:
+			case END_ARRAY:
+			default:
+				throw new IllegalArgumentException();
+			}
+		}
+
+		@Override
+		public void write(JsonWriter out, JsonElement value) throws IOException {
+			if (value == null || value.isJsonNull()) {
+				out.nullValue();
+			} else if (value.isJsonPrimitive()) {
+				JsonPrimitive primitive = value.getAsJsonPrimitive();
+				if (primitive.isNumber()) {
+					out.value(primitive.getAsNumber());
+				} else if (primitive.isBoolean()) {
+					out.value(primitive.getAsBoolean());
+				} else {
+					out.value(primitive.getAsString());
+				}
+
+			} else if (value.isJsonArray()) {
+				out.beginArray();
+				for (JsonElement e : value.getAsJsonArray()) {
+					write(out, e);
+				}
+				out.endArray();
+
+			} else if (value.isJsonObject()) {
+				out.beginObject();
+				for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) {
+					out.name(e.getKey());
+					write(out, e.getValue());
+				}
+				out.endObject();
+
+			} else {
+				throw new IllegalArgumentException("Couldn't write " + value.getClass());
+			}
+		}
+	};
+
+	public static final TypeAdapterFactory JSON_ELEMENT_FACTORY = newTypeHierarchyFactory(JsonElement.class, JSON_ELEMENT);
+
+	private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
+		private final Map<String, T> nameToConstant = new HashMap<String, T>();
+		private final Map<T, String> constantToName = new HashMap<T, String>();
+
+		public EnumTypeAdapter(Class<T> classOfT) {
+			try {
+				for (T constant : classOfT.getEnumConstants()) {
+					String name = constant.name();
+					SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class);
+					if (annotation != null) {
+						name = annotation.value();
+						for (String alternate : annotation.alternate()) {
+							nameToConstant.put(alternate, constant);
+						}
+					}
+					nameToConstant.put(name, constant);
+					constantToName.put(constant, name);
+				}
+			} catch (NoSuchFieldException e) {
+				throw new AssertionError("Missing field in " + classOfT.getName());
+			}
+		}
+
+		@Override
+		public T read(JsonReader in) throws IOException {
+			if (in.peek() == JsonToken.NULL) {
+				in.nextNull();
+				return null;
+			}
+			return nameToConstant.get(in.nextString());
+		}
+
+		@Override
+		public void write(JsonWriter out, T value) throws IOException {
+			out.value(value == null ? null : constantToName.get(value));
+		}
+	}
+
+	public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory() {
+		@SuppressWarnings({ "rawtypes", "unchecked" })
+		@Override
+		public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+			Class<? super T> rawType = typeToken.getRawType();
+			if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) {
+				return null;
+			}
+			if (!rawType.isEnum()) {
+				rawType = rawType.getSuperclass(); // handle anonymous subclasses
+			}
+			return (TypeAdapter<T>) new EnumTypeAdapter(rawType);
+		}
+	};
+
+	public static <TT> TypeAdapterFactory newFactory(final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) {
+		return new TypeAdapterFactory() {
+			@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+			@Override
+			public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+				return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null;
+			}
+		};
+	}
+
+	public static <TT> TypeAdapterFactory newFactory(final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
+		return new TypeAdapterFactory() {
+			@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+			@Override
+			public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+				return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null;
+			}
+
+			@Override
+			public String toString() {
+				return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]";
+			}
+		};
+	}
+
+	public static <TT> TypeAdapterFactory newFactory(final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) {
+		return new TypeAdapterFactory() {
+			@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+			@Override
+			public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+				Class<? super T> rawType = typeToken.getRawType();
+				return (rawType == unboxed || rawType == boxed) ? (TypeAdapter<T>) typeAdapter : null;
+			}
+
+			@Override
+			public String toString() {
+				return "Factory[type=" + boxed.getName() + "+" + unboxed.getName() + ",adapter=" + typeAdapter + "]";
+			}
+		};
+	}
+
+	public static <TT> TypeAdapterFactory newFactoryForMultipleTypes(final Class<TT> base, final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) {
+		return new TypeAdapterFactory() {
+			@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+			@Override
+			public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+				Class<? super T> rawType = typeToken.getRawType();
+				return (rawType == base || rawType == sub) ? (TypeAdapter<T>) typeAdapter : null;
+			}
+
+			@Override
+			public String toString() {
+				return "Factory[type=" + base.getName() + "+" + sub.getName() + ",adapter=" + typeAdapter + "]";
+			}
+		};
+	}
+
+	/**
+	 * Returns a factory for all subtypes of {@code typeAdapter}. We do a runtime
+	 * check to confirm that the deserialized type matches the type requested.
+	 */
+	public static <T1> TypeAdapterFactory newTypeHierarchyFactory(final Class<T1> clazz, final TypeAdapter<T1> typeAdapter) {
+		return new TypeAdapterFactory() {
+			@SuppressWarnings("unchecked")
+			@Override
+			public <T2> TypeAdapter<T2> create(Gson gson, TypeToken<T2> typeToken) {
+				final Class<? super T2> requestedType = typeToken.getRawType();
+				if (!clazz.isAssignableFrom(requestedType)) {
+					return null;
+				}
+				return (TypeAdapter<T2>) new TypeAdapter<T1>() {
+					@Override
+					public void write(JsonWriter out, T1 value) throws IOException {
+						typeAdapter.write(out, value);
+					}
+
+					@Override
+					public T1 read(JsonReader in) throws IOException {
+						T1 result = typeAdapter.read(in);
+						if (result != null && !requestedType.isInstance(result)) {
+							throw new JsonSyntaxException("Expected a " + requestedType.getName() + " but was " + result.getClass().getName());
+						}
+						return result;
+					}
+				};
+			}
+
+			@Override
+			public String toString() {
+				return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";
+			}
+		};
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/util/ISO8601Utils.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/util/ISO8601Utils.java
new file mode 100644
index 0000000..a45147d
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/bind/util/ISO8601Utils.java
@@ -0,0 +1,380 @@
+package cn.emay.sdk.util.json.gson.internal.bind.util;
+
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Utilities methods for manipulating dates in iso8601 format. This is much much
+ * faster and GC friendly than using SimpleDateFormat so highly suitable if you
+ * (un)serialize lots of date objects.
+ * 
+ * Supported parse format:
+ * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:]mm]]
+ * 
+ * @see <a href="http://www.w3.org/TR/NOTE-datetime">this specification</a>
+ */
+// Date parsing code from Jackson databind ISO8601Utils.java
+// https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
+public class ISO8601Utils {
+	/**
+	 * ID to represent the 'UTC' string, default timezone since Jackson 2.7
+	 * 
+	 * @since 2.7
+	 */
+	private static final String UTC_ID = "UTC";
+	/**
+	 * The UTC timezone, prefetched to avoid more lookups.
+	 * 
+	 * @since 2.7
+	 */
+	private static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone(UTC_ID);
+
+	/*
+	 * /********************************************************** /* Formatting
+	 * /**********************************************************
+	 */
+
+	/**
+	 * Format a date into 'yyyy-MM-ddThh:mm:ssZ' (default timezone, no milliseconds
+	 * precision)
+	 * 
+	 * @param date
+	 *            the date to format
+	 * @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ'
+	 */
+	public static String format(Date date) {
+		return format(date, false, TIMEZONE_UTC);
+	}
+
+	/**
+	 * Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone)
+	 * 
+	 * @param date
+	 *            the date to format
+	 * @param millis
+	 *            true to include millis precision otherwise false
+	 * @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z'
+	 */
+	public static String format(Date date, boolean millis) {
+		return format(date, millis, TIMEZONE_UTC);
+	}
+
+	/**
+	 * Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
+	 * 
+	 * @param date
+	 *            the date to format
+	 * @param millis
+	 *            true to include millis precision otherwise false
+	 * @param tz
+	 *            timezone to use for the formatting (UTC will produce 'Z')
+	 * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
+	 */
+	public static String format(Date date, boolean millis, TimeZone tz) {
+		Calendar calendar = new GregorianCalendar(tz, Locale.US);
+		calendar.setTime(date);
+
+		// estimate capacity of buffer as close as we can (yeah, that's pedantic ;)
+		int capacity = "yyyy-MM-ddThh:mm:ss".length();
+		capacity += millis ? ".sss".length() : 0;
+		capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length();
+		StringBuilder formatted = new StringBuilder(capacity);
+
+		padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length());
+		formatted.append('-');
+		padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length());
+		formatted.append('-');
+		padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length());
+		formatted.append('T');
+		padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length());
+		formatted.append(':');
+		padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length());
+		formatted.append(':');
+		padInt(formatted, calendar.get(Calendar.SECOND), "ss".length());
+		if (millis) {
+			formatted.append('.');
+			padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length());
+		}
+
+		int offset = tz.getOffset(calendar.getTimeInMillis());
+		if (offset != 0) {
+			int hours = Math.abs((offset / (60 * 1000)) / 60);
+			int minutes = Math.abs((offset / (60 * 1000)) % 60);
+			formatted.append(offset < 0 ? '-' : '+');
+			padInt(formatted, hours, "hh".length());
+			formatted.append(':');
+			padInt(formatted, minutes, "mm".length());
+		} else {
+			formatted.append('Z');
+		}
+
+		return formatted.toString();
+	}
+
+	/*
+	 * /********************************************************** /* Parsing
+	 * /**********************************************************
+	 */
+
+	/**
+	 * Parse a date from ISO-8601 formatted string. It expects a format
+	 * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:mm]]]
+	 * 
+	 * @param date
+	 *            ISO string to parse in the appropriate format.
+	 * @param pos
+	 *            The position to start parsing from, updated to where parsing
+	 *            stopped.
+	 * @return the parsed date
+	 * @throws ParseException
+	 *             if the date is not in the appropriate format
+	 */
+	public static Date parse(String date, ParsePosition pos) throws ParseException {
+		Exception fail = null;
+		try {
+			int offset = pos.getIndex();
+
+			// extract year
+			int year = parseInt(date, offset, offset += 4);
+			if (checkOffset(date, offset, '-')) {
+				offset += 1;
+			}
+
+			// extract month
+			int month = parseInt(date, offset, offset += 2);
+			if (checkOffset(date, offset, '-')) {
+				offset += 1;
+			}
+
+			// extract day
+			int day = parseInt(date, offset, offset += 2);
+			// default time value
+			int hour = 0;
+			int minutes = 0;
+			int seconds = 0;
+			int milliseconds = 0; // always use 0 otherwise returned date will include millis of current time
+
+			// if the value has no time component (and no time zone), we are done
+			boolean hasT = checkOffset(date, offset, 'T');
+
+			if (!hasT && (date.length() <= offset)) {
+				Calendar calendar = new GregorianCalendar(year, month - 1, day);
+
+				pos.setIndex(offset);
+				return calendar.getTime();
+			}
+
+			if (hasT) {
+
+				// extract hours, minutes, seconds and milliseconds
+				hour = parseInt(date, offset += 1, offset += 2);
+				if (checkOffset(date, offset, ':')) {
+					offset += 1;
+				}
+
+				minutes = parseInt(date, offset, offset += 2);
+				if (checkOffset(date, offset, ':')) {
+					offset += 1;
+				}
+				// second and milliseconds can be optional
+				if (date.length() > offset) {
+					char c = date.charAt(offset);
+					if (c != 'Z' && c != '+' && c != '-') {
+						seconds = parseInt(date, offset, offset += 2);
+						if (seconds > 59 && seconds < 63)
+							seconds = 59; // truncate up to 3 leap seconds
+						// milliseconds can be optional in the format
+						if (checkOffset(date, offset, '.')) {
+							offset += 1;
+							int endOffset = indexOfNonDigit(date, offset + 1); // assume at least one digit
+							int parseEndOffset = Math.min(endOffset, offset + 3); // parse up to 3 digits
+							int fraction = parseInt(date, offset, parseEndOffset);
+							// compensate for "missing" digits
+							switch (parseEndOffset - offset) { // number of digits parsed
+							case 2:
+								milliseconds = fraction * 10;
+								break;
+							case 1:
+								milliseconds = fraction * 100;
+								break;
+							default:
+								milliseconds = fraction;
+							}
+							offset = endOffset;
+						}
+					}
+				}
+			}
+
+			// extract timezone
+			if (date.length() <= offset) {
+				throw new IllegalArgumentException("No time zone indicator");
+			}
+
+			TimeZone timezone = null;
+			char timezoneIndicator = date.charAt(offset);
+
+			if (timezoneIndicator == 'Z') {
+				timezone = TIMEZONE_UTC;
+				offset += 1;
+			} else if (timezoneIndicator == '+' || timezoneIndicator == '-') {
+				String timezoneOffset = date.substring(offset);
+
+				// When timezone has no minutes, we should append it, valid timezones are, for
+				// example: +00:00, +0000 and +00
+				timezoneOffset = timezoneOffset.length() >= 5 ? timezoneOffset : timezoneOffset + "00";
+
+				offset += timezoneOffset.length();
+				// 18-Jun-2015, tatu: Minor simplification, skip offset of "+0000"/"+00:00"
+				if ("+0000".equals(timezoneOffset) || "+00:00".equals(timezoneOffset)) {
+					timezone = TIMEZONE_UTC;
+				} else {
+					// 18-Jun-2015, tatu: Looks like offsets only work from GMT, not UTC...
+					// not sure why, but that's the way it looks. Further, Javadocs for
+					// `java.util.TimeZone` specifically instruct use of GMT as base for
+					// custom timezones... odd.
+					String timezoneId = "GMT" + timezoneOffset;
+					// String timezoneId = "UTC" + timezoneOffset;
+
+					timezone = TimeZone.getTimeZone(timezoneId);
+
+					String act = timezone.getID();
+					if (!act.equals(timezoneId)) {
+						/*
+						 * 22-Jan-2015, tatu: Looks like canonical version has colons, but we may be
+						 * given one without. If so, don't sweat. Yes, very inefficient. Hopefully not
+						 * hit often. If it becomes a perf problem, add 'loose' comparison instead.
+						 */
+						String cleaned = act.replace(":", "");
+						if (!cleaned.equals(timezoneId)) {
+							throw new IndexOutOfBoundsException("Mismatching time zone indicator: " + timezoneId + " given, resolves to " + timezone.getID());
+						}
+					}
+				}
+			} else {
+				throw new IndexOutOfBoundsException("Invalid time zone indicator '" + timezoneIndicator + "'");
+			}
+
+			Calendar calendar = new GregorianCalendar(timezone);
+			calendar.setLenient(false);
+			calendar.set(Calendar.YEAR, year);
+			calendar.set(Calendar.MONTH, month - 1);
+			calendar.set(Calendar.DAY_OF_MONTH, day);
+			calendar.set(Calendar.HOUR_OF_DAY, hour);
+			calendar.set(Calendar.MINUTE, minutes);
+			calendar.set(Calendar.SECOND, seconds);
+			calendar.set(Calendar.MILLISECOND, milliseconds);
+
+			pos.setIndex(offset);
+			return calendar.getTime();
+			// If we get a ParseException it'll already have the right message/offset.
+			// Other exception types can convert here.
+		} catch (IndexOutOfBoundsException e) {
+			fail = e;
+		} catch (NumberFormatException e) {
+			fail = e;
+		} catch (IllegalArgumentException e) {
+			fail = e;
+		}
+		String input = (date == null) ? null : ('"' + date + "'");
+		String msg = fail.getMessage();
+		if (msg == null || msg.isEmpty()) {
+			msg = "(" + fail.getClass().getName() + ")";
+		}
+		ParseException ex = new ParseException("Failed to parse date [" + input + "]: " + msg, pos.getIndex());
+		ex.initCause(fail);
+		throw ex;
+	}
+
+	/**
+	 * Check if the expected character exist at the given offset in the value.
+	 * 
+	 * @param value
+	 *            the string to check at the specified offset
+	 * @param offset
+	 *            the offset to look for the expected character
+	 * @param expected
+	 *            the expected character
+	 * @return true if the expected character exist at the given offset
+	 */
+	private static boolean checkOffset(String value, int offset, char expected) {
+		return (offset < value.length()) && (value.charAt(offset) == expected);
+	}
+
+	/**
+	 * Parse an integer located between 2 given offsets in a string
+	 * 
+	 * @param value
+	 *            the string to parse
+	 * @param beginIndex
+	 *            the start index for the integer in the string
+	 * @param endIndex
+	 *            the end index for the integer in the string
+	 * @return the int
+	 * @throws NumberFormatException
+	 *             if the value is not a number
+	 */
+	private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException {
+		if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) {
+			throw new NumberFormatException(value);
+		}
+		// use same logic as in Integer.parseInt() but less generic we're not supporting
+		// negative values
+		int i = beginIndex;
+		int result = 0;
+		int digit;
+		if (i < endIndex) {
+			digit = Character.digit(value.charAt(i++), 10);
+			if (digit < 0) {
+				throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex));
+			}
+			result = -digit;
+		}
+		while (i < endIndex) {
+			digit = Character.digit(value.charAt(i++), 10);
+			if (digit < 0) {
+				throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex));
+			}
+			result *= 10;
+			result -= digit;
+		}
+		return -result;
+	}
+
+	/**
+	 * Zero pad a number to a specified length
+	 * 
+	 * @param buffer
+	 *            buffer to use for padding
+	 * @param value
+	 *            the integer value to pad if necessary.
+	 * @param length
+	 *            the length of the string we should zero pad
+	 */
+	private static void padInt(StringBuilder buffer, int value, int length) {
+		String strValue = Integer.toString(value);
+		for (int i = length - strValue.length(); i > 0; i--) {
+			buffer.append('0');
+		}
+		buffer.append(strValue);
+	}
+
+	/**
+	 * Returns the index of the first character in the string that is not a digit,
+	 * starting at offset.
+	 */
+	private static int indexOfNonDigit(String string, int offset) {
+		for (int i = offset; i < string.length(); i++) {
+			char c = string.charAt(i);
+			if (c < '0' || c > '9')
+				return i;
+		}
+		return string.length();
+	}
+
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/package-info.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/package-info.java
new file mode 100644
index 0000000..a1fbaa7
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/internal/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * Do NOT use any class in this package as they are meant for internal use in
+ * Gson. These classes will very likely change incompatibly in future versions.
+ * You have been warned.
+ *
+ * @author Inderjeet Singh, Joel Leitch, Jesse Wilson
+ */
+package cn.emay.sdk.util.json.gson.internal;
\ No newline at end of file
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/package-info.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/package-info.java
new file mode 100644
index 0000000..109157d
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/package-info.java
@@ -0,0 +1,14 @@
+/**
+ * This package provides the {@link com.google.gson.Gson} class to convert Json
+ * to Java and vice-versa.
+ *
+ * <p>
+ * The primary class to use is {@link com.google.gson.Gson} which can be
+ * constructed with {@code new Gson()} (using default settings) or by using
+ * {@link com.google.gson.GsonBuilder} (to configure various options such as
+ * using versioning and so on).
+ * </p>
+ *
+ * @author Inderjeet Singh, Joel Leitch
+ */
+package cn.emay.sdk.util.json.gson;
\ No newline at end of file
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/reflect/TypeToken.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/reflect/TypeToken.java
new file mode 100644
index 0000000..0bb6930
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/reflect/TypeToken.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.reflect;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.HashMap;
+import java.util.Map;
+
+import cn.emay.sdk.util.json.gson.internal.$Gson$Preconditions;
+import cn.emay.sdk.util.json.gson.internal.$Gson$Types;
+
+/**
+ * Represents a generic type {@code T}. Java doesn't yet provide a way to
+ * represent generic types, so this class does. Forces clients to create a
+ * subclass of this class which enables retrieval the type information even at
+ * runtime.
+ *
+ * <p>
+ * For example, to create a type literal for {@code List<String>}, you can
+ * create an empty anonymous inner class:
+ *
+ * <p>
+ * {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
+ *
+ * <p>
+ * This syntax cannot be used to create type literals that have wildcard
+ * parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}.
+ *
+ * @author Bob Lee
+ * @author Sven Mawson
+ * @author Jesse Wilson
+ */
+public class TypeToken<T> {
+	final Class<? super T> rawType;
+	final Type type;
+	final int hashCode;
+
+	/**
+	 * Constructs a new type literal. Derives represented class from type parameter.
+	 *
+	 * <p>
+	 * Clients create an empty anonymous subclass. Doing so embeds the type
+	 * parameter in the anonymous class's type hierarchy so we can reconstitute it
+	 * at runtime despite erasure.
+	 */
+	@SuppressWarnings("unchecked")
+	protected TypeToken() {
+		this.type = getSuperclassTypeParameter(getClass());
+		this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
+		this.hashCode = type.hashCode();
+	}
+
+	/**
+	 * Unsafe. Constructs a type literal manually.
+	 */
+	@SuppressWarnings("unchecked")
+	TypeToken(Type type) {
+		this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
+		this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
+		this.hashCode = this.type.hashCode();
+	}
+
+	/**
+	 * Returns the type from super class's type parameter in
+	 * {@link $Gson$Types#canonicalize canonical form}.
+	 */
+	static Type getSuperclassTypeParameter(Class<?> subclass) {
+		Type superclass = subclass.getGenericSuperclass();
+		if (superclass instanceof Class) {
+			throw new RuntimeException("Missing type parameter.");
+		}
+		ParameterizedType parameterized = (ParameterizedType) superclass;
+		return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
+	}
+
+	/**
+	 * Returns the raw (non-generic) type for this type.
+	 */
+	public final Class<? super T> getRawType() {
+		return rawType;
+	}
+
+	/**
+	 * Gets underlying {@code Type} instance.
+	 */
+	public final Type getType() {
+		return type;
+	}
+
+	/**
+	 * Check if this type is assignable from the given class object.
+	 *
+	 * @deprecated this implementation may be inconsistent with javac for types with
+	 *             wildcards.
+	 */
+	@Deprecated
+	public boolean isAssignableFrom(Class<?> cls) {
+		return isAssignableFrom((Type) cls);
+	}
+
+	/**
+	 * Check if this type is assignable from the given Type.
+	 *
+	 * @deprecated this implementation may be inconsistent with javac for types with
+	 *             wildcards.
+	 */
+	@Deprecated
+	public boolean isAssignableFrom(Type from) {
+		if (from == null) {
+			return false;
+		}
+
+		if (type.equals(from)) {
+			return true;
+		}
+
+		if (type instanceof Class<?>) {
+			return rawType.isAssignableFrom($Gson$Types.getRawType(from));
+		} else if (type instanceof ParameterizedType) {
+			return isAssignableFrom(from, (ParameterizedType) type, new HashMap<String, Type>());
+		} else if (type instanceof GenericArrayType) {
+			return rawType.isAssignableFrom($Gson$Types.getRawType(from)) && isAssignableFrom(from, (GenericArrayType) type);
+		} else {
+			throw buildUnexpectedTypeError(type, Class.class, ParameterizedType.class, GenericArrayType.class);
+		}
+	}
+
+	/**
+	 * Check if this type is assignable from the given type token.
+	 *
+	 * @deprecated this implementation may be inconsistent with javac for types with
+	 *             wildcards.
+	 */
+	@Deprecated
+	public boolean isAssignableFrom(TypeToken<?> token) {
+		return isAssignableFrom(token.getType());
+	}
+
+	/**
+	 * Private helper function that performs some assignability checks for the
+	 * provided GenericArrayType.
+	 */
+	private static boolean isAssignableFrom(Type from, GenericArrayType to) {
+		Type toGenericComponentType = to.getGenericComponentType();
+		if (toGenericComponentType instanceof ParameterizedType) {
+			Type t = from;
+			if (from instanceof GenericArrayType) {
+				t = ((GenericArrayType) from).getGenericComponentType();
+			} else if (from instanceof Class<?>) {
+				Class<?> classType = (Class<?>) from;
+				while (classType.isArray()) {
+					classType = classType.getComponentType();
+				}
+				t = classType;
+			}
+			return isAssignableFrom(t, (ParameterizedType) toGenericComponentType, new HashMap<String, Type>());
+		}
+		// No generic defined on "to"; therefore, return true and let other
+		// checks determine assignability
+		return true;
+	}
+
+	/**
+	 * Private recursive helper function to actually do the type-safe checking of
+	 * assignability.
+	 */
+	private static boolean isAssignableFrom(Type from, ParameterizedType to, Map<String, Type> typeVarMap) {
+
+		if (from == null) {
+			return false;
+		}
+
+		if (to.equals(from)) {
+			return true;
+		}
+
+		// First figure out the class and any type information.
+		Class<?> clazz = $Gson$Types.getRawType(from);
+		ParameterizedType ptype = null;
+		if (from instanceof ParameterizedType) {
+			ptype = (ParameterizedType) from;
+		}
+
+		// Load up parameterized variable info if it was parameterized.
+		if (ptype != null) {
+			Type[] tArgs = ptype.getActualTypeArguments();
+			TypeVariable<?>[] tParams = clazz.getTypeParameters();
+			for (int i = 0; i < tArgs.length; i++) {
+				Type arg = tArgs[i];
+				TypeVariable<?> var = tParams[i];
+				while (arg instanceof TypeVariable<?>) {
+					TypeVariable<?> v = (TypeVariable<?>) arg;
+					arg = typeVarMap.get(v.getName());
+				}
+				typeVarMap.put(var.getName(), arg);
+			}
+
+			// check if they are equivalent under our current mapping.
+			if (typeEquals(ptype, to, typeVarMap)) {
+				return true;
+			}
+		}
+
+		for (Type itype : clazz.getGenericInterfaces()) {
+			if (isAssignableFrom(itype, to, new HashMap<String, Type>(typeVarMap))) {
+				return true;
+			}
+		}
+
+		// Interfaces didn't work, try the superclass.
+		Type sType = clazz.getGenericSuperclass();
+		return isAssignableFrom(sType, to, new HashMap<String, Type>(typeVarMap));
+	}
+
+	/**
+	 * Checks if two parameterized types are exactly equal, under the variable
+	 * replacement described in the typeVarMap.
+	 */
+	private static boolean typeEquals(ParameterizedType from, ParameterizedType to, Map<String, Type> typeVarMap) {
+		if (from.getRawType().equals(to.getRawType())) {
+			Type[] fromArgs = from.getActualTypeArguments();
+			Type[] toArgs = to.getActualTypeArguments();
+			for (int i = 0; i < fromArgs.length; i++) {
+				if (!matches(fromArgs[i], toArgs[i], typeVarMap)) {
+					return false;
+				}
+			}
+			return true;
+		}
+		return false;
+	}
+
+	private static AssertionError buildUnexpectedTypeError(Type token, Class<?>... expected) {
+
+		// Build exception message
+		StringBuilder exceptionMessage = new StringBuilder("Unexpected type. Expected one of: ");
+		for (Class<?> clazz : expected) {
+			exceptionMessage.append(clazz.getName()).append(", ");
+		}
+		exceptionMessage.append("but got: ").append(token.getClass().getName()).append(", for type token: ").append(token.toString()).append('.');
+
+		return new AssertionError(exceptionMessage.toString());
+	}
+
+	/**
+	 * Checks if two types are the same or are equivalent under a variable mapping
+	 * given in the type map that was provided.
+	 */
+	private static boolean matches(Type from, Type to, Map<String, Type> typeMap) {
+		return to.equals(from) || (from instanceof TypeVariable && to.equals(typeMap.get(((TypeVariable<?>) from).getName())));
+
+	}
+
+	@Override
+	public final int hashCode() {
+		return this.hashCode;
+	}
+
+	@Override
+	public final boolean equals(Object o) {
+		return o instanceof TypeToken<?> && $Gson$Types.equals(type, ((TypeToken<?>) o).type);
+	}
+
+	@Override
+	public final String toString() {
+		return $Gson$Types.typeToString(type);
+	}
+
+	/**
+	 * Gets type literal for the given {@code Type} instance.
+	 */
+	public static TypeToken<?> get(Type type) {
+		return new TypeToken<Object>(type);
+	}
+
+	/**
+	 * Gets type literal for the given {@code Class} instance.
+	 */
+	public static <T> TypeToken<T> get(Class<T> type) {
+		return new TypeToken<T>(type);
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/reflect/package-info.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/reflect/package-info.java
new file mode 100644
index 0000000..42cd953
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/reflect/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * This package provides utility classes for finding type information for
+ * generic types.
+ * 
+ * @author Inderjeet Singh, Joel Leitch
+ */
+package cn.emay.sdk.util.json.gson.reflect;
\ No newline at end of file
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonReader.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonReader.java
new file mode 100644
index 0000000..ce682ed
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonReader.java
@@ -0,0 +1,1624 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.stream;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+
+import cn.emay.sdk.util.json.gson.internal.JsonReaderInternalAccess;
+import cn.emay.sdk.util.json.gson.internal.bind.JsonTreeReader;
+
+/**
+ * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
+ * encoded value as a stream of tokens. This stream includes both literal values
+ * (strings, numbers, booleans, and nulls) as well as the begin and end
+ * delimiters of objects and arrays. The tokens are traversed in depth-first
+ * order, the same order that they appear in the JSON document. Within JSON
+ * objects, name/value pairs are represented by a single token.
+ *
+ * <h3>Parsing JSON</h3> To create a recursive descent parser for your own JSON
+ * streams, first create an entry point method that creates a
+ * {@code JsonReader}.
+ *
+ * <p>
+ * Next, create handler methods for each structure in your JSON text. You'll
+ * need a method for each object type and for each array type.
+ * <ul>
+ * <li>Within <strong>array handling</strong> methods, first call
+ * {@link #beginArray} to consume the array's opening bracket. Then create a
+ * while loop that accumulates values, terminating when {@link #hasNext} is
+ * false. Finally, read the array's closing bracket by calling
+ * {@link #endArray}.
+ * <li>Within <strong>object handling</strong> methods, first call
+ * {@link #beginObject} to consume the object's opening brace. Then create a
+ * while loop that assigns values to local variables based on their name. This
+ * loop should terminate when {@link #hasNext} is false. Finally, read the
+ * object's closing brace by calling {@link #endObject}.
+ * </ul>
+ * <p>
+ * When a nested object or array is encountered, delegate to the corresponding
+ * handler method.
+ *
+ * <p>
+ * When an unknown name is encountered, strict parsers should fail with an
+ * exception. Lenient parsers should call {@link #skipValue()} to recursively
+ * skip the value's nested tokens, which may otherwise conflict.
+ *
+ * <p>
+ * If a value may be null, you should first check using {@link #peek()}. Null
+ * literals can be consumed using either {@link #nextNull()} or
+ * {@link #skipValue()}.
+ *
+ * <h3>Example</h3> Suppose we'd like to parse a stream of messages such as the
+ * following:
+ * 
+ * <pre>
+ *  {@code
+ * [
+ *   {
+ *     "id": 912345678901,
+ *     "text": "How do I read a JSON stream in Java?",
+ *     "geo": null,
+ *     "user": {
+ *       "name": "json_newb",
+ *       "followers_count": 41
+ *      }
+ *   },
+ *   {
+ *     "id": 912345678902,
+ *     "text": "@json_newb just use JsonReader!",
+ *     "geo": [50.454722, -104.606667],
+ *     "user": {
+ *       "name": "jesse",
+ *       "followers_count": 2
+ *     }
+ *   }
+ * ]}
+ * </pre>
+ * 
+ * This code implements the parser for the above structure:
+ * 
+ * <pre>
+ *    {@code
+ *
+ *   public List<Message> readJsonStream(InputStream in) throws IOException {
+ *     JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+ *     try {
+ *       return readMessagesArray(reader);
+ *     } finally {
+ *       reader.close();
+ *     }
+ *   }
+ *
+ *   public List<Message> readMessagesArray(JsonReader reader) throws IOException {
+ *     List<Message> messages = new ArrayList<Message>();
+ *
+ *     reader.beginArray();
+ *     while (reader.hasNext()) {
+ *       messages.add(readMessage(reader));
+ *     }
+ *     reader.endArray();
+ *     return messages;
+ *   }
+ *
+ *   public Message readMessage(JsonReader reader) throws IOException {
+ *     long id = -1;
+ *     String text = null;
+ *     User user = null;
+ *     List<Double> geo = null;
+ *
+ *     reader.beginObject();
+ *     while (reader.hasNext()) {
+ *       String name = reader.nextName();
+ *       if (name.equals("id")) {
+ *         id = reader.nextLong();
+ *       } else if (name.equals("text")) {
+ *         text = reader.nextString();
+ *       } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
+ *         geo = readDoublesArray(reader);
+ *       } else if (name.equals("user")) {
+ *         user = readUser(reader);
+ *       } else {
+ *         reader.skipValue();
+ *       }
+ *     }
+ *     reader.endObject();
+ *     return new Message(id, text, user, geo);
+ *   }
+ *
+ *   public List<Double> readDoublesArray(JsonReader reader) throws IOException {
+ *     List<Double> doubles = new ArrayList<Double>();
+ *
+ *     reader.beginArray();
+ *     while (reader.hasNext()) {
+ *       doubles.add(reader.nextDouble());
+ *     }
+ *     reader.endArray();
+ *     return doubles;
+ *   }
+ *
+ *   public User readUser(JsonReader reader) throws IOException {
+ *     String username = null;
+ *     int followersCount = -1;
+ *
+ *     reader.beginObject();
+ *     while (reader.hasNext()) {
+ *       String name = reader.nextName();
+ *       if (name.equals("name")) {
+ *         username = reader.nextString();
+ *       } else if (name.equals("followers_count")) {
+ *         followersCount = reader.nextInt();
+ *       } else {
+ *         reader.skipValue();
+ *       }
+ *     }
+ *     reader.endObject();
+ *     return new User(username, followersCount);
+ *   }}
+ * </pre>
+ *
+ * <h3>Number Handling</h3> This reader permits numeric values to be read as
+ * strings and string values to be read as numbers. For example, both elements
+ * of the JSON array {@code
+ * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
+ * This behavior is intended to prevent lossy numeric conversions: double is
+ * JavaScript's only numeric type and very large values like {@code
+ * 9007199254740993} cannot be represented exactly on that platform. To minimize
+ * precision loss, extremely large values should be written and read as strings
+ * in JSON.
+ *
+ * <a name="nonexecuteprefix"/>
+ * <h3>Non-Execute Prefix</h3> Web servers that serve private data using JSON
+ * may be vulnerable to <a href=
+ * "http://en.wikipedia.org/wiki/JSON#Cross-site_request_forgery">Cross-site
+ * request forgery</a> attacks. In such an attack, a malicious site gains access
+ * to a private JSON file by executing it with an HTML {@code <script>} tag.
+ *
+ * <p>
+ * Prefixing JSON files with <code>")]}'\n"</code> makes them non-executable by
+ * {@code <script>} tags, disarming the attack. Since the prefix is malformed
+ * JSON, strict parsing fails when it is encountered. This class permits the
+ * non-execute prefix when {@link #setLenient(boolean) lenient parsing} is
+ * enabled.
+ *
+ * <p>
+ * Each {@code JsonReader} may be used to read a single JSON stream. Instances
+ * of this class are not thread safe.
+ *
+ * @author Jesse Wilson
+ * @since 1.6
+ */
+public class JsonReader implements Closeable {
+	/** The only non-execute prefix this parser permits */
+	private static final char[] NON_EXECUTE_PREFIX = ")]}'\n".toCharArray();
+	private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10;
+
+	private static final int PEEKED_NONE = 0;
+	private static final int PEEKED_BEGIN_OBJECT = 1;
+	private static final int PEEKED_END_OBJECT = 2;
+	private static final int PEEKED_BEGIN_ARRAY = 3;
+	private static final int PEEKED_END_ARRAY = 4;
+	private static final int PEEKED_TRUE = 5;
+	private static final int PEEKED_FALSE = 6;
+	private static final int PEEKED_NULL = 7;
+	private static final int PEEKED_SINGLE_QUOTED = 8;
+	private static final int PEEKED_DOUBLE_QUOTED = 9;
+	private static final int PEEKED_UNQUOTED = 10;
+	/** When this is returned, the string value is stored in peekedString. */
+	private static final int PEEKED_BUFFERED = 11;
+	private static final int PEEKED_SINGLE_QUOTED_NAME = 12;
+	private static final int PEEKED_DOUBLE_QUOTED_NAME = 13;
+	private static final int PEEKED_UNQUOTED_NAME = 14;
+	/** When this is returned, the integer value is stored in peekedLong. */
+	private static final int PEEKED_LONG = 15;
+	private static final int PEEKED_NUMBER = 16;
+	private static final int PEEKED_EOF = 17;
+
+	/* State machine when parsing numbers */
+	private static final int NUMBER_CHAR_NONE = 0;
+	private static final int NUMBER_CHAR_SIGN = 1;
+	private static final int NUMBER_CHAR_DIGIT = 2;
+	private static final int NUMBER_CHAR_DECIMAL = 3;
+	private static final int NUMBER_CHAR_FRACTION_DIGIT = 4;
+	private static final int NUMBER_CHAR_EXP_E = 5;
+	private static final int NUMBER_CHAR_EXP_SIGN = 6;
+	private static final int NUMBER_CHAR_EXP_DIGIT = 7;
+
+	/** The input JSON. */
+	private final Reader in;
+
+	/** True to accept non-spec compliant JSON */
+	private boolean lenient = false;
+
+	/**
+	 * Use a manual buffer to easily read and unread upcoming characters, and also
+	 * so we can create strings without an intermediate StringBuilder. We decode
+	 * literals directly out of this buffer, so it must be at least as long as the
+	 * longest token that can be reported as a number.
+	 */
+	private final char[] buffer = new char[1024];
+	private int pos = 0;
+	private int limit = 0;
+
+	private int lineNumber = 0;
+	private int lineStart = 0;
+
+	int peeked = PEEKED_NONE;
+
+	/**
+	 * A peeked value that was composed entirely of digits with an optional leading
+	 * dash. Positive values may not have a leading 0.
+	 */
+	private long peekedLong;
+
+	/**
+	 * The number of characters in a peeked number literal. Increment 'pos' by this
+	 * after reading a number.
+	 */
+	private int peekedNumberLength;
+
+	/**
+	 * A peeked string that should be parsed on the next double, long or string.
+	 * This is populated before a numeric value is parsed and used if that parsing
+	 * fails.
+	 */
+	private String peekedString;
+
+	/*
+	 * The nesting stack. Using a manual array rather than an ArrayList saves 20%.
+	 */
+	private int[] stack = new int[32];
+	private int stackSize = 0;
+	{
+		stack[stackSize++] = JsonScope.EMPTY_DOCUMENT;
+	}
+
+	/*
+	 * The path members. It corresponds directly to stack: At indices where the
+	 * stack contains an object (EMPTY_OBJECT, DANGLING_NAME or NONEMPTY_OBJECT),
+	 * pathNames contains the name at this scope. Where it contains an array
+	 * (EMPTY_ARRAY, NONEMPTY_ARRAY) pathIndices contains the current index in that
+	 * array. Otherwise the value is undefined, and we take advantage of that by
+	 * incrementing pathIndices when doing so isn't useful.
+	 */
+	private String[] pathNames = new String[32];
+	private int[] pathIndices = new int[32];
+
+	/**
+	 * Creates a new instance that reads a JSON-encoded stream from {@code in}.
+	 */
+	public JsonReader(Reader in) {
+		if (in == null) {
+			throw new NullPointerException("in == null");
+		}
+		this.in = in;
+	}
+
+	/**
+	 * Configure this parser to be liberal in what it accepts. By default, this
+	 * parser is strict and only accepts JSON as specified by
+	 * <a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
+	 * parser to lenient causes it to ignore the following syntax errors:
+	 *
+	 * <ul>
+	 * <li>Streams that start with the <a href="#nonexecuteprefix">non-execute
+	 * prefix</a>, <code>")]}'\n"</code>.
+	 * <li>Streams that include multiple top-level values. With strict parsing, each
+	 * stream must contain exactly one top-level value.
+	 * <li>Top-level values of any type. With strict parsing, the top-level value
+	 * must be an object or an array.
+	 * <li>Numbers may be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
+	 * infinities}.
+	 * <li>End of line comments starting with {@code //} or {@code #} and ending
+	 * with a newline character.
+	 * <li>C-style comments starting with {@code /*} and ending with
+	 * {@code *}{@code /}. Such comments may not be nested.
+	 * <li>Names that are unquoted or {@code 'single quoted'}.
+	 * <li>Strings that are unquoted or {@code 'single quoted'}.
+	 * <li>Array elements separated by {@code ;} instead of {@code ,}.
+	 * <li>Unnecessary array separators. These are interpreted as if null was the
+	 * omitted value.
+	 * <li>Names and values separated by {@code =} or {@code =>} instead of
+	 * {@code :}.
+	 * <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
+	 * </ul>
+	 */
+	public final void setLenient(boolean lenient) {
+		this.lenient = lenient;
+	}
+
+	/**
+	 * Returns true if this parser is liberal in what it accepts.
+	 */
+	public final boolean isLenient() {
+		return lenient;
+	}
+
+	/**
+	 * Consumes the next token from the JSON stream and asserts that it is the
+	 * beginning of a new array.
+	 */
+	public void beginArray() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+		if (p == PEEKED_BEGIN_ARRAY) {
+			push(JsonScope.EMPTY_ARRAY);
+			pathIndices[stackSize - 1] = 0;
+			peeked = PEEKED_NONE;
+		} else {
+			throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+	}
+
+	/**
+	 * Consumes the next token from the JSON stream and asserts that it is the end
+	 * of the current array.
+	 */
+	public void endArray() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+		if (p == PEEKED_END_ARRAY) {
+			stackSize--;
+			pathIndices[stackSize - 1]++;
+			peeked = PEEKED_NONE;
+		} else {
+			throw new IllegalStateException("Expected END_ARRAY but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+	}
+
+	/**
+	 * Consumes the next token from the JSON stream and asserts that it is the
+	 * beginning of a new object.
+	 */
+	public void beginObject() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+		if (p == PEEKED_BEGIN_OBJECT) {
+			push(JsonScope.EMPTY_OBJECT);
+			peeked = PEEKED_NONE;
+		} else {
+			throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+	}
+
+	/**
+	 * Consumes the next token from the JSON stream and asserts that it is the end
+	 * of the current object.
+	 */
+	public void endObject() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+		if (p == PEEKED_END_OBJECT) {
+			stackSize--;
+			pathNames[stackSize] = null; // Free the last path name so that it can be garbage collected!
+			pathIndices[stackSize - 1]++;
+			peeked = PEEKED_NONE;
+		} else {
+			throw new IllegalStateException("Expected END_OBJECT but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+	}
+
+	/**
+	 * Returns true if the current array or object has another element.
+	 */
+	public boolean hasNext() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+		return p != PEEKED_END_OBJECT && p != PEEKED_END_ARRAY;
+	}
+
+	/**
+	 * Returns the type of the next token without consuming it.
+	 */
+	public JsonToken peek() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+
+		switch (p) {
+		case PEEKED_BEGIN_OBJECT:
+			return JsonToken.BEGIN_OBJECT;
+		case PEEKED_END_OBJECT:
+			return JsonToken.END_OBJECT;
+		case PEEKED_BEGIN_ARRAY:
+			return JsonToken.BEGIN_ARRAY;
+		case PEEKED_END_ARRAY:
+			return JsonToken.END_ARRAY;
+		case PEEKED_SINGLE_QUOTED_NAME:
+		case PEEKED_DOUBLE_QUOTED_NAME:
+		case PEEKED_UNQUOTED_NAME:
+			return JsonToken.NAME;
+		case PEEKED_TRUE:
+		case PEEKED_FALSE:
+			return JsonToken.BOOLEAN;
+		case PEEKED_NULL:
+			return JsonToken.NULL;
+		case PEEKED_SINGLE_QUOTED:
+		case PEEKED_DOUBLE_QUOTED:
+		case PEEKED_UNQUOTED:
+		case PEEKED_BUFFERED:
+			return JsonToken.STRING;
+		case PEEKED_LONG:
+		case PEEKED_NUMBER:
+			return JsonToken.NUMBER;
+		case PEEKED_EOF:
+			return JsonToken.END_DOCUMENT;
+		default:
+			throw new AssertionError();
+		}
+	}
+
+	int doPeek() throws IOException {
+		int peekStack = stack[stackSize - 1];
+		if (peekStack == JsonScope.EMPTY_ARRAY) {
+			stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;
+		} else if (peekStack == JsonScope.NONEMPTY_ARRAY) {
+			// Look for a comma before the next element.
+			int c = nextNonWhitespace(true);
+			switch (c) {
+			case ']':
+				return peeked = PEEKED_END_ARRAY;
+			case ';':
+				checkLenient(); // fall-through
+			case ',':
+				break;
+			default:
+				throw syntaxError("Unterminated array");
+			}
+		} else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) {
+			stack[stackSize - 1] = JsonScope.DANGLING_NAME;
+			// Look for a comma before the next element.
+			if (peekStack == JsonScope.NONEMPTY_OBJECT) {
+				int c = nextNonWhitespace(true);
+				switch (c) {
+				case '}':
+					return peeked = PEEKED_END_OBJECT;
+				case ';':
+					checkLenient(); // fall-through
+				case ',':
+					break;
+				default:
+					throw syntaxError("Unterminated object");
+				}
+			}
+			int c = nextNonWhitespace(true);
+			switch (c) {
+			case '"':
+				return peeked = PEEKED_DOUBLE_QUOTED_NAME;
+			case '\'':
+				checkLenient();
+				return peeked = PEEKED_SINGLE_QUOTED_NAME;
+			case '}':
+				if (peekStack != JsonScope.NONEMPTY_OBJECT) {
+					return peeked = PEEKED_END_OBJECT;
+				} else {
+					throw syntaxError("Expected name");
+				}
+			default:
+				checkLenient();
+				pos--; // Don't consume the first character in an unquoted string.
+				if (isLiteral((char) c)) {
+					return peeked = PEEKED_UNQUOTED_NAME;
+				} else {
+					throw syntaxError("Expected name");
+				}
+			}
+		} else if (peekStack == JsonScope.DANGLING_NAME) {
+			stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;
+			// Look for a colon before the value.
+			int c = nextNonWhitespace(true);
+			switch (c) {
+			case ':':
+				break;
+			case '=':
+				checkLenient();
+				if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
+					pos++;
+				}
+				break;
+			default:
+				throw syntaxError("Expected ':'");
+			}
+		} else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
+			if (lenient) {
+				consumeNonExecutePrefix();
+			}
+			stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
+		} else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
+			int c = nextNonWhitespace(false);
+			if (c == -1) {
+				return peeked = PEEKED_EOF;
+			} else {
+				checkLenient();
+				pos--;
+			}
+		} else if (peekStack == JsonScope.CLOSED) {
+			throw new IllegalStateException("JsonReader is closed");
+		}
+
+		int c = nextNonWhitespace(true);
+		switch (c) {
+		case ']':
+			if (peekStack == JsonScope.EMPTY_ARRAY) {
+				return peeked = PEEKED_END_ARRAY;
+			}
+			// fall-through to handle ",]"
+		case ';':
+		case ',':
+			// In lenient mode, a 0-length literal in an array means 'null'.
+			if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {
+				checkLenient();
+				pos--;
+				return peeked = PEEKED_NULL;
+			} else {
+				throw syntaxError("Unexpected value");
+			}
+		case '\'':
+			checkLenient();
+			return peeked = PEEKED_SINGLE_QUOTED;
+		case '"':
+			return peeked = PEEKED_DOUBLE_QUOTED;
+		case '[':
+			return peeked = PEEKED_BEGIN_ARRAY;
+		case '{':
+			return peeked = PEEKED_BEGIN_OBJECT;
+		default:
+			pos--; // Don't consume the first character in a literal value.
+		}
+
+		int result = peekKeyword();
+		if (result != PEEKED_NONE) {
+			return result;
+		}
+
+		result = peekNumber();
+		if (result != PEEKED_NONE) {
+			return result;
+		}
+
+		if (!isLiteral(buffer[pos])) {
+			throw syntaxError("Expected value");
+		}
+
+		checkLenient();
+		return peeked = PEEKED_UNQUOTED;
+	}
+
+	private int peekKeyword() throws IOException {
+		// Figure out which keyword we're matching against by its first character.
+		char c = buffer[pos];
+		String keyword;
+		String keywordUpper;
+		int peeking;
+		if (c == 't' || c == 'T') {
+			keyword = "true";
+			keywordUpper = "TRUE";
+			peeking = PEEKED_TRUE;
+		} else if (c == 'f' || c == 'F') {
+			keyword = "false";
+			keywordUpper = "FALSE";
+			peeking = PEEKED_FALSE;
+		} else if (c == 'n' || c == 'N') {
+			keyword = "null";
+			keywordUpper = "NULL";
+			peeking = PEEKED_NULL;
+		} else {
+			return PEEKED_NONE;
+		}
+
+		// Confirm that chars [1..length) match the keyword.
+		int length = keyword.length();
+		for (int i = 1; i < length; i++) {
+			if (pos + i >= limit && !fillBuffer(i + 1)) {
+				return PEEKED_NONE;
+			}
+			c = buffer[pos + i];
+			if (c != keyword.charAt(i) && c != keywordUpper.charAt(i)) {
+				return PEEKED_NONE;
+			}
+		}
+
+		if ((pos + length < limit || fillBuffer(length + 1)) && isLiteral(buffer[pos + length])) {
+			return PEEKED_NONE; // Don't match trues, falsey or nullsoft!
+		}
+
+		// We've found the keyword followed either by EOF or by a non-literal character.
+		pos += length;
+		return peeked = peeking;
+	}
+
+	private int peekNumber() throws IOException {
+		// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field
+		// access.
+		char[] buffer = this.buffer;
+		int p = pos;
+		int l = limit;
+
+		long value = 0; // Negative to accommodate Long.MIN_VALUE more easily.
+		boolean negative = false;
+		boolean fitsInLong = true;
+		int last = NUMBER_CHAR_NONE;
+
+		int i = 0;
+
+		charactersOfNumber: for (; true; i++) {
+			if (p + i == l) {
+				if (i == buffer.length) {
+					// Though this looks like a well-formed number, it's too long to continue
+					// reading. Give up
+					// and let the application handle this as an unquoted literal.
+					return PEEKED_NONE;
+				}
+				if (!fillBuffer(i + 1)) {
+					break;
+				}
+				p = pos;
+				l = limit;
+			}
+
+			char c = buffer[p + i];
+			switch (c) {
+			case '-':
+				if (last == NUMBER_CHAR_NONE) {
+					negative = true;
+					last = NUMBER_CHAR_SIGN;
+					continue;
+				} else if (last == NUMBER_CHAR_EXP_E) {
+					last = NUMBER_CHAR_EXP_SIGN;
+					continue;
+				}
+				return PEEKED_NONE;
+
+			case '+':
+				if (last == NUMBER_CHAR_EXP_E) {
+					last = NUMBER_CHAR_EXP_SIGN;
+					continue;
+				}
+				return PEEKED_NONE;
+
+			case 'e':
+			case 'E':
+				if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT) {
+					last = NUMBER_CHAR_EXP_E;
+					continue;
+				}
+				return PEEKED_NONE;
+
+			case '.':
+				if (last == NUMBER_CHAR_DIGIT) {
+					last = NUMBER_CHAR_DECIMAL;
+					continue;
+				}
+				return PEEKED_NONE;
+
+			default:
+				if (c < '0' || c > '9') {
+					if (!isLiteral(c)) {
+						break charactersOfNumber;
+					}
+					return PEEKED_NONE;
+				}
+				if (last == NUMBER_CHAR_SIGN || last == NUMBER_CHAR_NONE) {
+					value = -(c - '0');
+					last = NUMBER_CHAR_DIGIT;
+				} else if (last == NUMBER_CHAR_DIGIT) {
+					if (value == 0) {
+						return PEEKED_NONE; // Leading '0' prefix is not allowed (since it could be octal).
+					}
+					long newValue = value * 10 - (c - '0');
+					fitsInLong &= value > MIN_INCOMPLETE_INTEGER || (value == MIN_INCOMPLETE_INTEGER && newValue < value);
+					value = newValue;
+				} else if (last == NUMBER_CHAR_DECIMAL) {
+					last = NUMBER_CHAR_FRACTION_DIGIT;
+				} else if (last == NUMBER_CHAR_EXP_E || last == NUMBER_CHAR_EXP_SIGN) {
+					last = NUMBER_CHAR_EXP_DIGIT;
+				}
+			}
+		}
+
+		// We've read a complete number. Decide if it's a PEEKED_LONG or a
+		// PEEKED_NUMBER.
+		if (last == NUMBER_CHAR_DIGIT && fitsInLong && (value != Long.MIN_VALUE || negative)) {
+			peekedLong = negative ? value : -value;
+			pos += i;
+			return peeked = PEEKED_LONG;
+		} else if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT || last == NUMBER_CHAR_EXP_DIGIT) {
+			peekedNumberLength = i;
+			return peeked = PEEKED_NUMBER;
+		} else {
+			return PEEKED_NONE;
+		}
+	}
+
+	private boolean isLiteral(char c) throws IOException {
+		switch (c) {
+		case '/':
+		case '\\':
+		case ';':
+		case '#':
+		case '=':
+			checkLenient(); // fall-through
+		case '{':
+		case '}':
+		case '[':
+		case ']':
+		case ':':
+		case ',':
+		case ' ':
+		case '\t':
+		case '\f':
+		case '\r':
+		case '\n':
+			return false;
+		default:
+			return true;
+		}
+	}
+
+	/**
+	 * Returns the next token, a {@link com.google.gson.stream.JsonToken#NAME
+	 * property name}, and consumes it.
+	 *
+	 * @throws java.io.IOException
+	 *             if the next token in the stream is not a property name.
+	 */
+	public String nextName() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+		String result;
+		if (p == PEEKED_UNQUOTED_NAME) {
+			result = nextUnquotedValue();
+		} else if (p == PEEKED_SINGLE_QUOTED_NAME) {
+			result = nextQuotedValue('\'');
+		} else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
+			result = nextQuotedValue('"');
+		} else {
+			throw new IllegalStateException("Expected a name but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+		peeked = PEEKED_NONE;
+		pathNames[stackSize - 1] = result;
+		return result;
+	}
+
+	/**
+	 * Returns the {@link com.google.gson.stream.JsonToken#STRING string} value of
+	 * the next token, consuming it. If the next token is a number, this method will
+	 * return its string form.
+	 *
+	 * @throws IllegalStateException
+	 *             if the next token is not a string or if this reader is closed.
+	 */
+	public String nextString() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+		String result;
+		if (p == PEEKED_UNQUOTED) {
+			result = nextUnquotedValue();
+		} else if (p == PEEKED_SINGLE_QUOTED) {
+			result = nextQuotedValue('\'');
+		} else if (p == PEEKED_DOUBLE_QUOTED) {
+			result = nextQuotedValue('"');
+		} else if (p == PEEKED_BUFFERED) {
+			result = peekedString;
+			peekedString = null;
+		} else if (p == PEEKED_LONG) {
+			result = Long.toString(peekedLong);
+		} else if (p == PEEKED_NUMBER) {
+			result = new String(buffer, pos, peekedNumberLength);
+			pos += peekedNumberLength;
+		} else {
+			throw new IllegalStateException("Expected a string but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+		peeked = PEEKED_NONE;
+		pathIndices[stackSize - 1]++;
+		return result;
+	}
+
+	/**
+	 * Returns the {@link com.google.gson.stream.JsonToken#BOOLEAN boolean} value of
+	 * the next token, consuming it.
+	 *
+	 * @throws IllegalStateException
+	 *             if the next token is not a boolean or if this reader is closed.
+	 */
+	public boolean nextBoolean() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+		if (p == PEEKED_TRUE) {
+			peeked = PEEKED_NONE;
+			pathIndices[stackSize - 1]++;
+			return true;
+		} else if (p == PEEKED_FALSE) {
+			peeked = PEEKED_NONE;
+			pathIndices[stackSize - 1]++;
+			return false;
+		}
+		throw new IllegalStateException("Expected a boolean but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+	}
+
+	/**
+	 * Consumes the next token from the JSON stream and asserts that it is a literal
+	 * null.
+	 *
+	 * @throws IllegalStateException
+	 *             if the next token is not null or if this reader is closed.
+	 */
+	public void nextNull() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+		if (p == PEEKED_NULL) {
+			peeked = PEEKED_NONE;
+			pathIndices[stackSize - 1]++;
+		} else {
+			throw new IllegalStateException("Expected null but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+	}
+
+	/**
+	 * Returns the {@link com.google.gson.stream.JsonToken#NUMBER double} value of
+	 * the next token, consuming it. If the next token is a string, this method will
+	 * attempt to parse it as a double using {@link Double#parseDouble(String)}.
+	 *
+	 * @throws IllegalStateException
+	 *             if the next token is not a literal value.
+	 * @throws NumberFormatException
+	 *             if the next literal value cannot be parsed as a double, or is
+	 *             non-finite.
+	 */
+	public double nextDouble() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+
+		if (p == PEEKED_LONG) {
+			peeked = PEEKED_NONE;
+			pathIndices[stackSize - 1]++;
+			return (double) peekedLong;
+		}
+
+		if (p == PEEKED_NUMBER) {
+			peekedString = new String(buffer, pos, peekedNumberLength);
+			pos += peekedNumberLength;
+		} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
+			peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
+		} else if (p == PEEKED_UNQUOTED) {
+			peekedString = nextUnquotedValue();
+		} else if (p != PEEKED_BUFFERED) {
+			throw new IllegalStateException("Expected a double but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+
+		peeked = PEEKED_BUFFERED;
+		double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
+		if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
+			throw new MalformedJsonException("JSON forbids NaN and infinities: " + result + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+		peekedString = null;
+		peeked = PEEKED_NONE;
+		pathIndices[stackSize - 1]++;
+		return result;
+	}
+
+	/**
+	 * Returns the {@link com.google.gson.stream.JsonToken#NUMBER long} value of the
+	 * next token, consuming it. If the next token is a string, this method will
+	 * attempt to parse it as a long. If the next token's numeric value cannot be
+	 * exactly represented by a Java {@code long}, this method throws.
+	 *
+	 * @throws IllegalStateException
+	 *             if the next token is not a literal value.
+	 * @throws NumberFormatException
+	 *             if the next literal value cannot be parsed as a number, or
+	 *             exactly represented as a long.
+	 */
+	public long nextLong() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+
+		if (p == PEEKED_LONG) {
+			peeked = PEEKED_NONE;
+			pathIndices[stackSize - 1]++;
+			return peekedLong;
+		}
+
+		if (p == PEEKED_NUMBER) {
+			peekedString = new String(buffer, pos, peekedNumberLength);
+			pos += peekedNumberLength;
+		} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
+			peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
+			try {
+				long result = Long.parseLong(peekedString);
+				peeked = PEEKED_NONE;
+				pathIndices[stackSize - 1]++;
+				return result;
+			} catch (NumberFormatException ignored) {
+				// Fall back to parse as a double below.
+			}
+		} else {
+			throw new IllegalStateException("Expected a long but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+
+		peeked = PEEKED_BUFFERED;
+		double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
+		long result = (long) asDouble;
+		if (result != asDouble) { // Make sure no precision was lost casting to 'long'.
+			throw new NumberFormatException("Expected a long but was " + peekedString + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+		peekedString = null;
+		peeked = PEEKED_NONE;
+		pathIndices[stackSize - 1]++;
+		return result;
+	}
+
+	/**
+	 * Returns the string up to but not including {@code quote}, unescaping any
+	 * character escape sequences encountered along the way. The opening quote
+	 * should have already been read. This consumes the closing quote, but does not
+	 * include it in the returned string.
+	 *
+	 * @param quote
+	 *            either ' or ".
+	 * @throws NumberFormatException
+	 *             if any unicode escape sequences are malformed.
+	 */
+	private String nextQuotedValue(char quote) throws IOException {
+		// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field
+		// access.
+		char[] buffer = this.buffer;
+		StringBuilder builder = new StringBuilder();
+		while (true) {
+			int p = pos;
+			int l = limit;
+			/* the index of the first character not yet appended to the builder. */
+			int start = p;
+			while (p < l) {
+				int c = buffer[p++];
+
+				if (c == quote) {
+					pos = p;
+					builder.append(buffer, start, p - start - 1);
+					return builder.toString();
+				} else if (c == '\\') {
+					pos = p;
+					builder.append(buffer, start, p - start - 1);
+					builder.append(readEscapeCharacter());
+					p = pos;
+					l = limit;
+					start = p;
+				} else if (c == '\n') {
+					lineNumber++;
+					lineStart = p;
+				}
+			}
+
+			builder.append(buffer, start, p - start);
+			pos = p;
+			if (!fillBuffer(1)) {
+				throw syntaxError("Unterminated string");
+			}
+		}
+	}
+
+	/**
+	 * Returns an unquoted value as a string.
+	 */
+	@SuppressWarnings("fallthrough")
+	private String nextUnquotedValue() throws IOException {
+		StringBuilder builder = null;
+		int i = 0;
+
+		findNonLiteralCharacter: while (true) {
+			for (; pos + i < limit; i++) {
+				switch (buffer[pos + i]) {
+				case '/':
+				case '\\':
+				case ';':
+				case '#':
+				case '=':
+					checkLenient(); // fall-through
+				case '{':
+				case '}':
+				case '[':
+				case ']':
+				case ':':
+				case ',':
+				case ' ':
+				case '\t':
+				case '\f':
+				case '\r':
+				case '\n':
+					break findNonLiteralCharacter;
+				}
+			}
+
+			// Attempt to load the entire literal into the buffer at once.
+			if (i < buffer.length) {
+				if (fillBuffer(i + 1)) {
+					continue;
+				} else {
+					break;
+				}
+			}
+
+			// use a StringBuilder when the value is too long. This is too long to be a
+			// number!
+			if (builder == null) {
+				builder = new StringBuilder();
+			}
+			builder.append(buffer, pos, i);
+			pos += i;
+			i = 0;
+			if (!fillBuffer(1)) {
+				break;
+			}
+		}
+
+		String result;
+		if (builder == null) {
+			result = new String(buffer, pos, i);
+		} else {
+			builder.append(buffer, pos, i);
+			result = builder.toString();
+		}
+		pos += i;
+		return result;
+	}
+
+	private void skipQuotedValue(char quote) throws IOException {
+		// Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field
+		// access.
+		char[] buffer = this.buffer;
+		do {
+			int p = pos;
+			int l = limit;
+			/* the index of the first character not yet appended to the builder. */
+			while (p < l) {
+				int c = buffer[p++];
+				if (c == quote) {
+					pos = p;
+					return;
+				} else if (c == '\\') {
+					pos = p;
+					readEscapeCharacter();
+					p = pos;
+					l = limit;
+				} else if (c == '\n') {
+					lineNumber++;
+					lineStart = p;
+				}
+			}
+			pos = p;
+		} while (fillBuffer(1));
+		throw syntaxError("Unterminated string");
+	}
+
+	private void skipUnquotedValue() throws IOException {
+		do {
+			int i = 0;
+			for (; pos + i < limit; i++) {
+				switch (buffer[pos + i]) {
+				case '/':
+				case '\\':
+				case ';':
+				case '#':
+				case '=':
+					checkLenient(); // fall-through
+				case '{':
+				case '}':
+				case '[':
+				case ']':
+				case ':':
+				case ',':
+				case ' ':
+				case '\t':
+				case '\f':
+				case '\r':
+				case '\n':
+					pos += i;
+					return;
+				}
+			}
+			pos += i;
+		} while (fillBuffer(1));
+	}
+
+	/**
+	 * Returns the {@link com.google.gson.stream.JsonToken#NUMBER int} value of the
+	 * next token, consuming it. If the next token is a string, this method will
+	 * attempt to parse it as an int. If the next token's numeric value cannot be
+	 * exactly represented by a Java {@code int}, this method throws.
+	 *
+	 * @throws IllegalStateException
+	 *             if the next token is not a literal value.
+	 * @throws NumberFormatException
+	 *             if the next literal value cannot be parsed as a number, or
+	 *             exactly represented as an int.
+	 */
+	public int nextInt() throws IOException {
+		int p = peeked;
+		if (p == PEEKED_NONE) {
+			p = doPeek();
+		}
+
+		int result;
+		if (p == PEEKED_LONG) {
+			result = (int) peekedLong;
+			if (peekedLong != result) { // Make sure no precision was lost casting to 'int'.
+				throw new NumberFormatException("Expected an int but was " + peekedLong + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+			}
+			peeked = PEEKED_NONE;
+			pathIndices[stackSize - 1]++;
+			return result;
+		}
+
+		if (p == PEEKED_NUMBER) {
+			peekedString = new String(buffer, pos, peekedNumberLength);
+			pos += peekedNumberLength;
+		} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
+			peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
+			try {
+				result = Integer.parseInt(peekedString);
+				peeked = PEEKED_NONE;
+				pathIndices[stackSize - 1]++;
+				return result;
+			} catch (NumberFormatException ignored) {
+				// Fall back to parse as a double below.
+			}
+		} else {
+			throw new IllegalStateException("Expected an int but was " + peek() + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+
+		peeked = PEEKED_BUFFERED;
+		double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
+		result = (int) asDouble;
+		if (result != asDouble) { // Make sure no precision was lost casting to 'int'.
+			throw new NumberFormatException("Expected an int but was " + peekedString + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+		}
+		peekedString = null;
+		peeked = PEEKED_NONE;
+		pathIndices[stackSize - 1]++;
+		return result;
+	}
+
+	/**
+	 * Closes this JSON reader and the underlying {@link java.io.Reader}.
+	 */
+	public void close() throws IOException {
+		peeked = PEEKED_NONE;
+		stack[0] = JsonScope.CLOSED;
+		stackSize = 1;
+		in.close();
+	}
+
+	/**
+	 * Skips the next value recursively. If it is an object or array, all nested
+	 * elements are skipped. This method is intended for use when the JSON token
+	 * stream contains unrecognized or unhandled values.
+	 */
+	public void skipValue() throws IOException {
+		int count = 0;
+		do {
+			int p = peeked;
+			if (p == PEEKED_NONE) {
+				p = doPeek();
+			}
+
+			if (p == PEEKED_BEGIN_ARRAY) {
+				push(JsonScope.EMPTY_ARRAY);
+				count++;
+			} else if (p == PEEKED_BEGIN_OBJECT) {
+				push(JsonScope.EMPTY_OBJECT);
+				count++;
+			} else if (p == PEEKED_END_ARRAY) {
+				stackSize--;
+				count--;
+			} else if (p == PEEKED_END_OBJECT) {
+				stackSize--;
+				count--;
+			} else if (p == PEEKED_UNQUOTED_NAME || p == PEEKED_UNQUOTED) {
+				skipUnquotedValue();
+			} else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_SINGLE_QUOTED_NAME) {
+				skipQuotedValue('\'');
+			} else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) {
+				skipQuotedValue('"');
+			} else if (p == PEEKED_NUMBER) {
+				pos += peekedNumberLength;
+			}
+			peeked = PEEKED_NONE;
+		} while (count != 0);
+
+		pathIndices[stackSize - 1]++;
+		pathNames[stackSize - 1] = "null";
+	}
+
+	private void push(int newTop) {
+		if (stackSize == stack.length) {
+			int[] newStack = new int[stackSize * 2];
+			int[] newPathIndices = new int[stackSize * 2];
+			String[] newPathNames = new String[stackSize * 2];
+			System.arraycopy(stack, 0, newStack, 0, stackSize);
+			System.arraycopy(pathIndices, 0, newPathIndices, 0, stackSize);
+			System.arraycopy(pathNames, 0, newPathNames, 0, stackSize);
+			stack = newStack;
+			pathIndices = newPathIndices;
+			pathNames = newPathNames;
+		}
+		stack[stackSize++] = newTop;
+	}
+
+	/**
+	 * Returns true once {@code limit - pos >= minimum}. If the data is exhausted
+	 * before that many characters are available, this returns false.
+	 */
+	private boolean fillBuffer(int minimum) throws IOException {
+		char[] buffer = this.buffer;
+		lineStart -= pos;
+		if (limit != pos) {
+			limit -= pos;
+			System.arraycopy(buffer, pos, buffer, 0, limit);
+		} else {
+			limit = 0;
+		}
+
+		pos = 0;
+		int total;
+		while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) {
+			limit += total;
+
+			// if this is the first read, consume an optional byte order mark (BOM) if it
+			// exists
+			if (lineNumber == 0 && lineStart == 0 && limit > 0 && buffer[0] == '\ufeff') {
+				pos++;
+				lineStart++;
+				minimum++;
+			}
+
+			if (limit >= minimum) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	int getLineNumber() {
+		return lineNumber + 1;
+	}
+
+	int getColumnNumber() {
+		return pos - lineStart + 1;
+	}
+
+	/**
+	 * Returns the next character in the stream that is neither whitespace nor a
+	 * part of a comment. When this returns, the returned character is always at
+	 * {@code buffer[pos-1]}; this means the caller can always push back the
+	 * returned character by decrementing {@code pos}.
+	 */
+	private int nextNonWhitespace(boolean throwOnEof) throws IOException {
+		/*
+		 * This code uses ugly local variables 'p' and 'l' representing the 'pos' and
+		 * 'limit' fields respectively. Using locals rather than fields saves a few
+		 * field reads for each whitespace character in a pretty-printed document,
+		 * resulting in a 5% speedup. We need to flush 'p' to its field before any
+		 * (potentially indirect) call to fillBuffer() and reread both 'p' and 'l' after
+		 * any (potentially indirect) call to the same method.
+		 */
+		char[] buffer = this.buffer;
+		int p = pos;
+		int l = limit;
+		while (true) {
+			if (p == l) {
+				pos = p;
+				if (!fillBuffer(1)) {
+					break;
+				}
+				p = pos;
+				l = limit;
+			}
+
+			int c = buffer[p++];
+			if (c == '\n') {
+				lineNumber++;
+				lineStart = p;
+				continue;
+			} else if (c == ' ' || c == '\r' || c == '\t') {
+				continue;
+			}
+
+			if (c == '/') {
+				pos = p;
+				if (p == l) {
+					pos--; // push back '/' so it's still in the buffer when this method returns
+					boolean charsLoaded = fillBuffer(2);
+					pos++; // consume the '/' again
+					if (!charsLoaded) {
+						return c;
+					}
+				}
+
+				checkLenient();
+				char peek = buffer[pos];
+				switch (peek) {
+				case '*':
+					// skip a /* c-style comment */
+					pos++;
+					if (!skipTo("*/")) {
+						throw syntaxError("Unterminated comment");
+					}
+					p = pos + 2;
+					l = limit;
+					continue;
+
+				case '/':
+					// skip a // end-of-line comment
+					pos++;
+					skipToEndOfLine();
+					p = pos;
+					l = limit;
+					continue;
+
+				default:
+					return c;
+				}
+			} else if (c == '#') {
+				pos = p;
+				/*
+				 * Skip a # hash end-of-line comment. The JSON RFC doesn't specify this
+				 * behaviour, but it's required to parse existing documents. See
+				 * http://b/2571423.
+				 */
+				checkLenient();
+				skipToEndOfLine();
+				p = pos;
+				l = limit;
+			} else {
+				pos = p;
+				return c;
+			}
+		}
+		if (throwOnEof) {
+			throw new EOFException("End of input" + " at line " + getLineNumber() + " column " + getColumnNumber());
+		} else {
+			return -1;
+		}
+	}
+
+	private void checkLenient() throws IOException {
+		if (!lenient) {
+			throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON");
+		}
+	}
+
+	/**
+	 * Advances the position until after the next newline character. If the line is
+	 * terminated by "\r\n", the '\n' must be consumed as whitespace by the caller.
+	 */
+	private void skipToEndOfLine() throws IOException {
+		while (pos < limit || fillBuffer(1)) {
+			char c = buffer[pos++];
+			if (c == '\n') {
+				lineNumber++;
+				lineStart = pos;
+				break;
+			} else if (c == '\r') {
+				break;
+			}
+		}
+	}
+
+	/**
+	 * @param toFind
+	 *            a string to search for. Must not contain a newline.
+	 */
+	private boolean skipTo(String toFind) throws IOException {
+		outer: for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) {
+			if (buffer[pos] == '\n') {
+				lineNumber++;
+				lineStart = pos + 1;
+				continue;
+			}
+			for (int c = 0; c < toFind.length(); c++) {
+				if (buffer[pos + c] != toFind.charAt(c)) {
+					continue outer;
+				}
+			}
+			return true;
+		}
+		return false;
+	}
+
+	@Override
+	public String toString() {
+		return getClass().getSimpleName() + " at line " + getLineNumber() + " column " + getColumnNumber();
+	}
+
+	/**
+	 * Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to
+	 * the current location in the JSON value.
+	 */
+	public String getPath() {
+		StringBuilder result = new StringBuilder().append('$');
+		for (int i = 0, size = stackSize; i < size; i++) {
+			switch (stack[i]) {
+			case JsonScope.EMPTY_ARRAY:
+			case JsonScope.NONEMPTY_ARRAY:
+				result.append('[').append(pathIndices[i]).append(']');
+				break;
+
+			case JsonScope.EMPTY_OBJECT:
+			case JsonScope.DANGLING_NAME:
+			case JsonScope.NONEMPTY_OBJECT:
+				result.append('.');
+				if (pathNames[i] != null) {
+					result.append(pathNames[i]);
+				}
+				break;
+
+			case JsonScope.NONEMPTY_DOCUMENT:
+			case JsonScope.EMPTY_DOCUMENT:
+			case JsonScope.CLOSED:
+				break;
+			}
+		}
+		return result.toString();
+	}
+
+	/**
+	 * Unescapes the character identified by the character or characters that
+	 * immediately follow a backslash. The backslash '\' should have already been
+	 * read. This supports both unicode escapes "u000A" and two-character escapes
+	 * "\n".
+	 *
+	 * @throws NumberFormatException
+	 *             if any unicode escape sequences are malformed.
+	 */
+	private char readEscapeCharacter() throws IOException {
+		if (pos == limit && !fillBuffer(1)) {
+			throw syntaxError("Unterminated escape sequence");
+		}
+
+		char escaped = buffer[pos++];
+		switch (escaped) {
+		case 'u':
+			if (pos + 4 > limit && !fillBuffer(4)) {
+				throw syntaxError("Unterminated escape sequence");
+			}
+			// Equivalent to Integer.parseInt(stringPool.get(buffer, pos, 4), 16);
+			char result = 0;
+			for (int i = pos, end = i + 4; i < end; i++) {
+				char c = buffer[i];
+				result <<= 4;
+				if (c >= '0' && c <= '9') {
+					result += (c - '0');
+				} else if (c >= 'a' && c <= 'f') {
+					result += (c - 'a' + 10);
+				} else if (c >= 'A' && c <= 'F') {
+					result += (c - 'A' + 10);
+				} else {
+					throw new NumberFormatException("\\u" + new String(buffer, pos, 4));
+				}
+			}
+			pos += 4;
+			return result;
+
+		case 't':
+			return '\t';
+
+		case 'b':
+			return '\b';
+
+		case 'n':
+			return '\n';
+
+		case 'r':
+			return '\r';
+
+		case 'f':
+			return '\f';
+
+		case '\n':
+			lineNumber++;
+			lineStart = pos;
+			// fall-through
+
+		case '\'':
+		case '"':
+		case '\\':
+		default:
+			return escaped;
+		}
+	}
+
+	/**
+	 * Throws a new IO exception with the given message and a context snippet with
+	 * this reader's content.
+	 */
+	private IOException syntaxError(String message) throws IOException {
+		throw new MalformedJsonException(message + " at line " + getLineNumber() + " column " + getColumnNumber() + " path " + getPath());
+	}
+
+	/**
+	 * Consumes the non-execute prefix if it exists.
+	 */
+	private void consumeNonExecutePrefix() throws IOException {
+		// fast forward through the leading whitespace
+		nextNonWhitespace(true);
+		pos--;
+
+		if (pos + NON_EXECUTE_PREFIX.length > limit && !fillBuffer(NON_EXECUTE_PREFIX.length)) {
+			return;
+		}
+
+		for (int i = 0; i < NON_EXECUTE_PREFIX.length; i++) {
+			if (buffer[pos + i] != NON_EXECUTE_PREFIX[i]) {
+				return; // not a security token!
+			}
+		}
+
+		// we consumed a security token!
+		pos += NON_EXECUTE_PREFIX.length;
+	}
+
+	static {
+		JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() {
+			@Override
+			public void promoteNameToValue(JsonReader reader) throws IOException {
+				if (reader instanceof JsonTreeReader) {
+					((JsonTreeReader) reader).promoteNameToValue();
+					return;
+				}
+				int p = reader.peeked;
+				if (p == PEEKED_NONE) {
+					p = reader.doPeek();
+				}
+				if (p == PEEKED_DOUBLE_QUOTED_NAME) {
+					reader.peeked = PEEKED_DOUBLE_QUOTED;
+				} else if (p == PEEKED_SINGLE_QUOTED_NAME) {
+					reader.peeked = PEEKED_SINGLE_QUOTED;
+				} else if (p == PEEKED_UNQUOTED_NAME) {
+					reader.peeked = PEEKED_UNQUOTED;
+				} else {
+					throw new IllegalStateException(
+							"Expected a name but was " + reader.peek() + " " + " at line " + reader.getLineNumber() + " column " + reader.getColumnNumber() + " path " + reader.getPath());
+				}
+			}
+		};
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonScope.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonScope.java
new file mode 100644
index 0000000..88fc9f0
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonScope.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.stream;
+
+/**
+ * Lexical scoping elements within a JSON reader or writer.
+ *
+ * @author Jesse Wilson
+ * @since 1.6
+ */
+final class JsonScope {
+
+	/**
+	 * An array with no elements requires no separators or newlines before it is
+	 * closed.
+	 */
+	static final int EMPTY_ARRAY = 1;
+
+	/**
+	 * A array with at least one value requires a comma and newline before the next
+	 * element.
+	 */
+	static final int NONEMPTY_ARRAY = 2;
+
+	/**
+	 * An object with no name/value pairs requires no separators or newlines before
+	 * it is closed.
+	 */
+	static final int EMPTY_OBJECT = 3;
+
+	/**
+	 * An object whose most recent element is a key. The next element must be a
+	 * value.
+	 */
+	static final int DANGLING_NAME = 4;
+
+	/**
+	 * An object with at least one name/value pair requires a comma and newline
+	 * before the next element.
+	 */
+	static final int NONEMPTY_OBJECT = 5;
+
+	/**
+	 * No object or array has been started.
+	 */
+	static final int EMPTY_DOCUMENT = 6;
+
+	/**
+	 * A document with at an array or object.
+	 */
+	static final int NONEMPTY_DOCUMENT = 7;
+
+	/**
+	 * A document that's been closed and cannot be accessed.
+	 */
+	static final int CLOSED = 8;
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonToken.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonToken.java
new file mode 100644
index 0000000..89eb37d
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonToken.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.stream;
+
+/**
+ * A structure, name or value type in a JSON-encoded string.
+ *
+ * @author Jesse Wilson
+ * @since 1.6
+ */
+public enum JsonToken {
+
+	/**
+	 * The opening of a JSON array. Written using {@link JsonWriter#beginArray} and
+	 * read using {@link JsonReader#beginArray}.
+	 */
+	BEGIN_ARRAY,
+
+	/**
+	 * The closing of a JSON array. Written using {@link JsonWriter#endArray} and
+	 * read using {@link JsonReader#endArray}.
+	 */
+	END_ARRAY,
+
+	/**
+	 * The opening of a JSON object. Written using {@link JsonWriter#beginObject}
+	 * and read using {@link JsonReader#beginObject}.
+	 */
+	BEGIN_OBJECT,
+
+	/**
+	 * The closing of a JSON object. Written using {@link JsonWriter#endObject} and
+	 * read using {@link JsonReader#endObject}.
+	 */
+	END_OBJECT,
+
+	/**
+	 * A JSON property name. Within objects, tokens alternate between names and
+	 * their values. Written using {@link JsonWriter#name} and read using
+	 * {@link JsonReader#nextName}
+	 */
+	NAME,
+
+	/**
+	 * A JSON string.
+	 */
+	STRING,
+
+	/**
+	 * A JSON number represented in this API by a Java {@code double}, {@code
+	 * long}, or {@code int}.
+	 */
+	NUMBER,
+
+	/**
+	 * A JSON {@code true} or {@code false}.
+	 */
+	BOOLEAN,
+
+	/**
+	 * A JSON {@code null}.
+	 */
+	NULL,
+
+	/**
+	 * The end of the JSON stream. This sentinel value is returned by
+	 * {@link JsonReader#peek()} to signal that the JSON-encoded value has no more
+	 * tokens.
+	 */
+	END_DOCUMENT
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonWriter.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonWriter.java
new file mode 100644
index 0000000..0eb4260
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/JsonWriter.java
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.stream;
+
+import static cn.emay.sdk.util.json.gson.stream.JsonScope.DANGLING_NAME;
+import static cn.emay.sdk.util.json.gson.stream.JsonScope.EMPTY_ARRAY;
+import static cn.emay.sdk.util.json.gson.stream.JsonScope.EMPTY_DOCUMENT;
+import static cn.emay.sdk.util.json.gson.stream.JsonScope.EMPTY_OBJECT;
+import static cn.emay.sdk.util.json.gson.stream.JsonScope.NONEMPTY_ARRAY;
+import static cn.emay.sdk.util.json.gson.stream.JsonScope.NONEMPTY_DOCUMENT;
+import static cn.emay.sdk.util.json.gson.stream.JsonScope.NONEMPTY_OBJECT;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
+ * encoded value to a stream, one token at a time. The stream includes both
+ * literal values (strings, numbers, booleans and nulls) as well as the begin
+ * and end delimiters of objects and arrays.
+ *
+ * <h3>Encoding JSON</h3> To encode your data as JSON, create a new
+ * {@code JsonWriter}. Each JSON document must contain one top-level array or
+ * object. Call methods on the writer as you walk the structure's contents,
+ * nesting arrays and objects as necessary:
+ * <ul>
+ * <li>To write <strong>arrays</strong>, first call {@link #beginArray()}. Write
+ * each of the array's elements with the appropriate {@link #value} methods or
+ * by nesting other arrays and objects. Finally close the array using
+ * {@link #endArray()}.
+ * <li>To write <strong>objects</strong>, first call {@link #beginObject()}.
+ * Write each of the object's properties by alternating calls to {@link #name}
+ * with the property's value. Write property values with the appropriate
+ * {@link #value} method or by nesting other objects or arrays. Finally close
+ * the object using {@link #endObject()}.
+ * </ul>
+ *
+ * <h3>Example</h3> Suppose we'd like to encode a stream of messages such as the
+ * following:
+ * 
+ * <pre>
+ *  {@code
+ * [
+ *   {
+ *     "id": 912345678901,
+ *     "text": "How do I stream JSON in Java?",
+ *     "geo": null,
+ *     "user": {
+ *       "name": "json_newb",
+ *       "followers_count": 41
+ *      }
+ *   },
+ *   {
+ *     "id": 912345678902,
+ *     "text": "@json_newb just use JsonWriter!",
+ *     "geo": [50.454722, -104.606667],
+ *     "user": {
+ *       "name": "jesse",
+ *       "followers_count": 2
+ *     }
+ *   }
+ * ]}
+ * </pre>
+ * 
+ * This code encodes the above structure:
+ * 
+ * <pre>
+ *    {@code
+ *   public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
+ *     JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
+ *     writer.setIndent("    ");
+ *     writeMessagesArray(writer, messages);
+ *     writer.close();
+ *   }
+ *
+ *   public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
+ *     writer.beginArray();
+ *     for (Message message : messages) {
+ *       writeMessage(writer, message);
+ *     }
+ *     writer.endArray();
+ *   }
+ *
+ *   public void writeMessage(JsonWriter writer, Message message) throws IOException {
+ *     writer.beginObject();
+ *     writer.name("id").value(message.getId());
+ *     writer.name("text").value(message.getText());
+ *     if (message.getGeo() != null) {
+ *       writer.name("geo");
+ *       writeDoublesArray(writer, message.getGeo());
+ *     } else {
+ *       writer.name("geo").nullValue();
+ *     }
+ *     writer.name("user");
+ *     writeUser(writer, message.getUser());
+ *     writer.endObject();
+ *   }
+ *
+ *   public void writeUser(JsonWriter writer, User user) throws IOException {
+ *     writer.beginObject();
+ *     writer.name("name").value(user.getName());
+ *     writer.name("followers_count").value(user.getFollowersCount());
+ *     writer.endObject();
+ *   }
+ *
+ *   public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
+ *     writer.beginArray();
+ *     for (Double value : doubles) {
+ *       writer.value(value);
+ *     }
+ *     writer.endArray();
+ *   }}
+ * </pre>
+ *
+ * <p>
+ * Each {@code JsonWriter} may be used to write a single JSON stream. Instances
+ * of this class are not thread safe. Calls that would result in a malformed
+ * JSON string will fail with an {@link IllegalStateException}.
+ *
+ * @author Jesse Wilson
+ * @since 1.6
+ */
+public class JsonWriter implements Closeable, Flushable {
+
+	/*
+	 * From RFC 7159, "All Unicode characters may be placed within the quotation
+	 * marks except for the characters that must be escaped: quotation mark, reverse
+	 * solidus, and the control characters (U+0000 through U+001F)."
+	 *
+	 * We also escape '\u2028' and '\u2029', which JavaScript interprets as newline
+	 * characters. This prevents eval() from failing with a syntax error.
+	 * http://code.google.com/p/google-gson/issues/detail?id=341
+	 */
+	private static final String[] REPLACEMENT_CHARS;
+	private static final String[] HTML_SAFE_REPLACEMENT_CHARS;
+	static {
+		REPLACEMENT_CHARS = new String[128];
+		for (int i = 0; i <= 0x1f; i++) {
+			REPLACEMENT_CHARS[i] = String.format("\\u%04x", (int) i);
+		}
+		REPLACEMENT_CHARS['"'] = "\\\"";
+		REPLACEMENT_CHARS['\\'] = "\\\\";
+		REPLACEMENT_CHARS['\t'] = "\\t";
+		REPLACEMENT_CHARS['\b'] = "\\b";
+		REPLACEMENT_CHARS['\n'] = "\\n";
+		REPLACEMENT_CHARS['\r'] = "\\r";
+		REPLACEMENT_CHARS['\f'] = "\\f";
+		HTML_SAFE_REPLACEMENT_CHARS = REPLACEMENT_CHARS.clone();
+		HTML_SAFE_REPLACEMENT_CHARS['<'] = "\\u003c";
+		HTML_SAFE_REPLACEMENT_CHARS['>'] = "\\u003e";
+		HTML_SAFE_REPLACEMENT_CHARS['&'] = "\\u0026";
+		HTML_SAFE_REPLACEMENT_CHARS['='] = "\\u003d";
+		HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027";
+	}
+
+	/** The output data, containing at most one top-level array or object. */
+	private final Writer out;
+
+	private int[] stack = new int[32];
+	private int stackSize = 0;
+	{
+		push(EMPTY_DOCUMENT);
+	}
+
+	/**
+	 * A string containing a full set of spaces for a single level of indentation,
+	 * or null for no pretty printing.
+	 */
+	private String indent;
+
+	/**
+	 * The name/value separator; either ":" or ": ".
+	 */
+	private String separator = ":";
+
+	private boolean lenient;
+
+	private boolean htmlSafe;
+
+	private String deferredName;
+
+	private boolean serializeNulls = true;
+
+	/**
+	 * Creates a new instance that writes a JSON-encoded stream to {@code out}. For
+	 * best performance, ensure {@link Writer} is buffered; wrapping in
+	 * {@link java.io.BufferedWriter BufferedWriter} if necessary.
+	 */
+	public JsonWriter(Writer out) {
+		if (out == null) {
+			throw new NullPointerException("out == null");
+		}
+		this.out = out;
+	}
+
+	/**
+	 * Sets the indentation string to be repeated for each level of indentation in
+	 * the encoded document. If {@code indent.isEmpty()} the encoded document will
+	 * be compact. Otherwise the encoded document will be more human-readable.
+	 *
+	 * @param indent
+	 *            a string containing only whitespace.
+	 */
+	public final void setIndent(String indent) {
+		if (indent.length() == 0) {
+			this.indent = null;
+			this.separator = ":";
+		} else {
+			this.indent = indent;
+			this.separator = ": ";
+		}
+	}
+
+	/**
+	 * Configure this writer to relax its syntax rules. By default, this writer only
+	 * emits well-formed JSON as specified by
+	 * <a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>. Setting the
+	 * writer to lenient permits the following:
+	 * <ul>
+	 * <li>Top-level values of any type. With strict writing, the top-level value
+	 * must be an object or an array.
+	 * <li>Numbers may be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
+	 * infinities}.
+	 * </ul>
+	 */
+	public final void setLenient(boolean lenient) {
+		this.lenient = lenient;
+	}
+
+	/**
+	 * Returns true if this writer has relaxed syntax rules.
+	 */
+	public boolean isLenient() {
+		return lenient;
+	}
+
+	/**
+	 * Configure this writer to emit JSON that's safe for direct inclusion in HTML
+	 * and XML documents. This escapes the HTML characters {@code <}, {@code >},
+	 * {@code &} and {@code =} before writing them to the stream. Without this
+	 * setting, your XML/HTML encoder should replace these characters with the
+	 * corresponding escape sequences.
+	 */
+	public final void setHtmlSafe(boolean htmlSafe) {
+		this.htmlSafe = htmlSafe;
+	}
+
+	/**
+	 * Returns true if this writer writes JSON that's safe for inclusion in HTML and
+	 * XML documents.
+	 */
+	public final boolean isHtmlSafe() {
+		return htmlSafe;
+	}
+
+	/**
+	 * Sets whether object members are serialized when their value is null. This has
+	 * no impact on array elements. The default is true.
+	 */
+	public final void setSerializeNulls(boolean serializeNulls) {
+		this.serializeNulls = serializeNulls;
+	}
+
+	/**
+	 * Returns true if object members are serialized when their value is null. This
+	 * has no impact on array elements. The default is true.
+	 */
+	public final boolean getSerializeNulls() {
+		return serializeNulls;
+	}
+
+	/**
+	 * Begins encoding a new array. Each call to this method must be paired with a
+	 * call to {@link #endArray}.
+	 *
+	 * @return this writer.
+	 */
+	public JsonWriter beginArray() throws IOException {
+		writeDeferredName();
+		return open(EMPTY_ARRAY, "[");
+	}
+
+	/**
+	 * Ends encoding the current array.
+	 *
+	 * @return this writer.
+	 */
+	public JsonWriter endArray() throws IOException {
+		return close(EMPTY_ARRAY, NONEMPTY_ARRAY, "]");
+	}
+
+	/**
+	 * Begins encoding a new object. Each call to this method must be paired with a
+	 * call to {@link #endObject}.
+	 *
+	 * @return this writer.
+	 */
+	public JsonWriter beginObject() throws IOException {
+		writeDeferredName();
+		return open(EMPTY_OBJECT, "{");
+	}
+
+	/**
+	 * Ends encoding the current object.
+	 *
+	 * @return this writer.
+	 */
+	public JsonWriter endObject() throws IOException {
+		return close(EMPTY_OBJECT, NONEMPTY_OBJECT, "}");
+	}
+
+	/**
+	 * Enters a new scope by appending any necessary whitespace and the given
+	 * bracket.
+	 */
+	private JsonWriter open(int empty, String openBracket) throws IOException {
+		beforeValue();
+		push(empty);
+		out.write(openBracket);
+		return this;
+	}
+
+	/**
+	 * Closes the current scope by appending any necessary whitespace and the given
+	 * bracket.
+	 */
+	private JsonWriter close(int empty, int nonempty, String closeBracket) throws IOException {
+		int context = peek();
+		if (context != nonempty && context != empty) {
+			throw new IllegalStateException("Nesting problem.");
+		}
+		if (deferredName != null) {
+			throw new IllegalStateException("Dangling name: " + deferredName);
+		}
+
+		stackSize--;
+		if (context == nonempty) {
+			newline();
+		}
+		out.write(closeBracket);
+		return this;
+	}
+
+	private void push(int newTop) {
+		if (stackSize == stack.length) {
+			int[] newStack = new int[stackSize * 2];
+			System.arraycopy(stack, 0, newStack, 0, stackSize);
+			stack = newStack;
+		}
+		stack[stackSize++] = newTop;
+	}
+
+	/**
+	 * Returns the value on the top of the stack.
+	 */
+	private int peek() {
+		if (stackSize == 0) {
+			throw new IllegalStateException("JsonWriter is closed.");
+		}
+		return stack[stackSize - 1];
+	}
+
+	/**
+	 * Replace the value on the top of the stack with the given value.
+	 */
+	private void replaceTop(int topOfStack) {
+		stack[stackSize - 1] = topOfStack;
+	}
+
+	/**
+	 * Encodes the property name.
+	 *
+	 * @param name
+	 *            the name of the forthcoming value. May not be null.
+	 * @return this writer.
+	 */
+	public JsonWriter name(String name) throws IOException {
+		if (name == null) {
+			throw new NullPointerException("name == null");
+		}
+		if (deferredName != null) {
+			throw new IllegalStateException();
+		}
+		if (stackSize == 0) {
+			throw new IllegalStateException("JsonWriter is closed.");
+		}
+		deferredName = name;
+		return this;
+	}
+
+	private void writeDeferredName() throws IOException {
+		if (deferredName != null) {
+			beforeName();
+			string(deferredName);
+			deferredName = null;
+		}
+	}
+
+	/**
+	 * Encodes {@code value}.
+	 *
+	 * @param value
+	 *            the literal string value, or null to encode a null literal.
+	 * @return this writer.
+	 */
+	public JsonWriter value(String value) throws IOException {
+		if (value == null) {
+			return nullValue();
+		}
+		writeDeferredName();
+		beforeValue();
+		string(value);
+		return this;
+	}
+
+	/**
+	 * Writes {@code value} directly to the writer without quoting or escaping.
+	 *
+	 * @param value
+	 *            the literal string value, or null to encode a null literal.
+	 * @return this writer.
+	 */
+	public JsonWriter jsonValue(String value) throws IOException {
+		if (value == null) {
+			return nullValue();
+		}
+		writeDeferredName();
+		beforeValue();
+		out.append(value);
+		return this;
+	}
+
+	/**
+	 * Encodes {@code null}.
+	 *
+	 * @return this writer.
+	 */
+	public JsonWriter nullValue() throws IOException {
+		if (deferredName != null) {
+			if (serializeNulls) {
+				writeDeferredName();
+			} else {
+				deferredName = null;
+				return this; // skip the name and the value
+			}
+		}
+		beforeValue();
+		out.write("null");
+		return this;
+	}
+
+	/**
+	 * Encodes {@code value}.
+	 *
+	 * @return this writer.
+	 */
+	public JsonWriter value(boolean value) throws IOException {
+		writeDeferredName();
+		beforeValue();
+		out.write(value ? "true" : "false");
+		return this;
+	}
+
+	/**
+	 * Encodes {@code value}.
+	 *
+	 * @param value
+	 *            a finite value. May not be {@link Double#isNaN() NaNs} or
+	 *            {@link Double#isInfinite() infinities}.
+	 * @return this writer.
+	 */
+	public JsonWriter value(double value) throws IOException {
+		if (Double.isNaN(value) || Double.isInfinite(value)) {
+			throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+		}
+		writeDeferredName();
+		beforeValue();
+		out.append(Double.toString(value));
+		return this;
+	}
+
+	/**
+	 * Encodes {@code value}.
+	 *
+	 * @return this writer.
+	 */
+	public JsonWriter value(long value) throws IOException {
+		writeDeferredName();
+		beforeValue();
+		out.write(Long.toString(value));
+		return this;
+	}
+
+	/**
+	 * Encodes {@code value}.
+	 *
+	 * @param value
+	 *            a finite value. May not be {@link Double#isNaN() NaNs} or
+	 *            {@link Double#isInfinite() infinities}.
+	 * @return this writer.
+	 */
+	public JsonWriter value(Number value) throws IOException {
+		if (value == null) {
+			return nullValue();
+		}
+
+		writeDeferredName();
+		String string = value.toString();
+		if (!lenient && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
+			throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+		}
+		beforeValue();
+		out.append(string);
+		return this;
+	}
+
+	/**
+	 * Ensures all buffered data is written to the underlying {@link Writer} and
+	 * flushes that writer.
+	 */
+	public void flush() throws IOException {
+		if (stackSize == 0) {
+			throw new IllegalStateException("JsonWriter is closed.");
+		}
+		out.flush();
+	}
+
+	/**
+	 * Flushes and closes this writer and the underlying {@link Writer}.
+	 *
+	 * @throws IOException
+	 *             if the JSON document is incomplete.
+	 */
+	public void close() throws IOException {
+		out.close();
+
+		int size = stackSize;
+		if (size > 1 || size == 1 && stack[size - 1] != NONEMPTY_DOCUMENT) {
+			throw new IOException("Incomplete document");
+		}
+		stackSize = 0;
+	}
+
+	private void string(String value) throws IOException {
+		String[] replacements = htmlSafe ? HTML_SAFE_REPLACEMENT_CHARS : REPLACEMENT_CHARS;
+		out.write("\"");
+		int last = 0;
+		int length = value.length();
+		for (int i = 0; i < length; i++) {
+			char c = value.charAt(i);
+			String replacement;
+			if (c < 128) {
+				replacement = replacements[c];
+				if (replacement == null) {
+					continue;
+				}
+			} else if (c == '\u2028') {
+				replacement = "\\u2028";
+			} else if (c == '\u2029') {
+				replacement = "\\u2029";
+			} else {
+				continue;
+			}
+			if (last < i) {
+				out.write(value, last, i - last);
+			}
+			out.write(replacement);
+			last = i + 1;
+		}
+		if (last < length) {
+			out.write(value, last, length - last);
+		}
+		out.write("\"");
+	}
+
+	private void newline() throws IOException {
+		if (indent == null) {
+			return;
+		}
+
+		out.write("\n");
+		for (int i = 1, size = stackSize; i < size; i++) {
+			out.write(indent);
+		}
+	}
+
+	/**
+	 * Inserts any necessary separators and whitespace before a name. Also adjusts
+	 * the stack to expect the name's value.
+	 */
+	private void beforeName() throws IOException {
+		int context = peek();
+		if (context == NONEMPTY_OBJECT) { // first in object
+			out.write(',');
+		} else if (context != EMPTY_OBJECT) { // not in an object!
+			throw new IllegalStateException("Nesting problem.");
+		}
+		newline();
+		replaceTop(DANGLING_NAME);
+	}
+
+	/**
+	 * Inserts any necessary separators and whitespace before a literal value,
+	 * inline array, or inline object. Also adjusts the stack to expect either a
+	 * closing bracket or another element.
+	 */
+	@SuppressWarnings("fallthrough")
+	private void beforeValue() throws IOException {
+		switch (peek()) {
+		case NONEMPTY_DOCUMENT:
+			if (!lenient) {
+				throw new IllegalStateException("JSON must have only one top-level value.");
+			}
+			// fall-through
+		case EMPTY_DOCUMENT: // first in document
+			replaceTop(NONEMPTY_DOCUMENT);
+			break;
+
+		case EMPTY_ARRAY: // first in array
+			replaceTop(NONEMPTY_ARRAY);
+			newline();
+			break;
+
+		case NONEMPTY_ARRAY: // another in array
+			out.append(',');
+			newline();
+			break;
+
+		case DANGLING_NAME: // value for name
+			out.append(separator);
+			replaceTop(NONEMPTY_OBJECT);
+			break;
+
+		default:
+			throw new IllegalStateException("Nesting problem.");
+		}
+	}
+}
diff --git a/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/MalformedJsonException.java b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/MalformedJsonException.java
new file mode 100644
index 0000000..4923055
--- /dev/null
+++ b/server/emaysms/src/main/java/cn/emay/sdk/util/json/gson/stream/MalformedJsonException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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 cn.emay.sdk.util.json.gson.stream;
+
+import java.io.IOException;
+
+/**
+ * Thrown when a reader encounters malformed JSON. Some syntax errors can be
+ * ignored by calling {@link JsonReader#setLenient(boolean)}.
+ */
+public final class MalformedJsonException extends IOException {
+	private static final long serialVersionUID = 1L;
+
+	public MalformedJsonException(String msg) {
+		super(msg);
+	}
+
+	public MalformedJsonException(String msg, Throwable throwable) {
+		super(msg);
+		// Using initCause() instead of calling super() because Java 1.5 didn't retrofit
+		// IOException
+		// with a constructor with Throwable. This was done in Java 1.6
+		initCause(throwable);
+	}
+
+	public MalformedJsonException(Throwable throwable) {
+		// Using initCause() instead of calling super() because Java 1.5 didn't retrofit
+		// IOException
+		// with a constructor with Throwable. This was done in Java 1.6
+		initCause(throwable);
+	}
+}
diff --git a/server/emaysms/src/test/java/cn/emay/test/Test.java b/server/emaysms/src/test/java/cn/emay/test/Test.java
new file mode 100644
index 0000000..1f1a930
--- /dev/null
+++ b/server/emaysms/src/test/java/cn/emay/test/Test.java
@@ -0,0 +1,197 @@
+package cn.emay.test;
+
+import java.io.IOException;
+
+import cn.emay.sdk.client.SmsSDKClient;
+import cn.emay.sdk.core.dto.sms.common.CustomSmsIdAndMobile;
+import cn.emay.sdk.core.dto.sms.common.CustomSmsIdAndMobileAndContent;
+import cn.emay.sdk.core.dto.sms.common.PersonalityParams;
+import cn.emay.sdk.core.dto.sms.common.ResultModel;
+import cn.emay.sdk.core.dto.sms.request.BalanceRequest;
+import cn.emay.sdk.core.dto.sms.request.MoRequest;
+import cn.emay.sdk.core.dto.sms.request.ReportRequest;
+import cn.emay.sdk.core.dto.sms.request.RetrieveReportRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchOnlyRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsBatchRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityAllRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsPersonalityRequest;
+import cn.emay.sdk.core.dto.sms.request.SmsSingleRequest;
+import cn.emay.sdk.core.dto.sms.response.BalanceResponse;
+import cn.emay.sdk.core.dto.sms.response.MoResponse;
+import cn.emay.sdk.core.dto.sms.response.ReportResponse;
+import cn.emay.sdk.core.dto.sms.response.RetrieveReportResponse;
+import cn.emay.sdk.core.dto.sms.response.SmsResponse;
+import cn.emay.sdk.util.exception.SDKParamsException;
+
+public class Test {
+	public static void main(String[] args) throws SDKParamsException, IOException {
+		// sendSingleSms();
+		// sendBatchOnlySms();
+		// sendBatchSms();
+		// sendPersonalitySms();
+		// sendPersonalityAllSMS();
+		// getMo();
+		// getReport();
+		// getBalance();
+		retrieveReport();
+	}
+
+	public static void sendSingleSms() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		String mobile = "15345690849";
+		String content = "鐭俊鍐呭";
+		String customSmsId = "1";
+		String extendedCode = "01";
+		SmsSingleRequest request = new SmsSingleRequest(mobile, content, customSmsId, extendedCode, "");
+		ResultModel<SmsResponse> result = client.sendSingleSms(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse response = result.getResult();
+			System.out.println("sendSingleSms:" + response.toString());
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void sendBatchOnlySms() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		String[] mobiles = { "13800000000", "13800000001" };
+		String content = "鐭俊鍐呭";
+		String extendedCode = "01";
+		SmsBatchOnlyRequest request = new SmsBatchOnlyRequest(mobiles, content, "", extendedCode);
+		ResultModel<SmsResponse[]> result = client.sendBatchOnlySms(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse[] responses = result.getResult();
+			for (SmsResponse response : responses) {
+				System.out.println("sendBatchOnlySms:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void sendBatchSms() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		CustomSmsIdAndMobile[] cm = new CustomSmsIdAndMobile[2];
+		cm[0] = new CustomSmsIdAndMobile("1", "13800000000");
+		cm[1] = new CustomSmsIdAndMobile("2", "13800000001");
+		String content = "鐭俊鍐呭";
+		String extendedCode = "01";
+		SmsBatchRequest request = new SmsBatchRequest(cm, content, "", extendedCode);
+		ResultModel<SmsResponse[]> result = client.sendBatchSms(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse[] responses = result.getResult();
+			for (SmsResponse response : responses) {
+				System.out.println("sendBatchSms:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void sendPersonalitySms() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		String extendedCode = "01";
+
+		CustomSmsIdAndMobileAndContent[] smses = new CustomSmsIdAndMobileAndContent[2];
+		smses[0] = new CustomSmsIdAndMobileAndContent("1", "13800000000", "鐭俊鍐呭1");
+		smses[1] = new CustomSmsIdAndMobileAndContent("2", "13800000001", "鐭俊鍐呭2");
+		SmsPersonalityRequest request = new SmsPersonalityRequest(smses, "", extendedCode);
+		ResultModel<SmsResponse[]> result = client.sendPersonalitySms(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse[] responses = result.getResult();
+			for (SmsResponse response : responses) {
+				System.out.println("sendPersonalitySms:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void sendPersonalityAllSMS() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+
+		PersonalityParams[] smses = new PersonalityParams[2];
+		smses[0] = new PersonalityParams("1", "13800000000", "鐭俊鍐呭1", "1", null);
+		smses[1] = new PersonalityParams("2", "13800000001", "鐭俊鍐呭2", "2", null);
+		SmsPersonalityAllRequest request = new SmsPersonalityAllRequest(smses);
+		ResultModel<SmsResponse[]> result = client.sendPersonalityAllSMS(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse[] responses = result.getResult();
+			for (SmsResponse response : responses) {
+				System.out.println("sendPersonalityAllSMS:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void getReport() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		ReportRequest request = new ReportRequest();
+		ResultModel<ReportResponse[]> result = client.getReport(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			ReportResponse[] responses = result.getResult();
+			for (ReportResponse response : responses) {
+				System.out.println("getReport:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+
+	}
+
+	public static void getMo() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		MoRequest request = new MoRequest();
+		ResultModel<MoResponse[]> result = client.getMo(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			MoResponse[] responses = result.getResult();
+			for (MoResponse response : responses) {
+				System.out.println("getMo:" + response.toString());
+			}
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void getBalance() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		BalanceRequest request = new BalanceRequest();
+		ResultModel<BalanceResponse> result = client.getBalance(request);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			BalanceResponse response = result.getResult();
+			System.out.println("getBalance:" + response.getBalance());
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+	}
+
+	public static void retrieveReport() throws SDKParamsException {
+		SmsSDKClient client = new SmsSDKClient("127.0.0.1", 8999, "EUCP-EMY-SMS1-LPUE0", "749524E0CA7B76A2");
+		String startTime = "20180120110000";
+		String endTime = "20180120110500";
+		String smsid = "15167713536420020356";
+		RetrieveReportRequest reportRequest = new RetrieveReportRequest();
+		reportRequest.setSmsId(smsid);
+		reportRequest.setStartTime(startTime);
+		reportRequest.setEndTime(endTime);
+		ResultModel<RetrieveReportResponse> result = client.retrieveReport(reportRequest);
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			RetrieveReportResponse response = result.getResult();
+			System.out.println("retrieveReport:" + response.getCode());
+		} else {
+			System.out.println("璇锋眰澶辫触");
+		}
+
+	}
+
+}
diff --git a/server/platform/src/main/java/com/doumee/api/InterfaceLogController.java b/server/platform/src/main/java/com/doumee/api/InterfaceLogController.java
new file mode 100644
index 0000000..88ed735
--- /dev/null
+++ b/server/platform/src/main/java/com/doumee/api/InterfaceLogController.java
@@ -0,0 +1,89 @@
+package com.doumee.api;
+
+import com.doumee.core.annotation.excel.ExcelExporter;
+import com.doumee.core.annotation.pr.PreventRepeat;
+import com.doumee.core.model.ApiResponse;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.model.InterfaceLog;
+import com.doumee.service.business.InterfaceLogService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 姹熻箘韫�
+ * @date 2023/11/30 15:33
+ */
+@Api(tags = "涓夋柟骞冲彴鎺ュ彛浜や簰璁板綍")
+@RestController
+@RequestMapping("/business/interfaceLog")
+public class InterfaceLogController extends BaseController {
+
+    @Autowired
+    private InterfaceLogService interfaceLogService;
+
+    @PreventRepeat
+    @ApiOperation("鏂板缓")
+    @PostMapping("/create")
+    @RequiresPermissions("business:interfacelog:create")
+    public ApiResponse create(@RequestBody InterfaceLog interfaceLog) {
+        return ApiResponse.success(interfaceLogService.create(interfaceLog));
+    }
+
+    @ApiOperation("鏍规嵁ID鍒犻櫎")
+    @GetMapping("/delete/{id}")
+    @RequiresPermissions("business:interfacelog:delete")
+    public ApiResponse deleteById(@PathVariable Integer id) {
+        interfaceLogService.deleteById(id);
+        return ApiResponse.success(null);
+    }
+
+    @ApiOperation("鎵归噺鍒犻櫎")
+    @GetMapping("/delete/batch")
+    @RequiresPermissions("business:interfacelog:delete")
+    public ApiResponse deleteByIdInBatch(@RequestParam String ids) {
+        String [] idArray = ids.split(",");
+        List<Integer> idList = new ArrayList<>();
+        for (String id : idArray) {
+            idList.add(Integer.valueOf(id));
+        }
+        interfaceLogService.deleteByIdInBatch(idList);
+        return ApiResponse.success(null);
+    }
+
+    @ApiOperation("鏍规嵁ID淇敼")
+    @PostMapping("/updateById")
+    @RequiresPermissions("business:interfacelog:update")
+    public ApiResponse updateById(@RequestBody InterfaceLog interfaceLog) {
+        interfaceLogService.updateById(interfaceLog);
+        return ApiResponse.success(null);
+    }
+
+    @ApiOperation("鍒嗛〉鏌ヨ")
+    @PostMapping("/page")
+    @RequiresPermissions("business:interfacelog:query")
+    public ApiResponse<PageData<InterfaceLog>> findPage (@RequestBody PageWrap<InterfaceLog> pageWrap) {
+        return ApiResponse.success(interfaceLogService.findPage(pageWrap));
+    }
+
+    @ApiOperation("瀵煎嚭Excel")
+    @PostMapping("/exportExcel")
+    @RequiresPermissions("business:interfacelog:exportExcel")
+    public void exportExcel (@RequestBody PageWrap<InterfaceLog> pageWrap, HttpServletResponse response) {
+        ExcelExporter.build(InterfaceLog.class).export(interfaceLogService.findPage(pageWrap).getRecords(), "涓夋柟骞冲彴鎺ュ彛浜や簰璁板綍", response);
+    }
+
+    @ApiOperation("鏍规嵁ID鏌ヨ")
+    @GetMapping("/{id}")
+    @RequiresPermissions("business:interfacelog:query")
+    public ApiResponse findById(@PathVariable Integer id) {
+        return ApiResponse.success(interfaceLogService.findById(id));
+    }
+}
diff --git a/server/platform/src/main/resources/application.yml b/server/platform/src/main/resources/application.yml
index db2cf56..b77ea70 100644
--- a/server/platform/src/main/resources/application.yml
+++ b/server/platform/src/main/resources/application.yml
@@ -11,7 +11,7 @@
 #  application:
 #  name: doumeemes
   profiles:
-    active: pro
+    active: dev
 
   # JSON杩斿洖閰嶇疆
   jackson:
diff --git a/server/pom.xml b/server/pom.xml
index eceb798..32cda2b 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -14,6 +14,7 @@
     <module>service</module>
     <module>company</module>
     <module>dianziqian</module>
+      <module>emaysms</module>
   </modules>
 
   <parent>
diff --git a/server/service/pom.xml b/server/service/pom.xml
index 21188be..c150726 100644
--- a/server/service/pom.xml
+++ b/server/service/pom.xml
@@ -10,7 +10,18 @@
     </parent>
 
     <artifactId>service</artifactId>
-
+    <dependencies>
+        <dependency>
+            <groupId>com.doumee</groupId>
+            <artifactId>emaysms</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.doumee</groupId>
+            <artifactId>dianziqian</artifactId>
+            <version>1.0.0-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
     <properties>
         <maven.compiler.source>8</maven.compiler.source>
         <maven.compiler.target>8</maven.compiler.target>
diff --git a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroAuthFilter.java b/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroAuthFilter.java
deleted file mode 100644
index 8f0932f..0000000
--- a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroAuthFilter.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.doumee.config.shiroMemory;
-
-import com.doumee.core.model.ApiResponse;
-import com.alibaba.fastjson.JSON;
-import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
-import org.springframework.http.HttpStatus;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Shiro璁よ瘉杩囨护鍣紝澶勭悊鏈璇佹儏鍐电殑鍝嶅簲
- * @author Eva.Caesar Liu
- * @date 2023/02/14 11:14
- */
-public class ShiroAuthFilter extends FormAuthenticationFilter {
-
-    public ShiroAuthFilter() {
-        super();
-    }
-
-    @Override
-    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
-        HttpServletResponse servletResponse = (HttpServletResponse) response;
-        servletResponse.setHeader("content-type", "application/json;charset=UTF-8");
-        servletResponse.getWriter().write(JSON.toJSONString(ApiResponse.failed(HttpStatus.UNAUTHORIZED.value(), "鏈櫥褰曟垨鐧诲綍淇℃伅宸茶繃鏈�")));
-        return Boolean.FALSE;
-    }
-}
diff --git a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroCache.java b/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroCache.java
deleted file mode 100644
index 6390a49..0000000
--- a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroCache.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package com.doumee.config.shiroMemory;
-
-import com.doumee.service.proxy.CacheProxy;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.cache.Cache;
-import org.apache.shiro.cache.CacheException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Scope;
-import org.springframework.stereotype.Component;
-import org.springframework.util.CollectionUtils;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * Shiro缂撳瓨
- * @author Eva.Caesar Liu
- * @date 2023/02/14 11:14
- */
-@Scope(value = "prototype")
-@Slf4j
-//@Component
-public class ShiroCache implements Cache<Object, Serializable> {
-
-    private String keyPrefix = "";
-
-    @Autowired
-    private CacheProxy<Object, Serializable> cacheProxy;
-
-    public ShiroCache () {
-        log.debug("ShiroCache: new, keyPrefix = [" + keyPrefix + "]");
-    }
-
-    public ShiroCache(String keyPrefix) {
-        log.debug("ShiroCache: new, keyPrefix = [" + keyPrefix + "]");
-        this.keyPrefix = keyPrefix;
-    }
-
-    @Override
-    public Serializable get(Object key) throws CacheException {
-        if (key == null) {
-            return null;
-        }
-        return cacheProxy.get(getKey(key));
-    }
-
-    @Override
-    public Serializable put(Object key, Serializable value) throws CacheException {
-        if (key == null) {
-            return null;
-        }
-        cacheProxy.put(getKey(key), value);
-        return value;
-    }
-
-    public Serializable put(Object key, Serializable value, int timeout) throws CacheException {
-        if (key == null) {
-            return null;
-        }
-        cacheProxy.put(getKey(key), value, timeout);
-        return value;
-    }
-
-    @Override
-    public void clear() throws CacheException {
-        Set<Object> keys = this.keys();
-        cacheProxy.remove(keys);
-    }
-
-    @Override
-    public int size() {
-        return this.keys().size();
-    }
-
-    @Override
-    public Set<Object> keys() {
-        Set<Object> keys = cacheProxy.keys(keyPrefix + "*");
-        if (CollectionUtils.isEmpty(keys)) {
-            return Collections.emptySet();
-        }
-        return keys;
-    }
-
-    @Override
-    public Collection<Serializable> values() {
-        Collection<Serializable> values = new ArrayList<>();
-        Set<Object> keys = this.keys();
-        if (CollectionUtils.isEmpty(keys)) {
-            return values;
-        }
-        for (Object k : keys) {
-            values.add(cacheProxy.get(k));
-        }
-        return values;
-    }
-
-    @Override
-    public Serializable remove(Object key) throws CacheException {
-        if (key == null) {
-            return null;
-        }
-        Serializable value = this.get(getKey(key));
-        cacheProxy.remove(getKey(key));
-        return value;
-    }
-
-    private Object getKey (Object key) {
-        return (key instanceof String ? (this.keyPrefix + key) : key);
-    }
-}
diff --git a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroCacheManager.java b/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroCacheManager.java
deleted file mode 100644
index d437489..0000000
--- a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroCacheManager.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.doumee.config.shiroMemory;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.cache.Cache;
-import org.apache.shiro.cache.CacheException;
-import org.apache.shiro.cache.CacheManager;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.ApplicationContext;
-import org.springframework.stereotype.Component;
-
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-/**
- * 鑷畾涔塖hiro CacheManager
- * @author Eva.Caesar Liu
- * @date 2023/02/14 11:14
- */
-@Slf4j
-//@Component
-public class ShiroCacheManager implements CacheManager {
-
-    private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap();
-
-    private static ApplicationContext applicationContext;
-
-    @Override
-    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
-        log.debug("get cache, name=" + name);
-        Cache cache = this.caches.get(name);
-        if (cache == null) {
-            cache = applicationContext.getBean(ShiroCache.class, "shiro:cache:");
-            this.caches.put(name, cache);
-        }
-        return cache;
-    }
-
-    @Autowired
-    public void setApplicationContext (ApplicationContext applicationContext) {
-        if (ShiroCacheManager.applicationContext == null) {
-            ShiroCacheManager.applicationContext = applicationContext;
-        }
-    }
-}
diff --git a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroConfig.java b/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroConfig.java
deleted file mode 100644
index ef719af..0000000
--- a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroConfig.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package com.doumee.config.shiroMemory;
-
-import org.apache.coyote.http11.AbstractHttp11Protocol;
-import org.apache.shiro.mgt.SecurityManager;
-import org.apache.shiro.session.mgt.SessionManager;
-import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
-import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
-import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
-import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
-import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
-import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import javax.servlet.Filter;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * Shiro閰嶇疆
- * @author Eva.Caesar Liu
- * @date 2023/02/14 11:14
- */
-//@Configuration
-public class ShiroConfig {
-
-    @Value("${cache.session.expire}")
-    private int sessionExpireTime;
-
-    @Autowired
-    private ShiroCredentialsMatcher shiroCredentialsMatcher;
-
-    @Autowired
-    private ShiroSessionDAO shiroSessionDAO;
-
-    @Autowired
-    private ShiroCacheManager shiroCacheManager;
-
-    @Autowired
-    private ShiroRealm shiroRealm;
-
-    @Bean
-    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
-        DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
-        autoProxyCreator.setProxyTargetClass(true);
-        return autoProxyCreator;
-    }
-
-    @Bean
-    public SessionManager sessionManager() {
-        ShiroSessionManager sessionManager = new ShiroSessionManager();
-        sessionManager.setSessionDAO(shiroSessionDAO);
-        return sessionManager;
-    }
-
-    @Bean
-    public SecurityManager securityManager() {
-        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
-        securityManager.setRealm(shiroRealm);
-        securityManager.setSessionManager(this.sessionManager());
-        securityManager.setCacheManager(shiroCacheManager);
-        return securityManager;
-    }
-
-    @Bean
-    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
-        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
-        shiroFilterFactoryBean.setSecurityManager(securityManager);
-        Map<String, String> map = new LinkedHashMap<>();
-        // 璺緞鎷︽埅閰嶇疆
-        map.put("/system/dictData/companyUserRules","anon");
-        map.put("/system/login", "anon");
-        map.put("/system/logout", "anon");
-        map.put("/system/loginH5", "anon");
-        map.put("/common/captcha", "anon");
-        map.put("/business/areas/*", "anon");
-        map.put("/business/company/register", "anon");
-        map.put("/business/labels/page","anon");
-        map.put("/public/uploadPicture","anon");
-        map.put("/public/uploadLocal","anon");
-        map.put("/business/*/list","anon");
-        map.put("/business/goods/goodsPage","anon");
-        map.put("/business/goods/h5Image","anon");
-        map.put("/business/goods/export","anon");
-        map.put("/business/goods/listForH5","anon");
-
-
-        // - 鏀捐swagger
-        map.put("/doc.html", "anon");
-        map.put("/webjars/**", "anon");
-        map.put("/swagger-resources/**", "anon");
-        map.put("/v2/api-docs/**", "anon");
-
-        // - 鍏朵粬鎺ュ彛缁熶竴鎷︽埅
-        map.put("/**", "authc");
-        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
-        // 娣诲姞璁よ瘉杩囨护鍣�
-        Map<String, Filter> filters = new LinkedHashMap<>();
-        filters.put("authc", new ShiroAuthFilter());
-        shiroFilterFactoryBean.setFilters(filters);
-        return shiroFilterFactoryBean;
-    }
-
-    @Bean
-    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
-        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
-        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
-        return authorizationAttributeSourceAdvisor;
-    }
-
-    @Bean
-    public ShiroSessionDAO getShiroSessionDAO () {
-        shiroSessionDAO.setExpireTime(sessionExpireTime);
-        return shiroSessionDAO;
-    }
-
-    @Bean
-    public ShiroRealm getShiroRealm () {
-        shiroRealm.setCredentialsMatcher(shiroCredentialsMatcher);
-        return shiroRealm;
-    }
-
-    private int maxUploadSizeInMb = 10 * 1024 * 1024; // 10 MB
-
-    @Bean
-    public TomcatServletWebServerFactory tomcatEmbedded() {
-
-        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
-        tomcat.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
-            // connector other settings...
-            // configure maxSwallowSize
-            if ((connector.getProtocolHandler() instanceof AbstractHttp11Protocol<?>)) {
-                // -1 means unlimited, accept bytes
-                ((AbstractHttp11Protocol<?>) connector.getProtocolHandler()).setMaxSwallowSize(-1);
-            }
-        });
-        return tomcat;
-    }
-}
diff --git a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroCredentialsMatcher.java b/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroCredentialsMatcher.java
deleted file mode 100644
index 434588b..0000000
--- a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroCredentialsMatcher.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.doumee.config.shiroMemory;
-
-import com.doumee.core.utils.Utils;
-import com.doumee.dao.system.model.SystemUser;
-import com.doumee.service.system.SystemUserService;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.UsernamePasswordToken;
-import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Component;
-
-/**
- * Shiro瀵嗙爜姣斿澶勭悊
- * @author Eva.Caesar Liu
- * @date 2023/02/14 11:14
- */
-//@Component
-public class ShiroCredentialsMatcher extends HashedCredentialsMatcher {
-
-    @Lazy
-    @Autowired
-    private SystemUserService systemUserService;
-
-    @Override
-    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
-        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
-        SystemUser queryUserDto = new SystemUser();
-        queryUserDto.setUsername(usernamePasswordToken.getUsername());
-        queryUserDto.setDeleted(Boolean.FALSE);
-        SystemUser systemUser = systemUserService.findOne(queryUserDto);
-        if (systemUser == null) {
-            return Boolean.FALSE;
-        }
-        // 鍔犲瘑瀵嗙爜
-        String pwd = Utils.Secure.encryptPassword(new String(usernamePasswordToken.getPassword()), systemUser.getSalt());
-        // 姣旇緝瀵嗙爜
-        return this.equals(pwd, systemUser.getPassword());
-    }
-}
diff --git a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroRealm.java b/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroRealm.java
deleted file mode 100644
index 6e5d23e..0000000
--- a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroRealm.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.doumee.config.shiroMemory;
-
-import com.doumee.core.constants.ResponseStatus;
-import com.doumee.core.exception.BusinessException;
-import com.doumee.core.model.LoginUserInfo;
-import com.doumee.core.utils.Constants;
-import com.doumee.dao.system.model.SystemPermission;
-import com.doumee.dao.system.model.SystemRole;
-import com.doumee.dao.system.model.SystemUser;
-import com.doumee.service.system.SystemPermissionService;
-import com.doumee.service.system.SystemRoleService;
-import com.doumee.service.system.SystemUserService;
-import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.SimpleAuthenticationInfo;
-import org.apache.shiro.authz.AuthorizationInfo;
-import org.apache.shiro.authz.SimpleAuthorizationInfo;
-import org.apache.shiro.realm.AuthorizingRealm;
-import org.apache.shiro.subject.PrincipalCollection;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Component;
-
-import java.util.List;
-
-/**
- * 鑷畾涔塕ealm锛屽鐞嗚璇佸拰鏉冮檺
- * @author Eva.Caesar Liu
- * @date 2023/02/14 11:14
- */
-//@Component
-public class ShiroRealm extends AuthorizingRealm {
-
-    @Lazy
-    @Autowired
-    private SystemUserService systemUserService;
-
-    @Lazy
-    @Autowired
-    private SystemRoleService systemRoleService;
-
-    @Lazy
-    @Autowired
-    private SystemPermissionService systemPermissionService;
-
-    /**
-     * 鏉冮檺澶勭悊
-     * @author Eva.Caesar Liu
-     * @date 2023/02/14 11:14
-     */
-    @Override
-    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
-        LoginUserInfo loginUserInfo = (LoginUserInfo)principalCollection.getPrimaryPrincipal();
-        // 璁剧疆鐢ㄦ埛瑙掕壊鍜屾潈闄�
-        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
-        authorizationInfo.addRoles(loginUserInfo.getRoles());
-        authorizationInfo.addStringPermissions(loginUserInfo.getPermissions());
-        return authorizationInfo;
-    }
-
-    /**
-     * 璁よ瘉澶勭悊
-     * @author Eva.Caesar Liu
-     * @date 2023/02/14 11:14
-     */
-    @Override
-    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
-        // 鑾峰彇鐢ㄦ埛鍚�
-        String username = authenticationToken.getPrincipal().toString();
-        // 鏍规嵁鐢ㄦ埛鍚嶆煡璇㈢敤鎴峰璞�
-        SystemUser queryDto = new SystemUser();
-        queryDto.setUsername(username);
-        queryDto.setDeleted(Boolean.FALSE);
-        SystemUser user = systemUserService.findOne(queryDto);
-        if(!Constants.equalsInteger(user.getStatus(),Constants.ZERO)){
-            throw new BusinessException(ResponseStatus.NOT_ALLOWED.getCode(),"瀵逛笉璧凤紝璐﹀彿淇℃伅宸茶绂佺敤锛屽鏈夌枒闂鑱旂郴绯荤粺绠$悊鍛橈紒");
-        }
-        if (user == null) {
-            return null;
-        }
-        // 鑾峰彇鐧诲綍鐢ㄦ埛淇℃伅
-        List<SystemRole> roles = systemRoleService.findByUserId(user.getId());
-        List<SystemPermission> permissions = systemPermissionService.findByUserId(user.getId());
-        LoginUserInfo userInfo = LoginUserInfo.from(user, roles, permissions);
-        // 楠岃瘉鐢ㄦ埛
-        return new SimpleAuthenticationInfo(userInfo, user.getPassword(), this.getName());
-    }
-
-}
diff --git a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroSessionDAO.java b/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroSessionDAO.java
deleted file mode 100644
index b9e9316..0000000
--- a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroSessionDAO.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package com.doumee.config.shiroMemory;
-
-import lombok.Data;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.session.Session;
-import org.apache.shiro.session.UnknownSessionException;
-import org.apache.shiro.session.mgt.SimpleSession;
-import org.apache.shiro.session.mgt.eis.SessionDAO;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import java.io.Serializable;
-import java.util.*;
-
-/**
- * 鑷畾涔塖hiro SessionDAO锛屽皢浼氳瘽淇℃伅瀛樺叆缂撳瓨涓�
- * @author Eva.Caesar Liu
- * @date 2023/02/14 11:14
- */
-@Data
-@Slf4j
-//@Component
-public class ShiroSessionDAO implements SessionDAO {
-
-    private static final String KEY_PREFIX = "shiro:session:";
-
-    @Autowired
-    private ShiroCache shiroCache;
-
-    private int expireTime = 1800;
-
-    @Autowired
-    private ShiroTokenManager shiroTokenManager;
-
-    @Override
-    public Serializable create(Session session) {
-        if (session == null) {
-            log.error("session is null");
-            throw new UnknownSessionException("session is null");
-        }
-        Serializable sessionId = shiroTokenManager.build();
-        ((SimpleSession)session).setId(sessionId);
-        this.saveSession(session);
-        return sessionId;
-    }
-
-    @Override
-    public Session readSession(Serializable sessionId) throws UnknownSessionException{
-        if (sessionId == null) {
-            log.warn("session id is null");
-            return null;
-        }
-        if (sessionId instanceof String) {
-            // 瀵筍essionId杩涜楠岃瘉锛堝彲鐢ㄤ簬闃叉Session鎹曡幏銆佹毚鍔涙崟鎹夌瓑涓�绯诲垪瀹夊叏闂锛屾渶缁堝畨鍏ㄦ�у彇鍐充簬check濡備綍瀹炵幇锛�
-            shiroTokenManager.check((String) sessionId);
-        }
-        log.debug("read session from cache");
-        Session session = getSessionFromCache(sessionId);
-        if (session == null) {
-            throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
-        }
-        return session;
-    }
-
-    @Override
-    public void update(Session session) throws UnknownSessionException {
-        this.saveSession(session);
-    }
-
-    @Override
-    public void delete(Session session) {
-        if (session != null && session.getId() != null) {
-            shiroCache.remove(KEY_PREFIX + session.getId());
-        }
-    }
-
-    @Override
-    public Collection<Session> getActiveSessions() {
-        Set<Session> sessions = new HashSet<>();
-        Set<Object> keys = shiroCache.keys();
-        if (keys != null && keys.size() > 0) {
-            Iterator iter = keys.iterator();
-            while(iter.hasNext()) {
-                sessions.add((Session) shiroCache.get(iter.next()));
-            }
-        }
-        return sessions;
-    }
-
-    private void saveSession(Session session) throws UnknownSessionException {
-        if (session == null || session.getId() == null) {
-            log.error("session or session id is null");
-            throw new UnknownSessionException("session or session id is null");
-        }
-        shiroCache.put(KEY_PREFIX + session.getId(), (SimpleSession)session, expireTime);
-    }
-
-    private Session getSessionFromCache (Serializable sessionId) {
-        Serializable object = shiroCache.get(KEY_PREFIX + sessionId);
-        Session session = null;
-        if (object != null) {
-            session = (Session)shiroCache.get(KEY_PREFIX + sessionId);
-        }
-        return session;
-    }
-
-    public void setExpireTime (int expireTime) {
-        this.expireTime = expireTime;
-    }
-}
diff --git a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroSessionManager.java b/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroSessionManager.java
deleted file mode 100644
index 9f26e06..0000000
--- a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroSessionManager.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.doumee.config.shiroMemory;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.session.Session;
-import org.apache.shiro.session.mgt.DefaultSessionManager;
-import org.apache.shiro.session.mgt.SessionContext;
-import org.apache.shiro.session.mgt.SessionKey;
-import org.apache.shiro.web.servlet.Cookie;
-import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
-import org.apache.shiro.web.servlet.SimpleCookie;
-import org.apache.shiro.web.session.mgt.WebSessionManager;
-import org.apache.shiro.web.util.WebUtils;
-
-import javax.servlet.ServletRequest;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.Serializable;
-
-/**
- * 鑷畾涔変細璇濈鐞嗗櫒
- * @author Eva.Caesar Liu
- * @date 2023/02/14 11:14
- */
-@Slf4j
-public class ShiroSessionManager extends DefaultSessionManager implements WebSessionManager {
-
-    private static final String AUTH_TOKEN = "eva-auth-token";
-
-    @Override
-    protected void onStart(Session session, SessionContext context) {
-        super.onStart(session, context);
-        if (!WebUtils.isHttp(context)) {
-            log.debug("SessionContext argument is not Http compatible or does not have an Http request/response pair. No session ID cookie will be set.");
-            return;
-        }
-        HttpServletRequest request = WebUtils.getHttpRequest(context);
-        HttpServletResponse response = WebUtils.getHttpResponse(context);
-        Serializable sessionId = session.getId();
-        this.storeSessionId(sessionId, request, response);
-        request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
-        request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
-    }
-
-    @Override
-    public Serializable getSessionId(SessionKey key) {
-        Serializable sessionId = super.getSessionId(key);
-        if (sessionId == null && WebUtils.isWeb(key)) {
-            ServletRequest servletRequest = WebUtils.getRequest(key);
-            if (!(servletRequest instanceof HttpServletRequest)) {
-                log.trace("Can not get sessionId from header, the request is not HttpServletRequest");
-                return null;
-            }
-            HttpServletRequest request = (HttpServletRequest) servletRequest;
-            // 浠巆ookie涓幏鍙栬璇�
-            javax.servlet.http.Cookie[] cookies = request.getCookies();
-            if (cookies != null) {
-                for (javax.servlet.http.Cookie cookie : cookies) {
-                    if (AUTH_TOKEN.equals(cookie.getName())) {
-                        return cookie.getValue();
-                    }
-                }
-            }
-            // 浠巋eader涓幏鍙栬璇�
-            return request.getHeader(AUTH_TOKEN);
-        }
-        return sessionId;
-    }
-
-    @Override
-    public boolean isServletContainerSessions() {
-        return false;
-    }
-
-    private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
-        if (currentId == null) {
-            String msg = "sessionId cannot be null when persisting for subsequent requests.";
-            throw new IllegalArgumentException(msg);
-        }
-        Cookie cookie = new SimpleCookie(AUTH_TOKEN);
-        cookie.setHttpOnly(false);
-        String idString = currentId.toString();
-        cookie.setValue(idString);
-        cookie.saveTo(request, response);
-        log.trace("Set session ID cookie for session with id {}", idString);
-    }
-}
diff --git a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroTokenManager.java b/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroTokenManager.java
deleted file mode 100644
index 795bdcf..0000000
--- a/server/service/src/main/java/com/doumee/config/shiroMemory/ShiroTokenManager.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.doumee.config.shiroMemory;
-
-import com.doumee.core.exception.UnSafeSessionException;
-import org.springframework.stereotype.Component;
-
-import java.util.UUID;
-
-/**
- * 榛樿Token绠$悊鍣�
- * @author Eva.Caesar Liu
- * @date 2023/02/14 11:14
- */
-//@Component
-public class ShiroTokenManager {
-
-    String build() {
-        return UUID.randomUUID().toString();
-    }
-
-    void check(String token) throws UnSafeSessionException {
-        if (token == null || token.length() != 36) {
-            throw new UnSafeSessionException();
-        }
-    }
-}
diff --git a/server/service/src/main/java/com/doumee/core/utils/Constants.java b/server/service/src/main/java/com/doumee/core/utils/Constants.java
index 61b8652..396b880 100644
--- a/server/service/src/main/java/com/doumee/core/utils/Constants.java
+++ b/server/service/src/main/java/com/doumee/core/utils/Constants.java
@@ -54,6 +54,15 @@
     public static final String BRAND_IMG = "BRAND_IMG";
     public static final String PLATFORM = "PLATFORM";
     public static final String ZHUBO_ROOM_URL = "ZHUBO_ROOM_URL";
+    public static final String SMS_IP ="SMS_IP" ;
+    public static final String SMS_PORT ="SMS_PORT" ;
+    public static final String SMS_APPKEY="SMS_APPKEY";
+    public static final String SMS_APPSECRET ="SMS_APPSERECT" ;
+    public static final String SMS ="SMS" ;
+    public static final String SIGN = "SIGN";
+    public static final String SIGN_URL = "SIGN_URL";
+    public static final String SIGN_APPKEY = "SIGN_APPKEY";
+    public static final String SIGN_APPSECRET = "SIGN_APPSECRET";
 
     /**
      * 浼佷笟鏁版嵁鏉ユ簮 0骞冲彴娉ㄥ唽 1鍚庡彴瀵煎叆
diff --git a/server/service/src/main/java/com/doumee/dao/business/InterfaceLogMapper.java b/server/service/src/main/java/com/doumee/dao/business/InterfaceLogMapper.java
new file mode 100644
index 0000000..9d58748
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/InterfaceLogMapper.java
@@ -0,0 +1,11 @@
+package com.doumee.dao.business;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.doumee.dao.business.model.InterfaceLog;
+
+/**
+ * @author 姹熻箘韫�
+ * @date 2023/11/30 15:33
+ */
+public interface InterfaceLogMapper extends BaseMapper<InterfaceLog> {
+}
diff --git a/server/service/src/main/java/com/doumee/dao/business/model/InterfaceLog.java b/server/service/src/main/java/com/doumee/dao/business/model/InterfaceLog.java
new file mode 100644
index 0000000..3686f02
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/dao/business/model/InterfaceLog.java
@@ -0,0 +1,91 @@
+package com.doumee.dao.business.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.doumee.core.annotation.excel.ExcelColumn;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 涓夋柟骞冲彴鎺ュ彛浜や簰璁板綍
+ * @author 姹熻箘韫�
+ * @date 2023/11/30 15:33
+ */
+@Data
+@ApiModel("涓夋柟骞冲彴鎺ュ彛浜や簰璁板綍")
+@TableName("`interface_log`")
+public class InterfaceLog {
+
+    @TableId(type = IdType.AUTO)
+    @ApiModelProperty(value = "涓婚敭", example = "1")
+    @ExcelColumn(name="涓婚敭")
+    private Integer id;
+
+    @ApiModelProperty(value = "鍒涘缓浜虹紪鐮�", example = "1")
+    @ExcelColumn(name="鍒涘缓浜虹紪鐮�")
+    private Integer creator;
+
+    @ApiModelProperty(value = "鍒涘缓鏃堕棿")
+    @ExcelColumn(name="鍒涘缓鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date createDate;
+
+    @ApiModelProperty(value = "鏇存柊浜虹紪鐮�", example = "1")
+    @ExcelColumn(name="鏇存柊浜虹紪鐮�")
+    private Integer editor;
+
+    @ApiModelProperty(value = "鏇存柊鏃堕棿")
+    @ExcelColumn(name="鏇存柊鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date editDate;
+
+    @ApiModelProperty(value = "鏄惁鍒犻櫎0鍚� 1鏄�", example = "1")
+    @ExcelColumn(name="鏄惁鍒犻櫎0鍚� 1鏄�")
+    private Integer isdeleted;
+
+    @ApiModelProperty(value = "澶囨敞")
+    @ExcelColumn(name="澶囨敞")
+    private String remark;
+
+    @ApiModelProperty(value = "绫诲瀷 0璋冪敤 1鎺ㄩ�佹帴鍙�", example = "1")
+    @ExcelColumn(name="绫诲瀷 0璋冪敤 1鎺ㄩ�佹帴鍙�")
+    private Integer type;
+
+    @ApiModelProperty(value = "鎺ュ彛鍚嶇О")
+    @ExcelColumn(name="鎺ュ彛鍚嶇О")
+    private String name;
+
+    @ApiModelProperty(value = "鍦板潃淇℃伅")
+    @ExcelColumn(name="鍦板潃淇℃伅")
+    private String url;
+
+    @ApiModelProperty(value = "璇锋眰鍙傛暟")
+    @ExcelColumn(name="璇锋眰鍙傛暟")
+    private String request;
+
+    @ApiModelProperty(value = "鍝嶅簲鍙傛暟")
+    @ExcelColumn(name="鍝嶅簲鍙傛暟")
+    private String repose;
+
+    @ApiModelProperty(value = "璋冪敤缁撴灉 0鎴愬姛 1澶辫触", example = "1")
+    @ExcelColumn(name="璋冪敤缁撴灉 0鎴愬姛 1澶辫触")
+    private Integer success;
+
+    @ApiModelProperty(value = "骞冲彴 0娴峰悍瀹夐槻骞冲彴 1ERP绯荤粺", example = "1")
+    @ExcelColumn(name="骞冲彴 0娴峰悍瀹夐槻骞冲彴 1ERP绯荤粺")
+    private Integer plat;
+
+    @ApiModelProperty(value = "鍏宠仈瀵硅薄绫诲瀷  0缁勭粐 1浜哄憳 2闂ㄧ浜嬩欢 3璁惧", example = "1")
+    @ExcelColumn(name="鍏宠仈瀵硅薄绫诲瀷  0缁勭粐 1浜哄憳 2闂ㄧ浜嬩欢 3璁惧")
+    private Integer objType;
+
+    @ApiModelProperty(value = "鍏宠仈瀵硅薄缂栫爜锛堝涓敤鑻辨枃閫楀彿闅斿紑锛�")
+    @ExcelColumn(name="鍏宠仈瀵硅薄缂栫爜锛堝涓敤鑻辨枃閫楀彿闅斿紑锛�")
+    private String objId;
+
+}
diff --git a/server/service/src/main/java/com/doumee/dao/system/model/SystemDictData.java b/server/service/src/main/java/com/doumee/dao/system/model/SystemDictData.java
index d626924..5d37df0 100644
--- a/server/service/src/main/java/com/doumee/dao/system/model/SystemDictData.java
+++ b/server/service/src/main/java/com/doumee/dao/system/model/SystemDictData.java
@@ -59,5 +59,7 @@
 
     @ApiModelProperty(value = "鏄惁鍒犻櫎", hidden = true)
     private Boolean deleted;
+    @ApiModelProperty(value = "澶囨敞璇存槑" )
+    private String remark;
 
 }
diff --git a/server/service/src/main/java/com/doumee/service/business/InterfaceLogService.java b/server/service/src/main/java/com/doumee/service/business/InterfaceLogService.java
new file mode 100644
index 0000000..e17f4fc
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/service/business/InterfaceLogService.java
@@ -0,0 +1,98 @@
+package com.doumee.service.business;
+
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.dao.business.model.InterfaceLog;
+
+import java.util.List;
+
+/**
+ * 涓夋柟骞冲彴鎺ュ彛浜や簰璁板綍Service瀹氫箟
+ * @author 姹熻箘韫�
+ * @date 2023/11/30 15:33
+ */
+public interface InterfaceLogService {
+
+    /**
+     * 鍒涘缓
+     * 
+     * @param interfaceLog 瀹炰綋瀵硅薄
+     * @return Integer
+     */
+    Integer create(InterfaceLog interfaceLog);
+
+    /**
+     * 涓婚敭鍒犻櫎
+     *
+     * @param id 涓婚敭
+     */
+    void deleteById(Integer id);
+
+    /**
+     * 鍒犻櫎
+     *
+     * @param interfaceLog 瀹炰綋瀵硅薄
+     */
+    void delete(InterfaceLog interfaceLog);
+
+    /**
+     * 鎵归噺涓婚敭鍒犻櫎
+     *
+     * @param ids 涓婚敭闆�
+     */
+    void deleteByIdInBatch(List<Integer> ids);
+
+    /**
+     * 涓婚敭鏇存柊
+     *
+     * @param interfaceLog 瀹炰綋瀵硅薄
+     */
+    void updateById(InterfaceLog interfaceLog);
+
+    /**
+     * 鎵归噺涓婚敭鏇存柊
+     *
+     * @param interfaceLogs 瀹炰綋闆�
+     */
+    void updateByIdInBatch(List<InterfaceLog> interfaceLogs);
+
+    /**
+     * 涓婚敭鏌ヨ
+     *
+     * @param id 涓婚敭
+     * @return InterfaceLog
+     */
+    InterfaceLog findById(Integer id);
+
+    /**
+     * 鏉′欢鏌ヨ鍗曟潯璁板綍
+     *
+     * @param interfaceLog 瀹炰綋瀵硅薄
+     * @return InterfaceLog
+     */
+    InterfaceLog findOne(InterfaceLog interfaceLog);
+
+    /**
+     * 鏉′欢鏌ヨ
+     *
+     * @param interfaceLog 瀹炰綋瀵硅薄
+     * @return List<InterfaceLog>
+     */
+    List<InterfaceLog> findList(InterfaceLog interfaceLog);
+  
+    /**
+     * 鍒嗛〉鏌ヨ
+     *
+     * @param pageWrap 鍒嗛〉瀵硅薄
+     * @return PageData<InterfaceLog>
+     */
+    PageData<InterfaceLog> findPage(PageWrap<InterfaceLog> pageWrap);
+
+    /**
+     * 鏉′欢缁熻
+     *
+     * @param interfaceLog 瀹炰綋瀵硅薄
+     * @return long
+     */
+    long count(InterfaceLog interfaceLog);
+}
diff --git a/server/service/src/main/java/com/doumee/service/business/impl/InterfaceLogServiceImpl.java b/server/service/src/main/java/com/doumee/service/business/impl/InterfaceLogServiceImpl.java
new file mode 100644
index 0000000..c34d38b
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/service/business/impl/InterfaceLogServiceImpl.java
@@ -0,0 +1,157 @@
+package com.doumee.service.business.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.doumee.core.model.PageData;
+import com.doumee.core.model.PageWrap;
+import com.doumee.core.utils.Utils;
+import com.doumee.dao.business.InterfaceLogMapper;
+import com.doumee.dao.business.model.InterfaceLog;
+import com.doumee.service.business.InterfaceLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+
+/**
+ * 涓夋柟骞冲彴鎺ュ彛浜や簰璁板綍Service瀹炵幇
+ * @author 姹熻箘韫�
+ * @date 2023/11/30 15:33
+ */
+@Service
+public class InterfaceLogServiceImpl implements InterfaceLogService {
+
+    @Autowired
+    private InterfaceLogMapper interfaceLogMapper;
+
+    @Override
+    public Integer create(InterfaceLog interfaceLog) {
+        interfaceLogMapper.insert(interfaceLog);
+        return interfaceLog.getId();
+    }
+
+    @Override
+    public void deleteById(Integer id) {
+        interfaceLogMapper.deleteById(id);
+    }
+
+    @Override
+    public void delete(InterfaceLog interfaceLog) {
+        UpdateWrapper<InterfaceLog> deleteWrapper = new UpdateWrapper<>(interfaceLog);
+        interfaceLogMapper.delete(deleteWrapper);
+    }
+
+    @Override
+    public void deleteByIdInBatch(List<Integer> ids) {
+        if (CollectionUtils.isEmpty(ids)) {
+            return;
+        }
+        interfaceLogMapper.deleteBatchIds(ids);
+    }
+
+    @Override
+    public void updateById(InterfaceLog interfaceLog) {
+        interfaceLogMapper.updateById(interfaceLog);
+    }
+
+    @Override
+    public void updateByIdInBatch(List<InterfaceLog> interfaceLogs) {
+        if (CollectionUtils.isEmpty(interfaceLogs)) {
+            return;
+        }
+        for (InterfaceLog interfaceLog: interfaceLogs) {
+            this.updateById(interfaceLog);
+        }
+    }
+
+    @Override
+    public InterfaceLog findById(Integer id) {
+        return interfaceLogMapper.selectById(id);
+    }
+
+    @Override
+    public InterfaceLog findOne(InterfaceLog interfaceLog) {
+        QueryWrapper<InterfaceLog> wrapper = new QueryWrapper<>(interfaceLog);
+        return interfaceLogMapper.selectOne(wrapper);
+    }
+
+    @Override
+    public List<InterfaceLog> findList(InterfaceLog interfaceLog) {
+        QueryWrapper<InterfaceLog> wrapper = new QueryWrapper<>(interfaceLog);
+        return interfaceLogMapper.selectList(wrapper);
+    }
+  
+    @Override
+    public PageData<InterfaceLog> findPage(PageWrap<InterfaceLog> pageWrap) {
+        IPage<InterfaceLog> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
+        QueryWrapper<InterfaceLog> queryWrapper = new QueryWrapper<>();
+        Utils.MP.blankToNull(pageWrap.getModel());
+        if (pageWrap.getModel().getId() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getId, pageWrap.getModel().getId());
+        }
+        if (pageWrap.getModel().getCreator() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getCreator, pageWrap.getModel().getCreator());
+        }
+        if (pageWrap.getModel().getCreateDate() != null) {
+            queryWrapper.lambda().ge(InterfaceLog::getCreateDate, Utils.Date.getStart(pageWrap.getModel().getCreateDate()));
+            queryWrapper.lambda().le(InterfaceLog::getCreateDate, Utils.Date.getEnd(pageWrap.getModel().getCreateDate()));
+        }
+        if (pageWrap.getModel().getEditor() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getEditor, pageWrap.getModel().getEditor());
+        }
+        if (pageWrap.getModel().getEditDate() != null) {
+            queryWrapper.lambda().ge(InterfaceLog::getEditDate, Utils.Date.getStart(pageWrap.getModel().getEditDate()));
+            queryWrapper.lambda().le(InterfaceLog::getEditDate, Utils.Date.getEnd(pageWrap.getModel().getEditDate()));
+        }
+        if (pageWrap.getModel().getIsdeleted() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getIsdeleted, pageWrap.getModel().getIsdeleted());
+        }
+        if (pageWrap.getModel().getRemark() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getRemark, pageWrap.getModel().getRemark());
+        }
+        if (pageWrap.getModel().getType() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getType, pageWrap.getModel().getType());
+        }
+        if (pageWrap.getModel().getName() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getName, pageWrap.getModel().getName());
+        }
+        if (pageWrap.getModel().getUrl() != null) {
+            queryWrapper.lambda().like(InterfaceLog::getUrl, pageWrap.getModel().getUrl());
+        }
+        if (pageWrap.getModel().getRequest() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getRequest, pageWrap.getModel().getRequest());
+        }
+        if (pageWrap.getModel().getRepose() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getRepose, pageWrap.getModel().getRepose());
+        }
+        if (pageWrap.getModel().getSuccess() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getSuccess, pageWrap.getModel().getSuccess());
+        }
+        if (pageWrap.getModel().getPlat() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getPlat, pageWrap.getModel().getPlat());
+        }
+        if (pageWrap.getModel().getObjType() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getObjType, pageWrap.getModel().getObjType());
+        }
+        if (pageWrap.getModel().getObjId() != null) {
+            queryWrapper.lambda().eq(InterfaceLog::getObjId, pageWrap.getModel().getObjId());
+        }
+        for(PageWrap.SortData sortData: pageWrap.getSorts()) {
+            if (sortData.getDirection().equalsIgnoreCase(PageWrap.DESC)) {
+                queryWrapper.orderByDesc(sortData.getProperty());
+            } else {
+                queryWrapper.orderByAsc(sortData.getProperty());
+            }
+        }
+        return PageData.from(interfaceLogMapper.selectPage(page, queryWrapper));
+    }
+
+    @Override
+    public long count(InterfaceLog interfaceLog) {
+        QueryWrapper<InterfaceLog> wrapper = new QueryWrapper<>(interfaceLog);
+        return interfaceLogMapper.selectCount(wrapper);
+    }
+}
diff --git a/server/service/src/main/java/com/doumee/service/business/third/EmayService.java b/server/service/src/main/java/com/doumee/service/business/third/EmayService.java
new file mode 100644
index 0000000..0292bf6
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/service/business/third/EmayService.java
@@ -0,0 +1,91 @@
+package com.doumee.service.business.third;
+
+import cn.emay.sdk.client.SmsSDKClient;
+import cn.emay.sdk.core.dto.sms.common.CustomSmsIdAndMobile;
+import cn.emay.sdk.core.dto.sms.common.CustomSmsIdAndMobileAndContent;
+import cn.emay.sdk.core.dto.sms.common.PersonalityParams;
+import cn.emay.sdk.core.dto.sms.common.ResultModel;
+import cn.emay.sdk.core.dto.sms.request.*;
+import cn.emay.sdk.core.dto.sms.response.*;
+import cn.emay.sdk.util.exception.SDKParamsException;
+import com.alibaba.fastjson.JSONObject;
+import com.doumee.biz.system.SystemDictDataBiz;
+import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.model.InterfaceLog;
+import com.doumee.service.business.InterfaceLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.io.IOException;
+import java.util.Date;
+
+@Service
+public class EmayService {
+	private static SmsSDKClient client ;
+
+	public SmsSDKClient getClient() {
+		return client;
+	}
+
+	public void setClient(SmsSDKClient client) {
+		this.client = client;
+	}
+	@Autowired
+	private InterfaceLogService interfaceLogService  ;
+	@Autowired
+	private SystemDictDataBiz  systemDictDataBiz;
+	private   void saveInterfaceLog(String path,String name,String req ,String res) {
+		if(interfaceLogService !=null){
+			InterfaceLog hkMonitoryLogDO=new InterfaceLog();
+			hkMonitoryLogDO.setType(0);
+			hkMonitoryLogDO.setCreateDate(new Date());
+			hkMonitoryLogDO.setIsdeleted(0);
+			hkMonitoryLogDO.setRequest(req);
+			hkMonitoryLogDO.setRepose(res);
+			hkMonitoryLogDO.setName(name);
+			hkMonitoryLogDO.setUrl(path);
+			interfaceLogService.create(hkMonitoryLogDO);
+		}
+	}
+	private  static String  ip;
+	private static int port;
+	private static String appSecret;
+	private static String appKey;
+	@PostConstruct//鍚姩鍒濆鍖�
+	public int initClient(){
+		ip = systemDictDataBiz.queryByCode(Constants.SMS,Constants.SMS_IP).getCode();
+		port = Integer.parseInt(systemDictDataBiz.queryByCode(Constants.SMS,Constants.SMS_PORT).getCode());
+		appKey = systemDictDataBiz.queryByCode(Constants.SMS,Constants.SMS_APPKEY).getCode();
+		appSecret = systemDictDataBiz.queryByCode(Constants.SMS,Constants.SMS_APPSECRET).getCode();
+		return 0;
+	}
+
+	public   boolean sendSingleSms(String mobile,String content) throws SDKParamsException {
+		try {
+			client = new SmsSDKClient(ip,port,appKey,appSecret);
+		} catch (SDKParamsException e) {
+			throw new RuntimeException(e);
+		}
+		if(client == null){
+			return false;
+		}
+		String customSmsId = "1";
+		String extendedCode = "01";
+
+		SmsSingleRequest request = new SmsSingleRequest(mobile, content, customSmsId, extendedCode, "");
+		ResultModel<SmsResponse> result = client.sendSingleSms(request);
+		saveInterfaceLog(ip+"/"+port,"銆愮煭淇°�戝彂閫�", JSONObject.toJSONString(result),JSONObject.toJSONString(result));
+		if (result.getCode().equals("SUCCESS")) {
+			System.out.println("璇锋眰鎴愬姛");
+			SmsResponse response = result.getResult();
+			System.out.println("sendSingleSms:" + response.toString());
+			return  true;
+		} else {
+			System.out.println("璇锋眰澶辫触");
+			return false;
+		}
+	}
+
+
+}
diff --git a/server/service/src/main/java/com/doumee/service/business/third/SignService.java b/server/service/src/main/java/com/doumee/service/business/third/SignService.java
new file mode 100644
index 0000000..675bc7d
--- /dev/null
+++ b/server/service/src/main/java/com/doumee/service/business/third/SignService.java
@@ -0,0 +1,310 @@
+package com.doumee.service.business.third;
+
+import cn.emay.sdk.client.SmsSDKClient;
+import cn.emay.sdk.util.exception.SDKParamsException;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.doumee.biz.system.SystemDictDataBiz;
+import com.doumee.core.utils.Constants;
+import com.doumee.dao.business.model.InterfaceLog;
+import com.doumee.service.business.InterfaceLogService;
+import com.jzq.JzqHttpApiTool;
+import com.jzq.common.ResultInfo;
+import com.jzq.common.bean.sign.SignatoryReq;
+import com.jzq.common.http.HttpClientUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.io.File;
+import java.util.Date;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * <ul>
+ * <li>椤圭洰鍚嶇О : 鍚庡彴鏈嶅姟</li>
+ * <li>鏂囦欢鍚嶇О : JzqHttpApiTest</li>
+ * <li>鍒涘缓鏃堕棿 : 2019/8/23 9:14</li>
+ * <li>鎻� 杩� :  鐢ㄤ簬jzq鐨刟pi鏈嶈姹�
+ * </ul>
+ *
+ * @author yfx
+ * @title 鐢ㄤ簬jzq鐨刟pi鏈嶈姹�
+ */
+@Slf4j
+@Service
+public class SignService {
+    /**
+     * 鍚涘瓙绛炬祴璇曠幆澧僰ey鍜屾帴鍙e湴鍧�锛�
+     * appKey锛歞cb4bd535a09df3c
+     * appSecret锛歜87c346edcb4bd535a09df3ca8c45d9a
+     * services_url锛歨ttps://api.sandbox.junziqian.com
+     * 寮�鍙戞枃妗�: https://s.junziqian.com/api_doc/index.html
+     */
+    private static String SERVICE_URL="https://api.sandbox.junziqian.com";
+    private static  String APP_KEY="dcb4bd535a09df3c";
+    private static  String APP_SECRET="b87c346edcb4bd535a09df3ca8c45d9a";
+
+    @Autowired
+    private SystemDictDataBiz systemDictDataBiz;
+    @PostConstruct//鍚姩鍒濆鍖�
+    public int initClient(){
+        SERVICE_URL = systemDictDataBiz.queryByCode(Constants.SIGN,Constants.SIGN_URL).getCode();
+        APP_KEY = systemDictDataBiz.queryByCode(Constants.SIGN,Constants.SIGN_APPKEY).getCode();
+        APP_SECRET = systemDictDataBiz.queryByCode(Constants.SIGN,Constants.SIGN_APPSECRET).getCode();
+        initParams();
+        return 0;
+    }
+    //璇锋眰鐨刡ody鍐呭弬鏁�
+    private static  Map<String, Object> bodyParams;
+    @Autowired
+    public InterfaceLogService interfaceLogService = null;
+    private   void saveInterfaceLog(String path,String name,String req ,String res) {
+        if(interfaceLogService !=null){
+            InterfaceLog hkMonitoryLogDO=new InterfaceLog();
+            hkMonitoryLogDO.setType(0);
+            hkMonitoryLogDO.setCreateDate(new Date());
+            hkMonitoryLogDO.setIsdeleted(0);
+            hkMonitoryLogDO.setRequest(req);
+            hkMonitoryLogDO.setRepose(res);
+            hkMonitoryLogDO.setName(name);
+            hkMonitoryLogDO.setUrl(path);
+            interfaceLogService.create(hkMonitoryLogDO);
+        }
+    }
+
+    public void initParams(){
+        long ts=System.currentTimeMillis();
+        String nonce=DigestUtils.md5Hex(System.currentTimeMillis()+"");
+        String sign=DigestUtils.sha256Hex("nonce"+nonce+"ts"+ts+"app_key"+APP_KEY+"app_secret"+APP_SECRET);
+        bodyParams=new IdentityHashMap<>();
+        bodyParams.put("ts",ts);
+        bodyParams.put("app_key",APP_KEY);
+        bodyParams.put("sign",sign);
+        bodyParams.put("nonce",nonce);//杩欏彧鍙槸涓轰簡鐢熸垚涓�涓殢鏈哄��
+    }
+
+    /**
+     * 1.ping鏈嶅姟
+     */
+    public boolean ping(){
+        initParams();
+        try {
+            Map<String, Object>  params=bodyParams;
+            String url=SERVICE_URL+"/v2/ping";
+            //鎵�鏈夊弬鏁拌鍏ヤ簡body涓�
+            String str= HttpClientUtils.init().getPost(url,null,params,false);
+            log.info("杩斿洖缁撴灉涓�:"+str);
+            ResultInfo ri= JSONObject.parseObject(str,ResultInfo.class);
+            saveInterfaceLog(url,"銆愮數瀛愮銆憄ing鏈嶅姟",JSONObject.toJSONString(params),str);
+            return ri.isSuccess();
+        }catch (Exception e){
+
+        }
+        return false;
+    }
+
+
+    /**
+     * 鍙戣捣浼佷笟璁よ瘉鐢宠
+     * @param fullname
+     * @param creditCode
+     * @param legalName
+     * @param email
+     * @param businessimg
+     * @param notifyUrl
+     * @return
+     */
+    public boolean organizationCreate (String fullname,String creditCode,String legalName,String email,File  businessimg,String notifyUrl){
+        try {
+            initParams();
+            Map<String, Object>  params=bodyParams;
+            String url=SERVICE_URL+"/v2/user/organizationCreate";
+            params.put("name",fullname);
+            params.put("identificationType",1);
+            params.put("organizationRegNo",creditCode);
+            params.put("organizationType",0);
+            params.put("organizationCode",creditCode);
+            params.put("organizationRegImg",businessimg);
+            params.put("notifyUrl",notifyUrl);
+            params.put("legalName",legalName);
+            params.put("emailOrMobile",email);
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            saveInterfaceLog(url,"銆愮數瀛愮銆戝彂璧蜂紒涓氳璇佺敵璇�",JSONObject.toJSONString(params),str);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  true;
+            }
+            System.out.println(str);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return false;
+
+    }
+    /**
+     *閲嶆柊鍙戣捣浼佷笟璁よ瘉鐢宠
+     * @param fullname
+     * @param creditCode
+     * @param legalName
+     * @param email
+     * @param businessimg
+     * @param notifyUrl
+     * @return
+     */
+    public boolean organizationReApply(String fullname,String creditCode,String legalName,String email,File  businessimg,String notifyUrl){
+        try {
+            initParams();
+            Map<String, Object>  params=bodyParams;
+            String url=SERVICE_URL+"/v2/user/organizationReapply";
+            params.put("name",fullname);
+            params.put("identificationType",1);
+            params.put("organizationRegNo",creditCode);
+            params.put("organizationType",0);
+            params.put("organizationCode",creditCode);
+            params.put("organizationRegImg",businessimg);
+            params.put("notifyUrl",notifyUrl);
+            params.put("legalName",legalName);
+            params.put("emailOrMobile",email);
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            saveInterfaceLog(url,"銆愮數瀛愮銆戦噸鏂板彂璧蜂紒涓氳璇佺敵璇�",JSONObject.toJSONString(params),str);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  true;
+            }
+            System.out.println(str);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return false;
+
+    }
+
+    /**
+     * 鏌ヨ浼佷笟绛剧害鐘舵�� 瀹℃壒鐘舵��,0姝e湪鐢宠1閫氳繃2椹冲洖
+     * @param email
+     * @return
+     */
+    public int  organizationAuditStatus (String email){
+        try {
+            initParams();
+            Map<String, Object>  params=bodyParams;
+            String url=SERVICE_URL+"/v2/user/organizationAuditStatus";
+            params.put("emailOrMobile",email);
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            System.out.println(str);
+            saveInterfaceLog(url,"銆愮數瀛愮銆戞煡璇紒涓氱绾︾姸鎬�",JSONObject.toJSONString(params),str);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  json.getJSONObject("data").getIntValue("status");
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return  -1;//鏌ヨ澶辫触
+    }
+
+    /**
+     * 鍙戣捣绛剧害
+     * @param name
+     * @param file
+     * @param fullname
+     * @param creditCoe
+     * @param email
+     * @param postionJson
+     */
+    public String applySign(String name,File file,String fullname,String creditCoe,String email,String postionJson){
+      try {
+          String url=SERVICE_URL+"/v2/sign/applySign";
+          initParams();
+          Map<String, Object>  params=bodyParams;
+          params.put("contractName",name); //鍚堝悓鍚嶇О
+          params.put("serverCa",1); //浣跨敤浜戣瘉涔�
+          params.put("file",file);
+          params.put("dealType",5); //鎸囧畾鍚堝悓鏂囦欢绛剧讲鏂瑰紡 5 涓洪儴鍒嗚嚜鍔ㄧ
+          params.put("positionType",0); //鎸囧畾閫氳繃琛ㄥ崟鍩熸柟寮忚缃瀛椾綅缃�
+          params.put("fileType",0);
+          params.put("needQifengSign",1);
+          JSONArray signatories=new JSONArray();
+          SignatoryReq sReq=new SignatoryReq();
+          sReq.setFullName(fullname); //浼佷笟濮撳悕
+          sReq.setIdentityType(11); //璇佷欢绫诲瀷
+          sReq.setIdentityCard(creditCoe);//钀ヤ笟鎵х収鍙�
+          sReq.setEmail(email); //鍦ㄥ悰瀛愮娉ㄥ唽璁よ瘉鐨勯偖绠�
+//        sReq.setChapteJson("[{\"page\":0,\"chaptes\":[{\"offsetX\":0.12,\"offsetY\":0.23}]},{\"page\":1,\"chaptes\":[{\"offsetX\":0.45,\"offsetY\":0.67}]}]");
+          sReq.setChapteJson(postionJson);
+          sReq.setNoNeedVerify(1);
+          signatories.add(sReq);
+          params.put("signatories",signatories.toJSONString());
+          System.out.println(signatories.toJSONString());
+          String str= HttpClientUtils.init().getPost(url,null,params,true);
+          System.out.println(str);
+          saveInterfaceLog(url,"銆愮數瀛愮銆戝彂璧风绾�",JSONObject.toJSONString(params),str);
+          JSONObject json = JSONObject.parseObject(str);
+          if(json!=null && json.getBoolean("success")){
+              return  json.getString("data");
+          }
+      }catch (Exception e){
+
+      }
+      return null;
+
+    }
+
+
+    /**
+     * 鑾峰彇绛剧讲閾炬帴鍦板潃
+     * @param applyNo
+     * @param name
+     * @param creditCode
+     */
+    public String signLink(String applyNo,String name,String creditCode) {
+        try {
+            initParams();
+            Map<String, Object> params = bodyParams;
+            String url = SERVICE_URL + "/v2/sign/link";
+            params.put("applyNo",applyNo); //鍙戣捣鍚堝悓绛剧讲鎺ュ彛杩斿洖鐨凙PL缂栧彿
+            params.put("fullName",name); //鍙戣捣鍚堝悓绛剧讲鎺ュ彛闇�瑕佹墜鍔ㄧ缃插璞$殑濮撳悕
+            params.put("identityCard",creditCode); //鍙戣捣鍚堝悓绛剧讲鎺ュ彛涓渶瑕佹墜鍔ㄧ缃插璞$殑璇佷欢鍙�
+            params.put("identityType",11); //璇佷欢绫诲瀷锛屼釜浜�1 锛屼紒涓�11
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            System.out.println(str);
+            saveInterfaceLog(url,"銆愮數瀛愮銆戣幏鍙栫缃查摼鎺ュ湴鍧�",JSONObject.toJSONString(params),str);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  json.getString("data");
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return  null;
+    }
+    /**
+     * 鑾峰彇绛剧讲閾炬帴鍦板潃
+     * @param applyNo
+     */
+    public String  linkFile(String applyNo) {
+
+        try {
+            initParams();
+            Map<String, Object> params = bodyParams;
+            String url = SERVICE_URL + "/v2/sign/linkFile";
+            //鏋勫缓璇锋眰鍙傛暟
+            params.put("applyNo",applyNo); //鍙戣捣鍚堝悓绛剧讲鎺ュ彛杩斿洖鐨凙PL缂栧彿
+            String str= HttpClientUtils.init().getPost(url,null,params,true);
+            System.out.println(str);
+            saveInterfaceLog(url,"銆愮數瀛愮銆戣幏鍙栫缃查摼鎺ュ湴鍧�",JSONObject.toJSONString(params),str);
+            JSONObject json = JSONObject.parseObject(str);
+            if(json!=null && json.getBoolean("success")){
+                return  json.getString("data");
+            }
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return  null;
+    }
+
+}

--
Gitblit v1.9.3