From dd186abd09509c31efe2de90347ff1e53592f78e Mon Sep 17 00:00:00 2001 From: jinyu Date: Tue, 18 Aug 2015 22:18:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=AF=94=E8=BE=83=E5=A4=A7=E7=9A=84=E6=94=B9?= =?UTF-8?q?=E5=8A=A8:=E9=87=8D=E6=9E=84=E4=BA=86HttpClient=E9=83=A8?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGE.md | 4 + README.md | 2 +- pom.xml | 2 +- weixin4j-base/pom.xml | 14 +- .../com/foxinmy/weixin4j/api/BaseApi.java | 15 +- .../com/foxinmy/weixin4j/api/CashApi.java | 31 +- .../com/foxinmy/weixin4j/api/CouponApi.java | 15 +- .../com/foxinmy/weixin4j/api/Pay3Api.java | 29 +- .../weixin4j/exception/WeixinException.java | 20 +- .../weixin4j/http/AbstractHttpClient.java | 109 ++ .../weixin4j/http/AbstractHttpMessage.java | 84 -- .../weixin4j/http/AbstractHttpRequest.java | 49 - .../foxinmy/weixin4j/http/ContentType.java | 12 + .../com/foxinmy/weixin4j/http/Header.java | 24 - .../com/foxinmy/weixin4j/http/HttpClient.java | 61 +- .../weixin4j/http/HttpClientException.java | 31 + .../weixin4j/http/HttpEntityRequest.java | 18 - .../com/foxinmy/weixin4j/http/HttpGet.java | 34 - .../foxinmy/weixin4j/http/HttpHeaders.java | 1273 +++++++++++++++++ .../foxinmy/weixin4j/http/HttpMessage.java | 51 +- .../com/foxinmy/weixin4j/http/HttpMethod.java | 2 +- .../com/foxinmy/weixin4j/http/HttpParams.java | 150 +- .../com/foxinmy/weixin4j/http/HttpPost.java | 49 - .../weixin4j/http/HttpProtocolParams.java | 9 - .../foxinmy/weixin4j/http/HttpRequest.java | 77 +- .../foxinmy/weixin4j/http/HttpResponse.java | 62 +- .../weixin4j/http/HttpResponseHandler.java | 16 - .../com/foxinmy/weixin4j/http/HttpStatus.java | 20 + .../weixin4j/http/ResponseHandler.java | 16 - .../weixin4j/http/SimpleHttpClient.java | 257 ++-- .../weixin4j/http/SimpleHttpResponse.java | 94 ++ .../com/foxinmy/weixin4j/http/StatusLine.java | 38 - .../foxinmy/weixin4j/http/URLParameter.java | 35 + .../weixin4j/http/UrlEncodeParameter.java | 39 - .../weixin4j/http/apache/FormBodyPart.java | 2 +- .../foxinmy/weixin4j/http/apache/Header.java | 2 +- .../weixin4j/http/apache/HttpHeaders.java | 206 --- .../weixin4j/http/apache/HttpMultipart.java | 2 +- .../weixin4j/http/entity/FormUrlEntity.java | 44 +- .../http/factory/HttpClientFactory.java | 76 + .../weixin4j/http/factory/HttpComponent3.java | 202 +++ .../http/factory/HttpComponent3Factory.java | 20 + .../http/factory/HttpComponent3Response.java | 90 ++ .../http/factory/HttpComponent4Factory.java | 34 + .../http/factory/HttpComponent4_1.java | 157 ++ .../factory/HttpComponent4_1Response.java | 90 ++ .../http/factory/HttpComponent4_2.java | 141 ++ .../factory/HttpComponent4_2Response.java | 94 ++ .../http/factory/SimpleHttpClientFactory.java | 21 + .../http/weixin/WeixinHttpClient.java | 214 --- .../http/weixin/WeixinRequestExecutor.java | 194 +++ .../weixin4j/http/weixin/WeixinResponse.java | 45 +- ...net.java => WeixinSSLRequestExecutor.java} | 110 +- .../foxinmy/weixin4j/http/weixin/error.xml | 4 + .../foxinmy/weixin4j/model/MediaRecord.java | 8 +- .../com/foxinmy/weixin4j/model/NameValue.java | 24 - .../com/foxinmy/weixin4j/payment/PayUtil.java | 10 +- .../weixin4j/payment/WeixinPayProxy.java | 6 +- .../com/foxinmy/weixin4j/tuple/Article.java | 4 +- .../java/com/foxinmy/weixin4j/tuple/Card.java | 2 +- .../java/com/foxinmy/weixin4j/tuple/File.java | 2 +- .../com/foxinmy/weixin4j/tuple/Image.java | 2 +- .../com/foxinmy/weixin4j/tuple/MpNews.java | 2 +- .../com/foxinmy/weixin4j/tuple/MpVideo.java | 2 +- .../com/foxinmy/weixin4j/tuple/Music.java | 12 +- .../com/foxinmy/weixin4j/tuple/Video.java | 6 +- .../com/foxinmy/weixin4j/tuple/Voice.java | 2 +- .../util/LinkedCaseInsensitiveMap.java | 164 +++ .../foxinmy/weixin4j/util/MultiValueMap.java | 75 + .../weixin4j/{http => util}/NameValue.java | 2 +- .../weixin4j/util/URLEncodingUtil.java | 152 ++ ...onfigUtil.java => Weixin4jConfigUtil.java} | 4 +- .../{ErrorUtil.java => WeixinErrorUtil.java} | 4 +- .../weixin4j/base/test/HttpClientTest.java | 78 + weixin4j-mp/pom.xml | 2 +- .../foxinmy/weixin4j/mp/api/CustomApi.java | 24 +- .../com/foxinmy/weixin4j/mp/api/DataApi.java | 2 +- .../com/foxinmy/weixin4j/mp/api/GroupApi.java | 14 +- .../foxinmy/weixin4j/mp/api/HelperApi.java | 10 +- .../com/foxinmy/weixin4j/mp/api/MassApi.java | 12 +- .../com/foxinmy/weixin4j/mp/api/MediaApi.java | 96 +- .../com/foxinmy/weixin4j/mp/api/MenuApi.java | 6 +- .../foxinmy/weixin4j/mp/api/NotifyApi.java | 2 +- .../com/foxinmy/weixin4j/mp/api/OauthApi.java | 12 +- .../com/foxinmy/weixin4j/mp/api/Pay2Api.java | 28 +- .../com/foxinmy/weixin4j/mp/api/QrApi.java | 15 +- .../com/foxinmy/weixin4j/mp/api/TmplApi.java | 6 +- .../com/foxinmy/weixin4j/mp/api/UserApi.java | 6 +- .../foxinmy/weixin4j/mp/api/weixin.properties | 7 +- .../weixin4j/mp/payment/v2/PayUtil2.java | 6 +- .../mp/token/WeixinJSTicketCreator.java | 6 +- .../weixin4j/mp/token/WeixinTokenCreator.java | 6 +- .../foxinmy/weixin4j/mp/test/MediaTest.java | 7 +- .../foxinmy/weixin4j/mp/test/MenuTest.java | 6 +- .../com/foxinmy/weixin4j/mp/test/PayTest.java | 4 +- .../foxinmy/weixin4j/mp/test/TokenTest.java | 9 +- weixin4j-qy/pom.xml | 2 +- .../foxinmy/weixin4j/qy/WeixinSuiteProxy.java | 8 +- .../com/foxinmy/weixin4j/qy/api/AgentApi.java | 6 +- .../com/foxinmy/weixin4j/qy/api/BatchApi.java | 6 +- .../com/foxinmy/weixin4j/qy/api/ChatApi.java | 14 +- .../foxinmy/weixin4j/qy/api/HelperApi.java | 2 +- .../com/foxinmy/weixin4j/qy/api/MediaApi.java | 81 +- .../com/foxinmy/weixin4j/qy/api/MenuApi.java | 6 +- .../foxinmy/weixin4j/qy/api/NotifyApi.java | 2 +- .../com/foxinmy/weixin4j/qy/api/OauthApi.java | 10 +- .../com/foxinmy/weixin4j/qy/api/PartyApi.java | 8 +- .../com/foxinmy/weixin4j/qy/api/QyApi.java | 4 +- .../com/foxinmy/weixin4j/qy/api/SuiteApi.java | 10 +- .../com/foxinmy/weixin4j/qy/api/TagApi.java | 12 +- .../com/foxinmy/weixin4j/qy/api/UserApi.java | 22 +- .../foxinmy/weixin4j/qy/api/weixin.properties | 4 +- .../qy/suite/WeixinSuitePreCodeCreator.java | 6 +- .../qy/suite/WeixinSuiteTokenCreator.java | 6 +- .../qy/suite/WeixinTokenSuiteCreator.java | 6 +- .../qy/token/WeixinJSTicketCreator.java | 6 +- .../qy/token/WeixinProviderTokenCreator.java | 6 +- .../weixin4j/qy/token/WeixinTokenCreator.java | 8 +- .../foxinmy/weixin4j/qy/test/MediaTest.java | 5 +- .../foxinmy/weixin4j/qy/test/TokenTest.java | 6 +- 120 files changed, 4042 insertions(+), 1541 deletions(-) create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpMessage.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpRequest.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Header.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpClientException.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpEntityRequest.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpGet.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpHeaders.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpPost.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpProtocolParams.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponseHandler.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ResponseHandler.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpResponse.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/StatusLine.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/URLParameter.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/UrlEncodeParameter.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpHeaders.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpClientFactory.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Factory.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Response.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4Factory.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1Response.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2Response.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/SimpleHttpClientFactory.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinHttpClient.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/{SSLHttpClinet.java => WeixinSSLRequestExecutor.java} (53%) delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/NameValue.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/LinkedCaseInsensitiveMap.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MultiValueMap.java rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/{http => util}/NameValue.java (91%) create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/URLEncodingUtil.java rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/{ConfigUtil.java => Weixin4jConfigUtil.java} (93%) rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/{ErrorUtil.java => WeixinErrorUtil.java} (97%) create mode 100644 weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/HttpClientTest.java diff --git a/CHANGE.md b/CHANGE.md index f4cc3a93..e1c3cc8d 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -446,3 +446,7 @@ + `release`: weixin4j-[mp|qy] upgrade to 1.5.3,weixin4j-server upgrade to 1.0.5 + **weixin4j-[mp|qy]**: 媒体接口类(MediaApi)查询素材接口调整:去掉offset,count替换为Pageable类 + +* 2015-08-18 + + + 比较大的改动:重构了HttpClient部分 diff --git a/README.md b/README.md index 18fdcdd2..bdd030af 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ weixin4j ###2.直接下载jar包 - * [weixin4j-mp-xx-full.jar](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.foxinmy%22%20AND%20a%3A%22weixin4j-mp%22) [weixin4j-qy-xx-full.jar](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.foxinmy%22%20AND%20a%3A%22weixin4j-qy%22) [weixin4j-server.jar](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.foxinmy%22%20AND%20a%3A%22weixin4j-server%22) + * [weixin4j-mp-xx-full.jar](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.foxinmy%22%20AND%20a%3A%22weixin4j-mp%22) [weixin4j-qy-xx-full.jar](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.foxinmy%22%20AND%20a%3A%22weixin4j-qy%22) [weixin4j-server-xx.jar](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.foxinmy%22%20AND%20a%3A%22weixin4j-server%22) * weixin4j-mp & weixin4j-qy 所需的依赖包: [fastjson1.2.x](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.alibaba%22%20AND%20a%3A%22fastjson%22) diff --git a/pom.xml b/pom.xml index b72490d4..980de94d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.foxinmy weixin4j - 1.5.3 + 1.5.4-SNAPSHOT pom weixin4j https://github.com/foxinmy/weixin4j diff --git a/weixin4j-base/pom.xml b/weixin4j-base/pom.xml index 01f923de..beb55685 100644 --- a/weixin4j-base/pom.xml +++ b/weixin4j-base/pom.xml @@ -5,7 +5,7 @@ com.foxinmy weixin4j - 1.5.3 + 1.5.4-SNAPSHOT weixin4j-base weixin4j-base @@ -17,5 +17,17 @@ fastjson 1.2.3 + + commons-httpclient + commons-httpclient + 3.1 + true + + + org.apache.httpcomponents + httpclient + 4.3 + true + \ No newline at end of file diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/BaseApi.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/BaseApi.java index 0db7ca32..11cb658b 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/BaseApi.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/BaseApi.java @@ -4,11 +4,11 @@ import java.util.ResourceBundle; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient; +import com.foxinmy.weixin4j.http.weixin.WeixinRequestExecutor; import com.foxinmy.weixin4j.model.WeixinAccount; import com.foxinmy.weixin4j.token.FileTokenStorager; import com.foxinmy.weixin4j.token.TokenStorager; -import com.foxinmy.weixin4j.util.ConfigUtil; +import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; import com.foxinmy.weixin4j.util.Weixin4jConst; /** @@ -23,12 +23,12 @@ import com.foxinmy.weixin4j.util.Weixin4jConst; */ public abstract class BaseApi { - protected final WeixinHttpClient weixinClient; + protected final WeixinRequestExecutor weixinExecutor; protected abstract ResourceBundle weixinBundle(); public BaseApi() { - this.weixinClient = new WeixinHttpClient(); + this.weixinExecutor = new WeixinRequestExecutor(); } protected String getRequestUri(String key) { @@ -57,8 +57,9 @@ public abstract class BaseApi { public final static TokenStorager DEFAULT_TOKEN_STORAGER; static { - DEFAULT_WEIXIN_ACCOUNT = ConfigUtil.getWeixinAccount(); - DEFAULT_TOKEN_STORAGER = new FileTokenStorager(ConfigUtil.getValue( - "token_path", Weixin4jConst.DEFAULT_TOKEN_PATH)); + DEFAULT_WEIXIN_ACCOUNT = Weixin4jConfigUtil.getWeixinAccount(); + DEFAULT_TOKEN_STORAGER = new FileTokenStorager( + Weixin4jConfigUtil.getValue("token_path", + Weixin4jConst.DEFAULT_TOKEN_PATH)); } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CashApi.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CashApi.java index cd76523b..813518b8 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CashApi.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CashApi.java @@ -9,8 +9,9 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.weixin.SSLHttpClinet; +import com.foxinmy.weixin4j.http.weixin.WeixinRequestExecutor; import com.foxinmy.weixin4j.http.weixin.WeixinResponse; +import com.foxinmy.weixin4j.http.weixin.WeixinSSLRequestExecutor; import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.payment.PayURLConsts; import com.foxinmy.weixin4j.payment.PayUtil; @@ -69,9 +70,10 @@ public class CashApi { String param = XmlStream.map2xml(obj); WeixinResponse response = null; try { - SSLHttpClinet request = new SSLHttpClinet(weixinAccount.getMchId(), - ca); - response = request.post(PayURLConsts.MCH_REDPACKSEND_URL, param); + WeixinRequestExecutor weixinExecutor = new WeixinSSLRequestExecutor( + weixinAccount.getMchId(), ca); + response = weixinExecutor.post(PayURLConsts.MCH_REDPACKSEND_URL, + param); } finally { if (ca != null) { try { @@ -111,9 +113,10 @@ public class CashApi { String param = XmlStream.map2xml(para); WeixinResponse response = null; try { - SSLHttpClinet request = new SSLHttpClinet(weixinAccount.getMchId(), - ca); - response = request.post(PayURLConsts.MCH_REDPACKQUERY_URL, param); + WeixinRequestExecutor weixinExecutor = new WeixinSSLRequestExecutor( + weixinAccount.getMchId(), ca); + response = weixinExecutor.post(PayURLConsts.MCH_REDPACKQUERY_URL, + param); } finally { if (ca != null) { try { @@ -154,9 +157,10 @@ public class CashApi { String param = XmlStream.map2xml(obj); WeixinResponse response = null; try { - SSLHttpClinet request = new SSLHttpClinet(weixinAccount.getMchId(), - ca); - response = request.post(PayURLConsts.MCH_ENPAYMENT_URL, param); + WeixinRequestExecutor weixinExecutor = new WeixinSSLRequestExecutor( + weixinAccount.getMchId(), ca); + response = weixinExecutor.post(PayURLConsts.MCH_ENPAYMENT_URL, + param); } finally { if (ca != null) { try { @@ -199,9 +203,10 @@ public class CashApi { String param = XmlStream.map2xml(obj); WeixinResponse response = null; try { - SSLHttpClinet request = new SSLHttpClinet(weixinAccount.getMchId(), - ca); - response = request.post(PayURLConsts.MCH_ENPAYQUERY_URL, param); + WeixinRequestExecutor weixinExecutor = new WeixinSSLRequestExecutor( + weixinAccount.getMchId(), ca); + response = weixinExecutor.post(PayURLConsts.MCH_ENPAYQUERY_URL, + param); } finally { if (ca != null) { try { diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CouponApi.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CouponApi.java index b5fe4aae..daeb91d2 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CouponApi.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/CouponApi.java @@ -7,9 +7,9 @@ import java.util.Map; import com.alibaba.fastjson.TypeReference; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.weixin.SSLHttpClinet; -import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient; +import com.foxinmy.weixin4j.http.weixin.WeixinRequestExecutor; import com.foxinmy.weixin4j.http.weixin.WeixinResponse; +import com.foxinmy.weixin4j.http.weixin.WeixinSSLRequestExecutor; import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.payment.PayURLConsts; import com.foxinmy.weixin4j.payment.PayUtil; @@ -31,13 +31,13 @@ import com.foxinmy.weixin4j.xml.XmlStream; */ public class CouponApi { - private final WeixinHttpClient weixinClient; + private final WeixinRequestExecutor weixinClient; private final WeixinPayAccount weixinAccount; public CouponApi(WeixinPayAccount weixinAccount) { this.weixinAccount = weixinAccount; - this.weixinClient = new WeixinHttpClient(); + this.weixinClient = new WeixinRequestExecutor(); } /** @@ -80,9 +80,10 @@ public class CouponApi { String param = XmlStream.map2xml(map); WeixinResponse response = null; try { - SSLHttpClinet request = new SSLHttpClinet(weixinAccount.getMchId(), - ca); - response = request.post(PayURLConsts.MCH_COUPONSEND_URL, param); + WeixinRequestExecutor weixinExecutor = new WeixinSSLRequestExecutor( + weixinAccount.getMchId(), ca); + response = weixinExecutor.post(PayURLConsts.MCH_COUPONSEND_URL, + param); } finally { if (ca != null) { try { diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/Pay3Api.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/Pay3Api.java index 4865f2ab..d1c1bbb6 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/Pay3Api.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/Pay3Api.java @@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.api; import java.io.BufferedReader; import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -19,9 +18,9 @@ import java.util.Map; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.weixin.SSLHttpClinet; -import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient; +import com.foxinmy.weixin4j.http.weixin.WeixinRequestExecutor; import com.foxinmy.weixin4j.http.weixin.WeixinResponse; +import com.foxinmy.weixin4j.http.weixin.WeixinSSLRequestExecutor; import com.foxinmy.weixin4j.http.weixin.XmlResult; import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.model.WeixinPayAccount; @@ -36,10 +35,10 @@ import com.foxinmy.weixin4j.type.BillType; import com.foxinmy.weixin4j.type.CurrencyType; import com.foxinmy.weixin4j.type.IdQuery; import com.foxinmy.weixin4j.type.IdType; -import com.foxinmy.weixin4j.util.ConfigUtil; import com.foxinmy.weixin4j.util.DateUtil; import com.foxinmy.weixin4j.util.RandomUtil; import com.foxinmy.weixin4j.util.StringUtil; +import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; import com.foxinmy.weixin4j.xml.ListsuffixResultDeserializer; import com.foxinmy.weixin4j.xml.XmlStream; @@ -54,13 +53,13 @@ import com.foxinmy.weixin4j.xml.XmlStream; */ public class Pay3Api { - private final WeixinHttpClient weixinClient; + private final WeixinRequestExecutor weixinClient; private final WeixinPayAccount weixinAccount; public Pay3Api(WeixinPayAccount weixinAccount) { this.weixinAccount = weixinAccount; - this.weixinClient = new WeixinHttpClient(); + this.weixinClient = new WeixinRequestExecutor(); } /** @@ -144,9 +143,10 @@ public class Pay3Api { .paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); - SSLHttpClinet request = new SSLHttpClinet(weixinAccount.getMchId(), - ca); - response = request.post(PayURLConsts.MCH_REFUNDAPPLY_URL, param); + WeixinRequestExecutor weixinExecutor = new WeixinSSLRequestExecutor( + weixinAccount.getMchId(), ca); + response = weixinExecutor.post(PayURLConsts.MCH_REFUNDAPPLY_URL, + param); } finally { if (ca != null) { try { @@ -210,14 +210,14 @@ public class Pay3Api { public ApiResult reverseOrder(InputStream ca, IdQuery idQuery) throws WeixinException { try { - SSLHttpClinet request = new SSLHttpClinet(weixinAccount.getMchId(), - ca); + WeixinRequestExecutor weixinExecutor = new WeixinSSLRequestExecutor( + weixinAccount.getMchId(), ca); Map map = baseMap(idQuery); String sign = PayUtil .paysignMd5(map, weixinAccount.getPaySignKey()); map.put("sign", sign); String param = XmlStream.map2xml(map); - WeixinResponse response = request.post( + WeixinResponse response = weixinExecutor.post( PayURLConsts.MCH_ORDERREVERSE_URL, param); return response.getAsObject(new TypeReference() { }); @@ -315,7 +315,7 @@ public class Pay3Api { billType = BillType.ALL; } String formatBillDate = DateUtil.fortmat2yyyyMMdd(billDate); - String bill_path = ConfigUtil.getValue("bill_path"); + String bill_path = Weixin4jConfigUtil.getValue("bill_path"); String fileName = String.format("%s_%s_%s.txt", formatBillDate, billType.name().toLowerCase(), weixinAccount.getId()); File file = new File(String.format("%s/%s", bill_path, fileName)); @@ -337,8 +337,7 @@ public class Pay3Api { writer = new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file), Consts.GBK)); reader = new BufferedReader(new InputStreamReader( - new ByteArrayInputStream(response.getContent()), - com.foxinmy.weixin4j.model.Consts.GBK)); + response.getBody(), com.foxinmy.weixin4j.model.Consts.GBK)); String line = null; while ((line = reader.readLine()) != null) { writer.write(line); diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java index f05ccb30..1ad6c619 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/exception/WeixinException.java @@ -1,5 +1,9 @@ package com.foxinmy.weixin4j.exception; +import java.io.IOException; + +import com.foxinmy.weixin4j.util.StringUtil; + /** * 调用微信接口抛出的异常 * @@ -30,6 +34,10 @@ public class WeixinException extends Exception { super(e); } + public WeixinException(String msg, IOException ex) { + super(msg, ex); + } + public String getErrorCode() { return errorCode; } @@ -40,6 +48,16 @@ public class WeixinException extends Exception { @Override public String getMessage() { - return this.errorCode + "," + this.errorMsg; + StringBuilder buf = new StringBuilder(); + if (StringUtil.isNotBlank(errorCode)) { + buf.append(errorCode); + } + if (StringUtil.isNotBlank(errorMsg)) { + buf.append(" ").append(errorMsg); + } + if (buf.length() == 0) { + return super.getMessage(); + } + return buf.toString(); } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java new file mode 100644 index 00000000..48cebf0d --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java @@ -0,0 +1,109 @@ +package com.foxinmy.weixin4j.http; + +import java.util.Arrays; +import java.util.Set; + +import com.foxinmy.weixin4j.http.entity.FormUrlEntity; +import com.foxinmy.weixin4j.http.entity.HttpEntity; + +public abstract class AbstractHttpClient implements HttpClient { + + @Override + public HttpResponse get(String url) throws HttpClientException { + return execute(HttpMethod.GET, url); + } + + @Override + public HttpResponse get(String url, URLParameter... parameters) + throws HttpClientException { + return execute(HttpMethod.GET, url, parameters); + } + + @Override + public HttpHeaders head(String url) throws HttpClientException { + return head(url, (URLParameter[]) null); + } + + @Override + public HttpHeaders head(String url, URLParameter... parameters) + throws HttpClientException { + return execute(HttpMethod.HEAD, url, parameters).getHeaders(); + } + + @Override + public HttpResponse post(String url) throws HttpClientException { + return execute(HttpMethod.POST, url); + } + + @Override + public HttpResponse post(String url, URLParameter... parameters) + throws HttpClientException { + HttpEntity entity = null; + if (parameters != null && parameters.length > 0) { + entity = new FormUrlEntity(Arrays.asList(parameters)); + } + return post(url, entity); + } + + @Override + public HttpResponse post(String url, HttpEntity entity) + throws HttpClientException { + HttpRequest request = new HttpRequest(HttpMethod.POST, url); + request.setEntity(entity); + return execute(request); + } + + @Override + public void put(String url) throws HttpClientException { + execute(HttpMethod.PUT, url); + } + + @Override + public void put(String url, URLParameter... parameters) + throws HttpClientException { + execute(HttpMethod.PUT, url, parameters); + } + + @Override + public void delete(String url) throws HttpClientException { + execute(HttpMethod.DELETE, url); + } + + @Override + public void delete(String url, URLParameter... parameters) + throws HttpClientException { + execute(HttpMethod.DELETE, url, parameters); + } + + @Override + public Set options(String url) throws HttpClientException { + return options(url, (URLParameter[]) null); + } + + @Override + public Set options(String url, URLParameter... parameters) + throws HttpClientException { + HttpHeaders headers = execute(HttpMethod.OPTIONS, url, parameters) + .getHeaders(); + return headers.getAllow(); + } + + protected HttpResponse execute(HttpMethod method, String url) + throws HttpClientException { + return execute(new HttpRequest(method, url)); + } + + protected HttpResponse execute(HttpMethod method, String url, + URLParameter... parameters) throws HttpClientException { + StringBuilder buf = new StringBuilder(url); + if (parameters != null && parameters.length > 0) { + if (url.indexOf("?") < 0) { + buf.append("?"); + } else { + buf.append("&"); + } + buf.append(FormUrlEntity.formatParameters(Arrays.asList(parameters))); + } + return execute(new HttpRequest(method, buf.toString())); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpMessage.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpMessage.java deleted file mode 100644 index 1076c4a0..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpMessage.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.util.ArrayList; -import java.util.List; - -/** - * reference of apache pivot - * - * @className AbstractHttpMessage - * @author jy - * @date 2015年5月29日 - * @since JDK 1.7 - * @see - */ -public abstract class AbstractHttpMessage implements HttpMessage { - - protected final List
headers; - - public AbstractHttpMessage() { - this.headers = new ArrayList
(16); - } - - @Override - public Header[] getAllHeaders() { - return headers.toArray(new Header[headers.size()]); - } - - @Override - public boolean containsHeader(String name) { - for (int i = 0; i < headers.size(); i++) { - Header header = headers.get(i); - if (name.equalsIgnoreCase(header.getName())) { - return true; - } - } - return false; - } - - @Override - public void setHeaders(Header... headers) { - this.headers.clear(); - if (headers == null) { - return; - } - for (int i = 0; i < headers.length; i++) { - this.headers.add(headers[i]); - } - } - - @Override - public Header[] getHeaders(String name) { - List
headersFound = new ArrayList
(); - for (int i = 0; i < headers.size(); i++) { - Header header = headers.get(i); - if (name.equalsIgnoreCase(header.getName())) { - headersFound.add(header); - } - } - return headersFound.toArray(new Header[headersFound.size()]); - } - - @Override - public Header getFirstHeader(String name) { - for (int i = 0; i < headers.size(); i++) { - Header header = headers.get(i); - if (name.equalsIgnoreCase(header.getName())) { - return header; - } - } - return null; - } - - @Override - public Header getLastHeader(String name) { - // start at the end of the list and work backwards - for (int i = headers.size() - 1; i >= 0; i--) { - Header header = headers.get(i); - if (name.equalsIgnoreCase(header.getName())) { - return header; - } - } - return null; - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpRequest.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpRequest.java deleted file mode 100644 index 7ba3633b..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpRequest.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.util.ArrayList; -import java.util.List; - -/** - * reference of apache pivot - * - * @className AbstractHttpRequest - * @author jy - * @date 2015年5月29日 - * @since JDK 1.7 - * @see - */ -public abstract class AbstractHttpRequest extends AbstractHttpMessage implements - HttpRequest { - - @Override - public HttpVersion getProtocolVersion() { - return null; - } - - @Override - public HttpParams getParams() { - return new HttpParams(); - } - - @Override - public void addHeader(Header header) { - super.headers.add(header); - } - - @Override - public boolean removeHeader(String name) { - List
headersFound = new ArrayList
(); - for (int i = 0; i < headers.size(); i++) { - Header header = headers.get(i); - if (header.getName().equalsIgnoreCase(name)) { - headersFound.add(header); - } - } - return super.headers.removeAll(headersFound); - } - - @Override - public boolean removeHeader(Header header) { - return super.headers.remove(header); - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java index 1e1355ee..df3f2a65 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java @@ -3,6 +3,7 @@ package com.foxinmy.weixin4j.http; import java.io.Serializable; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; +import java.util.List; import java.util.Locale; import com.foxinmy.weixin4j.model.Consts; @@ -80,6 +81,17 @@ public final class ContentType implements Serializable { return buf.toString(); } + public static String toString(List contentTypes) { + if (contentTypes == null || contentTypes.isEmpty()) { + return null; + } + StringBuilder buf = new StringBuilder(); + for (ContentType contentType : contentTypes) { + buf.append(contentType.toString()).append(","); + } + return buf.delete(buf.length() - 1, buf.length()).toString(); + } + private static boolean valid(final String s) { for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Header.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Header.java deleted file mode 100644 index 06e96d24..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Header.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.foxinmy.weixin4j.http; - - -/** - * header like key-value - * @className Header - * @author jy - * @date 2015年5月29日 - * @since JDK 1.7 - * @see - */ -public class Header extends NameValue { - - private static final long serialVersionUID = -9029136315985402716L; - - public Header(String name, String value) { - super(name, value); - } - - @Override - public String toString() { - return "Header [name=" + getName() + ", value=" + getValue() + "]"; - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpClient.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpClient.java index 158371cb..79c1900b 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpClient.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpClient.java @@ -1,6 +1,8 @@ package com.foxinmy.weixin4j.http; -import java.io.IOException; +import java.util.Set; + +import com.foxinmy.weixin4j.http.entity.HttpEntity; /** * HTTP 接口 @@ -13,26 +15,51 @@ import java.io.IOException; */ public interface HttpClient { - /** - * 处理请求 - * - * @param request - * 请求 - * @return 响应 - * @throws IOException - */ - HttpResponse execute(HttpRequest request) throws IOException; + // get + HttpResponse get(String url) throws HttpClientException; + + HttpResponse get(String url, URLParameter... parameters) + throws HttpClientException; + + // head + HttpHeaders head(String url) throws HttpClientException; + + HttpHeaders head(String url, URLParameter... parameters) + throws HttpClientException; + + // post + HttpResponse post(String url) throws HttpClientException; + + HttpResponse post(String url, URLParameter... parameters) + throws HttpClientException; + + HttpResponse post(String url, HttpEntity httpEntity) + throws HttpClientException; + + // put + void put(String url) throws HttpClientException; + + void put(String url, URLParameter... parameters) throws HttpClientException; + + // delete + void delete(String url) throws HttpClientException; + + void delete(String url, URLParameter... parameters) + throws HttpClientException; + + // OPTIONS + Set options(String url) throws HttpClientException; + + Set options(String url, URLParameter... parameters) + throws HttpClientException; /** * 处理请求 * * @param request - * 请求 - * @param handler - * 处理器 - * @return 泛型处理结果 - * @throws IOException + * 请求对象 + * @return 响应对象 + * @throws HttpClientException */ - T execute(HttpRequest request, ResponseHandler handler) - throws IOException; + HttpResponse execute(HttpRequest request) throws HttpClientException; } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpClientException.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpClientException.java new file mode 100644 index 00000000..77139a77 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpClientException.java @@ -0,0 +1,31 @@ +package com.foxinmy.weixin4j.http; + + +public class HttpClientException extends Exception { + + private static final long serialVersionUID = -1760373205801759702L; + + /** + * Construct a new instance of {@code HttpClientException} with the given + * message. + * + * @param msg + * the message + */ + public HttpClientException(String msg) { + super(msg); + } + + /** + * Construct a new instance of {@code HttpClientException} with the given + * message and exception. + * + * @param msg + * the message + * @param ex + * the exception + */ + public HttpClientException(String msg, Throwable ex) { + super(msg, ex); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpEntityRequest.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpEntityRequest.java deleted file mode 100644 index 3a2da49c..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpEntityRequest.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import com.foxinmy.weixin4j.http.entity.HttpEntity; - -/** - * request with entity - * - * @className HttpEntityRequest - * @author jy - * @date 2015年5月29日 - * @since JDK 1.7 - * @see - */ -public abstract class HttpEntityRequest extends AbstractHttpRequest { - public abstract void setEntity(HttpEntity entity); - - public abstract HttpEntity getEntity(); -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpGet.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpGet.java deleted file mode 100644 index 72afadac..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpGet.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.net.URI; - -/** - * GET 请求 - * @className HttpGet - * @author jy - * @date 2015年5月29日 - * @since JDK 1.7 - * @see - */ -public class HttpGet extends AbstractHttpRequest { - - private final URI uri; - - public HttpGet(final URI uri) { - this.uri = uri; - } - - public HttpGet(final String uri) { - this(URI.create(uri)); - } - - @Override - public URI getURI() { - return this.uri; - } - - @Override - public HttpMethod getMethod() { - return HttpMethod.GET; - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpHeaders.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpHeaders.java new file mode 100644 index 00000000..5b11f94b --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpHeaders.java @@ -0,0 +1,1273 @@ +/* + * Copyright 2002-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.foxinmy.weixin4j.http; + +import java.io.Serializable; +import java.net.URI; +import java.nio.charset.Charset; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TimeZone; + +import com.foxinmy.weixin4j.util.LinkedCaseInsensitiveMap; +import com.foxinmy.weixin4j.util.MultiValueMap; +import com.foxinmy.weixin4j.util.StringUtil; + +/** + * Represents HTTP request and response headers, mapping string header names to + * list of string values. + * + *

+ * In addition to the normal methods defined by {@link Map}, this class offers + * the following convenience methods: + *

    + *
  • {@link #getFirst(String)} returns the first value associated with a given + * header name
  • + *
  • {@link #add(String, String)} adds a header value to the list of values + * for a header name
  • + *
  • {@link #set(String, String)} sets the header value to a single string + * value
  • + *
+ * + *

+ * + * @author Arjen Poutsma + * @author Sebastien Deleuze + * @since 3.0 + */ +public class HttpHeaders implements MultiValueMap, Serializable { + + private static final long serialVersionUID = -8578554704772377436L; + + /** + * The HTTP {@code Accept} header field name. + * + * @see Section + * 5.3.2 of RFC 7231 + */ + public static final String ACCEPT = "Accept"; + /** + * The HTTP {@code Accept-Charset} header field name. + * + * @see Section + * 5.3.3 of RFC 7231 + */ + public static final String ACCEPT_CHARSET = "Accept-Charset"; + /** + * The HTTP {@code Accept-Encoding} header field name. + * + * @see Section + * 5.3.4 of RFC 7231 + */ + public static final String ACCEPT_ENCODING = "Accept-Encoding"; + /** + * The HTTP {@code Accept-Language} header field name. + * + * @see Section + * 5.3.5 of RFC 7231 + */ + public static final String ACCEPT_LANGUAGE = "Accept-Language"; + /** + * The HTTP {@code Accept-Ranges} header field name. + * + * @see Section + * 5.3.5 of RFC 7233 + */ + public static final String ACCEPT_RANGES = "Accept-Ranges"; + /** + * The CORS {@code Access-Control-Allow-Credentials} response header field + * name. + * + * @see CORS W3C recommandation + */ + public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; + /** + * The CORS {@code Access-Control-Allow-Headers} response header field name. + * + * @see CORS W3C recommandation + */ + public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers"; + /** + * The CORS {@code Access-Control-Allow-Methods} response header field name. + * + * @see CORS W3C recommandation + */ + public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods"; + /** + * The CORS {@code Access-Control-Allow-Origin} response header field name. + * + * @see CORS W3C recommandation + */ + public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin"; + /** + * The CORS {@code Access-Control-Expose-Headers} response header field + * name. + * + * @see CORS W3C recommandation + */ + public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"; + /** + * The CORS {@code Access-Control-Max-Age} response header field name. + * + * @see CORS W3C recommandation + */ + public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age"; + /** + * The CORS {@code Access-Control-Request-Headers} request header field + * name. + * + * @see CORS W3C recommandation + */ + public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers"; + /** + * The CORS {@code Access-Control-Request-Method} request header field name. + * + * @see CORS W3C recommandation + */ + public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method"; + /** + * The HTTP {@code Age} header field name. + * + * @see Section 5.1 + * of RFC 7234 + */ + public static final String AGE = "Age"; + /** + * The HTTP {@code Allow} header field name. + * + * @see Section + * 7.4.1 of RFC 7231 + */ + public static final String ALLOW = "Allow"; + /** + * The HTTP {@code Authorization} header field name. + * + * @see Section 4.2 + * of RFC 7235 + */ + public static final String AUTHORIZATION = "Authorization"; + /** + * The HTTP {@code Cache-Control} header field name. + * + * @see Section 5.2 + * of RFC 7234 + */ + public static final String CACHE_CONTROL = "Cache-Control"; + /** + * The HTTP {@code Connection} header field name. + * + * @see Section 6.1 + * of RFC 7230 + */ + public static final String CONNECTION = "Connection"; + /** + * The HTTP {@code Content-Encoding} header field name. + * + * @see Section + * 3.1.2.2 of RFC 7231 + */ + public static final String CONTENT_ENCODING = "Content-Encoding"; + /** + * The HTTP {@code Content-Disposition} header field name + * + * @see RFC 6266 + */ + public static final String CONTENT_DISPOSITION = "Content-Disposition"; + /** + * The HTTP {@code Content-Language} header field name. + * + * @see Section + * 3.1.3.2 of RFC 7231 + */ + public static final String CONTENT_LANGUAGE = "Content-Language"; + /** + * The HTTP {@code Content-Length} header field name. + * + * @see Section + * 3.3.2 of RFC 7230 + */ + public static final String CONTENT_LENGTH = "Content-Length"; + /** + * The HTTP {@code Content-Location} header field name. + * + * @see Section + * 3.1.4.2 of RFC 7231 + */ + public static final String CONTENT_LOCATION = "Content-Location"; + /** + * The HTTP {@code Content-Range} header field name. + * + * @see Section 4.2 + * of RFC 7233 + */ + public static final String CONTENT_RANGE = "Content-Range"; + /** + * The HTTP {@code Content-Type} header field name. + * + * @see Section + * 3.1.1.5 of RFC 7231 + */ + public static final String CONTENT_TYPE = "Content-Type"; + /** + * The HTTP {@code Cookie} header field name. + * + * @see Section + * 4.3.4 of RFC 2109 + */ + public static final String COOKIE = "Cookie"; + /** + * The HTTP {@code Date} header field name. + * + * @see Section + * 7.1.1.2 of RFC 7231 + */ + public static final String DATE = "Date"; + /** + * The HTTP {@code ETag} header field name. + * + * @see Section 2.3 + * of RFC 7232 + */ + public static final String ETAG = "ETag"; + /** + * The HTTP {@code Expect} header field name. + * + * @see Section + * 5.1.1 of RFC 7231 + */ + public static final String EXPECT = "Expect"; + /** + * The HTTP {@code Expires} header field name. + * + * @see Section 5.3 + * of RFC 7234 + */ + public static final String EXPIRES = "Expires"; + /** + * The HTTP {@code From} header field name. + * + * @see Section + * 5.5.1 of RFC 7231 + */ + public static final String FROM = "From"; + /** + * The HTTP {@code Host} header field name. + * + * @see Section 5.4 + * of RFC 7230 + */ + public static final String HOST = "Host"; + /** + * The HTTP {@code If-Match} header field name. + * + * @see Section 3.1 + * of RFC 7232 + */ + public static final String IF_MATCH = "If-Match"; + /** + * The HTTP {@code If-Modified-Since} header field name. + * + * @see Section 3.3 + * of RFC 7232 + */ + public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + /** + * The HTTP {@code If-None-Match} header field name. + * + * @see Section 3.2 + * of RFC 7232 + */ + public static final String IF_NONE_MATCH = "If-None-Match"; + /** + * The HTTP {@code If-Range} header field name. + * + * @see Section 3.2 + * of RFC 7233 + */ + public static final String IF_RANGE = "If-Range"; + /** + * The HTTP {@code If-Unmodified-Since} header field name. + * + * @see Section 3.4 + * of RFC 7232 + */ + public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + /** + * The HTTP {@code Last-Modified} header field name. + * + * @see Section 2.2 + * of RFC 7232 + */ + public static final String LAST_MODIFIED = "Last-Modified"; + /** + * The HTTP {@code Link} header field name. + * + * @see RFC 5988 + */ + public static final String LINK = "Link"; + /** + * The HTTP {@code Location} header field name. + * + * @see Section + * 7.1.2 of RFC 7231 + */ + public static final String LOCATION = "Location"; + /** + * The HTTP {@code Max-Forwards} header field name. + * + * @see Section + * 5.1.2 of RFC 7231 + */ + public static final String MAX_FORWARDS = "Max-Forwards"; + /** + * The HTTP {@code Origin} header field name. + * + * @see RFC 6454 + */ + public static final String ORIGIN = "Origin"; + /** + * The HTTP {@code Pragma} header field name. + * + * @see Section 5.4 + * of RFC 7234 + */ + public static final String PRAGMA = "Pragma"; + /** + * The HTTP {@code Proxy-Authenticate} header field name. + * + * @see Section 4.3 + * of RFC 7235 + */ + public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; + /** + * The HTTP {@code Proxy-Authorization} header field name. + * + * @see Section 4.4 + * of RFC 7235 + */ + public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; + /** + * The HTTP {@code Range} header field name. + * + * @see Section 3.1 + * of RFC 7233 + */ + public static final String RANGE = "Range"; + /** + * The HTTP {@code Referer} header field name. + * + * @see Section + * 5.5.2 of RFC 7231 + */ + public static final String REFERER = "Referer"; + /** + * The HTTP {@code Retry-After} header field name. + * + * @see Section + * 7.1.3 of RFC 7231 + */ + public static final String RETRY_AFTER = "Retry-After"; + /** + * The HTTP {@code Server} header field name. + * + * @see Section + * 7.4.2 of RFC 7231 + */ + public static final String SERVER = "Server"; + /** + * The HTTP {@code Set-Cookie} header field name. + * + * @see Section + * 4.2.2 of RFC 2109 + */ + public static final String SET_COOKIE = "Set-Cookie"; + /** + * The HTTP {@code Set-Cookie2} header field name. + * + * @see RFC 2965 + */ + public static final String SET_COOKIE2 = "Set-Cookie2"; + /** + * The HTTP {@code TE} header field name. + * + * @see Section 4.3 + * of RFC 7230 + */ + public static final String TE = "TE"; + /** + * The HTTP {@code Trailer} header field name. + * + * @see Section 4.4 + * of RFC 7230 + */ + public static final String TRAILER = "Trailer"; + /** + * The HTTP {@code Transfer-Encoding} header field name. + * + * @see Section + * 3.3.1 of RFC 7230 + */ + public static final String TRANSFER_ENCODING = "Transfer-Encoding"; + /** + * The HTTP {@code Upgrade} header field name. + * + * @see Section 6.7 + * of RFC 7230 + */ + public static final String UPGRADE = "Upgrade"; + /** + * The HTTP {@code User-Agent} header field name. + * + * @see Section + * 5.5.3 of RFC 7231 + */ + public static final String USER_AGENT = "User-Agent"; + /** + * The HTTP {@code Vary} header field name. + * + * @see Section + * 7.1.4 of RFC 7231 + */ + public static final String VARY = "Vary"; + /** + * The HTTP {@code Via} header field name. + * + * @see Section + * 5.7.1 of RFC 7230 + */ + public static final String VIA = "Via"; + /** + * The HTTP {@code Warning} header field name. + * + * @see Section 5.5 + * of RFC 7234 + */ + public static final String WARNING = "Warning"; + /** + * The HTTP {@code WWW-Authenticate} header field name. + * + * @see Section 4.1 + * of RFC 7235 + */ + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + + /** + * Date formats as specified in the HTTP RFC + * + * @see Section + * 7.1.1.1 of RFC 7231 + */ + private static final String[] DATE_FORMATS = new String[] { + "EEE, dd MMM yyyy HH:mm:ss zzz", "EEE, dd-MMM-yy HH:mm:ss zzz", + "EEE MMM dd HH:mm:ss yyyy" }; + + private static TimeZone GMT = TimeZone.getTimeZone("GMT"); + + private final Map> headers; + + /** + * Constructs a new, empty instance of the {@code HttpHeaders} object. + */ + public HttpHeaders() { + this(new LinkedCaseInsensitiveMap>(8, Locale.ENGLISH), + false); + } + + /** + * Private constructor that can create read-only {@code HttpHeader} + * instances. + */ + private HttpHeaders(Map> headers, boolean readOnly) { + if (readOnly) { + Map> map = new LinkedCaseInsensitiveMap>( + headers.size(), Locale.ENGLISH); + for (Entry> entry : headers.entrySet()) { + List values = Collections.unmodifiableList(entry + .getValue()); + map.put(entry.getKey(), values); + } + this.headers = Collections.unmodifiableMap(map); + } else { + this.headers = headers; + } + } + + /** + * Set the list of acceptable {@linkplain MediaType media types}, as + * specified by the {@code Accept} header. + */ + public void setAccept(List acceptableMediaTypes) { + set(ACCEPT, ContentType.toString(acceptableMediaTypes)); + } + + /** + * Return the list of acceptable {@linkplain MediaType media types}, as + * specified by the {@code Accept} header. + *

+ * Returns an empty list when the acceptable media types are unspecified. + */ + public String getAccept() { + return getFirst(ACCEPT); + } + + /** + * Set the (new) value of the {@code Access-Control-Allow-Credentials} + * response header. + */ + public void setAccessControlAllowCredentials(boolean allowCredentials) { + set(ACCESS_CONTROL_ALLOW_CREDENTIALS, + Boolean.toString(allowCredentials)); + } + + /** + * Returns the value of the {@code Access-Control-Allow-Credentials} + * response header. + */ + public boolean getAccessControlAllowCredentials() { + return new Boolean(getFirst(ACCESS_CONTROL_ALLOW_CREDENTIALS)); + } + + /** + * Set the (new) value of the {@code Access-Control-Allow-Headers} response + * header. + */ + public void setAccessControlAllowHeaders(List allowedHeaders) { + set(ACCESS_CONTROL_ALLOW_HEADERS, + toCommaDelimitedString(allowedHeaders)); + } + + /** + * Returns the value of the {@code Access-Control-Allow-Headers} response + * header. + */ + public List getAccessControlAllowHeaders() { + return getFirstValueAsList(ACCESS_CONTROL_ALLOW_HEADERS); + } + + /** + * Set the (new) value of the {@code Access-Control-Allow-Methods} response + * header. + */ + public void setAccessControlAllowMethods(List allowedMethods) { + set(ACCESS_CONTROL_ALLOW_METHODS, StringUtil.join(allowedMethods, ',')); + } + + /** + * Returns the value of the {@code Access-Control-Allow-Methods} response + * header. + */ + public List getAccessControlAllowMethods() { + List result = new ArrayList(); + String value = getFirst(ACCESS_CONTROL_ALLOW_METHODS); + if (value != null) { + String[] tokens = value.split(",\\s*"); + for (String token : tokens) { + result.add(HttpMethod.valueOf(token)); + } + } + return result; + } + + /** + * Set the (new) value of the {@code Access-Control-Allow-Origin} response + * header. + */ + public void setAccessControlAllowOrigin(String allowedOrigin) { + set(ACCESS_CONTROL_ALLOW_ORIGIN, allowedOrigin); + } + + /** + * Returns the value of the {@code Access-Control-Allow-Origin} response + * header. + */ + public String getAccessControlAllowOrigin() { + return getFirst(ACCESS_CONTROL_ALLOW_ORIGIN); + } + + /** + * Set the (new) value of the {@code Access-Control-Expose-Headers} response + * header. + */ + public void setAccessControlExposeHeaders(List exposedHeaders) { + set(ACCESS_CONTROL_EXPOSE_HEADERS, + toCommaDelimitedString(exposedHeaders)); + } + + /** + * Returns the value of the {@code Access-Control-Expose-Headers} response + * header. + */ + public List getAccessControlExposeHeaders() { + return getFirstValueAsList(ACCESS_CONTROL_EXPOSE_HEADERS); + } + + /** + * Set the (new) value of the {@code Access-Control-Max-Age} response + * header. + */ + public void setAccessControlMaxAge(long maxAge) { + set(ACCESS_CONTROL_MAX_AGE, Long.toString(maxAge)); + } + + /** + * Returns the value of the {@code Access-Control-Max-Age} response header. + *

+ * Returns -1 when the max age is unknown. + */ + public long getAccessControlMaxAge() { + String value = getFirst(ACCESS_CONTROL_MAX_AGE); + return (value != null ? Long.parseLong(value) : -1); + } + + /** + * Set the (new) value of the {@code Access-Control-Request-Headers} request + * header. + */ + public void setAccessControlRequestHeaders(List requestHeaders) { + set(ACCESS_CONTROL_REQUEST_HEADERS, + toCommaDelimitedString(requestHeaders)); + } + + /** + * Returns the value of the {@code Access-Control-Request-Headers} request + * header. + */ + public List getAccessControlRequestHeaders() { + return getFirstValueAsList(ACCESS_CONTROL_REQUEST_HEADERS); + } + + /** + * Set the (new) value of the {@code Access-Control-Request-Method} request + * header. + */ + public void setAccessControlRequestMethod(HttpMethod requestedMethod) { + set(ACCESS_CONTROL_REQUEST_METHOD, requestedMethod.name()); + } + + /** + * Returns the value of the {@code Access-Control-Request-Method} request + * header. + */ + public HttpMethod getAccessControlRequestMethod() { + String value = getFirst(ACCESS_CONTROL_REQUEST_METHOD); + return (value != null ? HttpMethod.valueOf(value) : null); + } + + /** + * Set the list of acceptable {@linkplain Charset charsets}, as specified by + * the {@code Accept-Charset} header. + */ + public void setAcceptCharset(List acceptableCharsets) { + StringBuilder builder = new StringBuilder(); + for (Iterator iterator = acceptableCharsets.iterator(); iterator + .hasNext();) { + Charset charset = iterator.next(); + builder.append(charset.name().toLowerCase(Locale.ENGLISH)); + if (iterator.hasNext()) { + builder.append(", "); + } + } + set(ACCEPT_CHARSET, builder.toString()); + } + + /** + * Return the list of acceptable {@linkplain Charset charsets}, as specified + * by the {@code Accept-Charset} header. + */ + public List getAcceptCharset() { + List result = new ArrayList(); + String value = getFirst(ACCEPT_CHARSET); + if (value != null) { + String[] tokens = value.split(",\\s*"); + for (String token : tokens) { + int paramIdx = token.indexOf(';'); + String charsetName; + if (paramIdx == -1) { + charsetName = token; + } else { + charsetName = token.substring(0, paramIdx); + } + if (!charsetName.equals("*")) { + result.add(Charset.forName(charsetName)); + } + } + } + return result; + } + + /** + * Set the set of allowed {@link HttpMethod HTTP methods}, as specified by + * the {@code Allow} header. + */ + public void setAllow(Set allowedMethods) { + set(ALLOW, StringUtil.join(allowedMethods, ',')); + } + + /** + * Return the set of allowed {@link HttpMethod HTTP methods}, as specified + * by the {@code Allow} header. + *

+ * Returns an empty set when the allowed methods are unspecified. + */ + public Set getAllow() { + String value = getFirst(ALLOW); + if (StringUtil.isNotBlank(value)) { + List allowedMethod = new ArrayList(5); + String[] tokens = value.split(",\\s*"); + for (String token : tokens) { + allowedMethod.add(HttpMethod.valueOf(token)); + } + return EnumSet.copyOf(allowedMethod); + } else { + return EnumSet.noneOf(HttpMethod.class); + } + } + + /** + * Set the (new) value of the {@code Cache-Control} header. + */ + public void setCacheControl(String cacheControl) { + set(CACHE_CONTROL, cacheControl); + } + + /** + * Returns the value of the {@code Cache-Control} header. + */ + public String getCacheControl() { + return getFirst(CACHE_CONTROL); + } + + /** + * Set the (new) value of the {@code Connection} header. + */ + public void setConnection(String connection) { + set(CONNECTION, connection); + } + + /** + * Set the (new) value of the {@code Connection} header. + */ + public void setConnection(List connection) { + set(CONNECTION, toCommaDelimitedString(connection)); + } + + /** + * Returns the value of the {@code Connection} header. + */ + public List getConnection() { + return getFirstValueAsList(CONNECTION); + } + + /** + * Set the (new) value of the {@code Content-Disposition} header for + * {@code form-data}. + * + * @param name + * the control name + * @param filename + * the filename (may be {@code null}) + */ + public void setContentDispositionFormData(String name, String filename) { + StringBuilder builder = new StringBuilder("form-data; name=\""); + builder.append(name).append('\"'); + if (filename != null) { + builder.append("; filename=\""); + builder.append(filename).append('\"'); + } + set(CONTENT_DISPOSITION, builder.toString()); + } + + /** + * Set the length of the body in bytes, as specified by the + * {@code Content-Length} header. + */ + public void setContentLength(long contentLength) { + set(CONTENT_LENGTH, Long.toString(contentLength)); + } + + /** + * Return the length of the body in bytes, as specified by the + * {@code Content-Length} header. + *

+ * Returns -1 when the content-length is unknown. + */ + public long getContentLength() { + String value = getFirst(CONTENT_LENGTH); + return (value != null ? Long.parseLong(value) : -1); + } + + /** + * Set the {@linkplain ContentType media type} of the body, as specified by + * the {@code Content-Type} header. + */ + public void setContentType(ContentType contentType) { + set(CONTENT_TYPE, contentType.toString()); + } + + /** + * Return the {@linkplain MediaType media type} of the body, as specified by + * the {@code Content-Type} header. + *

+ * Returns {@code null} when the content-type is unknown. + */ + public String getContentType() { + return getFirst(CONTENT_TYPE); + } + + /** + * Set the date and time at which the message was created, as specified by + * the {@code Date} header. + *

+ * The date should be specified as the number of milliseconds since January + * 1, 1970 GMT. + */ + public void setDate(long date) { + setDate(DATE, date); + } + + /** + * Return the date and time at which the message was created, as specified + * by the {@code Date} header. + *

+ * The date is returned as the number of milliseconds since January 1, 1970 + * GMT. Returns -1 when the date is unknown. + * + * @throws IllegalArgumentException + * if the value can't be converted to a date + */ + public long getDate() { + return getFirstDate(DATE); + } + + /** + * Set the (new) entity tag of the body, as specified by the {@code ETag} + * header. + */ + public void setETag(String eTag) { + set(ETAG, eTag); + } + + /** + * Return the entity tag of the body, as specified by the {@code ETag} + * header. + */ + public String getETag() { + return getFirst(ETAG); + } + + /** + * Set the date and time at which the message is no longer valid, as + * specified by the {@code Expires} header. + *

+ * The date should be specified as the number of milliseconds since January + * 1, 1970 GMT. + */ + public void setExpires(long expires) { + setDate(EXPIRES, expires); + } + + /** + * Return the date and time at which the message is no longer valid, as + * specified by the {@code Expires} header. + *

+ * The date is returned as the number of milliseconds since January 1, 1970 + * GMT. Returns -1 when the date is unknown. + */ + public long getExpires() { + try { + return getFirstDate(EXPIRES); + } catch (IllegalArgumentException ex) { + return -1; + } + } + + /** + * Set the (new) value of the {@code If-Modified-Since} header. + *

+ * The date should be specified as the number of milliseconds since January + * 1, 1970 GMT. + */ + public void setIfModifiedSince(long ifModifiedSince) { + setDate(IF_MODIFIED_SINCE, ifModifiedSince); + } + + /** + * Return the value of the {@code If-Modified-Since} header. + *

+ * The date is returned as the number of milliseconds since January 1, 1970 + * GMT. Returns -1 when the date is unknown. + */ + public long getIfModifiedSince() { + return getFirstDate(IF_MODIFIED_SINCE); + } + + /** + * Set the (new) value of the {@code If-None-Match} header. + */ + public void setIfNoneMatch(String ifNoneMatch) { + set(IF_NONE_MATCH, ifNoneMatch); + } + + /** + * Set the (new) values of the {@code If-None-Match} header. + */ + public void setIfNoneMatch(List ifNoneMatchList) { + set(IF_NONE_MATCH, toCommaDelimitedString(ifNoneMatchList)); + } + + protected String toCommaDelimitedString(List list) { + StringBuilder builder = new StringBuilder(); + for (Iterator iterator = list.iterator(); iterator.hasNext();) { + String ifNoneMatch = iterator.next(); + builder.append(ifNoneMatch); + if (iterator.hasNext()) { + builder.append(", "); + } + } + return builder.toString(); + } + + /** + * Return the value of the {@code If-None-Match} header. + */ + public List getIfNoneMatch() { + return getFirstValueAsList(IF_NONE_MATCH); + } + + protected List getFirstValueAsList(String header) { + List result = new ArrayList(); + String value = getFirst(header); + if (value != null) { + String[] tokens = value.split(",\\s*"); + for (String token : tokens) { + result.add(token); + } + } + return result; + } + + /** + * Set the time the resource was last changed, as specified by the + * {@code Last-Modified} header. + *

+ * The date should be specified as the number of milliseconds since January + * 1, 1970 GMT. + */ + public void setLastModified(long lastModified) { + setDate(LAST_MODIFIED, lastModified); + } + + /** + * Return the time the resource was last changed, as specified by the + * {@code Last-Modified} header. + *

+ * The date is returned as the number of milliseconds since January 1, 1970 + * GMT. Returns -1 when the date is unknown. + */ + public long getLastModified() { + return getFirstDate(LAST_MODIFIED); + } + + /** + * Set the (new) location of a resource, as specified by the + * {@code Location} header. + */ + public void setLocation(URI location) { + set(LOCATION, location.toASCIIString()); + } + + /** + * Return the (new) location of a resource as specified by the + * {@code Location} header. + *

+ * Returns {@code null} when the location is unknown. + */ + public URI getLocation() { + String value = getFirst(LOCATION); + return (value != null ? URI.create(value) : null); + } + + /** + * Set the (new) value of the {@code Origin} header. + */ + public void setOrigin(String origin) { + set(ORIGIN, origin); + } + + /** + * Return the value of the {@code Origin} header. + */ + public String getOrigin() { + return getFirst(ORIGIN); + } + + /** + * Set the (new) value of the {@code Pragma} header. + */ + public void setPragma(String pragma) { + set(PRAGMA, pragma); + } + + /** + * Return the value of the {@code Pragma} header. + */ + public String getPragma() { + return getFirst(PRAGMA); + } + + /** + * Sets the (new) value of the {@code Range} header. + */ + public void setRange(String range) { + set(RANGE, range); + } + + /** + * Returns the value of the {@code Range} header. + *

+ * Returns an empty list when the range is unknown. + */ + public String getRange() { + return getFirst(RANGE); + } + + /** + * Set the (new) value of the {@code Upgrade} header. + */ + public void setUpgrade(String upgrade) { + set(UPGRADE, upgrade); + } + + /** + * Returns the value of the {@code Upgrade} header. + */ + public String getUpgrade() { + return getFirst(UPGRADE); + } + + /** + * Parse the first header value for the given header name as a date, return + * -1 if there is no value, or raise {@link IllegalArgumentException} if the + * value cannot be parsed as a date. + */ + public long getFirstDate(String headerName) { + String headerValue = getFirst(headerName); + if (headerValue == null) { + return -1; + } + for (String dateFormat : DATE_FORMATS) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat( + dateFormat, Locale.US); + simpleDateFormat.setTimeZone(GMT); + try { + return simpleDateFormat.parse(headerValue).getTime(); + } catch (ParseException ex) { + // ignore + } + } + throw new IllegalArgumentException("Cannot parse date value \"" + + headerValue + "\" for \"" + headerName + "\" header"); + } + + /** + * Set the given date under the given header name after formatting it as a + * string using the pattern {@code "EEE, dd MMM yyyy HH:mm:ss zzz"}. The + * equivalent of {@link #set(String, String)} but for date headers. + */ + public void setDate(String headerName, long date) { + SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], + Locale.US); + dateFormat.setTimeZone(GMT); + set(headerName, dateFormat.format(new Date(date))); + } + + /** + * Return the first header value for the given header name, if any. + * + * @param headerName + * the header name + * @return the first header value, or {@code null} if none + */ + @Override + public String getFirst(String headerName) { + List headerValues = this.headers.get(headerName); + return (headerValues != null ? headerValues.get(0) : null); + } + + /** + * Add the given, single header value under the given name. + * + * @param headerName + * the header name + * @param headerValue + * the header value + * @throws UnsupportedOperationException + * if adding headers is not supported + * @see #put(String, List) + * @see #set(String, String) + */ + @Override + public void add(String headerName, String headerValue) { + List headerValues = this.headers.get(headerName); + if (headerValues == null) { + headerValues = new LinkedList(); + this.headers.put(headerName, headerValues); + } + headerValues.add(headerValue); + } + + /** + * Set the given, single header value under the given name. + * + * @param headerName + * the header name + * @param headerValue + * the header value + * @throws UnsupportedOperationException + * if adding headers is not supported + * @see #put(String, List) + * @see #add(String, String) + */ + @Override + public void set(String headerName, String headerValue) { + List headerValues = new LinkedList(); + headerValues.add(headerValue); + this.headers.put(headerName, headerValues); + } + + @Override + public void setAll(Map values) { + for (Entry entry : values.entrySet()) { + set(entry.getKey(), entry.getValue()); + } + } + + @Override + public Map toSingleValueMap() { + LinkedHashMap singleValueMap = new LinkedHashMap( + this.headers.size()); + for (Entry> entry : this.headers.entrySet()) { + singleValueMap.put(entry.getKey(), entry.getValue().get(0)); + } + return singleValueMap; + } + + // Map implementation + + @Override + public int size() { + return this.headers.size(); + } + + @Override + public boolean isEmpty() { + return this.headers.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return this.headers.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return this.headers.containsValue(value); + } + + @Override + public List get(Object key) { + return this.headers.get(key); + } + + @Override + public List put(String key, List value) { + return this.headers.put(key, value); + } + + @Override + public List remove(Object key) { + return this.headers.remove(key); + } + + @Override + public void putAll(Map> map) { + this.headers.putAll(map); + } + + @Override + public void clear() { + this.headers.clear(); + } + + @Override + public Set keySet() { + return this.headers.keySet(); + } + + @Override + public Collection> values() { + return this.headers.values(); + } + + @Override + public Set>> entrySet() { + return this.headers.entrySet(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof HttpHeaders)) { + return false; + } + HttpHeaders otherHeaders = (HttpHeaders) other; + return this.headers.equals(otherHeaders.headers); + } + + @Override + public int hashCode() { + return this.headers.hashCode(); + } + + @Override + public String toString() { + return this.headers.toString(); + } + + /** + * Return a {@code HttpHeaders} object that can only be read, not written + * to. + */ + public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) { + return new HttpHeaders(headers, true); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMessage.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMessage.java index ff952dff..5fccf31e 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMessage.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMessage.java @@ -11,57 +11,10 @@ package com.foxinmy.weixin4j.http; * @see */ public interface HttpMessage { - /** - * HTTP版本 + * HTTP报头 * * @return */ - HttpVersion getProtocolVersion(); - - /** - * 全部表头 - * - * @return - */ - Header[] getAllHeaders(); - - /** - * 设置表头 - * - * @param headers - */ - void setHeaders(Header... headers); - - /** - * 是否包含某一表头 - * - * @param name - * @return - */ - boolean containsHeader(String name); - - /** - * 名称查找表头 - * - * @param name - * @return - */ - Header[] getHeaders(String name); - - /** - * 查找最先匹配表头 - * - * @param name - * @return - */ - Header getFirstHeader(String name); - - /** - * 查找最后匹配表头 - * - * @param name - * @return - */ - Header getLastHeader(String name); + HttpHeaders getHeaders(); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMethod.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMethod.java index a078e077..7d38899c 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMethod.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMethod.java @@ -10,5 +10,5 @@ package com.foxinmy.weixin4j.http; * @see */ public enum HttpMethod { - GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE, CONNECT } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpParams.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpParams.java index b10664a3..bf2241d6 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpParams.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpParams.java @@ -1,83 +1,119 @@ package com.foxinmy.weixin4j.http; -import java.util.HashMap; -import java.util.Map; +import java.net.Proxy; -public class HttpParams { - private Map params; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; - public HttpParams() { - this.params = new HashMap(); +/** + * Http 参数 + * + * @className HttpParams + * @author jy + * @date 2015年8月13日 + * @since JDK 1.7 + * @see + */ +public final class HttpParams { + + private boolean allowUserInteraction = true; + private int connectTimeout = 5000; + private int socketTimeout = 5000; + private int readTimeout = 5000; + private int chunkSize = 4096; + private long ifModifiedSince = 0l; + private boolean followRedirects = false; + + /** + * 代理对象 + */ + private Proxy proxy; + /** + * SSL对象 + */ + private SSLContext sslContext; + /** + * hostname对象 + */ + private HostnameVerifier hostnameVerifier; + + public boolean isAllowUserInteraction() { + return allowUserInteraction; } public void setAllowUserInteraction(boolean allowUserInteraction) { - params.put(HttpProtocolParams.ALLOW_USER_INTERACTION, - Boolean.toString(allowUserInteraction)); - } - - public boolean getAllowUserInteraction() { - String allowUserInteraction = params - .get(HttpProtocolParams.ALLOW_USER_INTERACTION); - return allowUserInteraction != null - && Boolean.parseBoolean(allowUserInteraction); - } - - public void setConnectTimeout(int timeout) { - if (timeout < 0) { - timeout = 0; - } - params.put(HttpProtocolParams.CONNECT_TIMEOUT, - Integer.toString(timeout)); + this.allowUserInteraction = allowUserInteraction; } public int getConnectTimeout() { - String timeout = params.get(HttpProtocolParams.CONNECT_TIMEOUT); - if (timeout == null) { - return 5000; - } - return Integer.parseInt(timeout); + return connectTimeout; } - public void setReadTimeout(int timeout) { - if (timeout < 0) { - timeout = 5000; - } - params.put(HttpProtocolParams.READ_TIMEOUT, Integer.toString(timeout)); + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public int getSocketTimeout() { + return socketTimeout; + } + + public void setSocketTimeout(int socketTimeout) { + this.socketTimeout = socketTimeout; } public int getReadTimeout() { - String timeout = params.get(HttpProtocolParams.READ_TIMEOUT); - if (timeout == null) { - return 5000; - } - return Integer.parseInt(timeout); + return readTimeout; } - public void setIfmodifiedsince(long ifmodifiedsince) { - if (ifmodifiedsince < 0) { - ifmodifiedsince = 5000; - } - params.put(HttpProtocolParams.IFMODIFIED_SINCE, - Long.toString(ifmodifiedsince)); + public void setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; } - public long getIfmodifiedsince() { - String ifmodifiedsince = params - .get(HttpProtocolParams.IFMODIFIED_SINCE); - if (ifmodifiedsince == null) { - return 0l; - } - return Long.parseLong(ifmodifiedsince); + public int getChunkSize() { + return chunkSize; + } + + public void setChunkSize(int chunkSize) { + this.chunkSize = chunkSize; + } + + public long getIfModifiedSince() { + return ifModifiedSince; + } + + public void setIfModifiedSince(long ifModifiedSince) { + this.ifModifiedSince = ifModifiedSince; + } + + public boolean isFollowRedirects() { + return followRedirects; } public void setFollowRedirects(boolean followRedirects) { - params.put(HttpProtocolParams.FOLLOW_REDIRECTS, - Boolean.toString(followRedirects)); + this.followRedirects = followRedirects; } - public boolean getFollowRedirects() { - String followRedirects = params - .get(HttpProtocolParams.FOLLOW_REDIRECTS); - return followRedirects != null && Boolean.parseBoolean(followRedirects); + public Proxy getProxy() { + return proxy; + } + + public void setProxy(Proxy proxy) { + this.proxy = proxy; + } + + public SSLContext getSSLContext() { + return sslContext; + } + + public void setSSLContext(SSLContext sslContext) { + this.sslContext = sslContext; + } + + public HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + + public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { + this.hostnameVerifier = hostnameVerifier; } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpPost.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpPost.java deleted file mode 100644 index ef5b3796..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpPost.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.net.URI; - -import com.foxinmy.weixin4j.http.entity.HttpEntity; - -/** - * POST 请求 - * - * @className HttpPost - * @author jy - * @date 2015年5月29日 - * @since JDK 1.7 - * @see - */ -public class HttpPost extends HttpEntityRequest { - - private final URI uri; - - public HttpPost(final URI uri) { - this.uri = uri; - } - - public HttpPost(final String uri) { - this(URI.create(uri)); - } - - @Override - public HttpMethod getMethod() { - return HttpMethod.POST; - } - - @Override - public URI getURI() { - return this.uri; - } - - private HttpEntity entity; - - @Override - public HttpEntity getEntity() { - return entity; - } - - @Override - public void setEntity(HttpEntity entity) { - this.entity = entity; - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpProtocolParams.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpProtocolParams.java deleted file mode 100644 index 9d864eb2..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpProtocolParams.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.foxinmy.weixin4j.http; - -public final class HttpProtocolParams { - public final static String ALLOW_USER_INTERACTION = "http.allow.user.interaction"; - public final static String CONNECT_TIMEOUT = "http.connect.timeout"; - public final static String READ_TIMEOUT = "http.connect.timeout"; - public final static String IFMODIFIED_SINCE = "http.ifmodified.since"; - public final static String FOLLOW_REDIRECTS = "http.follow.redirects"; -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpRequest.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpRequest.java index 61e8d29a..8145e976 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpRequest.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpRequest.java @@ -2,6 +2,8 @@ package com.foxinmy.weixin4j.http; import java.net.URI; +import com.foxinmy.weixin4j.http.entity.HttpEntity; + /** * HTTP 请求 * @@ -11,49 +13,74 @@ import java.net.URI; * @since JDK 1.7 * @see */ -public interface HttpRequest extends HttpMessage { - +public class HttpRequest implements HttpMessage { /** * 请求方式 * * @return */ - HttpMethod getMethod(); + private final HttpMethod method; /** * 请求路径 * * @return */ - URI getURI(); - + private final URI uri; /** - * 请求参数 + * 协议参数 * * @return */ - HttpParams getParams(); - + private HttpParams params; /** - * 新增表头 - * - * @param header + * 内容参数 */ - void addHeader(Header header); - + private HttpEntity entity; /** - * 移除表头 - * - * @param name - * @return + * 请求表头 */ - boolean removeHeader(String name); + private HttpHeaders headers; - /** - * 移除表头 - * - * @param name - * @return - */ - boolean removeHeader(Header name); + public HttpRequest(HttpMethod method, URI uri) { + this.method = method; + this.uri = uri; + } + + public HttpRequest(HttpMethod method, String url) { + this(method, URI.create(url)); + } + + public HttpMethod getMethod() { + return method; + } + + public URI getURI() { + return uri; + } + + public HttpParams getParams() { + return params; + } + + public void setParams(HttpParams params) { + this.params = params; + } + + public HttpEntity getEntity() { + return entity; + } + + public void setEntity(HttpEntity entity) { + this.entity = entity; + } + + public void setHeaders(HttpHeaders headers) { + this.headers = headers; + } + + @Override + public HttpHeaders getHeaders() { + return headers; + } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java index decab2e8..45e1c661 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java @@ -1,5 +1,7 @@ package com.foxinmy.weixin4j.http; +import java.io.InputStream; + /** * HTTP 响应 * @@ -9,44 +11,30 @@ package com.foxinmy.weixin4j.http; * @since JDK 1.7 * @see */ -public class HttpResponse extends AbstractHttpMessage implements HttpMessage { +public interface HttpResponse extends HttpMessage { + /** + * HTTP协议 + * + * @return + */ + HttpVersion getProtocol(); - private HttpVersion httpVersion; - private StatusLine statusLine; - private byte[] content; + /** + * 响应状态 + * + * @return + */ + HttpStatus getStatus() throws HttpClientException; - @Override - public HttpVersion getProtocolVersion() { - return httpVersion; - } + /** + * 响应内容 + * + * @return + */ + InputStream getBody() throws HttpClientException; - public StatusLine getStatusLine() { - return statusLine; - } - - public byte[] getContent() { - return content; - } - - public void setStatusLine(StatusLine statusLine) { - this.statusLine = statusLine; - } - - public void setContent(byte[] content) { - this.content = content; - } - - public HttpVersion getHttpVersion() { - return httpVersion; - } - - public void setHttpVersion(HttpVersion httpVersion) { - this.httpVersion = httpVersion; - } - - @Override - public String toString() { - return "HttpResponse [httpVersion=" + httpVersion + ", statusLine=" - + statusLine + "]"; - } + /** + * 释放资源 + */ + void close(); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponseHandler.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponseHandler.java deleted file mode 100644 index d1fce8c1..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponseHandler.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.io.IOException; - -/** - * 响应处理 - * - * @className HttpResponseHandler - * @author jy - * @date 2015年5月30日 - * @since JDK 1.7 - * @see - */ -public interface HttpResponseHandler { - T handleResponse(HttpResponse response) throws IOException; -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java index 9ecc0f79..72dba067 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java @@ -172,4 +172,24 @@ public final class HttpStatus { /** 507 Insufficient Storage (WebDAV - RFC 2518) */ public static final int SC_INSUFFICIENT_STORAGE = 507; + private final int statusCode; + private final String statusText; + + public HttpStatus(int statusCode, String statusText) { + this.statusCode = statusCode; + this.statusText = statusText; + } + + public int getStatusCode() { + return statusCode; + } + + public String getStatusText() { + return statusText; + } + + @Override + public String toString() { + return "[" + statusCode + "," + statusText + "]"; + } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ResponseHandler.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ResponseHandler.java deleted file mode 100644 index 8a568dbe..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ResponseHandler.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.io.IOException; - -/** - * 响应处理 - * - * @className ResponseHandler - * @author jy - * @date 2015年5月30日 - * @since JDK 1.7 - * @see - */ -public interface ResponseHandler { - T handleResponse(HttpResponse response) throws IOException; -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java index 910cf4c0..2c4c5991 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java @@ -1,16 +1,15 @@ package com.foxinmy.weixin4j.http; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; +import java.net.Proxy; import java.net.URI; -import java.net.URL; -import java.security.GeneralSecurityException; +import java.net.URLConnection; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -25,6 +24,7 @@ import javax.net.ssl.X509TrustManager; import com.foxinmy.weixin4j.http.entity.HttpEntity; import com.foxinmy.weixin4j.model.Consts; +import com.foxinmy.weixin4j.util.StringUtil; /** * HTTP 简单实现 @@ -35,7 +35,7 @@ import com.foxinmy.weixin4j.model.Consts; * @since JDK 1.7 * @see */ -public class SimpleHttpClient implements HttpClient { +public class SimpleHttpClient extends AbstractHttpClient implements HttpClient { protected HostnameVerifier createHostnameVerifier() { return new HostnameVerifier() { @@ -67,142 +67,147 @@ public class SimpleHttpClient implements HttpClient { }; } - protected HttpURLConnection createHttpConnection(URI uri) + protected HttpURLConnection createHttpConnection(HttpRequest request) throws IOException { - URL url = uri.toURL(); + URI uri = request.getURI(); + HttpParams params = request.getParams(); + Proxy proxy = params != null ? params.getProxy() : null; + URLConnection urlConnection = proxy != null ? uri.toURL() + .openConnection(proxy) : uri.toURL().openConnection(); if (uri.getScheme().equals("https")) { - HttpsURLConnection connection = (HttpsURLConnection) url - .openConnection(); - SSLContext sslContext = null; try { - sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, - new X509TrustManager[] { createX509TrustManager() }, - new java.security.SecureRandom()); - } catch (GeneralSecurityException e) { - throw new IOException(e); + SSLContext sslContext = null; + HostnameVerifier hostnameVerifier = null; + if (params != null) { + sslContext = params.getSSLContext(); + hostnameVerifier = params.getHostnameVerifier(); + } + if (sslContext == null) { + sslContext = SSLContext.getInstance("TLS"); + sslContext + .init(null, + new X509TrustManager[] { createX509TrustManager() }, + new java.security.SecureRandom()); + } + if (hostnameVerifier == null) { + hostnameVerifier = createHostnameVerifier(); + } + HttpsURLConnection connection = (HttpsURLConnection) urlConnection; + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(hostnameVerifier); + return connection; + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new IOException(e.getMessage()); } - connection.setSSLSocketFactory(sslContext.getSocketFactory()); - connection.setHostnameVerifier(createHostnameVerifier()); - return connection; } else { - return (HttpURLConnection) url.openConnection(); + return (HttpURLConnection) urlConnection; } } protected Map createDefualtHeader() { - Map params = new HashMap(); - params.put("User-Agent", "simple httpclient/java 1.5"); - params.put("Accept", "text/xml,text/javascript"); - params.put("Accept-Charset", Consts.UTF_8.name()); - params.put("Accept-Encoding", Consts.UTF_8.name()); - return params; + Map header = new HashMap(); + header.put("User-Agent", "simple-httpclient"); + header.put("Accept", "text/xml,text/javascript"); + header.put("Accept-Charset", Consts.UTF_8.name()); + header.put("Accept-Encoding", Consts.UTF_8.name()); + return header; } @Override - public HttpResponse execute(HttpRequest request) throws IOException { - // create connection object - HttpURLConnection connection = createHttpConnection(request.getURI()); - // set parameters - HttpParams params = request.getParams(); - connection.setRequestMethod(request.getMethod().name()); - connection.setAllowUserInteraction(params.getAllowUserInteraction()); - connection.setConnectTimeout(params.getConnectTimeout()); - connection.setReadTimeout(params.getReadTimeout()); - connection.setIfModifiedSince(params.getIfmodifiedsince()); - connection.setInstanceFollowRedirects(params.getFollowRedirects()); - connection.setDoInput(true); - connection.setDoOutput(true); - // set headers - Header[] headers = request.getAllHeaders(); - for (Header header : headers) { - connection.setRequestProperty(header.getName(), header.getValue()); - } - for (Iterator> headerIterator = createDefualtHeader() - .entrySet().iterator(); headerIterator.hasNext();) { - Entry header = headerIterator.next(); - connection.setRequestProperty(header.getKey(), header.getValue()); - } - HttpEntity httpEntity = null; - if (request instanceof HttpEntityRequest) { - httpEntity = ((HttpEntityRequest) request).getEntity(); - connection.setUseCaches(false); - if (httpEntity != null) { - if (httpEntity.getContentLength() > 0l) { - connection.setFixedLengthStreamingMode(httpEntity - .getContentLength()); - } - if (httpEntity.getContentType() != null) { - connection.setRequestProperty("Content-Type", httpEntity - .getContentType().getMimeType()); - } + public HttpResponse execute(HttpRequest request) throws HttpClientException { + HttpResponse response = null; + try { + // create connection object + HttpURLConnection connection = createHttpConnection(request); + String method = request.getMethod().name(); + // set parameters + HttpParams params = request.getParams(); + if (params != null) { + connection.setAllowUserInteraction(params + .isAllowUserInteraction()); + connection.setConnectTimeout(params.getConnectTimeout()); + connection.setReadTimeout(params.getReadTimeout()); + connection.setIfModifiedSince(params.getIfModifiedSince()); + connection.setInstanceFollowRedirects(params + .isFollowRedirects()); } - } - connection.connect(); - // open stream - if (httpEntity != null) { - OutputStream output = connection.getOutputStream(); - httpEntity.writeTo(output); - output.flush(); - output.close(); - } - // building response object - StatusLine statusLine = new StatusLine(connection.getResponseCode(), - connection.getResponseMessage()); - byte[] content = null; - if (statusLine.getStatusCode() < 300) { - ByteArrayOutputStream os = null; - InputStream is = null; - try { - is = connection.getInputStream(); - os = new ByteArrayOutputStream(); - byte[] buffer = new byte[4096]; - int n = 0; - while (-1 != (n = is.read(buffer))) { - os.write(buffer, 0, n); - } - content = os.toByteArray(); - } catch (IOException e) { - ; - } finally { - if (os != null) { - os.close(); - } - if (is != null) { - is.close(); - } - } - } - HttpResponse response = new HttpResponse(); - String httpVersion = connection.getHeaderField(null); - if (httpVersion != null) { - if (httpVersion.contains(HttpVersion.HTTP_1_0_STRING)) { - response.setHttpVersion(HttpVersion.HTTP_1_0); - } else if (httpVersion.contains(HttpVersion.HTTP_1_1_STRING)) { - response.setHttpVersion(HttpVersion.HTTP_1_1); + connection.setRequestMethod(method); + connection.setDoInput(true); + connection.setInstanceFollowRedirects("GET".equals(method)); + if ("PUT".equals(method) || "POST".equals(method) + || "PATCH".equals(method) || "DELETE".equals(method)) { + connection.setDoOutput(true); } else { - response.setHttpVersion(new HttpVersion(httpVersion, true)); + connection.setDoOutput(false); + } + // set headers + for (Iterator> headerIterator = createDefualtHeader() + .entrySet().iterator(); headerIterator.hasNext();) { + Entry header = headerIterator.next(); + connection.setRequestProperty(header.getKey(), + header.getValue()); + } + HttpHeaders headers = request.getHeaders(); + if (headers != null) { + for (Iterator>> headerIterator = headers + .entrySet().iterator(); headerIterator.hasNext();) { + Entry> header = headerIterator.next(); + if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) { + connection.setRequestProperty(header.getKey(), + StringUtil.join(header.getValue(), ';')); + } else { + for (String headerValue : header.getValue()) { + connection.addRequestProperty(header.getKey(), + headerValue != null ? headerValue : ""); + } + } + } + } + // set inputstream + HttpEntity httpEntity = request.getEntity(); + if (httpEntity != null) { + connection.setUseCaches(false); + if (httpEntity != null) { + // Read Out Exception when connection.disconnect(); + /* + if (httpEntity.getContentLength() > 0l) { + connection.setFixedLengthStreamingMode(httpEntity + .getContentLength()); + } else { + connection + .setChunkedStreamingMode(params != null ? params + .getChunkSize() : 4096); + }*/ + if (httpEntity.getContentLength() > 0l) { + connection.setRequestProperty(HttpHeaders.CONTENT_LENGTH, + Long.toString(httpEntity.getContentLength())); + } + if (httpEntity.getContentType() != null) { + connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, + httpEntity.getContentType().getMimeType()); + } + } + } + // connect + connection.connect(); + // open stream + if (httpEntity != null) { + OutputStream output = connection.getOutputStream(); + httpEntity.writeTo(output); + output.flush(); + output.close(); + } + // building response + response = new SimpleHttpResponse(connection); + } catch (IOException e) { + throw new HttpClientException("I/O error on " + + request.getMethod().name() + " request for \"" + + request.getURI().toString() + "\":" + e.getMessage(), e); + } finally { + if (response != null) { + response.close(); } } - List

responseHeaders = new ArrayList
(); - Map> headerFields = connection.getHeaderFields(); - for (Iterator>> headerIterator = headerFields - .entrySet().iterator(); headerIterator.hasNext();) { - Entry> headerEntry = headerIterator.next(); - for (String value : headerEntry.getValue()) { - responseHeaders.add(new Header(headerEntry.getKey(), value)); - } - } - response.setHeaders(responseHeaders.toArray(new Header[responseHeaders - .size()])); - response.setStatusLine(statusLine); - response.setContent(content); return response; } - - @Override - public T execute(HttpRequest request, - ResponseHandler handler) throws IOException { - return handler.handleResponse(execute(request)); - } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpResponse.java new file mode 100644 index 00000000..fb70be55 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpResponse.java @@ -0,0 +1,94 @@ +package com.foxinmy.weixin4j.http; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * Simple Response + * + * @className SimpleHttpResponse + * @author jy + * @date 2015年8月14日 + * @since JDK 1.7 + * @see + */ +public class SimpleHttpResponse implements HttpResponse { + + private final HttpURLConnection connection; + + private HttpHeaders headers; + private HttpVersion protocol; + private HttpStatus status; + + public SimpleHttpResponse(HttpURLConnection connection) { + this.connection = connection; + } + + @Override + public HttpVersion getProtocol() { + String version = connection.getHeaderField(null); + if (version == null) { + return null; + } + if (protocol == null) { + if (version.contains(HttpVersion.HTTP_1_0_STRING)) { + protocol = HttpVersion.HTTP_1_0; + } else if (version.contains(HttpVersion.HTTP_1_1_STRING)) { + protocol = HttpVersion.HTTP_1_1; + } else { + String connect = connection.getHeaderField("Connection"); + protocol = new HttpVersion(version, + "keep-alive".equalsIgnoreCase(connect)); + } + } + return protocol; + } + + @Override + public HttpHeaders getHeaders() { + if (headers == null) { + headers = new HttpHeaders(); + Map> headerFields = connection + .getHeaderFields(); + for (Iterator>> headerIterator = headerFields + .entrySet().iterator(); headerIterator.hasNext();) { + Entry> headerEntry = headerIterator.next(); + headers.put(headerEntry.getKey(), headerEntry.getValue()); + } + } + return headers; + } + + @Override + public HttpStatus getStatus() throws HttpClientException { + if (status == null) { + try { + status = new HttpStatus(connection.getResponseCode(), + connection.getResponseMessage()); + } catch (IOException e) { + throw new HttpClientException("I/O Error on getStatus", e); + } + } + return status; + } + + @Override + public InputStream getBody() throws HttpClientException { + try { + return connection.getErrorStream() != null ? connection + .getErrorStream() : connection.getInputStream(); + } catch (IOException e) { + throw new HttpClientException("I/O Error on getBody", e); + } + } + + @Override + public void close() { + connection.disconnect(); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/StatusLine.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/StatusLine.java deleted file mode 100644 index a6517380..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/StatusLine.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.io.Serializable; - -/** - * 状态码 - * - * @className StatusLine - * @author jy - * @date 2015年5月29日 - * @since JDK 1.7 - * @see - */ -public class StatusLine implements Serializable { - - private static final long serialVersionUID = -4182333834588893408L; - - private final int statusCode; - private final String statusText; - - public StatusLine(int statusCode, String statusText) { - this.statusCode = statusCode; - this.statusText = statusText; - } - - public int getStatusCode() { - return statusCode; - } - - public String getStatusText() { - return statusText; - } - - @Override - public String toString() { - return statusCode + "," + statusText; - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/URLParameter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/URLParameter.java new file mode 100644 index 00000000..f9591bc3 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/URLParameter.java @@ -0,0 +1,35 @@ +package com.foxinmy.weixin4j.http; + +import com.foxinmy.weixin4j.model.Consts; +import com.foxinmy.weixin4j.util.NameValue; +import com.foxinmy.weixin4j.util.URLEncodingUtil; + +/** + * 键值对参数 + * + * @className UrlParameter + * @author jy + * @date 2015年5月29日 + * @since JDK 1.7 + * @see + */ +public class URLParameter extends NameValue { + + private static final long serialVersionUID = -115491642760990655L; + + public URLParameter(String name, String value) { + super(name, value); + } + + public String encoding() { + return String.format("%s=%s", + URLEncodingUtil.encoding(getName(), Consts.UTF_8, true), + URLEncodingUtil.encoding(getValue(), Consts.UTF_8, true)); + } + + @Override + public String toString() { + return String.format("[URLParameter name=%s, value=%s]", getName(), + getValue()); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/UrlEncodeParameter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/UrlEncodeParameter.java deleted file mode 100644 index 8802d5b8..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/UrlEncodeParameter.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -import com.foxinmy.weixin4j.model.Consts; - -/** - * 键值对参数 - * - * @className UrlEncodeParameter - * @author jy - * @date 2015年5月29日 - * @since JDK 1.7 - * @see - */ -public class UrlEncodeParameter extends NameValue { - - private static final long serialVersionUID = -115491642760990655L; - - public UrlEncodeParameter(String name, String value) { - super(name, value); - } - - public String encodingParameter() { - try { - return String.format("&%s=%s", getName(), - URLEncoder.encode(getValue(), Consts.UTF_8.name())); - } catch (UnsupportedEncodingException e) { - return String.format("&%s=%s", getName(), getValue()); - } - } - - @Override - public String toString() { - return String.format("[HttpParameter name=%s, value=%s]", getName(), - getValue()); - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FormBodyPart.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FormBodyPart.java index aeb0584d..3f298395 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FormBodyPart.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FormBodyPart.java @@ -27,7 +27,7 @@ package com.foxinmy.weixin4j.http.apache; -import com.foxinmy.weixin4j.http.NameValue; +import com.foxinmy.weixin4j.util.NameValue; /** diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/Header.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/Header.java index 74077d81..f6782fd4 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/Header.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/Header.java @@ -36,7 +36,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import com.foxinmy.weixin4j.http.NameValue; +import com.foxinmy.weixin4j.util.NameValue; /** * The header of an entity (see RFC 2045). diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpHeaders.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpHeaders.java deleted file mode 100644 index 57202640..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpHeaders.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - * - */ - -package com.foxinmy.weixin4j.http.apache; - -/** - * Constants enumerating the HTTP headers. All headers defined in RFC1945 (HTTP/1.0), RFC2616 (HTTP/1.1), and RFC2518 - * (WebDAV) are listed. - * - * @since 4.1 - */ -public final class HttpHeaders { - - private HttpHeaders() { - } - - /** RFC 2616 (HTTP/1.1) Section 14.1 */ - public static final String ACCEPT = "Accept"; - - /** RFC 2616 (HTTP/1.1) Section 14.2 */ - public static final String ACCEPT_CHARSET = "Accept-Charset"; - - /** RFC 2616 (HTTP/1.1) Section 14.3 */ - public static final String ACCEPT_ENCODING = "Accept-Encoding"; - - /** RFC 2616 (HTTP/1.1) Section 14.4 */ - public static final String ACCEPT_LANGUAGE = "Accept-Language"; - - /** RFC 2616 (HTTP/1.1) Section 14.5 */ - public static final String ACCEPT_RANGES = "Accept-Ranges"; - - /** RFC 2616 (HTTP/1.1) Section 14.6 */ - public static final String AGE = "Age"; - - /** RFC 1945 (HTTP/1.0) Section 10.1, RFC 2616 (HTTP/1.1) Section 14.7 */ - public static final String ALLOW = "Allow"; - - /** RFC 1945 (HTTP/1.0) Section 10.2, RFC 2616 (HTTP/1.1) Section 14.8 */ - public static final String AUTHORIZATION = "Authorization"; - - /** RFC 2616 (HTTP/1.1) Section 14.9 */ - public static final String CACHE_CONTROL = "Cache-Control"; - - /** RFC 2616 (HTTP/1.1) Section 14.10 */ - public static final String CONNECTION = "Connection"; - - /** RFC 1945 (HTTP/1.0) Section 10.3, RFC 2616 (HTTP/1.1) Section 14.11 */ - public static final String CONTENT_ENCODING = "Content-Encoding"; - - /** RFC 2616 (HTTP/1.1) Section 14.12 */ - public static final String CONTENT_LANGUAGE = "Content-Language"; - - /** RFC 1945 (HTTP/1.0) Section 10.4, RFC 2616 (HTTP/1.1) Section 14.13 */ - public static final String CONTENT_LENGTH = "Content-Length"; - - /** RFC 2616 (HTTP/1.1) Section 14.14 */ - public static final String CONTENT_LOCATION = "Content-Location"; - - /** RFC 2616 (HTTP/1.1) Section 14.15 */ - public static final String CONTENT_MD5 = "Content-MD5"; - - /** RFC 2616 (HTTP/1.1) Section 14.16 */ - public static final String CONTENT_RANGE = "Content-Range"; - - /** RFC 1945 (HTTP/1.0) Section 10.5, RFC 2616 (HTTP/1.1) Section 14.17 */ - public static final String CONTENT_TYPE = "Content-Type"; - - /** RFC 1945 (HTTP/1.0) Section 10.6, RFC 2616 (HTTP/1.1) Section 14.18 */ - public static final String DATE = "Date"; - - /** RFC 2518 (WevDAV) Section 9.1 */ - public static final String DAV = "Dav"; - - /** RFC 2518 (WevDAV) Section 9.2 */ - public static final String DEPTH = "Depth"; - - /** RFC 2518 (WevDAV) Section 9.3 */ - public static final String DESTINATION = "Destination"; - - /** RFC 2616 (HTTP/1.1) Section 14.19 */ - public static final String ETAG = "ETag"; - - /** RFC 2616 (HTTP/1.1) Section 14.20 */ - public static final String EXPECT = "Expect"; - - /** RFC 1945 (HTTP/1.0) Section 10.7, RFC 2616 (HTTP/1.1) Section 14.21 */ - public static final String EXPIRES = "Expires"; - - /** RFC 1945 (HTTP/1.0) Section 10.8, RFC 2616 (HTTP/1.1) Section 14.22 */ - public static final String FROM = "From"; - - /** RFC 2616 (HTTP/1.1) Section 14.23 */ - public static final String HOST = "Host"; - - /** RFC 2518 (WevDAV) Section 9.4 */ - public static final String IF = "If"; - - /** RFC 2616 (HTTP/1.1) Section 14.24 */ - public static final String IF_MATCH = "If-Match"; - - /** RFC 1945 (HTTP/1.0) Section 10.9, RFC 2616 (HTTP/1.1) Section 14.25 */ - public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; - - /** RFC 2616 (HTTP/1.1) Section 14.26 */ - public static final String IF_NONE_MATCH = "If-None-Match"; - - /** RFC 2616 (HTTP/1.1) Section 14.27 */ - public static final String IF_RANGE = "If-Range"; - - /** RFC 2616 (HTTP/1.1) Section 14.28 */ - public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; - - /** RFC 1945 (HTTP/1.0) Section 10.10, RFC 2616 (HTTP/1.1) Section 14.29 */ - public static final String LAST_MODIFIED = "Last-Modified"; - - /** RFC 1945 (HTTP/1.0) Section 10.11, RFC 2616 (HTTP/1.1) Section 14.30 */ - public static final String LOCATION = "Location"; - - /** RFC 2518 (WevDAV) Section 9.5 */ - public static final String LOCK_TOKEN = "Lock-Token"; - - /** RFC 2616 (HTTP/1.1) Section 14.31 */ - public static final String MAX_FORWARDS = "Max-Forwards"; - - /** RFC 2518 (WevDAV) Section 9.6 */ - public static final String OVERWRITE = "Overwrite"; - - /** RFC 1945 (HTTP/1.0) Section 10.12, RFC 2616 (HTTP/1.1) Section 14.32 */ - public static final String PRAGMA = "Pragma"; - - /** RFC 2616 (HTTP/1.1) Section 14.33 */ - public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; - - /** RFC 2616 (HTTP/1.1) Section 14.34 */ - public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; - - /** RFC 2616 (HTTP/1.1) Section 14.35 */ - public static final String RANGE = "Range"; - - /** RFC 1945 (HTTP/1.0) Section 10.13, RFC 2616 (HTTP/1.1) Section 14.36 */ - public static final String REFERER = "Referer"; - - /** RFC 2616 (HTTP/1.1) Section 14.37 */ - public static final String RETRY_AFTER = "Retry-After"; - - /** RFC 1945 (HTTP/1.0) Section 10.14, RFC 2616 (HTTP/1.1) Section 14.38 */ - public static final String SERVER = "Server"; - - /** RFC 2518 (WevDAV) Section 9.7 */ - public static final String STATUS_URI = "Status-URI"; - - /** RFC 2616 (HTTP/1.1) Section 14.39 */ - public static final String TE = "TE"; - - /** RFC 2518 (WevDAV) Section 9.8 */ - public static final String TIMEOUT = "Timeout"; - - /** RFC 2616 (HTTP/1.1) Section 14.40 */ - public static final String TRAILER = "Trailer"; - - /** RFC 2616 (HTTP/1.1) Section 14.41 */ - public static final String TRANSFER_ENCODING = "Transfer-Encoding"; - - /** RFC 2616 (HTTP/1.1) Section 14.42 */ - public static final String UPGRADE = "Upgrade"; - - /** RFC 1945 (HTTP/1.0) Section 10.15, RFC 2616 (HTTP/1.1) Section 14.43 */ - public static final String USER_AGENT = "User-Agent"; - - /** RFC 2616 (HTTP/1.1) Section 14.44 */ - public static final String VARY = "Vary"; - - /** RFC 2616 (HTTP/1.1) Section 14.45 */ - public static final String VIA = "Via"; - - /** RFC 2616 (HTTP/1.1) Section 14.46 */ - public static final String WARNING = "Warning"; - - /** RFC 1945 (HTTP/1.0) Section 10.16, RFC 2616 (HTTP/1.1) Section 14.47 */ - public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; - -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpMultipart.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpMultipart.java index cc3c4d12..4fd3071f 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpMultipart.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpMultipart.java @@ -36,7 +36,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; -import com.foxinmy.weixin4j.http.NameValue; +import com.foxinmy.weixin4j.util.NameValue; /** * HttpMultipart represents a collection of MIME multipart encoded content diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FormUrlEntity.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FormUrlEntity.java index a7f5d34a..4af9a779 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FormUrlEntity.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FormUrlEntity.java @@ -1,26 +1,56 @@ package com.foxinmy.weixin4j.http.entity; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import com.foxinmy.weixin4j.http.ContentType; -import com.foxinmy.weixin4j.http.UrlEncodeParameter; +import com.foxinmy.weixin4j.http.URLParameter; +import com.foxinmy.weixin4j.model.Consts; +import com.foxinmy.weixin4j.util.URLEncodingUtil; public class FormUrlEntity extends StringEntity { + private static final String PARAMETER_SEPARATOR = "&"; - public FormUrlEntity(List parameters) { + public FormUrlEntity(List parameters) { super(formatParameters(parameters), ContentType.APPLICATION_FORM_URLENCODED); } - private static String formatParameters(List parameters) { - final StringBuilder body = new StringBuilder(); - UrlEncodeParameter parameter = parameters.get(0); - body.append(parameter.encodingParameter()); + public FormUrlEntity(Map parameters) { + super(formatParameters(parameters), + ContentType.APPLICATION_FORM_URLENCODED); + } + + public static String formatParameters(List parameters) { + StringBuilder body = new StringBuilder(); + URLParameter parameter = parameters.get(0); + body.append(parameter.encoding()); for (int i = 1; i < parameters.size(); i++) { body.append(PARAMETER_SEPARATOR); parameter = parameters.get(i); - body.append(parameter.encodingParameter()); + body.append(parameter.encoding()); + } + return body.toString(); + } + + public static String formatParameters(Map parameters) { + StringBuilder body = new StringBuilder(); + Iterator> it = parameters.entrySet().iterator(); + it.hasNext(); + Entry parameter = it.next(); + body.append(String.format("%s=%s", URLEncodingUtil.encoding( + parameter.getKey(), Consts.UTF_8, true), URLEncodingUtil + .encoding(parameter.getValue(), Consts.UTF_8, true))); + while (it.hasNext()) { + parameter = it.next(); + body.append(PARAMETER_SEPARATOR).append( + String.format("%s=%s", URLEncodingUtil.encoding( + parameter.getKey(), Consts.UTF_8, true), + URLEncodingUtil.encoding(parameter.getValue(), + Consts.UTF_8, true))); } return body.toString(); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpClientFactory.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpClientFactory.java new file mode 100644 index 00000000..673c0249 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpClientFactory.java @@ -0,0 +1,76 @@ +package com.foxinmy.weixin4j.http.factory; + +import com.foxinmy.weixin4j.http.HttpClient; + +/** + * HttpClient工厂生产类:参考netty的InternalLoggerFactory + * + * @className HttpClientFactory + * @author jy + * @date 2015年8月12日 + * @since JDK 1.7 + * @see + */ +public abstract class HttpClientFactory { + + /** + * 默认的HttpClient + */ + private static volatile HttpClientFactory defaultFactory = newDefaultFactory(); + + /** + * ApachHttpClient -> SimpleHttpClient(HttpURLConnection) + * + * @return + */ + private static HttpClientFactory newDefaultFactory() { + HttpClientFactory f; + try { + f = new HttpComponent4Factory(); + } catch (Throwable e1) { + try { + f = new HttpComponent3Factory(); + } catch (Throwable e2) { + f = new SimpleHttpClientFactory(); + } + } + return f; + } + + /** + * 获取默认的HttpClient + * + * @return + */ + public static HttpClientFactory getDefaultFactory() { + return defaultFactory; + } + + /** + * 显式设置默认的HttpClient + * + * @param defaultFactory + */ + public static void setDefaultFactory(HttpClientFactory defaultFactory) { + if (defaultFactory == null) { + throw new NullPointerException("defaultFactory"); + } + HttpClientFactory.defaultFactory = defaultFactory; + } + + /** + * 获取HttpClient实例 + * + * @return + */ + public static HttpClient getInstance() { + return getDefaultFactory().newInstance(); + } + + /** + * 获取HttpClient实例 + * + * @return + */ + public abstract HttpClient newInstance(); +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3.java new file mode 100644 index 00000000..619dcb16 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3.java @@ -0,0 +1,202 @@ +package com.foxinmy.weixin4j.http.factory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.net.ssl.SSLContext; + +import org.apache.commons.httpclient.ConnectTimeoutException; +import org.apache.commons.httpclient.URI; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.DeleteMethod; +import org.apache.commons.httpclient.methods.EntityEnclosingMethod; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.methods.InputStreamRequestEntity; +import org.apache.commons.httpclient.methods.OptionsMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.RequestEntity; +import org.apache.commons.httpclient.methods.TraceMethod; +import org.apache.commons.httpclient.params.HttpConnectionParams; +import org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory; +import org.apache.commons.httpclient.protocol.Protocol; +import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; + +import com.foxinmy.weixin4j.http.AbstractHttpClient; +import com.foxinmy.weixin4j.http.HttpClient; +import com.foxinmy.weixin4j.http.HttpClientException; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpMethod; +import com.foxinmy.weixin4j.http.HttpParams; +import com.foxinmy.weixin4j.http.HttpRequest; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.apache.MultipartEntity; +import com.foxinmy.weixin4j.http.entity.HttpEntity; +import com.foxinmy.weixin4j.model.Consts; +import com.foxinmy.weixin4j.util.StringUtil; + +public class HttpComponent3 extends AbstractHttpClient implements HttpClient { + + private final org.apache.commons.httpclient.HttpClient httpClient; + + private final Map methodMap; + + public HttpComponent3(org.apache.commons.httpclient.HttpClient httpClient) { + this.httpClient = httpClient; + this.methodMap = new HashMap(); + methodMap.put(HttpMethod.GET, new GetMethod()); + methodMap.put(HttpMethod.HEAD, new HeadMethod()); + methodMap.put(HttpMethod.POST, new PostMethod()); + methodMap.put(HttpMethod.PUT, new PutMethod()); + methodMap.put(HttpMethod.DELETE, new DeleteMethod()); + methodMap.put(HttpMethod.OPTIONS, new OptionsMethod()); + } + + @Override + public HttpResponse execute(HttpRequest request) throws HttpClientException { + HttpResponse response = null; + try { + URI uri = new URI(request.getURI().toString(), false, + Consts.UTF_8.name()); + org.apache.commons.httpclient.HttpMethod httpMethod = methodMap + .get(request.getMethod()); + if (request.getMethod() == HttpMethod.TRACE) { + httpMethod = new TraceMethod(uri.getEscapedURI()); + } else { + httpMethod.setURI(uri); + } + HttpParams params = request.getParams(); + if (params != null) { + Proxy proxy = params.getProxy(); + if (proxy != null) { + InetSocketAddress socketAddress = (InetSocketAddress) proxy + .address(); + httpClient.getHostConfiguration().setProxy( + socketAddress.getHostName(), + socketAddress.getPort()); + } + SSLContext sslContext = params.getSSLContext(); + if (sslContext != null) { + Protocol protocol = new Protocol("https", + new SSLProtocolSocketFactory(sslContext), 443); + httpClient.getHostConfiguration().setHost(uri.getHost(), + uri.getPort(), protocol); + } + httpClient.getHttpConnectionManager().getParams() + .setConnectionTimeout(params.getConnectTimeout()); + httpClient.getHttpConnectionManager().getParams() + .setSendBufferSize(params.getChunkSize()); + httpMethod.getParams().setUriCharset(Consts.UTF_8.name()); + httpMethod.getParams().setSoTimeout(params.getSocketTimeout()); + httpMethod.getParams().setContentCharset(Consts.UTF_8.name()); + } + com.foxinmy.weixin4j.http.HttpHeaders headers = request + .getHeaders(); + if (headers != null) { + for (Iterator>> headerIterator = headers + .entrySet().iterator(); headerIterator.hasNext();) { + Entry> header = headerIterator.next(); + if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) { + httpMethod.setRequestHeader(header.getKey(), + StringUtil.join(header.getValue(), ';')); + } else { + for (String headerValue : header.getValue()) { + httpMethod.addRequestHeader(header.getKey(), + headerValue != null ? headerValue : ""); + } + } + } + } + HttpEntity entity = request.getEntity(); + if (entity != null) { + if (entity.getContentLength() > 0l) { + httpMethod.addRequestHeader(HttpHeaders.CONTENT_LENGTH, + Long.toString(entity.getContentLength())); + } + if (entity.getContentType() != null) { + httpMethod.addRequestHeader(HttpHeaders.CONTENT_TYPE, + entity.getContentType().toString()); + } + RequestEntity requestEntity = null; + if (entity instanceof MultipartEntity) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + entity.writeTo(os); + os.flush(); + requestEntity = new ByteArrayRequestEntity( + os.toByteArray(), entity.getContentType() + .toString()); + os.close(); + } else { + requestEntity = new InputStreamRequestEntity( + entity.getContent(), entity.getContentType() + .toString()); + } + ((EntityEnclosingMethod) httpMethod) + .setRequestEntity(requestEntity); + } + httpClient.executeMethod(httpMethod); + response = new HttpComponent3Response(httpMethod); + } catch (IOException e) { + throw new HttpClientException("I/O error on " + + request.getMethod().name() + " request for \"" + + request.getURI().toString() + "\":" + e.getMessage(), e); + } finally { + if (response != null) { + response.close(); + } + } + return response; + } + + private static class SSLProtocolSocketFactory implements + ProtocolSocketFactory { + + private final SSLContext sslContext; + + public SSLProtocolSocketFactory(SSLContext sslContext) { + this.sslContext = sslContext; + } + + @Override + public Socket createSocket(String host, int port, + InetAddress localAddress, int localPort) throws IOException, + UnknownHostException { + return sslContext.getSocketFactory().createSocket(host, port, + localAddress, localPort); + } + + @Override + public Socket createSocket(String host, int port, + InetAddress localAddress, int localPort, + HttpConnectionParams params) throws IOException, + UnknownHostException, ConnectTimeoutException { + if (params == null) { + throw new IllegalArgumentException("Parameters may not be null"); + } + int timeout = params.getConnectionTimeout(); + if (timeout == 0) { + return createSocket(host, port, localAddress, localPort); + } else { + return ControllerThreadSocketFactory.createSocket(this, host, + port, localAddress, localPort, timeout); + } + } + + @Override + public Socket createSocket(String host, int port) throws IOException, + UnknownHostException { + return sslContext.getSocketFactory().createSocket(host, port); + } + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Factory.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Factory.java new file mode 100644 index 00000000..995a57be --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Factory.java @@ -0,0 +1,20 @@ +package com.foxinmy.weixin4j.http.factory; + +import org.apache.commons.httpclient.HttpClient; + +/** + * 使用Apache的HttpClient3.x + * + * @className HttpComponent3Factory + * @author jy + * @date 2015年8月12日 + * @since JDK 1.7 + * @see + */ +public class HttpComponent3Factory extends HttpClientFactory { + + @Override + public com.foxinmy.weixin4j.http.HttpClient newInstance() { + return new HttpComponent3(new HttpClient()); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Response.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Response.java new file mode 100644 index 00000000..0fe3bb42 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Response.java @@ -0,0 +1,90 @@ +package com.foxinmy.weixin4j.http.factory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpMethod; + +import com.foxinmy.weixin4j.http.HttpClientException; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.HttpStatus; +import com.foxinmy.weixin4j.http.HttpVersion; + +/** + * HttpComponent3 Response + * + * @className HttpComponent3Response + * @author jy + * @date 2015年8月17日 + * @since JDK 1.7 + * @see + */ +public class HttpComponent3Response implements HttpResponse { + + private final HttpMethod httpMethod; + + private HttpHeaders headers; + private HttpVersion protocol; + private HttpStatus status; + private InputStream body; + + public HttpComponent3Response(HttpMethod httpMethod) { + this.httpMethod = httpMethod; + try { + this.body = new ByteArrayInputStream(httpMethod.getResponseBody()); + } catch (IOException e) { + ; + } + } + + @Override + public HttpHeaders getHeaders() { + if (headers == null) { + headers = new HttpHeaders(); + Header[] headers = httpMethod.getResponseHeaders(); + for (Header header : headers) { + this.headers.add(header.getName(), header.getValue()); + } + } + return headers; + } + + @Override + public HttpVersion getProtocol() { + org.apache.commons.httpclient.HttpVersion version = httpMethod + .getParams().getVersion(); + if (version == null) { + return null; + } + Header connection = httpMethod.getResponseHeader("Connection"); + if (protocol == null) { + protocol = new HttpVersion("HTTP", version.getMinor(), + version.getMajor(), connection != null + && "keep-alive".equalsIgnoreCase(connection + .getValue())); + } + return protocol; + } + + @Override + public HttpStatus getStatus() throws HttpClientException { + if (status == null) { + status = new HttpStatus(httpMethod.getStatusCode(), + httpMethod.getStatusText()); + } + return status; + } + + @Override + public InputStream getBody() throws HttpClientException { + return body; + } + + @Override + public void close() { + httpMethod.releaseConnection(); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4Factory.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4Factory.java new file mode 100644 index 00000000..c757a1b9 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4Factory.java @@ -0,0 +1,34 @@ +package com.foxinmy.weixin4j.http.factory; + +import org.apache.http.util.VersionInfo; + +import com.foxinmy.weixin4j.http.HttpClient; + +/** + * 使用Apache的HttpClient4.x + * + * @className HttpComponent4Factory + * @author jy + * @date 2015年8月12日 + * @since JDK 1.7 + * @see + */ +public class HttpComponent4Factory extends HttpClientFactory { + + @Override + public HttpClient newInstance() { + VersionInfo version = VersionInfo.loadVersionInfo( + "org.apache.http.client", HttpClient.class.getClassLoader()); + String release = (version != null) ? version.getRelease() + : VersionInfo.UNAVAILABLE; + if (release.startsWith("4.")) { + if (release.startsWith("4.0") || release.startsWith("4.1") + || release.startsWith("4.2")) { + return new HttpComponent4_1(); + } else { + return new HttpComponent4_2(); + } + } + throw new RuntimeException("unknown the HttpClient version:" + release); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1.java new file mode 100644 index 00000000..0fda8be7 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1.java @@ -0,0 +1,157 @@ +package com.foxinmy.weixin4j.http.factory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.http.HttpHost; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.HttpTrace; +import org.apache.http.conn.params.ConnRoutePNames; +import org.apache.http.conn.scheme.Scheme; +import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.entity.AbstractHttpEntity; +import org.apache.http.entity.InputStreamEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; + +import com.foxinmy.weixin4j.http.AbstractHttpClient; +import com.foxinmy.weixin4j.http.HttpClient; +import com.foxinmy.weixin4j.http.HttpClientException; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpMethod; +import com.foxinmy.weixin4j.http.HttpParams; +import com.foxinmy.weixin4j.http.HttpRequest; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.apache.MultipartEntity; +import com.foxinmy.weixin4j.http.entity.HttpEntity; +import com.foxinmy.weixin4j.model.Consts; +import com.foxinmy.weixin4j.util.StringUtil; + +/** + * Requires Apache HttpComponents 4.2 or lower + * + * @className HttpComponent4_1 + * @author jy + * @date 2015年8月18日 + * @since JDK 1.7 + * @see + */ +public class HttpComponent4_1 extends AbstractHttpClient implements HttpClient { + + private final org.apache.http.client.HttpClient httpClient; + private final Map methodMap; + + public HttpComponent4_1() { + this.httpClient = new DefaultHttpClient(); + this.methodMap = new HashMap(); + methodMap.put(HttpMethod.GET, new HttpGet()); + methodMap.put(HttpMethod.HEAD, new HttpHead()); + methodMap.put(HttpMethod.POST, new HttpPost()); + methodMap.put(HttpMethod.PUT, new HttpPut()); + methodMap.put(HttpMethod.DELETE, new HttpDelete()); + methodMap.put(HttpMethod.OPTIONS, new HttpOptions()); + methodMap.put(HttpMethod.TRACE, new HttpTrace()); + } + + @Override + public HttpResponse execute(HttpRequest request) throws HttpClientException { + HttpResponse response = null; + try { + HttpRequestBase uriRequest = methodMap.get(request.getMethod()); + uriRequest.setURI(request.getURI()); + HttpParams params = request.getParams(); + if (params != null) { + if (params.getProxy() != null) { + InetSocketAddress socketAddress = (InetSocketAddress) params + .getProxy().address(); + HttpHost proxy = new HttpHost(socketAddress.getHostName(), + socketAddress.getPort()); + uriRequest.getParams().setParameter( + ConnRoutePNames.DEFAULT_PROXY, proxy); + } + uriRequest.getParams().setIntParameter( + CoreConnectionPNames.SOCKET_BUFFER_SIZE, + params.getChunkSize()); + uriRequest.getParams().setIntParameter( + CoreConnectionPNames.SO_TIMEOUT, + params.getSocketTimeout()); + uriRequest.getParams().setIntParameter( + CoreConnectionPNames.CONNECTION_TIMEOUT, + params.getConnectTimeout()); + uriRequest.getParams().setParameter( + CoreProtocolPNames.HTTP_CONTENT_CHARSET, Consts.UTF_8); + uriRequest.getParams().setParameter( + HttpHeaders.CONTENT_ENCODING, Consts.UTF_8); + uriRequest.getParams().setParameter(HttpHeaders.ACCEPT_CHARSET, + Consts.UTF_8); + if (params.getSSLContext() != null) { + SSLSocketFactory socketFactory = new SSLSocketFactory( + params.getSSLContext()); + Scheme scheme = new Scheme("https", socketFactory, 433); + httpClient.getConnectionManager().getSchemeRegistry() + .register(scheme); + } + } + HttpHeaders headers = request.getHeaders(); + if (headers != null) { + for (Iterator>> headerIterator = headers + .entrySet().iterator(); headerIterator.hasNext();) { + Entry> header = headerIterator.next(); + if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) { + uriRequest.addHeader(header.getKey(), + StringUtil.join(header.getValue(), ';')); + } else { + for (String headerValue : header.getValue()) { + uriRequest.addHeader(header.getKey(), + headerValue != null ? headerValue : ""); + } + } + } + } + HttpEntity entity = request.getEntity(); + if (entity != null) { + AbstractHttpEntity httpEntity = null; + if (entity instanceof MultipartEntity) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + entity.writeTo(os); + os.flush(); + httpEntity = new org.apache.http.entity.ByteArrayEntity( + os.toByteArray()); + os.close(); + } else { + httpEntity = new InputStreamEntity(entity.getContent(), + entity.getContentLength()); + } + httpEntity.setContentType(entity.getContentType().toString()); + ((HttpEntityEnclosingRequestBase) uriRequest) + .setEntity(httpEntity); + } + org.apache.http.HttpResponse httpResponse = httpClient + .execute(uriRequest); + response = new HttpComponent4_1Response(httpClient, httpResponse); + } catch (IOException e) { + throw new HttpClientException("I/O error on " + + request.getMethod().name() + " request for \"" + + request.getURI().toString() + "\":" + e.getMessage(), e); + } finally { + if (response != null) { + response.close(); + } + } + return response; + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1Response.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1Response.java new file mode 100644 index 00000000..c1175617 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1Response.java @@ -0,0 +1,90 @@ +package com.foxinmy.weixin4j.http.factory; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.ProtocolVersion; +import org.apache.http.StatusLine; + +import com.foxinmy.weixin4j.http.HttpClientException; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.HttpStatus; +import com.foxinmy.weixin4j.http.HttpVersion; + +/** + * HttpComponent4 Response:Requires Apache HttpComponents 4.2 or lower + * + * @className HttpComponent4_1Response + * @author jy + * @date 2015年8月18日 + * @since JDK 1.7 + * @see + */ +public class HttpComponent4_1Response implements HttpResponse { + private final org.apache.http.client.HttpClient httpClient; + private final org.apache.http.HttpResponse httpResponse; + + private HttpHeaders headers; + private HttpVersion protocol; + private HttpStatus status; + + public HttpComponent4_1Response( + org.apache.http.client.HttpClient httpClient, + org.apache.http.HttpResponse httpResponse) { + this.httpClient = httpClient; + this.httpResponse = httpResponse; + } + + @Override + public HttpHeaders getHeaders() { + if (headers == null) { + headers = new HttpHeaders(); + Header[] headers = httpResponse.getAllHeaders(); + for (Header header : headers) { + this.headers.add(header.getName(), header.getValue()); + } + } + return headers; + } + + @Override + public HttpVersion getProtocol() { + if (protocol == null) { + ProtocolVersion version = httpResponse.getProtocolVersion(); + Header connection = httpResponse.getFirstHeader("Connection"); + protocol = new HttpVersion(version.getProtocol(), + version.getMajor(), version.getMinor(), connection != null + && connection.getValue().equalsIgnoreCase( + "keep-alive")); + } + return protocol; + } + + @Override + public HttpStatus getStatus() throws HttpClientException { + if (status == null) { + StatusLine statusLine = httpResponse.getStatusLine(); + status = new HttpStatus(statusLine.getStatusCode(), + statusLine.getReasonPhrase()); + } + return status; + } + + @Override + public InputStream getBody() throws HttpClientException { + try { + HttpEntity entity = this.httpResponse.getEntity(); + return (entity != null ? entity.getContent() : null); + } catch (IOException e) { + throw new HttpClientException("I/O Error on getBody", e); + } + } + + @Override + public void close() { + httpClient.getConnectionManager().closeExpiredConnections(); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2.java new file mode 100644 index 00000000..5ba8ddbe --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2.java @@ -0,0 +1,141 @@ +package com.foxinmy.weixin4j.http.factory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.config.RequestConfig.Builder; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.HttpTrace; +import org.apache.http.entity.AbstractHttpEntity; +import org.apache.http.entity.InputStreamEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + +import com.foxinmy.weixin4j.http.AbstractHttpClient; +import com.foxinmy.weixin4j.http.HttpClient; +import com.foxinmy.weixin4j.http.HttpClientException; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpMethod; +import com.foxinmy.weixin4j.http.HttpParams; +import com.foxinmy.weixin4j.http.HttpRequest; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.apache.MultipartEntity; +import com.foxinmy.weixin4j.http.entity.HttpEntity; +import com.foxinmy.weixin4j.model.Consts; +import com.foxinmy.weixin4j.util.StringUtil; + +/** + * Requires Apache HttpComponents 4.3 or higher + * + * @className HttpComponent4_2 + * @author jy + * @date 2015年8月18日 + * @since JDK 1.7 + * @see + */ +public class HttpComponent4_2 extends AbstractHttpClient implements HttpClient { + + private CloseableHttpClient httpClient; + private final Map methodMap; + + public HttpComponent4_2() { + this.httpClient = HttpClients.createDefault(); + this.methodMap = new HashMap(); + methodMap.put(HttpMethod.GET, new HttpGet()); + methodMap.put(HttpMethod.HEAD, new HttpHead()); + methodMap.put(HttpMethod.POST, new HttpPost()); + methodMap.put(HttpMethod.PUT, new HttpPut()); + methodMap.put(HttpMethod.DELETE, new HttpDelete()); + methodMap.put(HttpMethod.OPTIONS, new HttpOptions()); + methodMap.put(HttpMethod.TRACE, new HttpTrace()); + } + + @Override + public HttpResponse execute(HttpRequest request) throws HttpClientException { + HttpResponse response = null; + try { + HttpRequestBase uriRequest = methodMap.get(request.getMethod()); + uriRequest.setURI(request.getURI()); + HttpParams params = request.getParams(); + if (params != null) { + Builder requestConfig = RequestConfig.custom() + .setSocketTimeout(params.getSocketTimeout()) + .setConnectTimeout(params.getConnectTimeout()) + .setConnectionRequestTimeout(params.getReadTimeout()); + if (params.getProxy() != null) { + InetSocketAddress socketAddress = (InetSocketAddress) params + .getProxy().address(); + HttpHost proxy = new HttpHost(socketAddress.getHostName(), + socketAddress.getPort()); + requestConfig.setProxy(proxy); + } + if (params.getSSLContext() != null) { + httpClient = HttpClients.custom() + .setSslcontext(params.getSSLContext()).build(); + } + uriRequest.setConfig(requestConfig.build()); + } + HttpHeaders headers = request.getHeaders(); + if (headers != null) { + for (Iterator>> headerIterator = headers + .entrySet().iterator(); headerIterator.hasNext();) { + Entry> header = headerIterator.next(); + if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) { + uriRequest.addHeader(header.getKey(), + StringUtil.join(header.getValue(), ';')); + } else { + for (String headerValue : header.getValue()) { + uriRequest.addHeader(header.getKey(), + headerValue != null ? headerValue : ""); + } + } + } + } + HttpEntity entity = request.getEntity(); + if (entity != null) { + AbstractHttpEntity httpEntity = null; + if (entity instanceof MultipartEntity) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + entity.writeTo(os); + os.flush(); + httpEntity = new org.apache.http.entity.ByteArrayEntity( + os.toByteArray()); + os.close(); + } else { + httpEntity = new InputStreamEntity(entity.getContent(), + entity.getContentLength()); + } + httpEntity.setContentType(entity.getContentType().toString()); + ((HttpEntityEnclosingRequestBase) uriRequest) + .setEntity(httpEntity); + } + CloseableHttpResponse httpResponse = httpClient.execute(uriRequest); + response = new HttpComponent4_2Response(httpResponse); + } catch (IOException e) { + throw new HttpClientException("I/O error on " + + request.getMethod().name() + " request for \"" + + request.getURI().toString() + "\":" + e.getMessage(), e); + } finally { + if (response != null) { + response.close(); + } + } + return response; + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2Response.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2Response.java new file mode 100644 index 00000000..c1f7a4d4 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2Response.java @@ -0,0 +1,94 @@ +package com.foxinmy.weixin4j.http.factory; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.ProtocolVersion; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.util.EntityUtils; + +import com.foxinmy.weixin4j.http.HttpClientException; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.HttpStatus; +import com.foxinmy.weixin4j.http.HttpVersion; + +/** + * HttpComponent4 Response:Requires Apache HttpComponents 4.3 or higher + * + * @className HttpComponent4_2Response + * @author jy + * @date 2015年8月18日 + * @since JDK 1.7 + * @see + */ +public class HttpComponent4_2Response implements HttpResponse { + + private final CloseableHttpResponse httpResponse; + + private HttpHeaders headers; + private HttpVersion protocol; + private HttpStatus status; + + public HttpComponent4_2Response(CloseableHttpResponse httpResponse) { + this.httpResponse = httpResponse; + } + + @Override + public HttpHeaders getHeaders() { + if (headers == null) { + headers = new HttpHeaders(); + Header[] headers = httpResponse.getAllHeaders(); + for (Header header : headers) { + this.headers.add(header.getName(), header.getValue()); + } + } + return headers; + } + + @Override + public HttpVersion getProtocol() { + if (protocol == null) { + ProtocolVersion version = httpResponse.getProtocolVersion(); + Header connection = httpResponse.getFirstHeader("Connection"); + protocol = new HttpVersion(version.getProtocol(), + version.getMajor(), version.getMinor(), connection != null + && connection.getValue().equalsIgnoreCase( + "keep-alive")); + } + return protocol; + } + + @Override + public HttpStatus getStatus() throws HttpClientException { + if (status == null) { + StatusLine statusLine = httpResponse.getStatusLine(); + status = new HttpStatus(statusLine.getStatusCode(), + statusLine.getReasonPhrase()); + } + return status; + } + + @Override + public InputStream getBody() throws HttpClientException { + try { + HttpEntity entity = this.httpResponse.getEntity(); + return (entity != null ? entity.getContent() : null); + } catch (IOException e) { + throw new HttpClientException("I/O Error on getBody", e); + } + } + + @Override + public void close() { + try { + // EntityUtils.consume(httpResponse.getEntity()); + httpResponse.close(); + } catch (IOException ex) { + ; + } + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/SimpleHttpClientFactory.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/SimpleHttpClientFactory.java new file mode 100644 index 00000000..806ee5a0 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/SimpleHttpClientFactory.java @@ -0,0 +1,21 @@ +package com.foxinmy.weixin4j.http.factory; + +import com.foxinmy.weixin4j.http.HttpClient; +import com.foxinmy.weixin4j.http.SimpleHttpClient; + +/** + * HttpURLConnection的简单实现 + * + * @className SimpleHttpClientFactory + * @author jy + * @date 2015年8月12日 + * @since JDK 1.7 + * @see com.foxinmy.weixin4j.http.SimpleHttpClient + */ +public class SimpleHttpClientFactory extends HttpClientFactory { + + @Override + public HttpClient newInstance() { + return new SimpleHttpClient(); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinHttpClient.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinHttpClient.java deleted file mode 100644 index ec7477e6..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinHttpClient.java +++ /dev/null @@ -1,214 +0,0 @@ -package com.foxinmy.weixin4j.http.weixin; - -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.Map; - -import com.alibaba.fastjson.JSONException; -import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.ContentType; -import com.foxinmy.weixin4j.http.Header; -import com.foxinmy.weixin4j.http.HttpGet; -import com.foxinmy.weixin4j.http.HttpPost; -import com.foxinmy.weixin4j.http.HttpRequest; -import com.foxinmy.weixin4j.http.HttpResponse; -import com.foxinmy.weixin4j.http.SimpleHttpClient; -import com.foxinmy.weixin4j.http.StatusLine; -import com.foxinmy.weixin4j.http.UrlEncodeParameter; -import com.foxinmy.weixin4j.http.apache.FormBodyPart; -import com.foxinmy.weixin4j.http.apache.HttpHeaders; -import com.foxinmy.weixin4j.http.apache.MultipartEntity; -import com.foxinmy.weixin4j.http.entity.ByteArrayEntity; -import com.foxinmy.weixin4j.http.entity.FileEntity; -import com.foxinmy.weixin4j.http.entity.FormUrlEntity; -import com.foxinmy.weixin4j.http.entity.StringEntity; -import com.foxinmy.weixin4j.model.Consts; -import com.foxinmy.weixin4j.util.ErrorUtil; -import com.foxinmy.weixin4j.util.MapUtil; -import com.foxinmy.weixin4j.util.StringUtil; -import com.foxinmy.weixin4j.xml.XmlStream; - -public class WeixinHttpClient extends SimpleHttpClient { - - @Override - protected Map createDefualtHeader() { - Map params = super.createDefualtHeader(); - params.put(HttpHeaders.USER_AGENT, "weixin4j client/1.5"); - return params; - } - - public WeixinResponse get(String url) throws WeixinException { - return get(url, (UrlEncodeParameter[]) null); - } - - public WeixinResponse get(String url, Map para) - throws WeixinException { - return get( - String.format("%s?%s", url, - MapUtil.toJoinString(para, false, false, null)), - (UrlEncodeParameter[]) null); - } - - public WeixinResponse get(String url, UrlEncodeParameter... parameters) - throws WeixinException { - StringBuilder sb = new StringBuilder(url); - if (parameters != null && parameters.length > 0) { - if (url.indexOf("?") < 0) { - sb.append("?"); - } else { - sb.append("&"); - } - sb.append(parameters[0].encodingParameter()); - if (parameters.length > 1) { - for (int i = 1; i < parameters.length; i++) { - sb.append(parameters[i].encodingParameter()); - } - } - } - return doRequest(new HttpGet(sb.toString())); - } - - public WeixinResponse post(String url) throws WeixinException { - return post(url, (UrlEncodeParameter[]) null); - } - - public WeixinResponse post(String url, UrlEncodeParameter... parameters) - throws WeixinException { - HttpPost method = new HttpPost(url); - if (parameters != null && parameters.length > 0) { - method.setEntity(new FormUrlEntity(Arrays.asList(parameters))); - } - return doRequest(method); - } - - public WeixinResponse post(String url, String body) throws WeixinException { - HttpPost method = new HttpPost(url); - method.setEntity(new StringEntity(body)); - return doRequest(method); - } - - public WeixinResponse post(String url, byte[] bytes) throws WeixinException { - HttpPost method = new HttpPost(url); - method.setEntity(new ByteArrayEntity(bytes, - ContentType.MULTIPART_FORM_DATA)); - return doRequest(method); - } - - public WeixinResponse post(String url, File file) throws WeixinException { - HttpPost method = new HttpPost(url); - method.setEntity(new FileEntity(file)); - return doRequest(method); - } - - public WeixinResponse post(String url, FormBodyPart... paramters) - throws WeixinException { - HttpPost method = new HttpPost(url); - MultipartEntity entity = new MultipartEntity(); - for (FormBodyPart paramter : paramters) { - entity.addPart(paramter); - } - method.setEntity(entity); - - return doRequest(method); - } - - protected WeixinResponse doRequest(HttpRequest request) - throws WeixinException { - WeixinResponse response = null; - try { - HttpResponse httpResponse = execute(request); - StatusLine statusLine = httpResponse.getStatusLine(); - if (statusLine.getStatusCode() >= 300) { - throw new WeixinException(String.format("request fail : %d-%s", - statusLine.getStatusCode(), statusLine.getStatusText())); - } - response = new WeixinResponse(); - response.setContent(httpResponse.getContent()); - response.setHeaders(httpResponse.getAllHeaders()); - response.setHttpVersion(httpResponse.getHttpVersion()); - response.setStatusLine(httpResponse.getStatusLine()); - Header contentType = httpResponse - .getFirstHeader(HttpHeaders.CONTENT_TYPE); - Header disposition = httpResponse - .getFirstHeader("Content-disposition"); - // json - if (contentType.getValue().contains( - ContentType.APPLICATION_JSON.getMimeType()) - || (disposition != null && disposition.getValue().indexOf( - ".json") > 0)) { - checkJson(response); - } else if (contentType.getValue().contains( - ContentType.TEXT_XML.getMimeType())) { - checkXml(response); - } else if (contentType.getValue().contains( - ContentType.TEXT_PLAIN.getMimeType()) - || contentType.getValue().contains( - ContentType.TEXT_HTML.getMimeType())) { - try { - checkJson(response); - return response; - } catch (JSONException e) { - ; - } - try { - checkXml(response); - return response; - } catch (IllegalArgumentException ex) { - ; - } - throw new WeixinException(response.getAsString()); - } - } catch (IOException e) { - throw new WeixinException(e.getMessage()); - } finally { - // request.releaseConnection(); - } - return response; - } - - private void checkJson(WeixinResponse response) throws WeixinException { - JsonResult jsonResult = response.getAsJsonResult(); - response.setJsonResult(true); - if (jsonResult.getCode() != 0) { - if (StringUtil.isBlank(jsonResult.getDesc())) { - jsonResult.setDesc(ErrorUtil.getText(Integer - .toString(jsonResult.getCode()))); - } - throw new WeixinException(Integer.toString(jsonResult.getCode()), - jsonResult.getDesc()); - } - } - - private void checkXml(WeixinResponse response) throws WeixinException { - XmlResult xmlResult = null; - try { - xmlResult = response.getAsXmlResult(); - } catch (IllegalArgumentException ex) { - // - String newXml = response.getAsString() - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", ""); - xmlResult = XmlStream.fromXML(newXml, XmlResult.class); - response.setContent(newXml.getBytes(Consts.UTF_8)); - } - response.setXmlResult(true); - if (xmlResult.getReturnCode().equals("0")) { - return; - } - if (!xmlResult.getReturnCode().equalsIgnoreCase( - com.foxinmy.weixin4j.model.Consts.SUCCESS)) { - throw new WeixinException(xmlResult.getReturnCode(), - xmlResult.getReturnMsg()); - } - if (!xmlResult.getResultCode().equalsIgnoreCase( - com.foxinmy.weixin4j.model.Consts.SUCCESS)) { - throw new WeixinException(xmlResult.getErrCode(), - xmlResult.getErrCodeDes()); - } - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java new file mode 100644 index 00000000..c240770a --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java @@ -0,0 +1,194 @@ +package com.foxinmy.weixin4j.http.weixin; + +import java.util.Map; + +import com.alibaba.fastjson.JSONException; +import com.foxinmy.weixin4j.exception.WeixinException; +import com.foxinmy.weixin4j.http.ContentType; +import com.foxinmy.weixin4j.http.HttpClient; +import com.foxinmy.weixin4j.http.HttpClientException; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpMethod; +import com.foxinmy.weixin4j.http.HttpParams; +import com.foxinmy.weixin4j.http.HttpRequest; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.HttpStatus; +import com.foxinmy.weixin4j.http.apache.FormBodyPart; +import com.foxinmy.weixin4j.http.apache.HttpMultipartMode; +import com.foxinmy.weixin4j.http.apache.MultipartEntity; +import com.foxinmy.weixin4j.http.entity.FormUrlEntity; +import com.foxinmy.weixin4j.http.entity.HttpEntity; +import com.foxinmy.weixin4j.http.entity.StringEntity; +import com.foxinmy.weixin4j.http.factory.HttpClientFactory; +import com.foxinmy.weixin4j.http.factory.HttpComponent4Factory; +import com.foxinmy.weixin4j.model.Consts; +import com.foxinmy.weixin4j.util.StringUtil; +import com.foxinmy.weixin4j.util.WeixinErrorUtil; +import com.foxinmy.weixin4j.xml.XmlStream; + +/** + * 负责微信请求的执行 + * + * @className WeixinRequestExecutor + * @author jy + * @date 2015年8月15日 + * @since JDK 1.7 + * @see + */ +public class WeixinRequestExecutor { + + protected final HttpClient httpClient; + protected final HttpParams params; + + public WeixinRequestExecutor() { + this(new HttpParams()); + } + + public WeixinRequestExecutor(HttpParams params) { + HttpClientFactory.setDefaultFactory(new HttpComponent4Factory()); + this.httpClient = HttpClientFactory.getInstance(); + this.params = params; + } + + public WeixinResponse get(String url) throws WeixinException { + HttpRequest request = new HttpRequest(HttpMethod.GET, url); + return doRequest(request); + } + + public WeixinResponse get(String url, Map parameters) + throws WeixinException { + StringBuilder buf = new StringBuilder(url); + if (parameters != null && !parameters.isEmpty()) { + if (url.indexOf("?") < 0) { + buf.append("?"); + } else { + buf.append("&"); + } + buf.append(FormUrlEntity.formatParameters(parameters)); + } + return doRequest(new HttpRequest(HttpMethod.GET, buf.toString())); + } + + public WeixinResponse post(String url) throws WeixinException { + HttpRequest request = new HttpRequest(HttpMethod.POST, url); + return doRequest(request); + } + + public WeixinResponse post(String url, String body) throws WeixinException { + HttpEntity entity = new StringEntity(body); + HttpRequest request = new HttpRequest(HttpMethod.POST, url); + request.setEntity(entity); + return doRequest(request); + } + + public WeixinResponse post(String url, FormBodyPart... bodyParts) + throws WeixinException { + MultipartEntity entity = new MultipartEntity(HttpMultipartMode.STRICT, + null, Consts.UTF_8); + for (FormBodyPart bodyPart : bodyParts) { + entity.addPart(bodyPart); + } + HttpRequest request = new HttpRequest(HttpMethod.POST, url); + request.setEntity(entity); + return doRequest(request); + } + + public WeixinResponse doRequest(HttpRequest request) throws WeixinException { + request.setParams(params); + try { + HttpResponse httpResponse = httpClient.execute(request); + HttpStatus status = httpResponse.getStatus(); + HttpHeaders headers = httpResponse.getHeaders(); + if (status.getStatusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) { + throw new WeixinException(String.format("request fail : %d-%s", + status.getStatusCode(), status.getStatusText())); + } + WeixinResponse response = new WeixinResponse(headers, status, + httpResponse.getBody()); + String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE); + String disposition = headers + .getFirst(HttpHeaders.CONTENT_DISPOSITION); + // json + if (contentType + .contains(ContentType.APPLICATION_JSON.getMimeType()) + || (disposition != null && disposition.indexOf(".json") > 0)) { + checkJson(response); + } else if (contentType.contains(ContentType.TEXT_XML.getMimeType())) { + checkXml(response); + } else if (contentType.contains(ContentType.TEXT_PLAIN + .getMimeType()) + || contentType + .contains(ContentType.TEXT_HTML.getMimeType())) { + try { + checkJson(response); + return response; + } catch (JSONException e) { + ; + } + try { + checkXml(response); + return response; + } catch (IllegalArgumentException ex) { + ; + } + throw new WeixinException(response.getAsString()); + } + return response; + } catch (HttpClientException e) { + throw new WeixinException(e); + } + } + + protected void checkJson(WeixinResponse response) throws WeixinException { + JsonResult jsonResult = response.getAsJsonResult(); + response.setJsonResult(true); + if (jsonResult.getCode() != 0) { + if (StringUtil.isBlank(jsonResult.getDesc())) { + jsonResult.setDesc(WeixinErrorUtil.getText(Integer + .toString(jsonResult.getCode()))); + } + throw new WeixinException(Integer.toString(jsonResult.getCode()), + jsonResult.getDesc()); + } + } + + protected void checkXml(WeixinResponse response) throws WeixinException { + XmlResult xmlResult = null; + try { + xmlResult = response.getAsXmlResult(); + } catch (IllegalArgumentException ex) { + // + String newXml = response.getAsString() + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", ""); + xmlResult = XmlStream.fromXML(newXml, XmlResult.class); + response.setText(newXml); + } + response.setXmlResult(true); + if (xmlResult.getReturnCode().equals("0")) { + return; + } + if (!xmlResult.getReturnCode().equalsIgnoreCase( + com.foxinmy.weixin4j.model.Consts.SUCCESS)) { + throw new WeixinException(xmlResult.getReturnCode(), + xmlResult.getReturnMsg()); + } + if (!xmlResult.getResultCode().equalsIgnoreCase( + com.foxinmy.weixin4j.model.Consts.SUCCESS)) { + throw new WeixinException(xmlResult.getErrCode(), + xmlResult.getErrCodeDes()); + } + } + + public HttpClient getExecuteClient() { + return httpClient; + } + + public HttpParams getExecuteParams() { + return params; + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java index 131f795f..55b06cc0 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java @@ -1,17 +1,33 @@ package com.foxinmy.weixin4j.http.weixin; +import java.io.IOException; +import java.io.InputStream; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; -import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpStatus; +import com.foxinmy.weixin4j.util.IOUtil; import com.foxinmy.weixin4j.util.StringUtil; import com.foxinmy.weixin4j.xml.XmlStream; -public class WeixinResponse extends HttpResponse { +public class WeixinResponse { private boolean isJsonResult; private boolean isXmlResult; - private String text; + private volatile String text; + + private final HttpHeaders headers; + private final HttpStatus status; + private final InputStream body; + + public WeixinResponse(HttpHeaders headers, HttpStatus status, + InputStream body) { + this.headers = headers; + this.status = status; + this.body = body; + } public void setJsonResult(boolean isJsonResult) { this.isJsonResult = isJsonResult; @@ -23,11 +39,20 @@ public class WeixinResponse extends HttpResponse { public String getAsString() { if (text == null) { - text = StringUtil.newStringUtf8(getContent()); + try { + text = StringUtil.newStringUtf8(IOUtil.toByteArray(body)); + } catch (IOException e) { + e.printStackTrace(); + ; + } } return text; } + public void setText(String text) { + this.text = text; + } + public JsonResult getAsJsonResult() { return JSON.parseObject(getAsString(), JsonResult.class); } @@ -51,4 +76,16 @@ public class WeixinResponse extends HttpResponse { public XmlResult getAsXmlResult() { return XmlStream.fromXML(getAsString(), XmlResult.class); } + + public HttpHeaders getHeaders() { + return headers; + } + + public HttpStatus getStatus() { + return status; + } + + public InputStream getBody() { + return body; + } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/SSLHttpClinet.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinSSLRequestExecutor.java similarity index 53% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/SSLHttpClinet.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinSSLRequestExecutor.java index 404d09db..07fba7c2 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/SSLHttpClinet.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinSSLRequestExecutor.java @@ -1,60 +1,50 @@ -package com.foxinmy.weixin4j.http.weixin; - -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URI; -import java.net.URL; -import java.security.KeyStore; - -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; - -import com.foxinmy.weixin4j.exception.WeixinException; - -/** - * ssl请求 - * - * @className SSLHttpClinet - * @author jy - * @date 2014年11月6日 - * @since JDK 1.7 - * @see - */ -public class SSLHttpClinet extends WeixinHttpClient { - - private final SSLContext sslContext; - - public SSLHttpClinet(String password, InputStream inputStream) - throws WeixinException { - try { - KeyStore keyStore = KeyStore - .getInstance(com.foxinmy.weixin4j.model.Consts.PKCS12); - keyStore.load(inputStream, password.toCharArray()); - KeyManagerFactory kmf = KeyManagerFactory - .getInstance(com.foxinmy.weixin4j.model.Consts.SunX509); - kmf.init(keyStore, password.toCharArray()); - sslContext = SSLContext.getInstance("TLS"); - sslContext.init(kmf.getKeyManagers(), null, - new java.security.SecureRandom()); - } catch (Exception e) { - throw new WeixinException(e.getMessage()); - } - } - - public SSLHttpClinet(SSLContext sslContext) { - this.sslContext = sslContext; - } - - @Override - protected HttpURLConnection createHttpConnection(URI uri) - throws IOException { - URL url = uri.toURL(); - HttpsURLConnection connection = (HttpsURLConnection) url - .openConnection(); - connection.setSSLSocketFactory(sslContext.getSocketFactory()); - connection.setHostnameVerifier(super.createHostnameVerifier()); - return connection; - } -} +package com.foxinmy.weixin4j.http.weixin; + +import java.io.InputStream; +import java.security.KeyStore; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; + +import com.foxinmy.weixin4j.exception.WeixinException; + +/** + * 微信ssl请求 + * + * @className WeixinSSLRequestExecutor + * @author jy + * @date 2015年8月17日 + * @since JDK 1.7 + * @see + */ +public class WeixinSSLRequestExecutor extends WeixinRequestExecutor { + + private final SSLContext sslContext; + + public WeixinSSLRequestExecutor(String password, InputStream inputStream) + throws WeixinException { + try { + KeyStore keyStore = KeyStore + .getInstance(com.foxinmy.weixin4j.model.Consts.PKCS12); + keyStore.load(inputStream, password.toCharArray()); + KeyManagerFactory kmf = KeyManagerFactory + .getInstance(com.foxinmy.weixin4j.model.Consts.SunX509); + kmf.init(keyStore, password.toCharArray()); + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(kmf.getKeyManagers(), null, + new java.security.SecureRandom()); + } catch (Exception e) { + throw new WeixinException(e.getMessage()); + } + params.setSSLContext(sslContext); + } + + public WeixinSSLRequestExecutor(SSLContext sslContext) { + this.sslContext = sslContext; + params.setSSLContext(sslContext); + } + + public SSLContext getSSLContext() { + return sslContext; + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml index 2d215697..a9e04bdf 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml @@ -333,6 +333,10 @@ 40086 不合法的第三方应用appid + + 40092 + 导入文件存在不合法的内容 + 40117 分组名字不合法 diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/MediaRecord.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/MediaRecord.java index e1de4ef2..a8e82359 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/MediaRecord.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/MediaRecord.java @@ -45,6 +45,8 @@ public class MediaRecord implements Serializable { */ @JSONField(serialize = false, deserialize = false) private Pageable pageable; + @JSONField(serialize = false, deserialize = false) + private Pagedata pagedata; public int getTotalCount() { return totalCount; @@ -86,9 +88,11 @@ public class MediaRecord implements Serializable { this.pageable = pageable; } - @JSONField(serialize = false) public Pagedata getPagedata() { - return new Pagedata(pageable, totalCount, items); + if (pagedata == null) { + pagedata = new Pagedata(pageable, totalCount, items); + } + return pagedata; } @Override diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/NameValue.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/NameValue.java deleted file mode 100644 index 1c1fcf11..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/model/NameValue.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.foxinmy.weixin4j.model; - -import java.io.Serializable; - -public class NameValue implements Serializable { - - private static final long serialVersionUID = 4557003825642138566L; - - private final String name; - private final String value; - - public NameValue(String name, String value) { - this.name = name; - this.value = value; - } - - public String getName() { - return name; - } - - public String getValue() { - return value; - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayUtil.java index df7dd43c..c8500997 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/PayUtil.java @@ -7,7 +7,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.foxinmy.weixin4j.exception.PayException; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient; +import com.foxinmy.weixin4j.http.weixin.WeixinRequestExecutor; import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.model.WeixinPayAccount; @@ -17,7 +17,7 @@ import com.foxinmy.weixin4j.payment.mch.Order; import com.foxinmy.weixin4j.payment.mch.PrePay; import com.foxinmy.weixin4j.type.SignType; import com.foxinmy.weixin4j.type.TradeType; -import com.foxinmy.weixin4j.util.ConfigUtil; +import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; import com.foxinmy.weixin4j.util.DateUtil; import com.foxinmy.weixin4j.util.DigestUtil; import com.foxinmy.weixin4j.util.MapUtil; @@ -122,7 +122,7 @@ public class PayUtil { * href="http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1">统一下单接口 * @return 预支付对象 */ - private final static WeixinHttpClient httpClient = new WeixinHttpClient(); + private final static WeixinRequestExecutor httpClient = new WeixinRequestExecutor(); public static PrePay createPrePay(MchPayPackage payPackage, String paySignKey) throws PayException { @@ -305,7 +305,7 @@ public class PayUtil { private static String JSAPI() throws PayException { WeixinPayAccount weixinAccount = JSON.parseObject( - ConfigUtil.getValue("account"), WeixinPayAccount.class); + Weixin4jConfigUtil.getValue("account"), WeixinPayAccount.class); return createPayJsRequestJson("oyFLst1bqtuTcxK-ojF8hOGtLQao", "支付测试", "JSAPI01", 0.01d, "http://127.0.0.1/jsapi/notify", "127.0.0.0", weixinAccount); @@ -313,7 +313,7 @@ public class PayUtil { private static String NATIVE() { WeixinPayAccount weixinAccount = JSON.parseObject( - ConfigUtil.getValue("account"), WeixinPayAccount.class); + Weixin4jConfigUtil.getValue("account"), WeixinPayAccount.class); return createNativePayRequestURL(weixinAccount, "P1"); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java index 191e41c7..7ad7c0e2 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java @@ -29,7 +29,7 @@ import com.foxinmy.weixin4j.payment.mch.RefundRecord; import com.foxinmy.weixin4j.type.BillType; import com.foxinmy.weixin4j.type.CurrencyType; import com.foxinmy.weixin4j.type.IdQuery; -import com.foxinmy.weixin4j.util.ConfigUtil; +import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; import com.foxinmy.weixin4j.util.Weixin4jConst; /** @@ -55,7 +55,7 @@ public class WeixinPayProxy { * 使用weixin4j.properties配置的账号信息 */ public WeixinPayProxy() { - this(JSON.parseObject(ConfigUtil.getValue("account"), + this(JSON.parseObject(Weixin4jConfigUtil.getValue("account"), WeixinPayAccount.class)); } @@ -69,7 +69,7 @@ public class WeixinPayProxy { this.pay3Api = new Pay3Api(weixinAccount); this.couponApi = new CouponApi(weixinAccount); this.cashApi = new CashApi(weixinAccount); - this.DEFAULT_CA_FILE = ConfigUtil.getClassPathValue("ca_file", + this.DEFAULT_CA_FILE = Weixin4jConfigUtil.getClassPathValue("ca_file", Weixin4jConst.DEFAULT_CAFILE_PATH); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Article.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Article.java index 552348f8..74ef0695 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Article.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Article.java @@ -34,7 +34,7 @@ public class Article implements Serializable { /** * 图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200 */ - @JSONField(name = "pic_url") + @JSONField(name = "picurl") @XmlElement(name = "PicUrl") private String picUrl; /** @@ -46,7 +46,7 @@ public class Article implements Serializable { @JSONCreator public Article(@JSONField(name = "title") String title, @JSONField(name = "description") String desc, - @JSONField(name = "pic_url") String picUrl, + @JSONField(name = "picUrl") String picUrl, @JSONField(name = "url") String url) { this.title = title; this.desc = desc; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Card.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Card.java index d62bc5b7..eafb2ab7 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Card.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Card.java @@ -34,7 +34,7 @@ public class Card implements MassTuple { private String cardId; @JSONCreator - public Card(@JSONField(name = "card_id") String cardId) { + public Card(@JSONField(name = "cardId") String cardId) { this.cardId = cardId; } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/File.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/File.java index 3a55d4d7..9a57272e 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/File.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/File.java @@ -34,7 +34,7 @@ public class File implements NotifyTuple, ChatTuple { private String mediaId; @JSONCreator - public File(@JSONField(name = "media_id") String mediaId) { + public File(@JSONField(name = "mediaId") String mediaId) { this.mediaId = mediaId; } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Image.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Image.java index 8032868a..be0e0dba 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Image.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Image.java @@ -34,7 +34,7 @@ public class Image implements MassTuple, NotifyTuple, ChatTuple { private String mediaId; @JSONCreator - public Image(@JSONField(name = "media_id") String mediaId) { + public Image(@JSONField(name = "mediaId") String mediaId) { this.mediaId = mediaId; } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/MpNews.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/MpNews.java index f1b11e51..626d66db 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/MpNews.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/MpNews.java @@ -52,7 +52,7 @@ public class MpNews implements MassTuple, NotifyTuple { } @JSONCreator - public MpNews(@JSONField(name = "media_id") String mediaId) { + public MpNews(@JSONField(name = "mediaId") String mediaId) { this.mediaId = mediaId; this.articles = new LinkedList(); } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/MpVideo.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/MpVideo.java index 4600e6f2..f7faa8ed 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/MpVideo.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/MpVideo.java @@ -34,7 +34,7 @@ public class MpVideo implements MassTuple { private String mediaId; @JSONCreator - public MpVideo(@JSONField(name = "media_id") String mediaId) { + public MpVideo(@JSONField(name = "mediaId") String mediaId) { this.mediaId = mediaId; } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Music.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Music.java index c082ee9b..5aaac9aa 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Music.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Music.java @@ -39,13 +39,13 @@ public class Music implements NotifyTuple { /** * 音乐链接 */ - @JSONField(name = "music_url") + @JSONField(name = "musicurl") @XmlElement(name = "MusicUrl") private String musicUrl; /** * 高质量音乐链接,WIFI环境优先使用该链接播放音乐 */ - @JSONField(name = "hq_music_url") + @JSONField(name = "hqmusicurl") @XmlElement(name = "HQMusicUrl") private String hqMusicUrl; /** @@ -60,10 +60,10 @@ public class Music implements NotifyTuple { } public Music(@JSONField(name = "title") String title, - @JSONField(name = "description") String desc, - @JSONField(name = "music_url") String musicUrl, - @JSONField(name = "hq_music_url") String hqMusicUrl, - @JSONField(name = "thumb_media_id") String thumbMediaId) { + @JSONField(name = "desc") String desc, + @JSONField(name = "musicUrl") String musicUrl, + @JSONField(name = "hqMusicUrl") String hqMusicUrl, + @JSONField(name = "thumbMediaId") String thumbMediaId) { this.title = title; this.desc = desc; this.musicUrl = musicUrl; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Video.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Video.java index 70019231..0e12755b 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Video.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Video.java @@ -66,10 +66,10 @@ public class Video implements NotifyTuple { } @JSONCreator - public Video(@JSONField(name = "media_id") String mediaId, - @JSONField(name = "thumb_media_id") String thumbMediaId, + public Video(@JSONField(name = "mediaId") String mediaId, + @JSONField(name = "thumbMediaId") String thumbMediaId, @JSONField(name = "title") String title, - @JSONField(name = "description") String desc) { + @JSONField(name = "desc") String desc) { this.mediaId = mediaId; this.thumbMediaId = thumbMediaId; this.title = title; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Voice.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Voice.java index cfa1bec0..e2ee2492 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Voice.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/tuple/Voice.java @@ -25,7 +25,7 @@ public class Voice extends Image implements NotifyTuple { } @JSONCreator - public Voice(@JSONField(name = "media_id") String mediaId) { + public Voice(@JSONField(name = "mediaId") String mediaId) { super(mediaId); } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/LinkedCaseInsensitiveMap.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/LinkedCaseInsensitiveMap.java new file mode 100644 index 00000000..2fd9296e --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/LinkedCaseInsensitiveMap.java @@ -0,0 +1,164 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.foxinmy.weixin4j.util; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Locale; +import java.util.Map; + +/** + * {@link LinkedHashMap} variant that stores String keys in a case-insensitive + * manner, for example for key-based access in a results table. + * + *

+ * Preserves the original order as well as the original casing of keys, while + * allowing for contains, get and remove calls with any case of key. + * + *

+ * Does not support {@code null} keys. + * + * @author Juergen Hoeller + * @since 3.0 + */ +@SuppressWarnings("serial") +public class LinkedCaseInsensitiveMap extends LinkedHashMap { + + private final Map caseInsensitiveKeys; + + private final Locale locale; + + /** + * Create a new LinkedCaseInsensitiveMap for the default Locale. + * + * @see java.lang.String#toLowerCase() + */ + public LinkedCaseInsensitiveMap() { + this(null); + } + + /** + * Create a new LinkedCaseInsensitiveMap that stores lower-case keys + * according to the given Locale. + * + * @param locale + * the Locale to use for lower-case conversion + * @see java.lang.String#toLowerCase(java.util.Locale) + */ + public LinkedCaseInsensitiveMap(Locale locale) { + super(); + this.caseInsensitiveKeys = new HashMap(); + this.locale = (locale != null ? locale : Locale.getDefault()); + } + + /** + * Create a new LinkedCaseInsensitiveMap that wraps a {@link LinkedHashMap} + * with the given initial capacity and stores lower-case keys according to + * the default Locale. + * + * @param initialCapacity + * the initial capacity + * @see java.lang.String#toLowerCase() + */ + public LinkedCaseInsensitiveMap(int initialCapacity) { + this(initialCapacity, null); + } + + /** + * Create a new LinkedCaseInsensitiveMap that wraps a {@link LinkedHashMap} + * with the given initial capacity and stores lower-case keys according to + * the given Locale. + * + * @param initialCapacity + * the initial capacity + * @param locale + * the Locale to use for lower-case conversion + * @see java.lang.String#toLowerCase(java.util.Locale) + */ + public LinkedCaseInsensitiveMap(int initialCapacity, Locale locale) { + super(initialCapacity); + this.caseInsensitiveKeys = new HashMap(initialCapacity); + this.locale = (locale != null ? locale : Locale.getDefault()); + } + + @Override + public V put(String key, V value) { + String oldKey = this.caseInsensitiveKeys.put(convertKey(key), key); + if (oldKey != null && !oldKey.equals(key)) { + super.remove(oldKey); + } + return super.put(key, value); + } + + @Override + public void putAll(Map map) { + if (map.isEmpty()) { + return; + } + for (Map.Entry entry : map.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public boolean containsKey(Object key) { + return (key instanceof String && this.caseInsensitiveKeys + .containsKey(convertKey((String) key))); + } + + @Override + public V get(Object key) { + if (key instanceof String) { + return super.get(this.caseInsensitiveKeys + .get(convertKey((String) key))); + } else { + return null; + } + } + + @Override + public V remove(Object key) { + if (key instanceof String) { + return super.remove(this.caseInsensitiveKeys + .remove(convertKey((String) key))); + } else { + return null; + } + } + + @Override + public void clear() { + this.caseInsensitiveKeys.clear(); + super.clear(); + } + + /** + * Convert the given key to a case-insensitive key. + *

+ * The default implementation converts the key to lower-case according to + * this Map's Locale. + * + * @param key + * the user-specified key + * @return the key to use for storing + * @see java.lang.String#toLowerCase(java.util.Locale) + */ + protected String convertKey(String key) { + return key != null ? key.toLowerCase(this.locale) : null; + } + +} \ No newline at end of file diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MultiValueMap.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MultiValueMap.java new file mode 100644 index 00000000..2a5b915c --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MultiValueMap.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.foxinmy.weixin4j.util; + +import java.util.List; +import java.util.Map; + +/** + * Extension of the {@code Map} interface that stores multiple values. + * + * @author Arjen Poutsma + * @since 3.0 + */ +public interface MultiValueMap extends Map> { + + /** + * Return the first value for the given key. + * + * @param key + * the key + * @return the first value for the specified key, or {@code null} + */ + V getFirst(K key); + + /** + * Add the given single value to the current list of values for the given + * key. + * + * @param key + * the key + * @param value + * the value to be added + */ + void add(K key, V value); + + /** + * Set the given single value under the given key. + * + * @param key + * the key + * @param value + * the value to set + */ + void set(K key, V value); + + /** + * Set the given values under. + * + * @param values + * the values. + */ + void setAll(Map values); + + /** + * Returns the first values contained in this {@code MultiValueMap}. + * + * @return a single value representation of this map + */ + Map toSingleValueMap(); + +} \ No newline at end of file diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/NameValue.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/NameValue.java similarity index 91% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/NameValue.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/NameValue.java index c6454005..3a50144b 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/NameValue.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/NameValue.java @@ -1,4 +1,4 @@ -package com.foxinmy.weixin4j.http; +package com.foxinmy.weixin4j.util; import java.io.Serializable; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/URLEncodingUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/URLEncodingUtil.java new file mode 100644 index 00000000..e37510d2 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/URLEncodingUtil.java @@ -0,0 +1,152 @@ +package com.foxinmy.weixin4j.util; + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.BitSet; + +/** + * + * @className URLEncodingUtil + * @author jy + * @date 2015年8月15日 + * @since JDK 1.7 + * @see + */ +public final class URLEncodingUtil { + /** + * Unreserved characters, i.e. alphanumeric, plus: {@code _ - ! . ~ ' ( ) *} + *

+ * This list is the same as the {@code unreserved} list in RFC 2396 + */ + private static final BitSet UNRESERVED = new BitSet(256); + /** + * Punctuation characters: , ; : $ & + = + *

+ * These are the additional characters allowed by userinfo. + */ + private static final BitSet PUNCT = new BitSet(256); + /** + * Characters which are safe to use in userinfo, i.e. {@link #UNRESERVED} + * plus {@link #PUNCT}uation + */ + private static final BitSet USERINFO = new BitSet(256); + /** + * Characters which are safe to use in a path, i.e. {@link #UNRESERVED} plus + * {@link #PUNCT}uation plus / @ + */ + private static final BitSet PATHSAFE = new BitSet(256); + /** + * Characters which are safe to use in a fragment, i.e. {@link #RESERVED} + * plus {@link #UNRESERVED} + */ + private static final BitSet FRAGMENT = new BitSet(256); + + /** + * Reserved characters, i.e. {@code ;/?:@&=+$,[]} + *

+ * This list is the same as the {@code reserved} list in RFC 2396 as augmented by + * RFC 2732 + */ + private static final BitSet RESERVED = new BitSet(256); + + /** + * Safe characters for x-www-form-urlencoded data, as per + * java.net.URLEncoder and browser behaviour, i.e. alphanumeric plus + * {@code "-", "_", ".", "*"} + */ + private static final BitSet URLENCODER = new BitSet(256); + + static { + // unreserved chars + // alpha characters + for (int i = 'a'; i <= 'z'; i++) { + UNRESERVED.set(i); + } + for (int i = 'A'; i <= 'Z'; i++) { + UNRESERVED.set(i); + } + // numeric characters + for (int i = '0'; i <= '9'; i++) { + UNRESERVED.set(i); + } + UNRESERVED.set('_'); // these are the charactes of the "mark" list + UNRESERVED.set('-'); + UNRESERVED.set('.'); + UNRESERVED.set('*'); + URLENCODER.or(UNRESERVED); // skip remaining unreserved characters + UNRESERVED.set('!'); + UNRESERVED.set('~'); + UNRESERVED.set('\''); + UNRESERVED.set('('); + UNRESERVED.set(')'); + // punct chars + PUNCT.set(','); + PUNCT.set(';'); + PUNCT.set(':'); + PUNCT.set('$'); + PUNCT.set('&'); + PUNCT.set('+'); + PUNCT.set('='); + // Safe for userinfo + USERINFO.or(UNRESERVED); + USERINFO.or(PUNCT); + + // URL path safe + PATHSAFE.or(UNRESERVED); + PATHSAFE.set('/'); // segment separator + PATHSAFE.set(';'); // param separator + PATHSAFE.set(':'); // rest as per list in 2396, i.e. : @ & = + $ , + PATHSAFE.set('@'); + PATHSAFE.set('&'); + PATHSAFE.set('='); + PATHSAFE.set('+'); + PATHSAFE.set('$'); + PATHSAFE.set(','); + + RESERVED.set(';'); + RESERVED.set('/'); + RESERVED.set('?'); + RESERVED.set(':'); + RESERVED.set('@'); + RESERVED.set('&'); + RESERVED.set('='); + RESERVED.set('+'); + RESERVED.set('$'); + RESERVED.set(','); + RESERVED.set('['); // added by RFC 2732 + RESERVED.set(']'); // added by RFC 2732 + + FRAGMENT.or(RESERVED); + FRAGMENT.or(UNRESERVED); + } + + private static final int RADIX = 16; + + public static String encoding(final String content, final Charset charset, + final boolean blankAsPlus) { + if (content == null) { + return null; + } + StringBuilder buf = new StringBuilder(); + ByteBuffer bb = charset.encode(content); + while (bb.hasRemaining()) { + int b = bb.get() & 0xff; + if (URLENCODER.get(b)) { + buf.append((char) b); + } else if (blankAsPlus && b == ' ') { + buf.append('+'); + } else { + buf.append("%"); + char hex1 = Character.toUpperCase(Character.forDigit( + (b >> 4) & 0xF, RADIX)); + char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, + RADIX)); + buf.append(hex1); + buf.append(hex2); + } + } + return buf.toString(); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ConfigUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Weixin4jConfigUtil.java similarity index 93% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ConfigUtil.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Weixin4jConfigUtil.java index ff6cc057..680503cc 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ConfigUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Weixin4jConfigUtil.java @@ -11,13 +11,13 @@ import com.foxinmy.weixin4j.model.WeixinAccount; /** * 公众号配置信息 class路径下weixin4j.properties文件 * - * @className ConfigUtil + * @className Weixin4jConfigUtil * @author jy * @date 2014年10月31日 * @since JDK 1.7 * @see */ -public class ConfigUtil { +public class Weixin4jConfigUtil { private final static String CLASSPATH_PREFIX = "classpath:"; private final static String CLASSPATH_VALUE; private final static ResourceBundle weixinBundle; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ErrorUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/WeixinErrorUtil.java similarity index 97% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ErrorUtil.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/WeixinErrorUtil.java index 09a46fb2..b59da8f3 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ErrorUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/WeixinErrorUtil.java @@ -17,13 +17,13 @@ import com.foxinmy.weixin4j.http.weixin.WeixinResponse; /** * 接口调用错误获取 * - * @className ErrorUtil + * @className WeixinErrorUtil * @author jy * @date 2015年5月12日 * @since JDK 1.7 * @see */ -public final class ErrorUtil { +public final class WeixinErrorUtil { private static byte[] errorXmlByteArray; private final static Map errorCacheMap; static { diff --git a/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/HttpClientTest.java b/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/HttpClientTest.java new file mode 100644 index 00000000..17df70c2 --- /dev/null +++ b/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/HttpClientTest.java @@ -0,0 +1,78 @@ +package com.foxinmy.weixin4j.base.test; + +import org.apache.commons.httpclient.methods.GetMethod; + +import com.foxinmy.weixin4j.http.HttpClient; +import com.foxinmy.weixin4j.http.HttpClientException; +import com.foxinmy.weixin4j.http.HttpMethod; +import com.foxinmy.weixin4j.http.HttpParams; +import com.foxinmy.weixin4j.http.HttpRequest; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.factory.HttpClientFactory; +import com.foxinmy.weixin4j.http.factory.HttpComponent3Factory; +import com.foxinmy.weixin4j.http.factory.HttpComponent4Factory; +import com.foxinmy.weixin4j.http.factory.SimpleHttpClientFactory; +import com.foxinmy.weixin4j.model.WeixinAccount; +import com.foxinmy.weixin4j.token.FileTokenStorager; +import com.foxinmy.weixin4j.token.TokenHolder; +import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; + +public class HttpClientTest { + + static HttpRequest request = new HttpRequest(HttpMethod.GET, + "https://www.baidu.com"); + static { + HttpParams params = new HttpParams(); + //params.setProxy(new Proxy(Type.HTTP, new InetSocketAddress( + // "218.92.227.170", 11095))); + request.setParams(params); + } + + public static void test1() throws HttpClientException { + HttpClientFactory.setDefaultFactory(new SimpleHttpClientFactory()); + HttpClient httpClient = HttpClientFactory.getInstance(); + HttpResponse response = httpClient.execute(request); + print(response); + } + + public static void test2() throws HttpClientException { + HttpClientFactory.setDefaultFactory(new HttpComponent3Factory()); + HttpClient httpClient = HttpClientFactory.getInstance(); + HttpResponse response = httpClient.execute(request); + print(response); + } + + public static void test3() throws HttpClientException { + HttpClientFactory.setDefaultFactory(new HttpComponent4Factory()); + HttpClient httpClient = HttpClientFactory.getInstance(); + HttpResponse response = httpClient.execute(request); + print(response); + } + + public static void print(HttpResponse response) throws HttpClientException { + System.err.println(response.getStatus()); + System.err.println(response.getBody()); + System.err.println(response.getHeaders()); + System.err.println(response.getProtocol()); + } + + public static void test() { + org.apache.commons.httpclient.HttpClient hc = new org.apache.commons.httpclient.HttpClient(); + hc.getHostConfiguration().setProxy("127.0.0.1", 1080); + org.apache.commons.httpclient.HttpMethod hm = new GetMethod( + "http://www.baidu.com"); + try { + hc.executeMethod(hm); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) throws Exception { + //test1(); + System.out.println("---------------------"); + //test2(); + System.out.println("---------------------"); + //test3(); + } +} diff --git a/weixin4j-mp/pom.xml b/weixin4j-mp/pom.xml index f7ae1e29..846b1860 100644 --- a/weixin4j-mp/pom.xml +++ b/weixin4j-mp/pom.xml @@ -5,7 +5,7 @@ com.foxinmy weixin4j - 1.5.3 + 1.5.4-SNAPSHOT weixin4j-mp weixin4j-mp diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CustomApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CustomApi.java index 613d594c..1dd77fa1 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CustomApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CustomApi.java @@ -71,7 +71,7 @@ public class CustomApi extends MpApi { obj.put("pageindex", pageindex); String custom_record_uri = getRequestUri("custom_record_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(custom_record_uri, token.getAccessToken()), obj.toJSONString()); @@ -101,12 +101,12 @@ public class CustomApi extends MpApi { String text = ""; if (isOnline) { String getonlinekflist_uri = getRequestUri("getonlinekflist_uri"); - WeixinResponse response = weixinClient.get(String.format(getonlinekflist_uri, + WeixinResponse response = weixinExecutor.get(String.format(getonlinekflist_uri, token.getAccessToken())); text = response.getAsJson().getString("kf_online_list"); } else { String getkflist_uri = getRequestUri("getkflist_uri"); - WeixinResponse response = weixinClient.get(String.format(getkflist_uri, + WeixinResponse response = weixinExecutor.get(String.format(getkflist_uri, token.getAccessToken())); text = response.getAsJson().getString("kf_list"); } @@ -138,7 +138,7 @@ public class CustomApi extends MpApi { obj.put("password", DigestUtil.MD5(pwd)); String custom_add_uri = getRequestUri("custom_add_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(custom_add_uri, token.getAccessToken()), obj.toJSONString()); return response.getAsJsonResult(); @@ -169,7 +169,7 @@ public class CustomApi extends MpApi { obj.put("password", DigestUtil.MD5(pwd)); String custom_update_uri = getRequestUri("custom_update_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(custom_update_uri, token.getAccessToken()), obj.toJSONString()); return response.getAsJsonResult(); @@ -195,7 +195,7 @@ public class CustomApi extends MpApi { Token token = tokenHolder.getToken(); String custom_uploadheadimg_uri = getRequestUri("custom_uploadheadimg_uri"); byte[] bytes = IOUtil.toByteArray(new FileInputStream(headimg)); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(custom_uploadheadimg_uri, token.getAccessToken(), id), new FormBodyPart("media", new ByteArrayBody(bytes, headimg @@ -219,7 +219,7 @@ public class CustomApi extends MpApi { public JsonResult deleteAccount(String id) throws WeixinException { Token token = tokenHolder.getToken(); String custom_delete_uri = getRequestUri("custom_delete_uri"); - WeixinResponse response = weixinClient.get(String.format(custom_delete_uri, + WeixinResponse response = weixinExecutor.get(String.format(custom_delete_uri, token.getAccessToken(), id)); return response.getAsJsonResult(); @@ -251,7 +251,7 @@ public class CustomApi extends MpApi { obj.put("openid", userOpenId); obj.put("kf_account", kfAccount); obj.put("text", text); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(kfsession_create_uri, token.getAccessToken()), obj.toJSONString()); @@ -280,7 +280,7 @@ public class CustomApi extends MpApi { obj.put("openid", userOpenId); obj.put("kf_account", kfAccount); obj.put("text", text); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(kfsession_close_uri, token.getAccessToken()), obj.toJSONString()); @@ -301,7 +301,7 @@ public class CustomApi extends MpApi { public KfSession getKfSession(String userOpenId) throws WeixinException { Token token = tokenHolder.getToken(); String kfsession_get_uri = getRequestUri("kfsession_get_uri"); - WeixinResponse response = weixinClient.get(String.format(kfsession_get_uri, + WeixinResponse response = weixinExecutor.get(String.format(kfsession_get_uri, token.getAccessToken(), userOpenId)); KfSession session = response @@ -326,7 +326,7 @@ public class CustomApi extends MpApi { throws WeixinException { Token token = tokenHolder.getToken(); String kfsession_list_uri = getRequestUri("kfsession_list_uri"); - WeixinResponse response = weixinClient.get(String.format(kfsession_list_uri, + WeixinResponse response = weixinExecutor.get(String.format(kfsession_list_uri, token.getAccessToken(), kfAccount)); List sessionList = JSON.parseArray(response.getAsJson() @@ -346,7 +346,7 @@ public class CustomApi extends MpApi { public List listKfSessionWait() throws WeixinException { Token token = tokenHolder.getToken(); String kfsession_wait_uri = getRequestUri("kfsession_wait_uri"); - WeixinResponse response = weixinClient.get(String.format(kfsession_wait_uri, + WeixinResponse response = weixinExecutor.get(String.format(kfsession_wait_uri, token.getAccessToken())); List sessionList = JSON.parseArray(response.getAsJson() diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/DataApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/DataApi.java index 8587a1bc..c2dfffed 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/DataApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/DataApi.java @@ -123,7 +123,7 @@ public class DataApi extends MpApi { JSONObject obj = new JSONObject(); obj.put("begin_date", DateUtil.fortmat2yyyy_MM_dd(beginDate)); obj.put("end_date", DateUtil.fortmat2yyyy_MM_dd(endDate)); - WeixinResponse response = weixinClient.post(String.format(datacube_uri, + WeixinResponse response = weixinExecutor.post(String.format(datacube_uri, datacubeType.name().toLowerCase(), token.getAccessToken()), obj .toJSONString()); diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/GroupApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/GroupApi.java index fcd4e822..842eb166 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/GroupApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/GroupApi.java @@ -46,7 +46,7 @@ public class GroupApi extends MpApi { String group_create_uri = getRequestUri("group_create_uri"); Token token = tokenHolder.getToken(); Group group = new Group(name); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(group_create_uri, token.getAccessToken()), group.toCreateJson()); @@ -65,7 +65,7 @@ public class GroupApi extends MpApi { public List getGroups() throws WeixinException { String group_get_uri = getRequestUri("group_get_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.get(String.format(group_get_uri, + WeixinResponse response = weixinExecutor.get(String.format(group_get_uri, token.getAccessToken())); return JSON.parseArray(response.getAsJson().getString("groups"), @@ -86,7 +86,7 @@ public class GroupApi extends MpApi { public int getGroupByOpenId(String openId) throws WeixinException { String group_getid_uri = getRequestUri("group_getid_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(group_getid_uri, token.getAccessToken()), String.format("{\"openid\":\"%s\"}", openId)); @@ -112,7 +112,7 @@ public class GroupApi extends MpApi { Token token = tokenHolder.getToken(); Group group = new Group(groupId, name); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(group_modify_uri, token.getAccessToken()), group.toModifyJson()); return response.getAsJsonResult(); @@ -134,7 +134,7 @@ public class GroupApi extends MpApi { throws WeixinException { String group_move_uri = getRequestUri("group_move_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post(String.format(group_move_uri, + WeixinResponse response = weixinExecutor.post(String.format(group_move_uri, token.getAccessToken()), String.format( "{\"openid\":\"%s\",\"to_groupid\":%d}", openId, groupId)); @@ -160,7 +160,7 @@ public class GroupApi extends MpApi { JSONObject obj = new JSONObject(); obj.put("to_groupid", groupId); obj.put("openid_list", openIds); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(group_batchmove_uri, token.getAccessToken()), obj.toJSONString()); @@ -180,7 +180,7 @@ public class GroupApi extends MpApi { public JsonResult deleteGroup(int groupId) throws WeixinException { String group_delete_uri = getRequestUri("group_delete_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(group_delete_uri, token.getAccessToken()), String.format("{\"group\":{\"id\":%d}}", groupId)); diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java index 6181b4f9..9390b687 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/HelperApi.java @@ -53,7 +53,7 @@ public class HelperApi extends MpApi { JSONObject obj = new JSONObject(); obj.put("action", "long2short"); obj.put("long_url", url); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(shorturl_uri, token.getAccessToken()), obj.toJSONString()); @@ -75,7 +75,7 @@ public class HelperApi extends MpApi { public SemResult semantic(SemQuery semQuery) throws WeixinException { String semantic_uri = getRequestUri("semantic_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(semantic_uri, token.getAccessToken()), semQuery.toJson()); return response.getAsObject(new TypeReference() { @@ -93,7 +93,7 @@ public class HelperApi extends MpApi { public List getCallbackip() throws WeixinException { String getcallbackip_uri = getRequestUri("getcallbackip_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post(String.format(getcallbackip_uri, + WeixinResponse response = weixinExecutor.post(String.format(getcallbackip_uri, token.getAccessToken())); return JSON.parseArray(response.getAsJson().getString("ip_list"), String.class); @@ -115,7 +115,7 @@ public class HelperApi extends MpApi { public MenuSetting getMenuSetting() throws WeixinException { String menu_get_selfmenu_uri = getRequestUri("menu_get_selfmenu_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.get(String.format(menu_get_selfmenu_uri, + WeixinResponse response = weixinExecutor.get(String.format(menu_get_selfmenu_uri, token.getAccessToken())); JSONObject result = response.getAsJson(); @@ -188,7 +188,7 @@ public class HelperApi extends MpApi { public AutoReplySetting getAutoReplySetting() throws WeixinException { String autoreply_setting_get_uri = getRequestUri("autoreply_setting_get_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.get(String.format( + WeixinResponse response = weixinExecutor.get(String.format( autoreply_setting_get_uri, token.getAccessToken())); JSONObject result = response.getAsJson(); diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MassApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MassApi.java index f521fa4a..0ccf5814 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MassApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MassApi.java @@ -53,7 +53,7 @@ public class MassApi extends MpApi { Token token = tokenHolder.getToken(); JSONObject obj = new JSONObject(); obj.put("articles", articles); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(article_upload_uri, token.getAccessToken()), obj.toJSONString()); @@ -113,7 +113,7 @@ public class MassApi extends MpApi { obj.put("msgtype", msgtype); String mass_group_uri = getRequestUri("mass_group_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(mass_group_uri, token.getAccessToken()), obj.toJSONString()); @@ -182,7 +182,7 @@ public class MassApi extends MpApi { obj.put("msgtype", msgtype); String mass_openid_uri = getRequestUri("mass_openid_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(mass_openid_uri, token.getAccessToken()), obj.toJSONString()); @@ -230,7 +230,7 @@ public class MassApi extends MpApi { obj.put("msgid", msgid); String mass_delete_uri = getRequestUri("mass_delete_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(mass_delete_uri, token.getAccessToken()), obj.toJSONString()); @@ -262,7 +262,7 @@ public class MassApi extends MpApi { obj.put("msgtype", msgtype); String mass_preview_uri = getRequestUri("mass_preview_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(mass_preview_uri, token.getAccessToken()), obj.toJSONString()); @@ -284,7 +284,7 @@ public class MassApi extends MpApi { obj.put("msg_id", msgId); String mass_get_uri = getRequestUri("mass_get_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(mass_get_uri, token.getAccessToken()), obj.toJSONString()); diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MediaApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MediaApi.java index 4653e5be..283c93f3 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MediaApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MediaApi.java @@ -18,12 +18,13 @@ import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.parser.deserializer.ExtraProcessor; import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.http.ContentType; -import com.foxinmy.weixin4j.http.Header; -import com.foxinmy.weixin4j.http.HttpGet; -import com.foxinmy.weixin4j.http.HttpPost; +import com.foxinmy.weixin4j.http.HttpClientException; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpMethod; +import com.foxinmy.weixin4j.http.HttpParams; +import com.foxinmy.weixin4j.http.HttpRequest; import com.foxinmy.weixin4j.http.HttpResponse; import com.foxinmy.weixin4j.http.apache.FormBodyPart; -import com.foxinmy.weixin4j.http.apache.HttpHeaders; import com.foxinmy.weixin4j.http.apache.InputStreamBody; import com.foxinmy.weixin4j.http.apache.StringBody; import com.foxinmy.weixin4j.http.entity.StringEntity; @@ -41,13 +42,13 @@ import com.foxinmy.weixin4j.token.TokenHolder; import com.foxinmy.weixin4j.tuple.MpArticle; import com.foxinmy.weixin4j.tuple.MpVideo; import com.foxinmy.weixin4j.type.MediaType; -import com.foxinmy.weixin4j.util.ConfigUtil; -import com.foxinmy.weixin4j.util.ErrorUtil; import com.foxinmy.weixin4j.util.FileUtil; import com.foxinmy.weixin4j.util.IOUtil; import com.foxinmy.weixin4j.util.ObjectId; import com.foxinmy.weixin4j.util.StringUtil; +import com.foxinmy.weixin4j.util.Weixin4jConfigUtil; import com.foxinmy.weixin4j.util.Weixin4jConst; +import com.foxinmy.weixin4j.util.WeixinErrorUtil; /** * 素材相关API @@ -87,7 +88,7 @@ public class MediaApi extends MpApi { } String image_upload_uri = getRequestUri("image_upload_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post(String.format( + WeixinResponse response = weixinExecutor.post(String.format( image_upload_uri, token.getAccessToken()), new FormBodyPart("media", new InputStreamBody(is, ContentType.IMAGE_JPG.getMimeType(), fileName))); @@ -120,7 +121,7 @@ public class MediaApi extends MpApi { obj.put("description", description); String video_upload_uri = getRequestUri("video_upload_uri"); Token token = tokenHolder.getToken(); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(video_upload_uri, token.getAccessToken()), obj.toJSONString()); @@ -190,29 +191,32 @@ public class MediaApi extends MpApi { try { if (isMaterial) { String material_media_upload_uri = getRequestUri("material_media_upload_uri"); - response = weixinClient + response = weixinExecutor .post(String.format(material_media_upload_uri, token.getAccessToken()), new FormBodyPart( - "media", new InputStreamBody(is, mediaType - .getContentType().getMimeType(), - fileName)), new FormBodyPart("type", - new StringBody(mediaType.name(), Consts.UTF_8))); + "media", new InputStreamBody( + new ByteArrayInputStream(content), + mediaType.getContentType() + .getMimeType(), fileName)), + new FormBodyPart("type", new StringBody( + mediaType.name(), Consts.UTF_8))); return new MediaUploadResult(response.getAsJson().getString( "media_id"), mediaType, new Date()); } else { String media_upload_uri = getRequestUri("media_upload_uri"); - response = weixinClient.post(String.format(media_upload_uri, + response = weixinExecutor.post(String.format(media_upload_uri, token.getAccessToken(), mediaType.name()), - new FormBodyPart("media", new InputStreamBody(is, - mediaType.getContentType().getMimeType(), + new FormBodyPart("media", new InputStreamBody( + new ByteArrayInputStream(content), mediaType + .getContentType().getMimeType(), fileName))); JSONObject obj = response.getAsJson(); return new MediaUploadResult(obj.getString("media_id"), obj.getObject("type", MediaType.class), new Date( obj.getLong("created_at") * 1000l)); /* - * return response.getAsObject(new TypeReference() - * { }); + * return response.getAsObject(new + * TypeReference() { }); */ } } catch (UnsupportedEncodingException e) { @@ -244,7 +248,7 @@ public class MediaApi extends MpApi { */ public File downloadMediaFile(String mediaId, boolean isMaterial) throws WeixinException { - String media_path = ConfigUtil.getValue("media_path", + String media_path = Weixin4jConfigUtil.getValue("media_path", Weixin4jConst.DEFAULT_MEDIA_PATH); final String prefixName = String.format("%s.", mediaId); File[] files = new File(media_path).listFiles(new FilenameFilter() { @@ -301,45 +305,49 @@ public class MediaApi extends MpApi { throws WeixinException { Token token = tokenHolder.getToken(); try { - HttpResponse response = null; + HttpRequest request = null; if (isMaterial) { String material_media_download_uri = getRequestUri("material_media_download_uri"); - HttpPost method = new HttpPost(String.format( + request = new HttpRequest(HttpMethod.POST, String.format( material_media_download_uri, token.getAccessToken())); - method.setEntity(new StringEntity(String.format( + request.setEntity(new StringEntity(String.format( "{\"media_id\":\"%s\"}", mediaId))); - response = weixinClient.execute(method); } else { String meida_download_uri = getRequestUri("meida_download_uri"); - HttpGet method = new HttpGet(String.format(meida_download_uri, - token.getAccessToken(), mediaId)); - response = weixinClient.execute(method); + request = new HttpRequest(HttpMethod.GET, String.format( + meida_download_uri, token.getAccessToken(), mediaId)); } - byte[] content = response.getContent(); - Header contentType = response - .getFirstHeader(HttpHeaders.CONTENT_TYPE); - Header disposition = response.getFirstHeader("Content-disposition"); - if (contentType.getValue().contains( - ContentType.APPLICATION_JSON.getMimeType()) - || (disposition != null && disposition.getValue().indexOf( - ".json") > 0)) { + HttpParams params = weixinExecutor.getExecuteParams(); + request.setParams(params); + HttpResponse response = weixinExecutor.getExecuteClient().execute( + request); + byte[] content = IOUtil.toByteArray(response.getBody()); + HttpHeaders headers = response.getHeaders(); + String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE); + String disposition = headers + .getFirst(HttpHeaders.CONTENT_DISPOSITION); + if (contentType + .contains(ContentType.APPLICATION_JSON.getMimeType()) + || (disposition != null && disposition.indexOf(".json") > 0)) { JsonResult jsonResult = JSON.parseObject(content, 0, content.length, Consts.UTF_8.newDecoder(), JsonResult.class); if (jsonResult.getCode() != 0) { if (StringUtil.isBlank(jsonResult.getDesc())) { - jsonResult.setDesc(ErrorUtil.getText(Integer + jsonResult.setDesc(WeixinErrorUtil.getText(Integer .toString(jsonResult.getCode()))); } throw new WeixinException(Integer.toString(jsonResult .getCode()), jsonResult.getDesc()); } } - String fileName = String.format("%s.%s", mediaId, contentType - .getValue().split("/")[1]); + String fileName = String.format("%s.%s", mediaId, + contentType.split("/")[1]); return new MediaDownloadResult(content, - ContentType.create(contentType.getValue()), fileName); + ContentType.create(contentType), fileName); } catch (IOException e) { + throw new WeixinException("I/O Error on getBody"); + } catch (HttpClientException e) { throw new WeixinException(e); } } @@ -365,7 +373,7 @@ public class MediaApi extends MpApi { String material_article_upload_uri = getRequestUri("material_article_upload_uri"); JSONObject obj = new JSONObject(); obj.put("articles", articles); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(material_article_upload_uri, token.getAccessToken()), obj.toJSONString()); @@ -414,7 +422,7 @@ public class MediaApi extends MpApi { obj.put("articles", articles); obj.put("media_id", mediaId); obj.put("index", index); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(material_article_update_uri, token.getAccessToken()), obj.toJSONString()); @@ -437,7 +445,7 @@ public class MediaApi extends MpApi { String material_media_del_uri = getRequestUri("material_media_del_uri"); JSONObject obj = new JSONObject(); obj.put("media_id", mediaId); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(material_media_del_uri, token.getAccessToken()), obj.toJSONString()); @@ -466,7 +474,7 @@ public class MediaApi extends MpApi { JSONObject description = new JSONObject(); description.put("title", title); description.put("introduction", introduction); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(material_media_upload_uri, token.getAccessToken()), new FormBodyPart("media", new InputStreamBody(is, @@ -502,7 +510,7 @@ public class MediaApi extends MpApi { public MediaCounter countMaterialMedia() throws WeixinException { Token token = tokenHolder.getToken(); String material_media_count_uri = getRequestUri("material_media_count_uri"); - WeixinResponse response = weixinClient.get(String.format( + WeixinResponse response = weixinExecutor.get(String.format( material_media_count_uri, token.getAccessToken())); return response.getAsObject(new TypeReference() { @@ -534,7 +542,7 @@ public class MediaApi extends MpApi { obj.put("type", mediaType.name()); obj.put("offset", pageable.getOffset()); obj.put("count", pageable.getPageSize()); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(material_media_list_uri, token.getAccessToken()), obj.toJSONString()); MediaRecord mediaRecord = null; diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MenuApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MenuApi.java index a9372e75..b0a81085 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MenuApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/MenuApi.java @@ -50,7 +50,7 @@ public class MenuApi extends MpApi { Token token = tokenHolder.getToken(); JSONObject obj = new JSONObject(); obj.put("button", btnList); - WeixinResponse response = weixinClient.post( + WeixinResponse response = weixinExecutor.post( String.format(menu_create_uri, token.getAccessToken()), JSON.toJSONString(obj, new NameFilter() { @Override @@ -88,7 +88,7 @@ public class MenuApi extends MpApi { public List