@@ -19,7 +19,7 @@ public class WxConsts { | |||
public static final String XML_MSG_LINK = "link"; | |||
public static final String XML_MSG_EVENT = "event"; | |||
public static final String XML_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | |||
/////////////////////// | |||
// 主动发送消息(即客服消息)的消息类型 | |||
/////////////////////// | |||
@@ -33,7 +33,7 @@ public class WxConsts { | |||
public static final String CUSTOM_MSG_TRANSFER_CUSTOMER_SERVICE = "transfer_customer_service"; | |||
public static final String CUSTOM_MSG_SAFE_NO = "0"; | |||
public static final String CUSTOM_MSG_SAFE_YES = "1"; | |||
/////////////////////// | |||
// 群发消息的消息类型 | |||
/////////////////////// | |||
@@ -42,7 +42,7 @@ public class WxConsts { | |||
public static final String MASS_MSG_VOICE = "voice"; | |||
public static final String MASS_MSG_IMAGE = "image"; | |||
public static final String MASS_MSG_VIDEO = "mpvideo"; | |||
/////////////////////// | |||
// 群发消息后微信端推送给服务器的反馈消息 | |||
/////////////////////// | |||
@@ -57,25 +57,11 @@ public class WxConsts { | |||
public static final String MASS_ST_涉嫌版权 = "err(20013)"; | |||
public static final String MASS_ST_涉嫌互推_互相宣传 = "err(22000)"; | |||
public static final String MASS_ST_涉嫌其他 = "err(21000)"; | |||
/** | |||
* 群发反馈消息代码所对应的文字描述 | |||
*/ | |||
public static final Map<String, String> MASS_ST_2_DESC = new HashMap<String, String>(); | |||
static { | |||
MASS_ST_2_DESC.put(MASS_ST_SUCCESS, "发送成功"); | |||
MASS_ST_2_DESC.put(MASS_ST_FAIL, "发送失败"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌广告, "涉嫌广告"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌政治, "涉嫌政治"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌社会, "涉嫌社会"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌色情, "涉嫌色情"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌违法犯罪, "涉嫌违法犯罪"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌欺诈, "涉嫌欺诈"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌版权, "涉嫌版权"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌互推_互相宣传, "涉嫌互推_互相宣传"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌其他, "涉嫌其他"); | |||
} | |||
/////////////////////// | |||
// 微信端推送过来的事件类型 | |||
/////////////////////// | |||
@@ -103,11 +89,9 @@ public class WxConsts { | |||
public static final String EVT_USER_VIEW_CARD = "user_view_card"; | |||
public static final String EVT_USER_ENTER_SESSION_FROM_CARD = "user_enter_session_from_card"; | |||
public static final String EVT_CARD_SKU_REMIND = "card_sku_remind"; // 库存报警 | |||
public static final String EVT_KF_CREATE_SESSION = "kf_create_session"; // 客服接入会话 | |||
public static final String EVT_KF_CLOSE_SESSION = "kf_close_session"; // 客服关闭会话 | |||
public static final String EVT_KF_SWITCH_SESSION = "kf_switch_session"; // 客服转接会话 | |||
/////////////////////// | |||
// 上传多媒体文件的类型 | |||
/////////////////////// | |||
@@ -116,7 +100,6 @@ public class WxConsts { | |||
public static final String MEDIA_VIDEO = "video"; | |||
public static final String MEDIA_THUMB = "thumb"; | |||
public static final String MEDIA_FILE = "file"; | |||
/////////////////////// | |||
// 文件类型 | |||
/////////////////////// | |||
@@ -124,40 +107,63 @@ public class WxConsts { | |||
public static final String FILE_MP3 = "mp3"; | |||
public static final String FILE_AMR = "amr"; | |||
public static final String FILE_MP4 = "mp4"; | |||
/** | |||
* 点击推事件 | |||
*/ | |||
public static final String BUTTON_CLICK = "click"; | |||
/////////////////////// | |||
// 自定义菜单的按钮类型 | |||
/////////////////////// | |||
/** 点击推事件 */ | |||
public static final String BUTTON_CLICK = "click"; | |||
/** 跳转URL */ | |||
/** | |||
* 跳转URL | |||
*/ | |||
public static final String BUTTON_VIEW = "view"; | |||
/** 扫码推事件 */ | |||
/** | |||
* 扫码推事件 | |||
*/ | |||
public static final String BUTTON_SCANCODE_PUSH = "scancode_push"; | |||
/** 扫码推事件且弹出“消息接收中”提示框 */ | |||
/** | |||
* 扫码推事件且弹出“消息接收中”提示框 | |||
*/ | |||
public static final String BUTTON_SCANCODE_WAITMSG = "scancode_waitmsg"; | |||
/** 弹出系统拍照发图 */ | |||
/** | |||
* 弹出系统拍照发图 | |||
*/ | |||
public static final String BUTTON_PIC_SYSPHOTO = "pic_sysphoto"; | |||
/** 弹出拍照或者相册发图 */ | |||
/** | |||
* 弹出拍照或者相册发图 | |||
*/ | |||
public static final String BUTTON_PIC_PHOTO_OR_ALBUM = "pic_photo_or_album"; | |||
/** 弹出微信相册发图器 */ | |||
/** | |||
* 弹出微信相册发图器 | |||
*/ | |||
public static final String BUTTON_PIC_WEIXIN = "pic_weixin"; | |||
/** 弹出地理位置选择器 */ | |||
/** | |||
* 弹出地理位置选择器 | |||
*/ | |||
public static final String BUTTON_LOCATION_SELECT = "location_select"; | |||
/** 下发消息(除文本消息) */ | |||
/** | |||
* 下发消息(除文本消息) | |||
*/ | |||
public static final String BUTTON_MEDIA_ID = "media_id"; | |||
/** 跳转图文消息URL */ | |||
/** | |||
* 跳转图文消息URL | |||
*/ | |||
public static final String BUTTON_VIEW_LIMITED = "view_limited"; | |||
/** | |||
* 不弹出授权页面,直接跳转,只能获取用户openid | |||
*/ | |||
public static final String OAUTH2_SCOPE_BASE = "snsapi_base"; | |||
/////////////////////// | |||
// oauth2网页授权的scope | |||
/////////////////////// | |||
/** 不弹出授权页面,直接跳转,只能获取用户openid */ | |||
public static final String OAUTH2_SCOPE_BASE = "snsapi_base"; | |||
/** 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 */ | |||
/** | |||
* 弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 | |||
*/ | |||
public static final String OAUTH2_SCOPE_USER_INFO = "snsapi_userinfo"; | |||
/////////////////////// | |||
// 永久素材类型 | |||
/////////////////////// | |||
@@ -165,4 +171,18 @@ public class WxConsts { | |||
public static final String MATERIAL_VOICE = "voice"; | |||
public static final String MATERIAL_IMAGE = "image"; | |||
public static final String MATERIAL_VIDEO = "video"; | |||
static { | |||
MASS_ST_2_DESC.put(MASS_ST_SUCCESS, "发送成功"); | |||
MASS_ST_2_DESC.put(MASS_ST_FAIL, "发送失败"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌广告, "涉嫌广告"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌政治, "涉嫌政治"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌社会, "涉嫌社会"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌色情, "涉嫌色情"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌违法犯罪, "涉嫌违法犯罪"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌欺诈, "涉嫌欺诈"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌版权, "涉嫌版权"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌互推_互相宣传, "涉嫌互推_互相宣传"); | |||
MASS_ST_2_DESC.put(MASS_ST_涉嫌其他, "涉嫌其他"); | |||
} | |||
} |
@@ -10,15 +10,16 @@ public interface WxMessageDuplicateChecker { | |||
/** | |||
* <h2>公众号的排重方式</h2> | |||
* | |||
* <p> | |||
* <p>普通消息:关于重试的消息排重,推荐使用msgid排重。<a href="http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html">文档参考</a>。</p> | |||
* <p>事件消息:关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。<a href="http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html">文档参考</a></p> | |||
* | |||
* <p> | |||
* <h2>企业号的排重方式</h2> | |||
* | |||
* <p> | |||
* 官方文档完全没有写,参照公众号的方式排重。 | |||
* | |||
* <p> | |||
* <p>或者可以采取更简单的方式,如果有MsgId就用MsgId排重,如果没有就用FromUserName+CreateTime排重</p> | |||
* | |||
* @param messageId messageId需要根据上面讲的方式构造 | |||
* @return 如果是重复消息,返回true,否则返回false | |||
*/ | |||
@@ -46,7 +46,8 @@ public class WxMessageInMemoryDuplicateChecker implements WxMessageDuplicateChec | |||
/** | |||
* WxMsgIdInMemoryDuplicateChecker构造函数 | |||
* @param timeToLive 一个消息ID在内存的过期时间:毫秒 | |||
* | |||
* @param timeToLive 一个消息ID在内存的过期时间:毫秒 | |||
* @param clearPeriod 每隔多少周期检查消息ID是否过期:毫秒 | |||
*/ | |||
public WxMessageInMemoryDuplicateChecker(Long timeToLive, Long clearPeriod) { | |||
@@ -8,9 +8,13 @@ public class WxAccessToken implements Serializable { | |||
private static final long serialVersionUID = 8709719312922168909L; | |||
private String accessToken; | |||
private int expiresIn = -1; | |||
public static WxAccessToken fromJson(String json) { | |||
return WxGsonBuilder.create().fromJson(json, WxAccessToken.class); | |||
} | |||
public String getAccessToken() { | |||
return accessToken; | |||
} | |||
@@ -27,8 +31,4 @@ public class WxAccessToken implements Serializable { | |||
this.expiresIn = expiresIn; | |||
} | |||
public static WxAccessToken fromJson(String json) { | |||
return WxGsonBuilder.create().fromJson(json, WxAccessToken.class); | |||
} | |||
} |
@@ -9,7 +9,7 @@ public class WxJsapiSignature implements Serializable { | |||
private static final long serialVersionUID = -1116808193154384804L; | |||
private String appid; | |||
private String noncestr; | |||
private long timestamp; | |||
@@ -51,11 +51,11 @@ public class WxJsapiSignature implements Serializable { | |||
} | |||
public String getAppid() { | |||
return appid; | |||
return appid; | |||
} | |||
public void setAppid(String appid) { | |||
this.appid = appid; | |||
this.appid = appid; | |||
} | |||
} |
@@ -1,5 +1,7 @@ | |||
package me.chanjar.weixin.common.bean; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
import java.io.InputStream; | |||
import java.io.InputStreamReader; | |||
import java.io.Serializable; | |||
@@ -7,12 +9,10 @@ import java.nio.charset.StandardCharsets; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import me.chanjar.weixin.common.util.json.WxGsonBuilder; | |||
/** | |||
* 企业号菜单 | |||
* @author Daniel Qian | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class WxMenu implements Serializable { | |||
@@ -21,26 +21,6 @@ public class WxMenu implements Serializable { | |||
private List<WxMenuButton> buttons = new ArrayList<WxMenuButton>(); | |||
private WxMenuRule matchRule; | |||
public List<WxMenuButton> getButtons() { | |||
return buttons; | |||
} | |||
public void setButtons(List<WxMenuButton> buttons) { | |||
this.buttons = buttons; | |||
} | |||
public WxMenuRule getMatchRule() { | |||
return matchRule; | |||
} | |||
public void setMatchRule(WxMenuRule matchRule) { | |||
this.matchRule = matchRule; | |||
} | |||
public String toJson() { | |||
return WxGsonBuilder.create().toJson(this); | |||
} | |||
/** | |||
* 要用 http://mp.weixin.qq.com/wiki/16/ff9b7b85220e1396ffa16794a9d95adc.html 格式来反序列化 | |||
@@ -58,11 +38,31 @@ public class WxMenu implements Serializable { | |||
return WxGsonBuilder.create().fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), WxMenu.class); | |||
} | |||
public List<WxMenuButton> getButtons() { | |||
return buttons; | |||
} | |||
public void setButtons(List<WxMenuButton> buttons) { | |||
this.buttons = buttons; | |||
} | |||
public WxMenuRule getMatchRule() { | |||
return matchRule; | |||
} | |||
public void setMatchRule(WxMenuRule matchRule) { | |||
this.matchRule = matchRule; | |||
} | |||
public String toJson() { | |||
return WxGsonBuilder.create().toJson(this); | |||
} | |||
@Override | |||
public String toString() { | |||
return "WxMenu{" + | |||
"buttons=" + buttons + | |||
'}'; | |||
"buttons=" + buttons + | |||
'}'; | |||
} | |||
public static class WxMenuButton { | |||
@@ -71,7 +71,7 @@ public class WxMenu implements Serializable { | |||
private String name; | |||
private String key; | |||
private String url; | |||
private List<WxMenuButton> subButtons = new ArrayList<WxMenuButton>(); | |||
public String getType() { | |||
@@ -117,15 +117,15 @@ public class WxMenu implements Serializable { | |||
@Override | |||
public String toString() { | |||
return "WxMenuButton{" + | |||
"type='" + type + '\'' + | |||
", name='" + name + '\'' + | |||
", key='" + key + '\'' + | |||
", url='" + url + '\'' + | |||
", subButtons=" + subButtons + | |||
'}'; | |||
"type='" + type + '\'' + | |||
", name='" + name + '\'' + | |||
", key='" + key + '\'' + | |||
", url='" + url + '\'' + | |||
", subButtons=" + subButtons + | |||
'}'; | |||
} | |||
} | |||
public static class WxMenuRule { | |||
private String tagId; | |||
private String sex; | |||
@@ -134,51 +134,51 @@ public class WxMenu implements Serializable { | |||
private String city; | |||
private String clientPlatformType; | |||
private String language; | |||
public String getTagId() { | |||
return tagId; | |||
} | |||
public void setTagId(String tagId) { | |||
this.tagId = tagId; | |||
} | |||
public String getSex() { | |||
return sex; | |||
} | |||
public void setSex(String sex) { | |||
this.sex = sex; | |||
} | |||
public String getCountry() { | |||
return country; | |||
} | |||
public void setCountry(String country) { | |||
this.country = country; | |||
} | |||
public String getProvince() { | |||
return province; | |||
} | |||
public void setProvince(String province) { | |||
this.province = province; | |||
} | |||
public String getCity() { | |||
return city; | |||
} | |||
public void setCity(String city) { | |||
this.city = city; | |||
} | |||
public String getClientPlatformType() { | |||
return clientPlatformType; | |||
} | |||
public void setClientPlatformType(String clientPlatformType) { | |||
this.clientPlatformType = clientPlatformType; | |||
} | |||
@@ -194,15 +194,15 @@ public class WxMenu implements Serializable { | |||
@Override | |||
public String toString() { | |||
return "matchrule:{" + | |||
"tag_id='" + tagId + '\'' + | |||
", sex='" + sex + '\'' + | |||
", country" + country + '\'' + | |||
", province" + province + '\'' + | |||
", city" + city + '\'' + | |||
", client_platform_type" + clientPlatformType + '\'' + | |||
", language" + language + '\'' + | |||
"}"; | |||
"tag_id='" + tagId + '\'' + | |||
", sex='" + sex + '\'' + | |||
", country" + country + '\'' + | |||
", province" + province + '\'' + | |||
", city" + city + '\'' + | |||
", client_platform_type" + clientPlatformType + '\'' + | |||
", language" + language + '\'' + | |||
"}"; | |||
} | |||
} | |||
} |
@@ -6,19 +6,28 @@ import java.io.Serializable; | |||
/** | |||
* 微信错误码说明,请阅读: <a href="http://mp.weixin.qq.com/wiki/10/6380dc743053a91c544ffd2b7c959166.html">全局返回码说明</a> | |||
* @author Daniel Qian | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class WxError implements Serializable { | |||
private static final long serialVersionUID = 7869786563361406291L; | |||
private int errorCode; | |||
private String errorMsg; | |||
private String json; | |||
public static WxError fromJson(String json) { | |||
WxError error = WxGsonBuilder.create().fromJson(json, WxError.class); | |||
return error; | |||
} | |||
public static Builder newBuilder() { | |||
return new Builder(); | |||
} | |||
public int getErrorCode() { | |||
return errorCode; | |||
} | |||
@@ -43,43 +52,34 @@ public class WxError implements Serializable { | |||
this.json = json; | |||
} | |||
public static WxError fromJson(String json) { | |||
WxError error = WxGsonBuilder.create().fromJson(json, WxError.class); | |||
return error; | |||
} | |||
@Override | |||
public String toString() { | |||
if(json != null){ | |||
if (json != null) { | |||
return json; | |||
} | |||
return "错误: Code=" + errorCode + ", Msg=" + errorMsg; | |||
} | |||
public static Builder newBuilder(){ | |||
return new Builder(); | |||
} | |||
public static class Builder{ | |||
public static class Builder { | |||
private int errorCode; | |||
private String errorMsg; | |||
public Builder setErrorCode(int errorCode) { | |||
this.errorCode = errorCode; | |||
return this; | |||
} | |||
public Builder setErrorMsg(String errorMsg) { | |||
this.errorMsg = errorMsg; | |||
return this; | |||
} | |||
public WxError build(){ | |||
public WxError build() { | |||
WxError wxError = new WxError(); | |||
wxError.setErrorCode(this.errorCode); | |||
wxError.setErrorMsg(this.errorMsg); | |||
return wxError; | |||
} | |||
} | |||
} |
@@ -6,12 +6,16 @@ import java.io.Serializable; | |||
public class WxMediaUploadResult implements Serializable { | |||
private static final long serialVersionUID = 330834334738622341L; | |||
private String type; | |||
private String mediaId; | |||
private String thumbMediaId; | |||
private long createdAt; | |||
public static WxMediaUploadResult fromJson(String json) { | |||
return WxGsonBuilder.create().fromJson(json, WxMediaUploadResult.class); | |||
} | |||
public String getType() { | |||
return type; | |||
} | |||
@@ -44,14 +48,10 @@ public class WxMediaUploadResult implements Serializable { | |||
this.thumbMediaId = thumbMediaId; | |||
} | |||
public static WxMediaUploadResult fromJson(String json) { | |||
return WxGsonBuilder.create().fromJson(json, WxMediaUploadResult.class); | |||
} | |||
@Override | |||
public String toString() { | |||
return "WxUploadResult [type=" + type + ", media_id=" + mediaId + ", thumb_media_id=" + thumbMediaId | |||
+ ", created_at=" + createdAt + "]"; | |||
+ ", created_at=" + createdAt + "]"; | |||
} | |||
} |
@@ -5,7 +5,7 @@ import me.chanjar.weixin.common.bean.result.WxError; | |||
public class WxErrorException extends Exception { | |||
private static final long serialVersionUID = -6357149550353160810L; | |||
private WxError error; | |||
public WxErrorException(WxError error) { | |||
@@ -17,5 +17,5 @@ public class WxErrorException extends Exception { | |||
return error; | |||
} | |||
} |
@@ -26,6 +26,6 @@ package me.chanjar.weixin.common.session; | |||
public class Constants { | |||
public static final String Package = "me.chanjar.weixin.common.session"; | |||
public static final String Package = "me.chanjar.weixin.common.session"; | |||
} |
@@ -8,6 +8,11 @@ public interface InternalSession { | |||
*/ | |||
WxSession getSession(); | |||
/** | |||
* Return the <code>isValid</code> flag for this session. | |||
*/ | |||
boolean isValid(); | |||
/** | |||
* Set the <code>isValid</code> flag for this session. | |||
* | |||
@@ -15,11 +20,6 @@ public interface InternalSession { | |||
*/ | |||
void setValid(boolean isValid); | |||
/** | |||
* Return the <code>isValid</code> flag for this session. | |||
*/ | |||
boolean isValid(); | |||
/** | |||
* Return the session identifier for this session. | |||
*/ | |||
@@ -7,11 +7,10 @@ public interface InternalSessionManager { | |||
* specified session id (if any); otherwise return <code>null</code>. | |||
* | |||
* @param id The session id for the session to be returned | |||
* | |||
* @exception IllegalStateException if a new session cannot be | |||
* instantiated for any reason | |||
* @exception java.io.IOException if an input/output error occurs while | |||
* processing this request | |||
* @throws IllegalStateException if a new session cannot be | |||
* instantiated for any reason | |||
* @throws java.io.IOException if an input/output error occurs while | |||
* processing this request | |||
*/ | |||
InternalSession findSession(String id); | |||
@@ -23,10 +22,10 @@ public interface InternalSessionManager { | |||
* <code>null</code>. | |||
* | |||
* @param sessionId The session id which should be used to create the | |||
* new session; if <code>null</code>, a new session id will be | |||
* generated | |||
* @exception IllegalStateException if a new session cannot be | |||
* instantiated for any reason | |||
* new session; if <code>null</code>, a new session id will be | |||
* generated | |||
* @throws IllegalStateException if a new session cannot be | |||
* instantiated for any reason | |||
*/ | |||
InternalSession createSession(String sessionId); | |||
@@ -40,8 +39,8 @@ public interface InternalSessionManager { | |||
/** | |||
* Remove this Session from the active Sessions for this Manager. | |||
* | |||
* @param session Session to be removed | |||
* @param update Should the expiration statistics be updated | |||
* @param session Session to be removed | |||
* @param update Should the expiration statistics be updated | |||
*/ | |||
void remove(InternalSession session, boolean update); | |||
@@ -59,6 +58,7 @@ public interface InternalSessionManager { | |||
* @return number of sessions active | |||
*/ | |||
int getActiveSessions(); | |||
/** | |||
* Get a session from the recycled ones or create a new empty one. | |||
* The PersistentManager manager does not need to create session data | |||
@@ -88,6 +88,7 @@ public interface InternalSessionManager { | |||
* 要和{@link #setBackgroundProcessorDelay(int)}联合起来看 | |||
* 如果把这个数字设置为6(默认),那么就是说manager要等待 6 * backgroundProcessorDay的时间才会清理过期session | |||
* </pre> | |||
* | |||
* @param processExpiresFrequency the new manager checks frequency | |||
*/ | |||
void setProcessExpiresFrequency(int processExpiresFrequency); | |||
@@ -97,6 +98,7 @@ public interface InternalSessionManager { | |||
* Set the manager background processor delay | |||
* 设置manager sleep几秒,尝试执行一次background操作(清理过期session) | |||
* </pre> | |||
* | |||
* @param backgroundProcessorDelay | |||
*/ | |||
void setBackgroundProcessorDelay(int backgroundProcessorDelay); | |||
@@ -106,6 +108,7 @@ public interface InternalSessionManager { | |||
* Set the maximum number of active Sessions allowed, or -1 for | |||
* no limit. | |||
* 设置最大活跃session数,默认无限 | |||
* | |||
* @param max The new maximum number of sessions | |||
*/ | |||
void setMaxActiveSessions(int max); | |||
@@ -12,7 +12,6 @@ | |||
# 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. | |||
applicationSession.session.ise=invalid session state | |||
applicationSession.value.iae=null value | |||
fileStore.saving=Saving Session {0} to file {1} | |||
@@ -12,17 +12,68 @@ public class StandardSession implements WxSession, InternalSession { | |||
* The string manager for this package. | |||
*/ | |||
protected static final StringManager sm = | |||
StringManager.getManager(Constants.Package); | |||
StringManager.getManager(Constants.Package); | |||
/** | |||
* Type array. | |||
*/ | |||
protected static final String EMPTY_ARRAY[] = new String[0]; | |||
// ------------------------------ WxSession | |||
protected Map<String, Object> attributes = new ConcurrentHashMap<String, Object>(); | |||
/** | |||
* The session identifier of this Session. | |||
*/ | |||
protected String id = null; | |||
/** | |||
* Flag indicating whether this session is valid or not. | |||
*/ | |||
protected volatile boolean isValid = false; | |||
/** | |||
* We are currently processing a session expiration, so bypass | |||
* certain IllegalStateException tests. NOTE: This value is not | |||
* included in the serialized version of this object. | |||
*/ | |||
protected transient volatile boolean expiring = false; | |||
/** | |||
* The Manager with which this Session is associated. | |||
*/ | |||
protected transient InternalSessionManager manager = null; | |||
// ------------------------------ InternalSession | |||
/** | |||
* The time this session was created, in milliseconds since midnight, | |||
* January 1, 1970 GMT. | |||
*/ | |||
protected long creationTime = 0L; | |||
/** | |||
* The current accessed time for this session. | |||
*/ | |||
protected volatile long thisAccessedTime = creationTime; | |||
/** | |||
* The default maximum inactive interval for Sessions created by | |||
* this Manager. | |||
*/ | |||
protected int maxInactiveInterval = 30 * 60; | |||
/** | |||
* The facade associated with this session. NOTE: This value is not | |||
* included in the serialized version of this object. | |||
*/ | |||
protected transient StandardSessionFacade facade = null; | |||
/** | |||
* The access count for this session. | |||
*/ | |||
protected transient AtomicInteger accessCount = null; | |||
public StandardSession(InternalSessionManager manager) { | |||
this.manager = manager; | |||
this.accessCount = new AtomicInteger(); | |||
} | |||
@Override | |||
public Object getAttribute(String name) { | |||
if (!isValidInternal()) | |||
throw new IllegalStateException | |||
(sm.getString("sessionImpl.getAttribute.ise")); | |||
(sm.getString("sessionImpl.getAttribute.ise")); | |||
if (name == null) return null; | |||
@@ -33,7 +84,7 @@ public class StandardSession implements WxSession, InternalSession { | |||
public Enumeration<String> getAttributeNames() { | |||
if (!isValidInternal()) | |||
throw new IllegalStateException | |||
(sm.getString("sessionImpl.getAttributeNames.ise")); | |||
(sm.getString("sessionImpl.getAttributeNames.ise")); | |||
Set<String> names = new HashSet<String>(); | |||
names.addAll(attributes.keySet()); | |||
@@ -45,7 +96,7 @@ public class StandardSession implements WxSession, InternalSession { | |||
// Name cannot be null | |||
if (name == null) | |||
throw new IllegalArgumentException | |||
(sm.getString("sessionImpl.setAttribute.namenull")); | |||
(sm.getString("sessionImpl.setAttribute.namenull")); | |||
// Null value is the same as removeAttribute() | |||
if (value == null) { | |||
@@ -56,97 +107,32 @@ public class StandardSession implements WxSession, InternalSession { | |||
// Validate our current state | |||
if (!isValidInternal()) | |||
throw new IllegalStateException(sm.getString( | |||
"sessionImpl.setAttribute.ise", getIdInternal())); | |||
"sessionImpl.setAttribute.ise", getIdInternal())); | |||
attributes.put(name, value); | |||
} | |||
@Override | |||
public void removeAttribute(String name) { | |||
removeAttributeInternal(name); | |||
} | |||
@Override | |||
public void invalidate() { | |||
if (!isValidInternal()) | |||
throw new IllegalStateException | |||
(sm.getString("sessionImpl.invalidate.ise")); | |||
(sm.getString("sessionImpl.invalidate.ise")); | |||
// Cause this session to expire | |||
expire(); | |||
} | |||
// ------------------------------ InternalSession | |||
/** | |||
* The session identifier of this Session. | |||
*/ | |||
protected String id = null; | |||
/** | |||
* Flag indicating whether this session is valid or not. | |||
*/ | |||
protected volatile boolean isValid = false; | |||
/** | |||
* We are currently processing a session expiration, so bypass | |||
* certain IllegalStateException tests. NOTE: This value is not | |||
* included in the serialized version of this object. | |||
*/ | |||
protected transient volatile boolean expiring = false; | |||
/** | |||
* The Manager with which this Session is associated. | |||
*/ | |||
protected transient InternalSessionManager manager = null; | |||
/** | |||
* Type array. | |||
*/ | |||
protected static final String EMPTY_ARRAY[] = new String[0]; | |||
/** | |||
* The time this session was created, in milliseconds since midnight, | |||
* January 1, 1970 GMT. | |||
*/ | |||
protected long creationTime = 0L; | |||
/** | |||
* The current accessed time for this session. | |||
*/ | |||
protected volatile long thisAccessedTime = creationTime; | |||
/** | |||
* The default maximum inactive interval for Sessions created by | |||
* this Manager. | |||
*/ | |||
protected int maxInactiveInterval = 30 * 60; | |||
/** | |||
* The facade associated with this session. NOTE: This value is not | |||
* included in the serialized version of this object. | |||
*/ | |||
protected transient StandardSessionFacade facade = null; | |||
/** | |||
* The access count for this session. | |||
*/ | |||
protected transient AtomicInteger accessCount = null; | |||
public StandardSession(InternalSessionManager manager) { | |||
this.manager = manager; | |||
this.accessCount = new AtomicInteger(); | |||
} | |||
@Override | |||
public WxSession getSession() { | |||
if (facade == null){ | |||
if (facade == null) { | |||
facade = new StandardSessionFacade(this); | |||
} | |||
return (facade); | |||
@@ -161,16 +147,6 @@ public class StandardSession implements WxSession, InternalSession { | |||
return this.isValid; | |||
} | |||
/** | |||
* Set the <code>isValid</code> flag for this session. | |||
* | |||
* @param isValid The new value for the <code>isValid</code> flag | |||
*/ | |||
@Override | |||
public void setValid(boolean isValid) { | |||
this.isValid = isValid; | |||
} | |||
@Override | |||
public boolean isValid() { | |||
if (!this.isValid) { | |||
@@ -197,6 +173,16 @@ public class StandardSession implements WxSession, InternalSession { | |||
return this.isValid; | |||
} | |||
/** | |||
* Set the <code>isValid</code> flag for this session. | |||
* | |||
* @param isValid The new value for the <code>isValid</code> flag | |||
*/ | |||
@Override | |||
public void setValid(boolean isValid) { | |||
this.isValid = isValid; | |||
} | |||
@Override | |||
public String getIdInternal() { | |||
return (this.id); | |||
@@ -13,60 +13,26 @@ import java.util.concurrent.atomic.AtomicBoolean; | |||
*/ | |||
public class StandardSessionManager implements WxSessionManager, InternalSessionManager { | |||
protected final Logger log = LoggerFactory.getLogger(StandardSessionManager.class); | |||
protected static final StringManager sm = | |||
StringManager.getManager(Constants.Package); | |||
StringManager.getManager(Constants.Package); | |||
/** | |||
* The set of currently active Sessions for this Manager, keyed by | |||
* session identifier. | |||
* The descriptive name of this Manager implementation (for logging). | |||
*/ | |||
protected Map<String, InternalSession> sessions = new ConcurrentHashMap<String, InternalSession>(); | |||
@Override | |||
public WxSession getSession(String sessionId) { | |||
return getSession(sessionId, true); | |||
} | |||
@Override | |||
public WxSession getSession(String sessionId, boolean create) { | |||
if (sessionId == null) { | |||
throw new IllegalStateException | |||
(sm.getString("sessionManagerImpl.getSession.ise")); | |||
} | |||
InternalSession session = findSession(sessionId); | |||
if ((session != null) && !session.isValid()) { | |||
session = null; | |||
} | |||
if (session != null) { | |||
session.access(); | |||
return session.getSession(); | |||
} | |||
// Create a new session if requested and the response is not committed | |||
if (!create) { | |||
return (null); | |||
} | |||
session = createSession(sessionId); | |||
if (session == null) { | |||
return null; | |||
} | |||
session.access(); | |||
return session.getSession(); | |||
} | |||
private static final String name = "SessionManagerImpl"; | |||
protected final Logger log = LoggerFactory.getLogger(StandardSessionManager.class); | |||
private final Object maxActiveUpdateLock = new Object(); | |||
/** | |||
* 后台清理线程是否已经开启 | |||
*/ | |||
private final AtomicBoolean backgroundProcessStarted = new AtomicBoolean(false); | |||
// -------------------------------------- InternalSessionManager | |||
/** | |||
* The descriptive name of this Manager implementation (for logging). | |||
* The set of currently active Sessions for this Manager, keyed by | |||
* session identifier. | |||
*/ | |||
private static final String name = "SessionManagerImpl"; | |||
protected Map<String, InternalSession> sessions = new ConcurrentHashMap<String, InternalSession>(); | |||
/** | |||
* The maximum number of active Sessions allowed, or -1 for no limit. | |||
*/ | |||
@@ -84,22 +50,13 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
protected int maxInactiveInterval = 30 * 60; | |||
// Number of sessions created by this manager | |||
protected long sessionCounter=0; | |||
protected volatile int maxActive=0; | |||
private final Object maxActiveUpdateLock = new Object(); | |||
protected long sessionCounter = 0; | |||
protected volatile int maxActive = 0; | |||
/** | |||
* Processing time during session expiration. | |||
*/ | |||
protected long processingTime = 0; | |||
/** | |||
* Iteration count for background processing. | |||
*/ | |||
private int count = 0; | |||
/** | |||
* Frequency of the session expiration, and related manager operations. | |||
* Manager operations will be done once for the specified amount of | |||
@@ -107,16 +64,50 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
* checks will occur). | |||
*/ | |||
protected int processExpiresFrequency = 6; | |||
/** | |||
* background processor delay in seconds | |||
*/ | |||
protected int backgroundProcessorDelay = 10; | |||
/** | |||
* 后台清理线程是否已经开启 | |||
* Iteration count for background processing. | |||
*/ | |||
private final AtomicBoolean backgroundProcessStarted = new AtomicBoolean(false); | |||
private int count = 0; | |||
@Override | |||
public WxSession getSession(String sessionId) { | |||
return getSession(sessionId, true); | |||
} | |||
@Override | |||
public WxSession getSession(String sessionId, boolean create) { | |||
if (sessionId == null) { | |||
throw new IllegalStateException | |||
(sm.getString("sessionManagerImpl.getSession.ise")); | |||
} | |||
InternalSession session = findSession(sessionId); | |||
if ((session != null) && !session.isValid()) { | |||
session = null; | |||
} | |||
if (session != null) { | |||
session.access(); | |||
return session.getSession(); | |||
} | |||
// Create a new session if requested and the response is not committed | |||
if (!create) { | |||
return (null); | |||
} | |||
session = createSession(sessionId); | |||
if (session == null) { | |||
return null; | |||
} | |||
session.access(); | |||
return session.getSession(); | |||
} | |||
@Override | |||
public void remove(InternalSession session) { | |||
@@ -131,7 +122,6 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
} | |||
@Override | |||
public InternalSession findSession(String id) { | |||
@@ -145,15 +135,15 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
public InternalSession createSession(String sessionId) { | |||
if (sessionId == null) { | |||
throw new IllegalStateException | |||
(sm.getString("sessionManagerImpl.createSession.ise")); | |||
(sm.getString("sessionManagerImpl.createSession.ise")); | |||
} | |||
if ((maxActiveSessions >= 0) && | |||
(getActiveSessions() >= maxActiveSessions)) { | |||
(getActiveSessions() >= maxActiveSessions)) { | |||
rejectedSessions++; | |||
throw new TooManyActiveSessionsException( | |||
sm.getString("sessionManagerImpl.createSession.tmase"), | |||
maxActiveSessions); | |||
sm.getString("sessionManagerImpl.createSession.tmase"), | |||
maxActiveSessions); | |||
} | |||
// Recycle or create a Session instance | |||
@@ -216,14 +206,14 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
sessions.put(session.getIdInternal(), session); | |||
int size = getActiveSessions(); | |||
if( size > maxActive ) { | |||
synchronized(maxActiveUpdateLock) { | |||
if( size > maxActive ) { | |||
if (size > maxActive) { | |||
synchronized (maxActiveUpdateLock) { | |||
if (size > maxActive) { | |||
maxActive = size; | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
@@ -251,19 +241,19 @@ public class StandardSessionManager implements WxSessionManager, InternalSession | |||
long timeNow = System.currentTimeMillis(); | |||
InternalSession sessions[] = findSessions(); | |||
int expireHere = 0 ; | |||
int expireHere = 0; | |||
if(log.isDebugEnabled()) | |||
if (log.isDebugEnabled()) | |||
log.debug("Start expire sessions {} at {} sessioncount {}", getName(), timeNow, sessions.length); | |||
for (int i = 0; i < sessions.length; i++) { | |||
if (sessions[i]!=null && !sessions[i].isValid()) { | |||
if (sessions[i] != null && !sessions[i].isValid()) { | |||
expireHere++; | |||
} | |||
} | |||
long timeEnd = System.currentTimeMillis(); | |||
if(log.isDebugEnabled()) | |||
if (log.isDebugEnabled()) | |||
log.debug("End expire sessions {} processingTime {} expired sessions: {}", getName(), timeEnd - timeNow, expireHere); | |||
processingTime += ( timeEnd - timeNow ); | |||
processingTime += (timeEnd - timeNow); | |||
} | |||
@@ -21,37 +21,34 @@ package me.chanjar.weixin.common.session; | |||
* reached and the server is refusing to create any new sessions. | |||
*/ | |||
public class TooManyActiveSessionsException | |||
extends IllegalStateException | |||
{ | |||
private static final long serialVersionUID = 1L; | |||
extends IllegalStateException { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* The maximum number of active sessions the server will tolerate. | |||
*/ | |||
private final int maxActiveSessions; | |||
/** | |||
* The maximum number of active sessions the server will tolerate. | |||
*/ | |||
private final int maxActiveSessions; | |||
/** | |||
* Creates a new TooManyActiveSessionsException. | |||
* | |||
* @param message A description for the exception. | |||
* @param maxActive The maximum number of active sessions allowed by the | |||
* session manager. | |||
*/ | |||
public TooManyActiveSessionsException(String message, | |||
int maxActive) | |||
{ | |||
super(message); | |||
maxActiveSessions = maxActive; | |||
} | |||
/** | |||
* Gets the maximum number of sessions allowed by the session manager. | |||
* | |||
* @return The maximum number of sessions allowed by the session manager. | |||
*/ | |||
public int getMaxActiveSessions() | |||
{ | |||
return maxActiveSessions; | |||
} | |||
/** | |||
* Creates a new TooManyActiveSessionsException. | |||
* | |||
* @param message A description for the exception. | |||
* @param maxActive The maximum number of active sessions allowed by the | |||
* session manager. | |||
*/ | |||
public TooManyActiveSessionsException(String message, | |||
int maxActive) { | |||
super(message); | |||
maxActiveSessions = maxActive; | |||
} | |||
/** | |||
* Gets the maximum number of sessions allowed by the session manager. | |||
* | |||
* @return The maximum number of sessions allowed by the session manager. | |||
*/ | |||
public int getMaxActiveSessions() { | |||
return maxActiveSessions; | |||
} | |||
} |
@@ -7,7 +7,7 @@ public class StringUtils { | |||
/** | |||
* <p>Checks if a CharSequence is whitespace, empty ("") or null.</p> | |||
* | |||
* <p> | |||
* <pre> | |||
* StringUtils.isBlank(null) = true | |||
* StringUtils.isBlank("") = true | |||
@@ -16,9 +16,8 @@ public class StringUtils { | |||
* StringUtils.isBlank(" bob ") = false | |||
* </pre> | |||
* | |||
* @param cs the CharSequence to check, may be null | |||
* @param cs the CharSequence to check, may be null | |||
* @return {@code true} if the CharSequence is null, empty or whitespace | |||
* @since 2.0 | |||
* @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) | |||
*/ | |||
public static boolean isBlank(CharSequence cs) { | |||
@@ -36,7 +35,7 @@ public class StringUtils { | |||
/** | |||
* <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p> | |||
* | |||
* <p> | |||
* <pre> | |||
* StringUtils.isNotBlank(null) = false | |||
* StringUtils.isNotBlank("") = false | |||
@@ -45,10 +44,9 @@ public class StringUtils { | |||
* StringUtils.isNotBlank(" bob ") = true | |||
* </pre> | |||
* | |||
* @param cs the CharSequence to check, may be null | |||
* @param cs the CharSequence to check, may be null | |||
* @return {@code true} if the CharSequence is | |||
* not empty and not null and not whitespace | |||
* @since 2.0 | |||
* not empty and not null and not whitespace | |||
* @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) | |||
*/ | |||
public static boolean isNotBlank(CharSequence cs) { | |||
@@ -57,7 +55,7 @@ public class StringUtils { | |||
/** | |||
* <p>Checks if a CharSequence is empty ("") or null.</p> | |||
* | |||
* <p> | |||
* <pre> | |||
* StringUtils.isEmpty(null) = true | |||
* StringUtils.isEmpty("") = true | |||
@@ -65,12 +63,12 @@ public class StringUtils { | |||
* StringUtils.isEmpty("bob") = false | |||
* StringUtils.isEmpty(" bob ") = false | |||
* </pre> | |||
* | |||
* <p> | |||
* <p>NOTE: This method changed in Lang version 2.0. | |||
* It no longer trims the CharSequence. | |||
* That functionality is available in isBlank().</p> | |||
* | |||
* @param cs the CharSequence to check, may be null | |||
* @param cs the CharSequence to check, may be null | |||
* @return {@code true} if the CharSequence is empty or null | |||
* @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) | |||
*/ | |||
@@ -80,7 +78,7 @@ public class StringUtils { | |||
/** | |||
* <p>Checks if a CharSequence is not empty ("") and not null.</p> | |||
* | |||
* <p> | |||
* <pre> | |||
* StringUtils.isNotEmpty(null) = false | |||
* StringUtils.isNotEmpty("") = false | |||
@@ -89,7 +87,7 @@ public class StringUtils { | |||
* StringUtils.isNotEmpty(" bob ") = true | |||
* </pre> | |||
* | |||
* @param cs the CharSequence to check, may be null | |||
* @param cs the CharSequence to check, may be null | |||
* @return {@code true} if the CharSequence is not empty and not null | |||
* @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence) | |||
*/ | |||
@@ -3,24 +3,24 @@ package me.chanjar.weixin.common.util.crypto; | |||
import java.util.ArrayList; | |||
public class ByteGroup { | |||
ArrayList<Byte> byteContainer = new ArrayList<Byte>(); | |||
ArrayList<Byte> byteContainer = new ArrayList<Byte>(); | |||
public byte[] toBytes() { | |||
byte[] bytes = new byte[byteContainer.size()]; | |||
for (int i = 0; i < byteContainer.size(); i++) { | |||
bytes[i] = byteContainer.get(i); | |||
} | |||
return bytes; | |||
} | |||
byte[] bytes = new byte[byteContainer.size()]; | |||
for (int i = 0; i < byteContainer.size(); i++) { | |||
bytes[i] = byteContainer.get(i); | |||
} | |||
return bytes; | |||
} | |||
public ByteGroup addBytes(byte[] bytes) { | |||
for (byte b : bytes) { | |||
byteContainer.add(b); | |||
} | |||
return this; | |||
} | |||
public ByteGroup addBytes(byte[] bytes) { | |||
for (byte b : bytes) { | |||
byteContainer.add(b); | |||
} | |||
return this; | |||
} | |||
public int size() { | |||
return byteContainer.size(); | |||
} | |||
public int size() { | |||
return byteContainer.size(); | |||
} | |||
} |
@@ -1,6 +1,6 @@ | |||
/** | |||
* 对公众平台发送给公众账号的消息加解密示例代码. | |||
* | |||
* | |||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | |||
*/ | |||
@@ -16,53 +16,53 @@ import java.util.Arrays; | |||
*/ | |||
public class PKCS7Encoder { | |||
private static final Charset CHARSET = Charset.forName("utf-8"); | |||
private static final int BLOCK_SIZE = 32; | |||
private static final Charset CHARSET = Charset.forName("utf-8"); | |||
private static final int BLOCK_SIZE = 32; | |||
/** | |||
* 获得对明文进行补位填充的字节. | |||
* | |||
* @param count 需要进行填充补位操作的明文字节个数 | |||
* @return 补齐用的字节数组 | |||
*/ | |||
public static byte[] encode(int count) { | |||
// 计算需要填充的位数 | |||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); | |||
if (amountToPad == 0) { | |||
amountToPad = BLOCK_SIZE; | |||
} | |||
// 获得补位所用的字符 | |||
char padChr = chr(amountToPad); | |||
String tmp = new String(); | |||
for (int index = 0; index < amountToPad; index++) { | |||
tmp += padChr; | |||
} | |||
return tmp.getBytes(CHARSET); | |||
} | |||
/** | |||
* 获得对明文进行补位填充的字节. | |||
* | |||
* @param count 需要进行填充补位操作的明文字节个数 | |||
* @return 补齐用的字节数组 | |||
*/ | |||
public static byte[] encode(int count) { | |||
// 计算需要填充的位数 | |||
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); | |||
if (amountToPad == 0) { | |||
amountToPad = BLOCK_SIZE; | |||
} | |||
// 获得补位所用的字符 | |||
char padChr = chr(amountToPad); | |||
String tmp = new String(); | |||
for (int index = 0; index < amountToPad; index++) { | |||
tmp += padChr; | |||
} | |||
return tmp.getBytes(CHARSET); | |||
} | |||
/** | |||
* 删除解密后明文的补位字符 | |||
* | |||
* @param decrypted 解密后的明文 | |||
* @return 删除补位字符后的明文 | |||
*/ | |||
public static byte[] decode(byte[] decrypted) { | |||
int pad = (int) decrypted[decrypted.length - 1]; | |||
if (pad < 1 || pad > 32) { | |||
pad = 0; | |||
} | |||
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); | |||
} | |||
/** | |||
* 删除解密后明文的补位字符 | |||
* | |||
* @param decrypted 解密后的明文 | |||
* @return 删除补位字符后的明文 | |||
*/ | |||
public static byte[] decode(byte[] decrypted) { | |||
int pad = (int) decrypted[decrypted.length - 1]; | |||
if (pad < 1 || pad > 32) { | |||
pad = 0; | |||
} | |||
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); | |||
} | |||
/** | |||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码 | |||
* | |||
* @param a 需要转化的数字 | |||
* @return 转化得到的字符 | |||
*/ | |||
public static char chr(int a) { | |||
byte target = (byte) (a & 0xFF); | |||
return (char) target; | |||
} | |||
/** | |||
* 将数字转化成ASCII码对应的字符,用于对明文进行补码 | |||
* | |||
* @param a 需要转化的数字 | |||
* @return 转化得到的字符 | |||
*/ | |||
public static char chr(int a) { | |||
byte target = (byte) (a & 0xFF); | |||
return (char) target; | |||
} | |||
} |
@@ -1,10 +1,10 @@ | |||
package me.chanjar.weixin.common.util.crypto; | |||
import org.apache.commons.codec.digest.DigestUtils; | |||
import java.security.NoSuchAlgorithmException; | |||
import java.util.Arrays; | |||
import org.apache.commons.codec.digest.DigestUtils; | |||
/** | |||
* Created by Daniel Qian on 14/10/19. | |||
*/ | |||
@@ -37,4 +37,4 @@ public class SHA1 { | |||
} | |||
return DigestUtils.sha1Hex(sb.toString()); | |||
} | |||
} | |||
} |
@@ -2,6 +2,10 @@ | |||
* 对公众平台发送给公众账号的消息加解密示例代码. | |||
* | |||
* @copyright Copyright (c) 1998-2014 Tencent Inc. | |||
* <p> | |||
* 针对org.apache.commons.codec.binary.Base64, | |||
* 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本) | |||
* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi | |||
*/ | |||
// ------------------------------------------------------------------------ | |||
@@ -62,12 +66,51 @@ public class WxCryptUtil { | |||
* @param appidOrCorpid 公众平台appid/corpid | |||
*/ | |||
public WxCryptUtil(String token, String encodingAesKey, | |||
String appidOrCorpid) { | |||
String appidOrCorpid) { | |||
this.token = token; | |||
this.appidOrCorpid = appidOrCorpid; | |||
this.aesKey = Base64.decodeBase64(encodingAesKey + "="); | |||
} | |||
/** | |||
* 微信公众号支付签名算法(详见:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3) | |||
* @param packageParams 原始参数 | |||
* @param signKey 加密Key(即 商户Key) | |||
* @return 签名字符串 | |||
*/ | |||
public static String createSign(Map<String, String> packageParams, | |||
String signKey) { | |||
SortedMap<String, String> sortedMap = new TreeMap<String, String>(); | |||
sortedMap.putAll(packageParams); | |||
List<String> keys = new ArrayList<String>(packageParams.keySet()); | |||
Collections.sort(keys); | |||
StringBuffer toSign = new StringBuffer(); | |||
for (String key : keys) { | |||
String value = packageParams.get(key); | |||
if (null != value && !"".equals(value) && !"sign".equals(key) | |||
&& !"key".equals(key)) { | |||
toSign.append(key + "=" + value + "&"); | |||
} | |||
} | |||
toSign.append("key=" + signKey); | |||
String sign = DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | |||
return sign; | |||
} | |||
static String extractEncryptPart(String xml) { | |||
try { | |||
DocumentBuilder db = builderLocal.get(); | |||
Document document = db.parse(new InputSource(new StringReader(xml))); | |||
Element root = document.getDocumentElement(); | |||
return root.getElementsByTagName("Encrypt").item(0).getTextContent(); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
/** | |||
* 将公众平台回复用户的消息加密打包. | |||
* <ol> | |||
@@ -107,7 +150,7 @@ public class WxCryptUtil { | |||
byte[] randomStringBytes = randomStr.getBytes(CHARSET); | |||
byte[] plainTextBytes = plainText.getBytes(CHARSET); | |||
byte[] bytesOfSizeInNetworkOrder = number2BytesInNetworkOrder( | |||
plainTextBytes.length); | |||
plainTextBytes.length); | |||
byte[] appIdBytes = appidOrCorpid.getBytes(CHARSET); | |||
// randomStr + networkBytesOrder + text + appid | |||
@@ -157,7 +200,7 @@ public class WxCryptUtil { | |||
* @return 解密后的原文 | |||
*/ | |||
public String decrypt(String msgSignature, String timeStamp, String nonce, | |||
String encryptedXml) { | |||
String encryptedXml) { | |||
// 密钥,公众账号的app corpSecret | |||
// 提取密文 | |||
String cipherText = extractEncryptPart(encryptedXml); | |||
@@ -190,7 +233,7 @@ public class WxCryptUtil { | |||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); | |||
SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES"); | |||
IvParameterSpec iv = new IvParameterSpec( | |||
Arrays.copyOfRange(aesKey, 0, 16)); | |||
Arrays.copyOfRange(aesKey, 0, 16)); | |||
cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); | |||
// 使用BASE64对密文进行解码 | |||
@@ -213,9 +256,9 @@ public class WxCryptUtil { | |||
int xmlLength = bytesNetworkOrder2Number(networkOrder); | |||
xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), | |||
CHARSET); | |||
CHARSET); | |||
from_appid = new String( | |||
Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); | |||
Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} | |||
@@ -229,33 +272,6 @@ public class WxCryptUtil { | |||
} | |||
/** | |||
* 微信公众号支付签名算法(详见:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3) | |||
* @param packageParams 原始参数 | |||
* @param signKey 加密Key(即 商户Key) | |||
* @return 签名字符串 | |||
*/ | |||
public static String createSign(Map<String, String> packageParams, | |||
String signKey) { | |||
SortedMap<String, String> sortedMap = new TreeMap<String, String>(); | |||
sortedMap.putAll(packageParams); | |||
List<String> keys = new ArrayList<String>(packageParams.keySet()); | |||
Collections.sort(keys); | |||
StringBuffer toSign = new StringBuffer(); | |||
for (String key : keys) { | |||
String value = packageParams.get(key); | |||
if (null != value && !"".equals(value) && !"sign".equals(key) | |||
&& !"key".equals(key)) { | |||
toSign.append(key + "=" + value + "&"); | |||
} | |||
} | |||
toSign.append("key=" + signKey); | |||
String sign = DigestUtils.md5Hex(toSign.toString()).toUpperCase(); | |||
return sign; | |||
} | |||
/** | |||
* 将一个数字转换成生成4个字节的网络字节序bytes数组 | |||
* | |||
@@ -308,24 +324,12 @@ public class WxCryptUtil { | |||
* @return 生成的xml字符串 | |||
*/ | |||
private String generateXml(String encrypt, String signature, String timestamp, | |||
String nonce) { | |||
String nonce) { | |||
String format = "<xml>\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\n" | |||
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | |||
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" | |||
+ "</xml>"; | |||
+ "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\n" | |||
+ "<TimeStamp>%3$s</TimeStamp>\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\n" | |||
+ "</xml>"; | |||
return String.format(format, encrypt, signature, timestamp, nonce); | |||
} | |||
static String extractEncryptPart(String xml) { | |||
try { | |||
DocumentBuilder db = builderLocal.get(); | |||
Document document = db.parse(new InputSource(new StringReader(xml))); | |||
Element root = document.getDocumentElement(); | |||
return root.getElementsByTagName("Encrypt").item(0).getTextContent(); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} | |||
} | |||
} |
@@ -10,17 +10,18 @@ public class FileUtils { | |||
/** | |||
* 创建临时文件 | |||
* | |||
* @param inputStream | |||
* @param name 文件名 | |||
* @param ext 扩展名 | |||
* @param tmpDirFile 临时文件夹目录 | |||
* @param name 文件名 | |||
* @param ext 扩展名 | |||
* @param tmpDirFile 临时文件夹目录 | |||
*/ | |||
public static File createTmpFile(InputStream inputStream, String name, String ext, File tmpDirFile) throws IOException { | |||
FileOutputStream fos = null; | |||
try { | |||
File tmpFile; | |||
if (tmpDirFile == null) { | |||
tmpFile = File.createTempFile(name, '.' + ext); | |||
tmpFile = File.createTempFile(name, '.' + ext); | |||
} else { | |||
tmpFile = File.createTempFile(name, '.' + ext, tmpDirFile); | |||
} | |||
@@ -51,12 +52,13 @@ public class FileUtils { | |||
/** | |||
* 创建临时文件 | |||
* | |||
* @param inputStream | |||
* @param name 文件名 | |||
* @param ext 扩展名 | |||
* @param name 文件名 | |||
* @param ext 扩展名 | |||
*/ | |||
public static File createTmpFile(InputStream inputStream, String name, String ext) throws IOException { | |||
return createTmpFile(inputStream, name, ext, null); | |||
} | |||
} |
@@ -10,36 +10,42 @@ public interface ApacheHttpClientBuilder { | |||
/** | |||
* 构建httpclient实例 | |||
* | |||
* @return new instance of CloseableHttpClient | |||
*/ | |||
CloseableHttpClient build(); | |||
/** | |||
* 代理服务器地址 | |||
* | |||
* @param httpProxyHost | |||
*/ | |||
ApacheHttpClientBuilder httpProxyHost(String httpProxyHost); | |||
/** | |||
* 代理服务器端口 | |||
* | |||
* @param httpProxyPort | |||
*/ | |||
ApacheHttpClientBuilder httpProxyPort(int httpProxyPort); | |||
/** | |||
* 代理服务器用户名 | |||
* | |||
* @param httpProxyUsername | |||
*/ | |||
ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername); | |||
/** | |||
* 代理服务器密码 | |||
* | |||
* @param httpProxyPassword | |||
*/ | |||
ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword); | |||
/** | |||
* ssl连接socket工厂 | |||
* | |||
* @param sslConnectionSocketFactory | |||
*/ | |||
ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory); | |||
@@ -94,7 +94,7 @@ public class DefaultApacheHttpHttpClientBuilder implements ApacheHttpClientBuild | |||
return this; | |||
} | |||
public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory){ | |||
public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory) { | |||
this.sslConnectionSocketFactory = sslConnectionSocketFactory; | |||
return this; | |||
} | |||
@@ -103,18 +103,18 @@ public class DefaultApacheHttpHttpClientBuilder implements ApacheHttpClientBuild | |||
return idleConnectionMonitorThread; | |||
} | |||
private void prepare(){ | |||
private void prepare() { | |||
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() | |||
.register("http", plainConnectionSocketFactory) | |||
.register("https", sslConnectionSocketFactory) | |||
.build(); | |||
.register("http", plainConnectionSocketFactory) | |||
.register("https", sslConnectionSocketFactory) | |||
.build(); | |||
connectionManager = new PoolingHttpClientConnectionManager(registry); | |||
connectionManager.setMaxTotal(maxTotalConn); | |||
connectionManager.setDefaultMaxPerRoute(maxConnPerHost); | |||
connectionManager.setDefaultSocketConfig( | |||
SocketConfig.copy(SocketConfig.DEFAULT) | |||
.setSoTimeout(soTimeout) | |||
.build() | |||
SocketConfig.copy(SocketConfig.DEFAULT) | |||
.setSoTimeout(soTimeout) | |||
.build() | |||
); | |||
idleConnectionMonitorThread = new IdleConnectionMonitorThread(connectionManager, idleConnTimeout, checkWaitTime); | |||
@@ -122,22 +122,22 @@ public class DefaultApacheHttpHttpClientBuilder implements ApacheHttpClientBuild | |||
idleConnectionMonitorThread.start(); | |||
httpClientBuilder = HttpClients.custom() | |||
.setConnectionManager(connectionManager) | |||
.setDefaultRequestConfig( | |||
RequestConfig.custom() | |||
.setSocketTimeout(soTimeout) | |||
.setConnectTimeout(connectionTimeout) | |||
.setConnectionRequestTimeout(connectionRequestTimeout) | |||
.build() | |||
) | |||
.setRetryHandler(httpRequestRetryHandler); | |||
.setConnectionManager(connectionManager) | |||
.setDefaultRequestConfig( | |||
RequestConfig.custom() | |||
.setSocketTimeout(soTimeout) | |||
.setConnectTimeout(connectionTimeout) | |||
.setConnectionRequestTimeout(connectionRequestTimeout) | |||
.build() | |||
) | |||
.setRetryHandler(httpRequestRetryHandler); | |||
if (StringUtils.isNotBlank(httpProxyHost) && StringUtils.isNotBlank(httpProxyUsername)) { | |||
// 使用代理服务器 需要用户认证的代理服务器 | |||
CredentialsProvider credsProvider = new BasicCredentialsProvider(); | |||
credsProvider.setCredentials( | |||
new AuthScope(httpProxyHost, httpProxyPort), | |||
new UsernamePasswordCredentials(httpProxyUsername, httpProxyPassword)); | |||
new AuthScope(httpProxyHost, httpProxyPort), | |||
new UsernamePasswordCredentials(httpProxyUsername, httpProxyPassword)); | |||
httpClientBuilder.setDefaultCredentialsProvider(credsProvider); | |||
} | |||
@@ -148,7 +148,7 @@ public class DefaultApacheHttpHttpClientBuilder implements ApacheHttpClientBuild | |||
} | |||
public CloseableHttpClient build() { | |||
if(!prepared){ | |||
if (!prepared) { | |||
prepare(); | |||
prepared = true; | |||
} | |||
@@ -1,8 +1,5 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import org.apache.http.HttpEntity; | |||
import org.apache.http.HttpResponse; | |||
import org.apache.http.StatusLine; | |||
@@ -10,10 +7,13 @@ import org.apache.http.client.HttpResponseException; | |||
import org.apache.http.client.ResponseHandler; | |||
import org.apache.http.util.EntityUtils; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
public class InputStreamResponseHandler implements ResponseHandler<InputStream> { | |||
public static final ResponseHandler<InputStream> INSTANCE = new InputStreamResponseHandler(); | |||
public InputStream handleResponse(final HttpResponse response) throws IOException { | |||
final StatusLine statusLine = response.getStatusLine(); | |||
final HttpEntity entity = response.getEntity(); | |||
@@ -18,39 +18,39 @@ import java.io.IOException; | |||
*/ | |||
public class JoddGetRequestExecutor implements RequestExecutor<String, String> { | |||
@Override | |||
public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, | |||
String queryParam) throws WxErrorException, IOException { | |||
if (queryParam != null) { | |||
if (uri.indexOf('?') == -1) { | |||
uri += '?'; | |||
} | |||
uri += uri.endsWith("?") ? queryParam : '&' + queryParam; | |||
} | |||
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); | |||
if (httpProxy != null) { | |||
ProxyInfo proxyInfoObj = new ProxyInfo( | |||
ProxyInfo.ProxyType.HTTP, | |||
httpProxy.getHostName(), | |||
httpProxy.getPort(), "", ""); | |||
provider.useProxy(proxyInfoObj); | |||
} | |||
HttpRequest request = HttpRequest.get(uri); | |||
request.method("GET"); | |||
request.charset("UTF-8"); | |||
HttpResponse response = request.open(provider).send(); | |||
response.charset("UTF-8"); | |||
String result = response.bodyText(); | |||
WxError error = WxError.fromJson(result); | |||
if (error.getErrorCode() != 0) { | |||
throw new WxErrorException(error); | |||
} | |||
return result; | |||
@Override | |||
public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, | |||
String queryParam) throws WxErrorException, IOException { | |||
if (queryParam != null) { | |||
if (uri.indexOf('?') == -1) { | |||
uri += '?'; | |||
} | |||
uri += uri.endsWith("?") ? queryParam : '&' + queryParam; | |||
} | |||
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); | |||
if (httpProxy != null) { | |||
ProxyInfo proxyInfoObj = new ProxyInfo( | |||
ProxyInfo.ProxyType.HTTP, | |||
httpProxy.getHostName(), | |||
httpProxy.getPort(), "", ""); | |||
provider.useProxy(proxyInfoObj); | |||
} | |||
HttpRequest request = HttpRequest.get(uri); | |||
request.method("GET"); | |||
request.charset("UTF-8"); | |||
HttpResponse response = request.open(provider).send(); | |||
response.charset("UTF-8"); | |||
String result = response.bodyText(); | |||
WxError error = WxError.fromJson(result); | |||
if (error.getErrorCode() != 0) { | |||
throw new WxErrorException(error); | |||
} | |||
return result; | |||
} | |||
} |
@@ -18,33 +18,33 @@ import java.io.IOException; | |||
*/ | |||
public class JoddPostRequestExecutor implements RequestExecutor<String, String> { | |||
@Override | |||
public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, | |||
String postEntity) throws WxErrorException, IOException { | |||
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); | |||
if (httpProxy != null) { | |||
ProxyInfo proxyInfoObj = new ProxyInfo( | |||
ProxyInfo.ProxyType.HTTP, | |||
httpProxy.getAddress().getHostAddress(), | |||
httpProxy.getPort(), "", ""); | |||
provider.useProxy(proxyInfoObj); | |||
} | |||
HttpRequest request = HttpRequest.get(uri); | |||
request.method("POST"); | |||
request.charset("UTF-8"); | |||
request.bodyText(postEntity); | |||
HttpResponse response = request.open(provider).send(); | |||
response.charset("UTF-8"); | |||
String result = response.bodyText(); | |||
WxError error = WxError.fromJson(result); | |||
if (error.getErrorCode() != 0) { | |||
throw new WxErrorException(error); | |||
} | |||
return result; | |||
@Override | |||
public String execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, | |||
String postEntity) throws WxErrorException, IOException { | |||
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider(); | |||
if (httpProxy != null) { | |||
ProxyInfo proxyInfoObj = new ProxyInfo( | |||
ProxyInfo.ProxyType.HTTP, | |||
httpProxy.getAddress().getHostAddress(), | |||
httpProxy.getPort(), "", ""); | |||
provider.useProxy(proxyInfoObj); | |||
} | |||
HttpRequest request = HttpRequest.get(uri); | |||
request.method("POST"); | |||
request.charset("UTF-8"); | |||
request.bodyText(postEntity); | |||
HttpResponse response = request.open(provider).send(); | |||
response.charset("UTF-8"); | |||
String result = response.bodyText(); | |||
WxError error = WxError.fromJson(result); | |||
if (error.getErrorCode() != 0) { | |||
throw new WxErrorException(error); | |||
} | |||
return result; | |||
} | |||
} |
@@ -6,7 +6,6 @@ import me.chanjar.weixin.common.util.StringUtils; | |||
import me.chanjar.weixin.common.util.fs.FileUtils; | |||
import org.apache.http.Header; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.client.ClientProtocolException; | |||
import org.apache.http.client.config.RequestConfig; | |||
import org.apache.http.client.methods.CloseableHttpResponse; | |||
import org.apache.http.client.methods.HttpGet; | |||
@@ -21,11 +20,11 @@ import java.util.regex.Pattern; | |||
/** | |||
* 下载媒体文件请求执行器,请求的参数是String, 返回的结果是File | |||
* @author Daniel Qian | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class MediaDownloadRequestExecutor implements RequestExecutor<File, String> { | |||
private File tmpDirFile; | |||
public MediaDownloadRequestExecutor() { | |||
@@ -36,7 +35,7 @@ public class MediaDownloadRequestExecutor implements RequestExecutor<File, Strin | |||
super(); | |||
this.tmpDirFile = tmpDirFile; | |||
} | |||
@Override | |||
public File execute(CloseableHttpClient httpclient, HttpHost httpProxy, String uri, String queryParam) throws WxErrorException, IOException { | |||
@@ -46,7 +45,7 @@ public class MediaDownloadRequestExecutor implements RequestExecutor<File, Strin | |||
} | |||
uri += uri.endsWith("?") ? queryParam : '&' + queryParam; | |||
} | |||
HttpGet httpGet = new HttpGet(uri); | |||
if (httpProxy != null) { | |||
RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); | |||
@@ -74,7 +73,7 @@ public class MediaDownloadRequestExecutor implements RequestExecutor<File, Strin | |||
File localFile = FileUtils.createTmpFile(inputStream, name_ext[0], name_ext[1], tmpDirFile); | |||
return localFile; | |||
}finally { | |||
} finally { | |||
httpGet.releaseConnection(); | |||
} | |||
@@ -88,5 +87,5 @@ public class MediaDownloadRequestExecutor implements RequestExecutor<File, Strin | |||
String fileName = m.group(1); | |||
return fileName; | |||
} | |||
} |
@@ -5,7 +5,6 @@ import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import org.apache.http.HttpEntity; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.client.ClientProtocolException; | |||
import org.apache.http.client.config.RequestConfig; | |||
import org.apache.http.client.methods.CloseableHttpResponse; | |||
import org.apache.http.client.methods.HttpPost; | |||
@@ -19,8 +18,8 @@ import java.io.IOException; | |||
/** | |||
* 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String | |||
* @author Daniel Qian | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUploadResult, File> { | |||
@@ -33,10 +32,10 @@ public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUpload | |||
} | |||
if (file != null) { | |||
HttpEntity entity = MultipartEntityBuilder | |||
.create() | |||
.addBinaryBody("media", file) | |||
.setMode(HttpMultipartMode.RFC6532) | |||
.build(); | |||
.create() | |||
.addBinaryBody("media", file) | |||
.setMode(HttpMultipartMode.RFC6532) | |||
.build(); | |||
httpPost.setEntity(entity); | |||
httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString()); | |||
} | |||
@@ -47,7 +46,7 @@ public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUpload | |||
throw new WxErrorException(error); | |||
} | |||
return WxMediaUploadResult.fromJson(responseContent); | |||
}finally { | |||
} finally { | |||
httpPost.releaseConnection(); | |||
} | |||
} | |||
@@ -1,12 +1,11 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import java.io.IOException; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.client.ClientProtocolException; | |||
import org.apache.http.impl.client.CloseableHttpClient; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import java.io.IOException; | |||
/** | |||
* http请求执行器 | |||
@@ -17,11 +16,10 @@ import me.chanjar.weixin.common.exception.WxErrorException; | |||
public interface RequestExecutor<T, E> { | |||
/** | |||
* | |||
* @param httpclient 传入的httpClient | |||
* @param httpProxy http代理对象,如果没有配置代理则为空 | |||
* @param uri uri | |||
* @param data 数据 | |||
* @param httpProxy http代理对象,如果没有配置代理则为空 | |||
* @param uri uri | |||
* @param data 数据 | |||
* @throws WxErrorException | |||
* @throws ClientProtocolException | |||
* @throws IOException | |||
@@ -3,7 +3,6 @@ package me.chanjar.weixin.common.util.http; | |||
import me.chanjar.weixin.common.bean.result.WxError; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.client.ClientProtocolException; | |||
import org.apache.http.client.config.RequestConfig; | |||
import org.apache.http.client.methods.CloseableHttpResponse; | |||
import org.apache.http.client.methods.HttpGet; | |||
@@ -13,8 +12,8 @@ import java.io.IOException; | |||
/** | |||
* 简单的GET请求执行器,请求的参数是String, 返回的结果也是String | |||
* @author Daniel Qian | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class SimpleGetRequestExecutor implements RequestExecutor<String, String> { | |||
@@ -39,7 +38,7 @@ public class SimpleGetRequestExecutor implements RequestExecutor<String, String> | |||
throw new WxErrorException(error); | |||
} | |||
return responseContent; | |||
}finally { | |||
} finally { | |||
httpGet.releaseConnection(); | |||
} | |||
} | |||
@@ -4,7 +4,6 @@ import me.chanjar.weixin.common.bean.result.WxError; | |||
import me.chanjar.weixin.common.exception.WxErrorException; | |||
import org.apache.http.Consts; | |||
import org.apache.http.HttpHost; | |||
import org.apache.http.client.ClientProtocolException; | |||
import org.apache.http.client.config.RequestConfig; | |||
import org.apache.http.client.methods.CloseableHttpResponse; | |||
import org.apache.http.client.methods.HttpPost; | |||
@@ -15,8 +14,8 @@ import java.io.IOException; | |||
/** | |||
* 简单的POST请求执行器,请求的参数是String, 返回的结果也是String | |||
* @author Daniel Qian | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class SimplePostRequestExecutor implements RequestExecutor<String, String> { | |||
@@ -40,7 +39,7 @@ public class SimplePostRequestExecutor implements RequestExecutor<String, String | |||
throw new WxErrorException(error); | |||
} | |||
return responseContent; | |||
}finally { | |||
} finally { | |||
httpPost.releaseConnection(); | |||
} | |||
} | |||
@@ -1,7 +1,5 @@ | |||
package me.chanjar.weixin.common.util.http; | |||
import java.io.IOException; | |||
import org.apache.http.Consts; | |||
import org.apache.http.HttpEntity; | |||
import org.apache.http.HttpResponse; | |||
@@ -10,15 +8,17 @@ import org.apache.http.client.HttpResponseException; | |||
import org.apache.http.client.ResponseHandler; | |||
import org.apache.http.util.EntityUtils; | |||
import java.io.IOException; | |||
/** | |||
* copy from {@link org.apache.http.impl.client.BasicResponseHandler} | |||
* @author Daniel Qian | |||
* | |||
* @author Daniel Qian | |||
*/ | |||
public class Utf8ResponseHandler implements ResponseHandler<String> { | |||
public static final ResponseHandler<String> INSTANCE = new Utf8ResponseHandler(); | |||
public String handleResponse(final HttpResponse response) throws IOException { | |||
final StatusLine statusLine = response.getStatusLine(); | |||
final HttpEntity entity = response.getEntity(); | |||
@@ -15,101 +15,101 @@ import com.google.gson.JsonObject; | |||
public class GsonHelper { | |||
public static boolean isNull(JsonElement element) { | |||
return element == null || element.isJsonNull(); | |||
} | |||
public static boolean isNotNull(JsonElement element) { | |||
return !isNull(element); | |||
} | |||
public static Long getLong(JsonObject json, String property) { | |||
return getAsLong(json.get(property)); | |||
} | |||
public static long getPrimitiveLong(JsonObject json, String property) { | |||
return getAsPrimitiveLong(json.get(property)); | |||
} | |||
public static Integer getInteger(JsonObject json, String property) { | |||
return getAsInteger(json.get(property)); | |||
} | |||
public static int getPrimitiveInteger(JsonObject json, String property) { | |||
return getAsPrimitiveInt(json.get(property)); | |||
} | |||
public static Double getDouble(JsonObject json, String property) { | |||
return getAsDouble(json.get(property)); | |||
} | |||
public static double getPrimitiveDouble(JsonObject json, String property) { | |||
return getAsPrimitiveDouble(json.get(property)); | |||
} | |||
public static Float getFloat(JsonObject json, String property) { | |||
return getAsFloat(json.get(property)); | |||
} | |||
public static float getPrimitiveFloat(JsonObject json, String property) { | |||
return getAsPrimitiveFloat(json.get(property)); | |||
} | |||
public static Boolean getBoolean(JsonObject json, String property) { | |||
return getAsBoolean(json.get(property)); | |||
} | |||
public static String getString(JsonObject json, String property) { | |||
return getAsString(json.get(property)); | |||
} | |||
public static String getAsString(JsonElement element) { | |||
return isNull(element) ? null : element.getAsString(); | |||
} | |||
public static Long getAsLong(JsonElement element) { | |||
return isNull(element) ? null : element.getAsLong(); | |||
} | |||
public static long getAsPrimitiveLong(JsonElement element) { | |||
Long r = getAsLong(element); | |||
return r == null ? 0l : r; | |||
} | |||
public static Integer getAsInteger(JsonElement element) { | |||
return isNull(element) ? null : element.getAsInt(); | |||
} | |||
public static int getAsPrimitiveInt(JsonElement element) { | |||
Integer r = getAsInteger(element); | |||
return r == null ? 0 : r; | |||
} | |||
public static Boolean getAsBoolean(JsonElement element) { | |||
return isNull(element) ? null : element.getAsBoolean(); | |||
} | |||
public static boolean getAsPrimitiveBool(JsonElement element) { | |||
Boolean r = getAsBoolean(element); | |||
return r != null && r.booleanValue(); | |||
} | |||
public static Double getAsDouble(JsonElement element) { | |||
return isNull(element) ? null : element.getAsDouble(); | |||
} | |||
public static double getAsPrimitiveDouble(JsonElement element) { | |||
Double r = getAsDouble(element); | |||
return r == null ? 0d : r; | |||
} | |||
public static Float getAsFloat(JsonElement element) { | |||
return isNull(element) ? null : element.getAsFloat(); | |||
} | |||
public static float getAsPrimitiveFloat(JsonElement element) { | |||
Float r = getAsFloat(element); | |||
return r == null ? 0f : r; | |||
} | |||
public static boolean isNull(JsonElement element) { | |||
return element == null || element.isJsonNull(); | |||
} | |||
public static boolean isNotNull(JsonElement element) { | |||
return !isNull(element); | |||
} | |||
public static Long getLong(JsonObject json, String property) { | |||
return getAsLong(json.get(property)); | |||
} | |||
public static long getPrimitiveLong(JsonObject json, String property) { | |||
return getAsPrimitiveLong(json.get(property)); | |||
} | |||
public static Integer getInteger(JsonObject json, String property) { | |||
return getAsInteger(json.get(property)); | |||
} | |||
public static int getPrimitiveInteger(JsonObject json, String property) { | |||
return getAsPrimitiveInt(json.get(property)); | |||
} | |||
public static Double getDouble(JsonObject json, String property) { | |||
return getAsDouble(json.get(property)); | |||
} | |||
public static double getPrimitiveDouble(JsonObject json, String property) { | |||
return getAsPrimitiveDouble(json.get(property)); | |||
} | |||
public static Float getFloat(JsonObject json, String property) { | |||
return getAsFloat(json.get(property)); | |||
} | |||
public static float getPrimitiveFloat(JsonObject json, String property) { | |||
return getAsPrimitiveFloat(json.get(property)); | |||
} | |||
public static Boolean getBoolean(JsonObject json, String property) { | |||
return getAsBoolean(json.get(property)); | |||
} | |||
public static String getString(JsonObject json, String property) { | |||
return getAsString(json.get(property)); | |||
} | |||
public static String getAsString(JsonElement element) { | |||
return isNull(element) ? null : element.getAsString(); | |||
} | |||
public static Long getAsLong(JsonElement element) { | |||
return isNull(element) ? null : element.getAsLong(); | |||
} | |||
public static long getAsPrimitiveLong(JsonElement element) { | |||
Long r = getAsLong(element); | |||
return r == null ? 0l : r; | |||
} | |||
public static Integer getAsInteger(JsonElement element) { | |||
return isNull(element) ? null : element.getAsInt(); | |||
} | |||
public static int getAsPrimitiveInt(JsonElement element) { | |||
Integer r = getAsInteger(element); | |||
return r == null ? 0 : r; | |||
} | |||
public static Boolean getAsBoolean(JsonElement element) { | |||
return isNull(element) ? null : element.getAsBoolean(); | |||
} | |||
public static boolean getAsPrimitiveBool(JsonElement element) { | |||
Boolean r = getAsBoolean(element); | |||
return r != null && r.booleanValue(); | |||
} | |||
public static Double getAsDouble(JsonElement element) { | |||
return isNull(element) ? null : element.getAsDouble(); | |||
} | |||
public static double getAsPrimitiveDouble(JsonElement element) { | |||
Double r = getAsDouble(element); | |||
return r == null ? 0d : r; | |||
} | |||
public static Float getAsFloat(JsonElement element) { | |||
return isNull(element) ? null : element.getAsFloat(); | |||
} | |||
public static float getAsPrimitiveFloat(JsonElement element) { | |||
Float r = getAsFloat(element); | |||
return r == null ? 0f : r; | |||
} | |||
} |
@@ -14,9 +14,7 @@ import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import java.lang.reflect.Type; | |||
/** | |||
* | |||
* @author Daniel Qian | |||
* | |||
*/ | |||
public class WxAccessTokenAdapter implements JsonDeserializer<WxAccessToken> { | |||
@@ -32,5 +30,5 @@ public class WxAccessTokenAdapter implements JsonDeserializer<WxAccessToken> { | |||
} | |||
return accessToken; | |||
} | |||
} |
@@ -14,9 +14,7 @@ import me.chanjar.weixin.common.bean.result.WxError; | |||
import java.lang.reflect.Type; | |||
/** | |||
* | |||
* @author Daniel Qian | |||
* | |||
*/ | |||
public class WxErrorAdapter implements JsonDeserializer<WxError> { | |||
@@ -33,5 +31,5 @@ public class WxErrorAdapter implements JsonDeserializer<WxError> { | |||
wxError.setJson(json.toString()); | |||
return wxError; | |||
} | |||
} |
@@ -2,9 +2,9 @@ package me.chanjar.weixin.common.util.json; | |||
import com.google.gson.Gson; | |||
import com.google.gson.GsonBuilder; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.bean.WxMenu; | |||
import me.chanjar.weixin.common.bean.result.WxError; | |||
import me.chanjar.weixin.common.bean.WxAccessToken; | |||
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
public class WxGsonBuilder { | |||
@@ -14,9 +14,7 @@ import me.chanjar.weixin.common.bean.result.WxMediaUploadResult; | |||
import java.lang.reflect.Type; | |||
/** | |||
* | |||
* @author Daniel Qian | |||
* | |||
*/ | |||
public class WxMediaUploadResultAdapter implements JsonDeserializer<WxMediaUploadResult> { | |||
@@ -38,5 +36,5 @@ public class WxMediaUploadResultAdapter implements JsonDeserializer<WxMediaUploa | |||
} | |||
return uploadResult; | |||
} | |||
} |
@@ -8,22 +8,13 @@ | |||
*/ | |||
package me.chanjar.weixin.common.util.json; | |||
import com.google.gson.JsonArray; | |||
import com.google.gson.JsonDeserializationContext; | |||
import com.google.gson.JsonDeserializer; | |||
import com.google.gson.JsonElement; | |||
import com.google.gson.JsonObject; | |||
import com.google.gson.JsonParseException; | |||
import com.google.gson.JsonSerializationContext; | |||
import com.google.gson.JsonSerializer; | |||
import com.google.gson.*; | |||
import me.chanjar.weixin.common.bean.WxMenu; | |||
import java.lang.reflect.Type; | |||
/** | |||
* | |||
* @author Daniel Qian | |||
* | |||
*/ | |||
public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializer<WxMenu> { | |||
@@ -36,11 +27,11 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializ | |||
buttonArray.add(buttonJson); | |||
} | |||
json.add("button", buttonArray); | |||
if (menu.getMatchRule() != null) { | |||
json.add("matchrule", convertToJson(menu.getMatchRule())); | |||
} | |||
return json; | |||
} | |||
@@ -60,15 +51,15 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializ | |||
return buttonJson; | |||
} | |||
protected JsonObject convertToJson(WxMenu.WxMenuRule menuRule){ | |||
protected JsonObject convertToJson(WxMenu.WxMenuRule menuRule) { | |||
JsonObject matchRule = new JsonObject(); | |||
matchRule.addProperty("tag_id",menuRule.getTagId()); | |||
matchRule.addProperty("sex",menuRule.getSex()); | |||
matchRule.addProperty("country",menuRule.getCountry()); | |||
matchRule.addProperty("province",menuRule.getProvince()); | |||
matchRule.addProperty("city",menuRule.getCity()); | |||
matchRule.addProperty("client_platform_type",menuRule.getClientPlatformType()); | |||
matchRule.addProperty("language",menuRule.getLanguage()); | |||
matchRule.addProperty("tag_id", menuRule.getTagId()); | |||
matchRule.addProperty("sex", menuRule.getSex()); | |||
matchRule.addProperty("country", menuRule.getCountry()); | |||
matchRule.addProperty("province", menuRule.getProvince()); | |||
matchRule.addProperty("city", menuRule.getCity()); | |||
matchRule.addProperty("client_platform_type", menuRule.getClientPlatformType()); | |||
matchRule.addProperty("language", menuRule.getLanguage()); | |||
return matchRule; | |||
} | |||
@@ -96,7 +87,7 @@ public class WxMenuGsonAdapter implements JsonSerializer<WxMenu>, JsonDeserializ | |||
} | |||
return menu; | |||
} | |||
protected WxMenu.WxMenuButton convertFromJson(JsonObject json) { | |||
WxMenu.WxMenuButton button = new WxMenu.WxMenuButton(); | |||
button.setName(GsonHelper.getString(json, "name")); | |||
@@ -18,30 +18,24 @@ | |||
package me.chanjar.weixin.common.util.res; | |||
import java.text.MessageFormat; | |||
import java.util.Enumeration; | |||
import java.util.Hashtable; | |||
import java.util.LinkedHashMap; | |||
import java.util.Locale; | |||
import java.util.Map; | |||
import java.util.MissingResourceException; | |||
import java.util.ResourceBundle; | |||
import java.util.*; | |||
/** | |||
* An internationalization / localization helper class which reduces | |||
* the bother of handling ResourceBundles and takes care of the | |||
* common cases of message formating which otherwise require the | |||
* creation of Object arrays and such. | |||
* | |||
* <p> | |||
* <p>The StringManager operates on a package basis. One StringManager | |||
* per package can be created and accessed via the getManager method | |||
* call. | |||
* | |||
* <p> | |||
* <p>The StringManager will look for a ResourceBundle named by | |||
* the package name given plus the suffix of "LocalStrings". In | |||
* practice, this means that the localized information will be contained | |||
* in a LocalStrings.properties file located in the package | |||
* directory of the classpath. | |||
* | |||
* <p> | |||
* <p>Please see the documentation for java.util.ResourceBundle for | |||
* more information. | |||
* | |||
@@ -52,152 +46,80 @@ import java.util.ResourceBundle; | |||
*/ | |||
public class StringManager { | |||
private static int LOCALE_CACHE_SIZE = 10; | |||
/** | |||
* The ResourceBundle for this StringManager. | |||
*/ | |||
private final ResourceBundle bundle; | |||
private final Locale locale; | |||
/** | |||
* Creates a new StringManager for a given package. This is a | |||
* private method and all access to it is arbitrated by the | |||
* static getManager method call so that only one StringManager | |||
* per package will be created. | |||
* | |||
* @param packageName Name of package to create StringManager for. | |||
*/ | |||
private StringManager(String packageName, Locale locale) { | |||
String bundleName = packageName + ".LocalStrings"; | |||
ResourceBundle bnd = null; | |||
private static final Map<String, Map<Locale, StringManager>> managers = | |||
new Hashtable<String, Map<Locale, StringManager>>(); | |||
private static int LOCALE_CACHE_SIZE = 10; | |||
/** | |||
* The ResourceBundle for this StringManager. | |||
*/ | |||
private final ResourceBundle bundle; | |||
private final Locale locale; | |||
/** | |||
* Creates a new StringManager for a given package. This is a | |||
* private method and all access to it is arbitrated by the | |||
* static getManager method call so that only one StringManager | |||
* per package will be created. | |||
* | |||
* @param packageName Name of package to create StringManager for. | |||
*/ | |||
private StringManager(String packageName, Locale locale) { | |||
String bundleName = packageName + ".LocalStrings"; | |||
ResourceBundle bnd = null; | |||
try { | |||
bnd = ResourceBundle.getBundle(bundleName, locale); | |||
} catch (MissingResourceException ex) { | |||
// Try from the current loader (that's the case for trusted apps) | |||
// Should only be required if using a TC5 style classloader structure | |||
// where common != shared != server | |||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |||
if (cl != null) { | |||
try { | |||
bnd = ResourceBundle.getBundle(bundleName, locale); | |||
} catch( MissingResourceException ex ) { | |||
// Try from the current loader (that's the case for trusted apps) | |||
// Should only be required if using a TC5 style classloader structure | |||
// where common != shared != server | |||
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |||
if( cl != null ) { | |||
try { | |||
bnd = ResourceBundle.getBundle(bundleName, locale, cl); | |||
} catch(MissingResourceException ex2) { | |||
// Ignore | |||
} | |||
} | |||
} | |||
bundle = bnd; | |||
// Get the actual locale, which may be different from the requested one | |||
if (bundle != null) { | |||
Locale bundleLocale = bundle.getLocale(); | |||
if (bundleLocale.equals(Locale.ROOT)) { | |||
this.locale = Locale.ENGLISH; | |||
} else { | |||
this.locale = bundleLocale; | |||
} | |||
} else { | |||
this.locale = null; | |||
bnd = ResourceBundle.getBundle(bundleName, locale, cl); | |||
} catch (MissingResourceException ex2) { | |||
// Ignore | |||
} | |||
} | |||
} | |||
/** | |||
Get a string from the underlying resource bundle or return | |||
null if the String is not found. | |||
@param key to desired resource String | |||
@return resource String matching <i>key</i> from underlying | |||
bundle or null if not found. | |||
@throws IllegalArgumentException if <i>key</i> is null. | |||
*/ | |||
public String getString(String key) { | |||
if(key == null){ | |||
String msg = "key may not have a null value"; | |||
throw new IllegalArgumentException(msg); | |||
} | |||
String str = null; | |||
try { | |||
// Avoid NPE if bundle is null and treat it like an MRE | |||
if (bundle != null) { | |||
str = bundle.getString(key); | |||
} | |||
} catch(MissingResourceException mre) { | |||
//bad: shouldn't mask an exception the following way: | |||
// str = "[cannot find message associated with key '" + key + | |||
// "' due to " + mre + "]"; | |||
// because it hides the fact that the String was missing | |||
// from the calling code. | |||
//good: could just throw the exception (or wrap it in another) | |||
// but that would probably cause much havoc on existing | |||
// code. | |||
//better: consistent with container pattern to | |||
// simply return null. Calling code can then do | |||
// a null check. | |||
str = null; | |||
} | |||
return str; | |||
bundle = bnd; | |||
// Get the actual locale, which may be different from the requested one | |||
if (bundle != null) { | |||
Locale bundleLocale = bundle.getLocale(); | |||
if (bundleLocale.equals(Locale.ROOT)) { | |||
this.locale = Locale.ENGLISH; | |||
} else { | |||
this.locale = bundleLocale; | |||
} | |||
} else { | |||
this.locale = null; | |||
} | |||
/** | |||
* Get a string from the underlying resource bundle and format | |||
* it with the given set of arguments. | |||
* | |||
* @param key | |||
* @param args | |||
*/ | |||
public String getString(final String key, final Object... args) { | |||
String value = getString(key); | |||
if (value == null) { | |||
value = key; | |||
} | |||
MessageFormat mf = new MessageFormat(value); | |||
mf.setLocale(locale); | |||
return mf.format(args, new StringBuffer(), null).toString(); | |||
} | |||
/** | |||
* Identify the Locale this StringManager is associated with | |||
*/ | |||
public Locale getLocale() { | |||
return locale; | |||
} | |||
// -------------------------------------------------------------- | |||
// STATIC SUPPORT METHODS | |||
// -------------------------------------------------------------- | |||
private static final Map<String, Map<Locale,StringManager>> managers = | |||
new Hashtable<String, Map<Locale,StringManager>>(); | |||
/** | |||
* Get the StringManager for a particular package. If a manager for | |||
* a package already exists, it will be reused, else a new | |||
* StringManager will be created and returned. | |||
* | |||
* @param packageName The package name | |||
*/ | |||
public static final synchronized StringManager getManager( | |||
String packageName) { | |||
return getManager(packageName, Locale.getDefault()); | |||
} | |||
/** | |||
* Get the StringManager for a particular package and Locale. If a manager | |||
* for a package/Locale combination already exists, it will be reused, else | |||
* a new StringManager will be created and returned. | |||
* | |||
* @param packageName The package name | |||
* @param locale The Locale | |||
*/ | |||
public static final synchronized StringManager getManager( | |||
String packageName, Locale locale) { | |||
Map<Locale,StringManager> map = managers.get(packageName); | |||
if (map == null) { | |||
} | |||
/** | |||
* Get the StringManager for a particular package. If a manager for | |||
* a package already exists, it will be reused, else a new | |||
* StringManager will be created and returned. | |||
* | |||
* @param packageName The package name | |||
*/ | |||
public static final synchronized StringManager getManager( | |||
String packageName) { | |||
return getManager(packageName, Locale.getDefault()); | |||
} | |||
/** | |||
* Get the StringManager for a particular package and Locale. If a manager | |||
* for a package/Locale combination already exists, it will be reused, else | |||
* a new StringManager will be created and returned. | |||
* | |||
* @param packageName The package name | |||
* @param locale The Locale | |||
*/ | |||
public static final synchronized StringManager getManager( | |||
String packageName, Locale locale) { | |||
Map<Locale, StringManager> map = managers.get(packageName); | |||
if (map == null) { | |||
/* | |||
* Don't want the HashMap to be expanded beyond LOCALE_CACHE_SIZE. | |||
* Expansion occurs when size() exceeds capacity. Therefore keep | |||
@@ -206,43 +128,113 @@ public class StringManager { | |||
* for removal needs to use one less than the maximum desired size | |||
* | |||
*/ | |||
map = new LinkedHashMap<Locale,StringManager>(LOCALE_CACHE_SIZE, 1, true) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
protected boolean removeEldestEntry( | |||
Map.Entry<Locale,StringManager> eldest) { | |||
return size() > (LOCALE_CACHE_SIZE - 1); | |||
} | |||
}; | |||
managers.put(packageName, map); | |||
} | |||
map = new LinkedHashMap<Locale, StringManager>(LOCALE_CACHE_SIZE, 1, true) { | |||
private static final long serialVersionUID = 1L; | |||
StringManager mgr = map.get(locale); | |||
if (mgr == null) { | |||
mgr = new StringManager(packageName, locale); | |||
map.put(locale, mgr); | |||
@Override | |||
protected boolean removeEldestEntry( | |||
Map.Entry<Locale, StringManager> eldest) { | |||
return size() > (LOCALE_CACHE_SIZE - 1); | |||
} | |||
return mgr; | |||
}; | |||
managers.put(packageName, map); | |||
} | |||
/** | |||
* Retrieve the StringManager for a list of Locales. The first StringManager | |||
* found will be returned. | |||
* | |||
* @param requestedLocales the list of Locales | |||
* | |||
* @return the found StringManager or the default StringManager | |||
*/ | |||
public static StringManager getManager(String packageName, | |||
Enumeration<Locale> requestedLocales) { | |||
while (requestedLocales.hasMoreElements()) { | |||
Locale locale = requestedLocales.nextElement(); | |||
StringManager result = getManager(packageName, locale); | |||
if (result.getLocale().equals(locale)) { | |||
return result; | |||
} | |||
} | |||
// Return the default | |||
return getManager(packageName); | |||
StringManager mgr = map.get(locale); | |||
if (mgr == null) { | |||
mgr = new StringManager(packageName, locale); | |||
map.put(locale, mgr); | |||
} | |||
return mgr; | |||
} | |||
// -------------------------------------------------------------- | |||
// STATIC SUPPORT METHODS | |||
// -------------------------------------------------------------- | |||
/** | |||
* Retrieve the StringManager for a list of Locales. The first StringManager | |||
* found will be returned. | |||
* | |||
* @param requestedLocales the list of Locales | |||
* @return the found StringManager or the default StringManager | |||
*/ | |||
public static StringManager getManager(String packageName, | |||
Enumeration<Locale> requestedLocales) { | |||
while (requestedLocales.hasMoreElements()) { | |||
Locale locale = requestedLocales.nextElement(); | |||
StringManager result = getManager(packageName, locale); | |||
if (result.getLocale().equals(locale)) { | |||
return result; | |||
} | |||
} | |||
// Return the default | |||
return getManager(packageName); | |||
} | |||
/** | |||
* Get a string from the underlying resource bundle or return | |||
* null if the String is not found. | |||
* | |||
* @param key to desired resource String | |||
* @return resource String matching <i>key</i> from underlying | |||
* bundle or null if not found. | |||
* @throws IllegalArgumentException if <i>key</i> is null. | |||
*/ | |||
public String getString(String key) { | |||
if (key == null) { | |||
String msg = "key may not have a null value"; | |||
throw new IllegalArgumentException(msg); | |||
} | |||
String str = null; | |||
try { | |||
// Avoid NPE if bundle is null and treat it like an MRE | |||
if (bundle != null) { | |||
str = bundle.getString(key); | |||
} | |||
} catch (MissingResourceException mre) { | |||
//bad: shouldn't mask an exception the following way: | |||
// str = "[cannot find message associated with key '" + key + | |||
// "' due to " + mre + "]"; | |||
// because it hides the fact that the String was missing | |||
// from the calling code. | |||
//good: could just throw the exception (or wrap it in another) | |||
// but that would probably cause much havoc on existing | |||
// code. | |||
//better: consistent with container pattern to | |||
// simply return null. Calling code can then do | |||
// a null check. | |||
str = null; | |||
} | |||
return str; | |||
} | |||
/** | |||
* Get a string from the underlying resource bundle and format | |||
* it with the given set of arguments. | |||
* | |||
* @param key | |||
* @param args | |||
*/ | |||
public String getString(final String key, final Object... args) { | |||
String value = getString(key); | |||
if (value == null) { | |||
value = key; | |||
} | |||
MessageFormat mf = new MessageFormat(value); | |||
mf.setLocale(locale); | |||
return mf.format(args, new StringBuffer(), null).toString(); | |||
} | |||
/** | |||
* Identify the Locale this StringManager is associated with | |||
*/ | |||
public Locale getLocale() { | |||
return locale; | |||
} | |||
} |
@@ -1,7 +1,5 @@ | |||
package me.chanjar.weixin.common.util.xml; | |||
import java.io.Writer; | |||
import com.thoughtworks.xstream.XStream; | |||
import com.thoughtworks.xstream.core.util.QuickWriter; | |||
import com.thoughtworks.xstream.io.HierarchicalStreamWriter; | |||
@@ -10,6 +8,8 @@ import com.thoughtworks.xstream.io.xml.XppDriver; | |||
import com.thoughtworks.xstream.security.NullPermission; | |||
import com.thoughtworks.xstream.security.PrimitiveTypePermission; | |||
import java.io.Writer; | |||
public class XStreamInitializer { | |||
public static XStream getInstance() { | |||
@@ -22,6 +22,7 @@ public class XStreamInitializer { | |||
protected String SUFFIX_CDATA = "]]>"; | |||
protected String PREFIX_MEDIA_ID = "<MediaId>"; | |||
protected String SUFFIX_MEDIA_ID = "</MediaId>"; | |||
@Override | |||
protected void writeText(QuickWriter writer, String text) { | |||
if (text.startsWith(PREFIX_CDATA) && text.endsWith(SUFFIX_CDATA)) { | |||
@@ -14,5 +14,5 @@ public class WxAccessTokenTest { | |||
Assert.assertTrue(wxError.getExpiresIn() == 7200); | |||
} | |||
} |
@@ -15,7 +15,7 @@ public class WxErrorTest { | |||
Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); | |||
} | |||
public void testFromBadJson1() { | |||
String json = "{ \"errcode\": 40003, \"errmsg\": \"invalid openid\", \"media_id\": \"12323423dsfafsf232f\" }"; | |||
@@ -24,7 +24,7 @@ public class WxErrorTest { | |||
Assert.assertEquals(wxError.getErrorMsg(), "invalid openid"); | |||
} | |||
public void testFromBadJson2() { | |||
String json = "{\"access_token\":\"ACCESS_TOKEN\",\"expires_in\":7200}"; | |||
@@ -33,5 +33,5 @@ public class WxErrorTest { | |||
Assert.assertEquals(wxError.getErrorMsg(), null); | |||
} | |||
} |
@@ -8,51 +8,51 @@ import org.testng.annotations.Test; | |||
@Test | |||
public class WxMenuTest { | |||
@Test(dataProvider="wxReturnMenu") | |||
@Test(dataProvider = "wxReturnMenu") | |||
public void testFromJson(String json) { | |||
WxMenu menu = WxMenu.fromJson(json); | |||
Assert.assertEquals(menu.getButtons().size(), 3); | |||
} | |||
@Test(dataProvider="wxPushMenu") | |||
@Test(dataProvider = "wxPushMenu") | |||
public void testToJson(String json) { | |||
WxMenu menu = new WxMenu(); | |||
WxMenuButton button1 = new WxMenuButton(); | |||
button1.setType("click"); | |||
button1.setName("今日歌曲"); | |||
button1.setKey("V1001_TODAY_MUSIC"); | |||
WxMenuButton button2 = new WxMenuButton(); | |||
button2.setType("click"); | |||
button2.setName("歌手简介"); | |||
button2.setKey("V1001_TODAY_SINGER"); | |||
WxMenuButton button3 = new WxMenuButton(); | |||
button3.setName("菜单"); | |||
menu.getButtons().add(button1); | |||
menu.getButtons().add(button2); | |||
menu.getButtons().add(button3); | |||
WxMenuButton button31 = new WxMenuButton(); | |||
button31.setType("view"); | |||
button31.setName("搜索"); | |||
button31.setUrl("http://www.soso.com/"); | |||
WxMenuButton button32 = new WxMenuButton(); | |||
button32.setType("view"); | |||
button32.setName("视频"); | |||
button32.setUrl("http://v.qq.com/"); | |||
WxMenuButton button33 = new WxMenuButton(); | |||
button33.setType("click"); | |||
button33.setName("赞一下我们"); | |||
button33.setKey("V1001_GOOD"); | |||
button3.getSubButtons().add(button31); | |||
button3.getSubButtons().add(button32); | |||
button3.getSubButtons().add(button33); | |||
Assert.assertEquals(menu.toJson(), json); | |||
} | |||
@@ -78,82 +78,82 @@ public class WxMenuTest { | |||
Assert.assertEquals(menu.toJson(), json); | |||
} | |||
@DataProvider | |||
public Object[][] wxReturnMenu() { | |||
Object[][] res = menuJson(); | |||
Object[][] res = menuJson(); | |||
String json = "{ \"menu\" : " + res[0][0] + " }"; | |||
return new Object[][] { | |||
new Object[] { json } | |||
return new Object[][]{ | |||
new Object[]{json} | |||
}; | |||
} | |||
@DataProvider(name="wxPushMenu") | |||
@DataProvider(name = "wxPushMenu") | |||
public Object[][] menuJson() { | |||
String json = | |||
"{" | |||
+"\"button\":[" | |||
+"{" | |||
+"\"type\":\"click\"," | |||
+"\"name\":\"今日歌曲\"," | |||
+"\"key\":\"V1001_TODAY_MUSIC\"" | |||
+"}," | |||
+"{" | |||
+"\"type\":\"click\"," | |||
+"\"name\":\"歌手简介\"," | |||
+"\"key\":\"V1001_TODAY_SINGER\"" | |||
+"}," | |||
+"{" | |||
+"\"name\":\"菜单\"," | |||
+"\"sub_button\":[" | |||
+"{" | |||
+"\"type\":\"view\"," | |||
+"\"name\":\"搜索\"," | |||
+"\"url\":\"http://www.soso.com/\"" | |||
+"}," | |||
+"{" | |||
+"\"type\":\"view\"," | |||
+"\"name\":\"视频\"," | |||
+"\"url\":\"http://v.qq.com/\"" | |||
+"}," | |||
+"{" | |||
+"\"type\":\"click\"," | |||
+"\"name\":\"赞一下我们\"," | |||
+"\"key\":\"V1001_GOOD\"" | |||
+"}" | |||
+"]" | |||
+"}" | |||
+"]" | |||
+"}"; | |||
return new Object[][] { | |||
new Object[] { json } | |||
String json = | |||
"{" | |||
+ "\"button\":[" | |||
+ "{" | |||
+ "\"type\":\"click\"," | |||
+ "\"name\":\"今日歌曲\"," | |||
+ "\"key\":\"V1001_TODAY_MUSIC\"" | |||
+ "}," | |||
+ "{" | |||
+ "\"type\":\"click\"," | |||
+ "\"name\":\"歌手简介\"," | |||
+ "\"key\":\"V1001_TODAY_SINGER\"" | |||
+ "}," | |||
+ "{" | |||
+ "\"name\":\"菜单\"," | |||
+ "\"sub_button\":[" | |||
+ "{" | |||
+ "\"type\":\"view\"," | |||
+ "\"name\":\"搜索\"," | |||
+ "\"url\":\"http://www.soso.com/\"" | |||
+ "}," | |||
+ "{" | |||
+ "\"type\":\"view\"," | |||
+ "\"name\":\"视频\"," | |||
+ "\"url\":\"http://v.qq.com/\"" | |||
+ "}," | |||
+ "{" | |||
+ "\"type\":\"click\"," | |||
+ "\"name\":\"赞一下我们\"," | |||
+ "\"key\":\"V1001_GOOD\"" | |||
+ "}" | |||
+ "]" | |||
+ "}" | |||
+ "]" | |||
+ "}"; | |||
return new Object[][]{ | |||
new Object[]{json} | |||
}; | |||
} | |||
@DataProvider(name = "wxAddConditionalMenu") | |||
public Object[][] addConditionalMenuJson(){ | |||
public Object[][] addConditionalMenuJson() { | |||
String json = | |||
"{" | |||
+"\"button\":[" | |||
+"{" | |||
+"\"type\":\"click\"," | |||
+"\"name\":\"今日歌曲\"," | |||
+"\"key\":\"V1001_TODAY_MUSIC\"" | |||
+"}" | |||
+"]," | |||
+"\"matchrule\":{" | |||
+"\"group_id\":\"2\"," | |||
+"\"sex\":\"1\"," | |||
+"\"country\":\"中国\"," | |||
+"\"province\":\"广东\"," | |||
+"\"city\":\"广州\"," | |||
+"\"client_platform_type\":\"2\"," | |||
+"\"language\":\"zh_CN\"" | |||
+"}" | |||
+"}"; | |||
"{" | |||
+ "\"button\":[" | |||
+ "{" | |||
+ "\"type\":\"click\"," | |||
+ "\"name\":\"今日歌曲\"," | |||
+ "\"key\":\"V1001_TODAY_MUSIC\"" | |||
+ "}" | |||
+ "]," | |||
+ "\"matchrule\":{" | |||
+ "\"group_id\":\"2\"," | |||
+ "\"sex\":\"1\"," | |||
+ "\"country\":\"中国\"," | |||
+ "\"province\":\"广东\"," | |||
+ "\"city\":\"广州\"," | |||
+ "\"client_platform_type\":\"2\"," | |||
+ "\"language\":\"zh_CN\"" | |||
+ "}" | |||
+ "}"; | |||
return new Object[][]{ | |||
new Object[]{json} | |||
new Object[]{json} | |||
}; | |||
} | |||
} |
@@ -10,8 +10,8 @@ public class SessionTest { | |||
@DataProvider | |||
public Object[][] getSessionManager() { | |||
return new Object[][] { | |||
new Object[] { new StandardSessionManager() } | |||
return new Object[][]{ | |||
new Object[]{new StandardSessionManager()} | |||
}; | |||
} | |||
@@ -8,7 +8,7 @@ import org.testng.annotations.Test; | |||
public class WxMessageInMemoryDuplicateCheckerTest { | |||
public void test() throws InterruptedException { | |||
Long[] msgIds = new Long[] { 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l }; | |||
Long[] msgIds = new Long[]{1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l}; | |||
WxMessageInMemoryDuplicateChecker checker = new WxMessageInMemoryDuplicateChecker(2000l, 1000l); | |||
// 第一次检查 | |||
@@ -69,7 +69,7 @@ public class WxCryptUtilTest { | |||
} | |||
public void testValidateSignatureError() throws ParserConfigurationException, SAXException, | |||
IOException { | |||
IOException { | |||
try { | |||
WxCryptUtil pc = new WxCryptUtil(token, encodingAesKey, appId); | |||
String afterEncrpt = pc.encrypt(replyMsg); | |||