From 305738e8323b52ce6f0a0623cbbc7416d7c5bbe2 Mon Sep 17 00:00:00 2001 From: jinyu Date: Sat, 30 May 2015 20:35:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=BB=E6=8E=89httpclient=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGE.md | 6 +- weixin4j-base/pom.xml | 31 -- .../com/foxinmy/weixin4j/api/BaseApi.java | 4 +- .../weixin4j/http/AbstractHttpMessage.java | 84 ++++ .../weixin4j/http/AbstractHttpRequest.java | 49 ++ .../foxinmy/weixin4j/http/ContentType.java | 114 +++++ .../com/foxinmy/weixin4j/http/Header.java | 24 + .../com/foxinmy/weixin4j/http/HttpClient.java | 38 ++ .../weixin4j/http/HttpEntityRequest.java | 16 + .../com/foxinmy/weixin4j/http/HttpGet.java | 34 ++ .../foxinmy/weixin4j/http/HttpMessage.java | 67 +++ .../com/foxinmy/weixin4j/http/HttpMethod.java | 14 + .../com/foxinmy/weixin4j/http/HttpParams.java | 83 ++++ .../com/foxinmy/weixin4j/http/HttpPost.java | 48 ++ .../weixin4j/http/HttpProtocolParams.java | 9 + .../foxinmy/weixin4j/http/HttpRequest.java | 340 +++---------- .../foxinmy/weixin4j/http/HttpResponse.java | 52 ++ .../com/foxinmy/weixin4j/http/HttpStatus.java | 175 +++++++ .../foxinmy/weixin4j/http/HttpVersion.java | 122 +++++ .../com/foxinmy/weixin4j/http/NameValue.java | 24 + .../com/foxinmy/weixin4j/http/Parameter.java | 62 --- .../foxinmy/weixin4j/http/PartParameter.java | 35 -- .../com/foxinmy/weixin4j/http/Response.java | 110 ----- .../weixin4j/http/ResponseHandler.java | 16 + .../foxinmy/weixin4j/http/SSLHttpRequest.java | 52 -- .../weixin4j/http/SimpleHttpClient.java | 200 ++++++++ .../com/foxinmy/weixin4j/http/StatusLine.java | 38 ++ .../weixin4j/http/UrlEncodeParameter.java | 39 ++ .../http/apache/AbstractContentBody.java | 68 +++ .../weixin4j/http/apache/ByteArrayBody.java | 95 ++++ .../weixin4j/http/apache/ByteArrayBuffer.java | 345 +++++++++++++ .../weixin4j/http/apache/CharArrayBuffer.java | 460 ++++++++++++++++++ .../weixin4j/http/apache/ContentBody.java | 43 ++ .../http/apache/ContentDescriptor.java | 89 ++++ .../weixin4j/http/apache/FileBody.java | 123 +++++ .../weixin4j/http/apache/FormBodyPart.java | 110 +++++ .../foxinmy/weixin4j/http/apache/HTTP.java | 73 +++ .../foxinmy/weixin4j/http/apache/Header.java | 146 ++++++ .../weixin4j/http/apache/HttpHeaders.java | 206 ++++++++ .../weixin4j/http/apache/HttpMultipart.java | 278 +++++++++++ .../http/apache/HttpMultipartMode.java | 12 + .../weixin4j/http/apache/InputStreamBody.java | 92 ++++ .../foxinmy/weixin4j/http/apache/MIME.java | 48 ++ .../weixin4j/http/apache/MultipartEntity.java | 169 +++++++ .../weixin4j/http/apache/StringBody.java | 162 ++++++ .../weixin4j/http/entity/ByteArrayEntity.java | 52 ++ .../weixin4j/http/entity/FileEntity.java | 54 ++ .../weixin4j/http/entity/FormUrlEntity.java | 27 + .../weixin4j/http/entity/HttpEntity.java | 18 + .../weixin4j/http/entity/StringEntity.java | 43 ++ .../http/{ => weixin}/JsonResult.java | 2 +- .../weixin4j/http/{ => weixin}/README.md | 0 .../weixin4j/http/weixin/SSLHttpClinet.java | 60 +++ .../http/weixin/WeixinHttpClient.java | 213 ++++++++ .../weixin4j/http/weixin/WeixinResponse.java | 54 ++ .../weixin4j/http/{ => weixin}/XmlResult.java | 2 +- .../weixin4j/http/{ => weixin}/error.xml | 6 +- .../weixin4j/{util => page}/Pageable.java | 6 +- .../weixin4j/{util => page}/Pagedata.java | 2 +- .../foxinmy/weixin4j/{util => page}/Sort.java | 2 +- .../weixin4j/token/FileTokenHolder.java | 1 - .../weixin4j/token/RedisTokenHolder.java | 1 - .../com/foxinmy/weixin4j/util/ErrorUtil.java | 4 +- .../com/foxinmy/weixin4j/util/MapUtil.java | 3 +- .../foxinmy/weixin4j/util/PKCS7Encoder.java | 2 +- .../foxinmy/weixin4j/mp/WeixinPayProxy.java | 4 +- .../com/foxinmy/weixin4j/mp/WeixinProxy.java | 2 +- .../com/foxinmy/weixin4j/mp/api/CashApi.java | 12 +- .../foxinmy/weixin4j/mp/api/CouponApi.java | 12 +- .../foxinmy/weixin4j/mp/api/CustomApi.java | 35 +- .../com/foxinmy/weixin4j/mp/api/DataApi.java | 4 +- .../com/foxinmy/weixin4j/mp/api/GroupApi.java | 18 +- .../foxinmy/weixin4j/mp/api/HelperApi.java | 12 +- .../com/foxinmy/weixin4j/mp/api/MassApi.java | 18 +- .../com/foxinmy/weixin4j/mp/api/MediaApi.java | 49 +- .../com/foxinmy/weixin4j/mp/api/MenuApi.java | 10 +- .../foxinmy/weixin4j/mp/api/NotifyApi.java | 6 +- .../com/foxinmy/weixin4j/mp/api/OauthApi.java | 10 +- .../com/foxinmy/weixin4j/mp/api/Pay2Api.java | 27 +- .../com/foxinmy/weixin4j/mp/api/Pay3Api.java | 40 +- .../com/foxinmy/weixin4j/mp/api/PayApi.java | 8 +- .../com/foxinmy/weixin4j/mp/api/QrApi.java | 18 +- .../com/foxinmy/weixin4j/mp/api/TmplApi.java | 10 +- .../com/foxinmy/weixin4j/mp/api/UserApi.java | 10 +- .../foxinmy/weixin4j/mp/model/SemResult.java | 2 +- .../foxinmy/weixin4j/mp/payment/PayUtil.java | 13 +- .../weixin4j/mp/payment/v3/ApiResult.java | 2 +- .../mp/payment/v3/RedpacketSendResult.java | 2 +- .../mp/token/WeixinJSTicketCreator.java | 10 +- .../weixin4j/mp/token/WeixinTokenCreator.java | 12 +- .../foxinmy/weixin4j/mp/test/CouponTest.java | 7 +- .../foxinmy/weixin4j/mp/test/CustomTest.java | 2 +- .../foxinmy/weixin4j/mp/test/GroupTest.java | 2 +- .../foxinmy/weixin4j/mp/test/MassTest.java | 2 +- .../foxinmy/weixin4j/mp/test/MediaTest.java | 2 +- .../foxinmy/weixin4j/mp/test/MenuTest.java | 2 +- .../foxinmy/weixin4j/mp/test/NotifyTest.java | 2 +- .../com/foxinmy/weixin4j/mp/test/PayTest.java | 6 +- .../weixin4j/mp/test/TemplateTest.java | 2 +- .../foxinmy/weixin4j/mp/test/UserTest.java | 2 +- .../com/foxinmy/weixin4j/qy/WeixinProxy.java | 2 +- .../com/foxinmy/weixin4j/qy/api/AgentApi.java | 10 +- .../com/foxinmy/weixin4j/qy/api/BatchApi.java | 8 +- .../foxinmy/weixin4j/qy/api/HelperApi.java | 4 +- .../com/foxinmy/weixin4j/qy/api/MediaApi.java | 15 +- .../com/foxinmy/weixin4j/qy/api/MenuApi.java | 10 +- .../foxinmy/weixin4j/qy/api/NotifyApi.java | 4 +- .../com/foxinmy/weixin4j/qy/api/PartyApi.java | 12 +- .../com/foxinmy/weixin4j/qy/api/TagApi.java | 16 +- .../com/foxinmy/weixin4j/qy/api/UserApi.java | 20 +- .../weixin4j/qy/model/BatchResult.java | 2 +- .../qy/token/WeixinJSTicketCreator.java | 10 +- .../weixin4j/qy/token/WeixinTokenCreator.java | 12 +- .../foxinmy/weixin4j/qy/test/AgentTest.java | 2 +- .../foxinmy/weixin4j/qy/test/MenuTest.java | 2 +- .../foxinmy/weixin4j/qy/test/PartyTest.java | 2 +- .../com/foxinmy/weixin4j/qy/test/TagTest.java | 2 +- .../foxinmy/weixin4j/qy/test/UserTest.java | 2 +- .../server/test/MessageServerStartup.java | 4 +- 119 files changed, 4621 insertions(+), 850 deletions(-) create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpMessage.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpRequest.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java create 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/HttpClient.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpEntityRequest.java create 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/HttpMessage.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMethod.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpParams.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpPost.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpProtocolParams.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpVersion.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/NameValue.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Parameter.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/PartParameter.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Response.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ResponseHandler.java delete mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SSLHttpRequest.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java create 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/UrlEncodeParameter.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/AbstractContentBody.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ByteArrayBody.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ByteArrayBuffer.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/CharArrayBuffer.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ContentBody.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ContentDescriptor.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FileBody.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FormBodyPart.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HTTP.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/Header.java create 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/apache/HttpMultipart.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpMultipartMode.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/InputStreamBody.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/MIME.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/MultipartEntity.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/StringBody.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/ByteArrayEntity.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FileEntity.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FormUrlEntity.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/HttpEntity.java create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/StringEntity.java rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/{ => weixin}/JsonResult.java (92%) rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/{ => weixin}/README.md (100%) create mode 100644 weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/SSLHttpClinet.java create 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/WeixinResponse.java rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/{ => weixin}/XmlResult.java (94%) rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/{ => weixin}/error.xml (95%) rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/{util => page}/Pageable.java (91%) rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/{util => page}/Pagedata.java (97%) rename weixin4j-base/src/main/java/com/foxinmy/weixin4j/{util => page}/Sort.java (97%) diff --git a/CHANGE.md b/CHANGE.md index 8521d915..7d721ed0 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -299,4 +299,8 @@ * 2015-05-20 - + **weixin4j-server**: released 1.0.1 \ No newline at end of file + + **weixin4j-server**: released 1.0.1 + +* 2015-05-30 + + **大的调整:去掉httpclient依赖** \ No newline at end of file diff --git a/weixin4j-base/pom.xml b/weixin4j-base/pom.xml index d40bc0bb..c37b07b8 100644 --- a/weixin4j-base/pom.xml +++ b/weixin4j-base/pom.xml @@ -17,37 +17,6 @@ xstream 1.4.7 - - org.apache.httpcomponents - httpcore - 4.2.5 - - - org.apache.httpcomponents - httpclient - 4.2.5 - - - commons-codec - commons-codec - - - org.apache.httpcomponents - httpcore - - - - - org.apache.httpcomponents - httpmime - 4.2.6 - - - org.apache.httpcomponents - httpcore - - - com.alibaba fastjson 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 3befeb51..ab3bf49d 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 @@ -5,7 +5,7 @@ import java.util.ResourceBundle; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.foxinmy.weixin4j.http.HttpRequest; +import com.foxinmy.weixin4j.http.weixin.WeixinHttpClient; import com.foxinmy.weixin4j.xml.Map2ObjectConverter; import com.foxinmy.weixin4j.xml.XmlStream; import com.thoughtworks.xstream.core.ClassLoaderReference; @@ -22,7 +22,7 @@ import com.thoughtworks.xstream.mapper.DefaultMapper; * @see 微信企业号API文档 */ public abstract class BaseApi { - protected final HttpRequest request = new HttpRequest(); + protected final WeixinHttpClient weixinClient = new WeixinHttpClient(); protected final static XmlStream mapXstream = XmlStream.get(); static { mapXstream.alias("xml", Map.class); 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 new file mode 100644 index 00000000..1076c4a0 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpMessage.java @@ -0,0 +1,84 @@ +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 new file mode 100644 index 00000000..7ba3633b --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpRequest.java @@ -0,0 +1,49 @@ +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 new file mode 100644 index 00000000..43f8d8be --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java @@ -0,0 +1,114 @@ +package com.foxinmy.weixin4j.http; + +import java.io.Serializable; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; +import java.util.Locale; + +import com.foxinmy.weixin4j.model.Consts; + +/** + * reference of apache pivot + * + * @className ContentType + * @author jy + * @date 2015年5月29日 + * @since JDK 1.7 + * @see + */ +public final class ContentType implements Serializable { + + private static final long serialVersionUID = 1544245878894784980L; + + public static final ContentType APPLICATION_ATOM_XML = create( + "application/atom+xml", Consts.UTF_8); + public static final ContentType APPLICATION_FORM_URLENCODED = create( + "application/x-www-form-urlencoded", Consts.UTF_8); + public static final ContentType APPLICATION_JSON = create( + "application/json", Consts.UTF_8); + public static final ContentType APPLICATION_OCTET_STREAM = create( + "application/octet-stream", (Charset) null); + public static final ContentType APPLICATION_SVG_XML = create( + "application/svg+xml", Consts.UTF_8); + public static final ContentType APPLICATION_XHTML_XML = create( + "application/xhtml+xml", Consts.UTF_8); + public static final ContentType APPLICATION_XML = create("application/xml", + Consts.UTF_8); + public static final ContentType MULTIPART_FORM_DATA = create( + "multipart/form-data", Consts.UTF_8); + public static final ContentType TEXT_HTML = create("text/html", + Consts.UTF_8); + public static final ContentType TEXT_PLAIN = create("text/plain", + Consts.UTF_8); + public static final ContentType TEXT_XML = create("text/xml", Consts.UTF_8); + public static final ContentType WILDCARD = create("*/*", (Charset) null); + + // defaults + public static final ContentType DEFAULT_TEXT = TEXT_PLAIN; + public static final ContentType DEFAULT_BINARY = APPLICATION_OCTET_STREAM; + + private final String mimeType; + private final Charset charset; + + ContentType(final String mimeType, final Charset charset) { + this.mimeType = mimeType; + this.charset = charset; + } + + public String getMimeType() { + return this.mimeType; + } + + public Charset getCharset() { + return this.charset; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(this.mimeType); + if (this.charset != null) { + buf.append("; charset="); + buf.append(this.charset.name()); + } + return buf.toString(); + } + + private static boolean valid(final String s) { + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + if (ch == '"' || ch == ',' || ch == ';') { + return false; + } + } + return true; + } + + public static ContentType create(final String mimeType, + final Charset charset) { + if (mimeType == null) { + throw new IllegalArgumentException("MIME type may not be null"); + } + String type = mimeType.trim().toLowerCase(Locale.US); + if (type.length() == 0) { + throw new IllegalArgumentException("MIME type may not be empty"); + } + if (!valid(type)) { + throw new IllegalArgumentException( + "MIME type may not contain reserved characters"); + } + return new ContentType(type, charset); + } + + public static ContentType create(final String mimeType) { + return new ContentType(mimeType, (Charset) null); + } + + public static ContentType create(final String mimeType, final String charset) + throws UnsupportedCharsetException { + return create( + mimeType, + (charset != null && charset.length() > 0) ? Charset + .forName(charset) : null); + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..06e96d24 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Header.java @@ -0,0 +1,24 @@ +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 new file mode 100644 index 00000000..158371cb --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpClient.java @@ -0,0 +1,38 @@ +package com.foxinmy.weixin4j.http; + +import java.io.IOException; + +/** + * HTTP 接口 + * + * @className HttpClient + * @author jy + * @date 2015年5月30日 + * @since JDK 1.7 + * @see + */ +public interface HttpClient { + + /** + * 处理请求 + * + * @param request + * 请求 + * @return 响应 + * @throws IOException + */ + HttpResponse execute(HttpRequest request) throws IOException; + + /** + * 处理请求 + * + * @param request + * 请求 + * @param handler + * 处理器 + * @return 泛型处理结果 + * @throws IOException + */ + T execute(HttpRequest request, ResponseHandler handler) + throws IOException; +} 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 new file mode 100644 index 00000000..e4f842ca --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpEntityRequest.java @@ -0,0 +1,16 @@ +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 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 new file mode 100644 index 00000000..72afadac --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpGet.java @@ -0,0 +1,34 @@ +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/HttpMessage.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMessage.java new file mode 100644 index 00000000..ff952dff --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMessage.java @@ -0,0 +1,67 @@ +package com.foxinmy.weixin4j.http; + +/** + * HTTP messages consist of requests from client to server and responses from + * server to client. + * + * @className HttpMessage + * @author jy + * @date 2015年5月29日 + * @since JDK 1.7 + * @see + */ +public interface HttpMessage { + + /** + * 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); +} 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 new file mode 100644 index 00000000..a078e077 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpMethod.java @@ -0,0 +1,14 @@ +package com.foxinmy.weixin4j.http; + +/** + * 请求方法 + * + * @className HttpMethod + * @author jy + * @date 2015年5月29日 + * @since JDK 1.7 + * @see + */ +public enum HttpMethod { + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE +} 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 new file mode 100644 index 00000000..b10664a3 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpParams.java @@ -0,0 +1,83 @@ +package com.foxinmy.weixin4j.http; + +import java.util.HashMap; +import java.util.Map; + +public class HttpParams { + private Map params; + + public HttpParams() { + this.params = new HashMap(); + } + + 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)); + } + + public int getConnectTimeout() { + String timeout = params.get(HttpProtocolParams.CONNECT_TIMEOUT); + if (timeout == null) { + return 5000; + } + return Integer.parseInt(timeout); + } + + public void setReadTimeout(int timeout) { + if (timeout < 0) { + timeout = 5000; + } + params.put(HttpProtocolParams.READ_TIMEOUT, Integer.toString(timeout)); + } + + public int getReadTimeout() { + String timeout = params.get(HttpProtocolParams.READ_TIMEOUT); + if (timeout == null) { + return 5000; + } + return Integer.parseInt(timeout); + } + + public void setIfmodifiedsince(long ifmodifiedsince) { + if (ifmodifiedsince < 0) { + ifmodifiedsince = 5000; + } + params.put(HttpProtocolParams.IFMODIFIED_SINCE, + Long.toString(ifmodifiedsince)); + } + + public long getIfmodifiedsince() { + String ifmodifiedsince = params + .get(HttpProtocolParams.IFMODIFIED_SINCE); + if (ifmodifiedsince == null) { + return 0l; + } + return Long.parseLong(ifmodifiedsince); + } + + public void setFollowRedirects(boolean followRedirects) { + params.put(HttpProtocolParams.FOLLOW_REDIRECTS, + Boolean.toString(followRedirects)); + } + + public boolean getFollowRedirects() { + String followRedirects = params + .get(HttpProtocolParams.FOLLOW_REDIRECTS); + return followRedirects != null && Boolean.parseBoolean(followRedirects); + } +} 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 new file mode 100644 index 00000000..62267e02 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpPost.java @@ -0,0 +1,48 @@ +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; + } + + public void setEntity(HttpEntity httpEntity) { + this.entity = httpEntity; + } +} 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 new file mode 100644 index 00000000..9d864eb2 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpProtocolParams.java @@ -0,0 +1,9 @@ +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 d3ed3cdd..61e8d29a 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 @@ -1,281 +1,59 @@ -package com.foxinmy.weixin4j.http; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.http.Consts; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHeaders; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.NameValuePair; -import org.apache.http.StatusLine; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.client.params.CookiePolicy; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.FileEntity; -import org.apache.http.entity.StringEntity; -import org.apache.http.entity.mime.MultipartEntity; -import org.apache.http.impl.client.AbstractHttpClient; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.impl.conn.PoolingClientConnectionManager; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.CoreProtocolPNames; -import org.apache.http.util.EntityUtils; - -import com.alibaba.fastjson.JSONException; -import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.util.ErrorUtil; -import com.foxinmy.weixin4j.util.MapUtil; -import com.foxinmy.weixin4j.util.StringUtil; -import com.thoughtworks.xstream.mapper.CannotResolveClassException; - -/** - * 调用微信相关接口的HttpRequest,对于其他请求可能并不适用 - * - * @className HttpRequest - * @author jy - * @date 2014年8月21日 - * @since JDK 1.7 - * @see - */ -public class HttpRequest { - - protected AbstractHttpClient client; - - public HttpRequest() { - this(150, 100, 10000, 10000); - } - - public HttpRequest(int maxConPerRoute, int maxTotal, int socketTimeout, - int connectionTimeout) { - PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(); - // 指定IP并发最大数 - connectionManager.setDefaultMaxPerRoute(maxConPerRoute); - // socket最大创建数 - connectionManager.setMaxTotal(maxTotal); - - client = new DefaultHttpClient(connectionManager); - client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, - socketTimeout); - client.getParams().setParameter( - CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout); - client.getParams().setBooleanParameter( - CoreConnectionPNames.TCP_NODELAY, false); - client.getParams().setParameter( - CoreConnectionPNames.SOCKET_BUFFER_SIZE, 1024 * 1024); - client.getParams().setParameter(ClientPNames.COOKIE_POLICY, - CookiePolicy.IGNORE_COOKIES); - client.getParams().setParameter( - CoreProtocolPNames.HTTP_CONTENT_CHARSET, Consts.UTF_8); - client.getParams().setParameter(HttpHeaders.CONTENT_ENCODING, - Consts.UTF_8); - client.getParams().setParameter(HttpHeaders.ACCEPT_CHARSET, - Consts.UTF_8); - } - - public Response get(String url) throws WeixinException { - return get(url, (Parameter[]) null); - } - - public Response get(String url, Map para) - throws WeixinException { - return get( - String.format("%s?%s", url, - MapUtil.toJoinString(para, false, false, null)), - (Parameter[]) null); - } - - public Response get(String url, Parameter... 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(String.format("%s=%s", parameters[0].getName(), - parameters[0].getValue())); - if (parameters.length > 1) { - for (int i = 1; i < parameters.length; i++) { - sb.append(parameters[i].toGetPara()); - } - } - } - return doRequest(new HttpGet(sb.toString())); - } - - public Response post(String url) throws WeixinException { - return post(url, (Parameter[]) null); - } - - public Response post(String url, Parameter... parameters) - throws WeixinException { - HttpPost method = new HttpPost(url); - if (parameters != null && parameters.length > 0) { - List params = new ArrayList(); - for (Parameter parameter : parameters) { - params.add(parameter.toPostPara()); - } - method.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8)); - } - return doRequest(method); - } - - public Response post(String url, String body) throws WeixinException { - HttpPost method = new HttpPost(url); - method.setEntity(new StringEntity(body, ContentType.create( - ContentType.DEFAULT_TEXT.getMimeType(), Consts.UTF_8))); - return doRequest(method); - } - - public Response post(String url, byte[] bytes) throws WeixinException { - HttpPost method = new HttpPost(url); - method.setEntity(new ByteArrayEntity(bytes, ContentType.create( - ContentType.MULTIPART_FORM_DATA.getMimeType(), Consts.UTF_8))); - return doRequest(method); - } - - public Response post(String url, File file) throws WeixinException { - HttpPost method = new HttpPost(url); - method.setEntity(new FileEntity(file, ContentType.create( - ContentType.APPLICATION_OCTET_STREAM.getMimeType(), - Consts.UTF_8))); - return doRequest(method); - } - - public Response post(String url, PartParameter... paramters) - throws WeixinException { - HttpPost method = new HttpPost(url); - MultipartEntity entity = new MultipartEntity(); - for (PartParameter paramter : paramters) { - entity.addPart(paramter.getName(), paramter.getContentBody()); - } - method.setEntity(entity); - - return doRequest(method); - } - - protected Response doRequest(HttpRequestBase request) - throws WeixinException { - Response response = null; - try { - HttpResponse httpResponse = client.execute(request); - StatusLine statusLine = httpResponse.getStatusLine(); - HttpEntity httpEntity = httpResponse.getEntity(); - int status = statusLine.getStatusCode(); - if (status != HttpStatus.SC_OK) { - throw new WeixinException(Integer.toString(status), - "request fail"); - } - // 301或者302 - if (status == HttpStatus.SC_MOVED_PERMANENTLY - || status == HttpStatus.SC_MOVED_TEMPORARILY) { - throw new WeixinException(Integer.toString(status), - String.format("the page was redirected to %s", - httpResponse.getFirstHeader("location"))); - } - - byte[] data = EntityUtils.toByteArray(httpEntity); - response = new Response(); - response.setBody(data); - response.setStatusCode(status); - response.setStatusText(statusLine.getReasonPhrase()); - response.setStream(new ByteArrayInputStream(data)); - response.setText(new String(data, Consts.UTF_8)); - EntityUtils.consume(httpEntity); - 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 (CannotResolveClassException ex) { - ; - } - throw new WeixinException(response.getAsString()); - } - } catch (IOException e) { - throw new WeixinException(e.getMessage()); - } finally { - request.releaseConnection(); - } - return response; - } - - private void checkJson(Response 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(Response response) throws WeixinException { - XmlResult xmlResult = null; - try { - xmlResult = response.getAsXmlResult(); - } catch (CannotResolveClassException ex) { - // - String newXml = response.getAsString() - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", ""); - response.setText(newXml); - xmlResult = response.getAsXmlResult(); - } - 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()); - } - } -} +package com.foxinmy.weixin4j.http; + +import java.net.URI; + +/** + * HTTP 请求 + * + * @className HttpRequest + * @author jy + * @date 2015年5月29日 + * @since JDK 1.7 + * @see + */ +public interface HttpRequest extends HttpMessage { + + /** + * 请求方式 + * + * @return + */ + HttpMethod getMethod(); + + /** + * 请求路径 + * + * @return + */ + URI getURI(); + + /** + * 请求参数 + * + * @return + */ + HttpParams getParams(); + + /** + * 新增表头 + * + * @param header + */ + void addHeader(Header header); + + /** + * 移除表头 + * + * @param name + * @return + */ + boolean removeHeader(String name); + + /** + * 移除表头 + * + * @param name + * @return + */ + boolean removeHeader(Header name); +} 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 new file mode 100644 index 00000000..decab2e8 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java @@ -0,0 +1,52 @@ +package com.foxinmy.weixin4j.http; + +/** + * HTTP 响应 + * + * @className HttpResponse + * @author jy + * @date 2015年5月30日 + * @since JDK 1.7 + * @see + */ +public class HttpResponse extends AbstractHttpMessage implements HttpMessage { + + private HttpVersion httpVersion; + private StatusLine statusLine; + private byte[] content; + + @Override + public HttpVersion getProtocolVersion() { + return httpVersion; + } + + 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 + "]"; + } +} 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 new file mode 100644 index 00000000..9ecc0f79 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java @@ -0,0 +1,175 @@ +/* + * ==================================================================== + * 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; + +/** + * Constants enumerating the HTTP status codes. All status codes defined in + * RFC1945 (HTTP/1.0), RFC2616 (HTTP/1.1), and RFC2518 (WebDAV) are listed. + * + * @see StatusLine + * + * @since 4.0 + */ +public final class HttpStatus { + + // --- 1xx Informational --- + + /** 100 Continue (HTTP/1.1 - RFC 2616) */ + public static final int SC_CONTINUE = 100; + /** 101 Switching Protocols (HTTP/1.1 - RFC 2616) */ + public static final int SC_SWITCHING_PROTOCOLS = 101; + /** 102 Processing (WebDAV - RFC 2518) */ + public static final int SC_PROCESSING = 102; + + // --- 2xx Success --- + + /** 200 OK (HTTP/1.0 - RFC 1945) */ + public static final int SC_OK = 200; + /** 201 Created (HTTP/1.0 - RFC 1945) */ + public static final int SC_CREATED = 201; + /** 202 Accepted (HTTP/1.0 - RFC 1945) */ + public static final int SC_ACCEPTED = 202; + /** 203 Non Authoritative Information (HTTP/1.1 - RFC 2616) */ + public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203; + /** 204 No Content (HTTP/1.0 - RFC 1945) */ + public static final int SC_NO_CONTENT = 204; + /** 205 Reset Content (HTTP/1.1 - RFC 2616) */ + public static final int SC_RESET_CONTENT = 205; + /** 206 Partial Content (HTTP/1.1 - RFC 2616) */ + public static final int SC_PARTIAL_CONTENT = 206; + /** + * 207 Multi-Status (WebDAV - RFC 2518) or 207 Partial Update + * OK (HTTP/1.1 - draft-ietf-http-v11-spec-rev-01?) + */ + public static final int SC_MULTI_STATUS = 207; + + // --- 3xx Redirection --- + + /** 300 Mutliple Choices (HTTP/1.1 - RFC 2616) */ + public static final int SC_MULTIPLE_CHOICES = 300; + /** 301 Moved Permanently (HTTP/1.0 - RFC 1945) */ + public static final int SC_MOVED_PERMANENTLY = 301; + /** + * 302 Moved Temporarily (Sometimes Found) (HTTP/1.0 - RFC + * 1945) + */ + public static final int SC_MOVED_TEMPORARILY = 302; + /** 303 See Other (HTTP/1.1 - RFC 2616) */ + public static final int SC_SEE_OTHER = 303; + /** 304 Not Modified (HTTP/1.0 - RFC 1945) */ + public static final int SC_NOT_MODIFIED = 304; + /** 305 Use Proxy (HTTP/1.1 - RFC 2616) */ + public static final int SC_USE_PROXY = 305; + /** 307 Temporary Redirect (HTTP/1.1 - RFC 2616) */ + public static final int SC_TEMPORARY_REDIRECT = 307; + + // --- 4xx Client Error --- + + /** 400 Bad Request (HTTP/1.1 - RFC 2616) */ + public static final int SC_BAD_REQUEST = 400; + /** 401 Unauthorized (HTTP/1.0 - RFC 1945) */ + public static final int SC_UNAUTHORIZED = 401; + /** 402 Payment Required (HTTP/1.1 - RFC 2616) */ + public static final int SC_PAYMENT_REQUIRED = 402; + /** 403 Forbidden (HTTP/1.0 - RFC 1945) */ + public static final int SC_FORBIDDEN = 403; + /** 404 Not Found (HTTP/1.0 - RFC 1945) */ + public static final int SC_NOT_FOUND = 404; + /** 405 Method Not Allowed (HTTP/1.1 - RFC 2616) */ + public static final int SC_METHOD_NOT_ALLOWED = 405; + /** 406 Not Acceptable (HTTP/1.1 - RFC 2616) */ + public static final int SC_NOT_ACCEPTABLE = 406; + /** 407 Proxy Authentication Required (HTTP/1.1 - RFC 2616) */ + public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407; + /** 408 Request Timeout (HTTP/1.1 - RFC 2616) */ + public static final int SC_REQUEST_TIMEOUT = 408; + /** 409 Conflict (HTTP/1.1 - RFC 2616) */ + public static final int SC_CONFLICT = 409; + /** 410 Gone (HTTP/1.1 - RFC 2616) */ + public static final int SC_GONE = 410; + /** 411 Length Required (HTTP/1.1 - RFC 2616) */ + public static final int SC_LENGTH_REQUIRED = 411; + /** 412 Precondition Failed (HTTP/1.1 - RFC 2616) */ + public static final int SC_PRECONDITION_FAILED = 412; + /** 413 Request Entity Too Large (HTTP/1.1 - RFC 2616) */ + public static final int SC_REQUEST_TOO_LONG = 413; + /** 414 Request-URI Too Long (HTTP/1.1 - RFC 2616) */ + public static final int SC_REQUEST_URI_TOO_LONG = 414; + /** 415 Unsupported Media Type (HTTP/1.1 - RFC 2616) */ + public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415; + /** 416 Requested Range Not Satisfiable (HTTP/1.1 - RFC 2616) */ + public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416; + /** 417 Expectation Failed (HTTP/1.1 - RFC 2616) */ + public static final int SC_EXPECTATION_FAILED = 417; + + /** + * Static constant for a 418 error. 418 Unprocessable Entity + * (WebDAV drafts?) or 418 Reauthentication Required (HTTP/1.1 + * drafts?) + */ + // not used + // public static final int SC_UNPROCESSABLE_ENTITY = 418; + + /** + * Static constant for a 419 error. + * 419 Insufficient Space on Resource (WebDAV - + * draft-ietf-webdav-protocol-05?) or + * 419 Proxy Reauthentication Required (HTTP/1.1 drafts?) + */ + public static final int SC_INSUFFICIENT_SPACE_ON_RESOURCE = 419; + /** + * Static constant for a 420 error. 420 Method Failure (WebDAV - + * draft-ietf-webdav-protocol-05?) + */ + public static final int SC_METHOD_FAILURE = 420; + /** 422 Unprocessable Entity (WebDAV - RFC 2518) */ + public static final int SC_UNPROCESSABLE_ENTITY = 422; + /** 423 Locked (WebDAV - RFC 2518) */ + public static final int SC_LOCKED = 423; + /** 424 Failed Dependency (WebDAV - RFC 2518) */ + public static final int SC_FAILED_DEPENDENCY = 424; + + // --- 5xx Server Error --- + + /** 500 Server Error (HTTP/1.0 - RFC 1945) */ + public static final int SC_INTERNAL_SERVER_ERROR = 500; + /** 501 Not Implemented (HTTP/1.0 - RFC 1945) */ + public static final int SC_NOT_IMPLEMENTED = 501; + /** 502 Bad Gateway (HTTP/1.0 - RFC 1945) */ + public static final int SC_BAD_GATEWAY = 502; + /** 503 Service Unavailable (HTTP/1.0 - RFC 1945) */ + public static final int SC_SERVICE_UNAVAILABLE = 503; + /** 504 Gateway Timeout (HTTP/1.1 - RFC 2616) */ + public static final int SC_GATEWAY_TIMEOUT = 504; + /** 505 HTTP Version Not Supported (HTTP/1.1 - RFC 2616) */ + public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505; + + /** 507 Insufficient Storage (WebDAV - RFC 2518) */ + public static final int SC_INSUFFICIENT_STORAGE = 507; + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpVersion.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpVersion.java new file mode 100644 index 00000000..0f45a610 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpVersion.java @@ -0,0 +1,122 @@ +package com.foxinmy.weixin4j.http; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * reference of apache pivot + * + * @className HttpVersion + * @author jy + * @date 2015年5月29日 + * @since JDK 1.7 + * @see + */ +public class HttpVersion implements Comparable { + + private static final Pattern VERSION_PATTERN = Pattern + .compile("(\\S+)/(\\d+)\\.(\\d+)"); + + public static final String HTTP_1_0_STRING = "HTTP/1.0"; + public static final String HTTP_1_1_STRING = "HTTP/1.1"; + + /** + * HTTP/1.0 + */ + public static final HttpVersion HTTP_1_0 = new HttpVersion("HTTP", 1, 0, + false); + + /** + * HTTP/1.1 + */ + public static final HttpVersion HTTP_1_1 = new HttpVersion("HTTP", 1, 1, + true); + + private final String protocol; + private final int major; + private final int minor; + private final boolean keepAlive; + private final String text; + + public HttpVersion(String text, boolean keepAlive) { + if (text == null) { + throw new NullPointerException("text"); + } + + text = text.trim().toUpperCase(); + if (text.isEmpty()) { + throw new IllegalArgumentException("empty text"); + } + Matcher m = VERSION_PATTERN.matcher(text); + if (!m.matches()) { + throw new IllegalArgumentException("invalid version format: " + + text); + } + this.protocol = m.group(1); + this.major = Integer.parseInt(m.group(2)); + this.minor = Integer.parseInt(m.group(3)); + this.keepAlive = keepAlive; + this.text = protocol + '/' + major + '.' + minor; + } + + public HttpVersion(String protocol, int major, int minor, boolean keepAlive) { + this.protocol = protocol; + this.major = major; + this.minor = minor; + this.keepAlive = keepAlive; + this.text = protocol + '/' + major + '.' + minor; + } + + public String getProtocol() { + return protocol; + } + + public int getMajor() { + return major; + } + + public int getMinor() { + return minor; + } + + public boolean isKeepAlive() { + return keepAlive; + } + + public String getText() { + return text; + } + + @Override + public String toString() { + return getText(); + } + + @Override + public int hashCode() { + return (protocol.hashCode() * 31 + major) * 31 + minor; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof HttpVersion)) { + return false; + } + HttpVersion that = (HttpVersion) o; + return minor == that.minor && major == that.major + && protocol.equals(that.protocol); + } + + @Override + public int compareTo(HttpVersion o) { + int v = protocol.compareTo(o.protocol); + if (v != 0) { + return v; + } + v = major - o.major; + if (v != 0) { + return v; + } + return minor - o.minor; + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/NameValue.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/NameValue.java new file mode 100644 index 00000000..c6454005 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/NameValue.java @@ -0,0 +1,24 @@ +package com.foxinmy.weixin4j.http; + +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/http/Parameter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Parameter.java deleted file mode 100644 index 4f20188d..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Parameter.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -import org.apache.http.Consts; -import org.apache.http.NameValuePair; -import org.apache.http.message.BasicNameValuePair; - -public class Parameter { - - private String name; - private String value; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public Parameter() { - - } - - public Parameter(String name, String value) { - this.name = name; - this.value = value; - } - - public String toGetPara() { - try { - return String.format("&%s=%s", name, - URLEncoder.encode(value, Consts.UTF_8.name())); - } catch (UnsupportedEncodingException e) { - return String.format("&%s=%s", name, value); - } - } - - public NameValuePair toPostPara() { - try { - return new BasicNameValuePair(name, URLEncoder.encode(value, - Consts.UTF_8.name())); - } catch (UnsupportedEncodingException e) { - return new BasicNameValuePair(name, value); - } - } - - @Override - public String toString() { - return String.format("[Parameter name=%s, value=%s]", name, value); - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/PartParameter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/PartParameter.java deleted file mode 100644 index c522a8dd..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/PartParameter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import org.apache.http.entity.mime.content.ContentBody; - -public class PartParameter { - private String name; - private ContentBody contentBody; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public ContentBody getContentBody() { - return contentBody; - } - - public void setContentBody(ContentBody contentBody) { - this.contentBody = contentBody; - } - - public PartParameter(String name, ContentBody contentBody) { - super(); - this.name = name; - this.contentBody = contentBody; - } - - @Override - public String toString() { - return "PartParameter [name=" + name + ", contentBody=" + contentBody + "]"; - } -} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Response.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Response.java deleted file mode 100644 index 9ea367c4..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/Response.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.io.InputStream; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.TypeReference; -import com.foxinmy.weixin4j.xml.XmlStream; - -public class Response { - - private String text; - private int statusCode; - private String statusText; - private byte[] body; - private InputStream stream; - private boolean isJsonResult; - private boolean isXmlResult; - - public Response() { - } - - public Response(String text) { - this.text = text; - } - - public void setJsonResult(boolean isJsonResult) { - this.isJsonResult = isJsonResult; - } - - public void setXmlResult(boolean isXmlResult) { - this.isXmlResult = isXmlResult; - } - - public String getAsString() { - return text; - } - - public JsonResult getAsJsonResult() { - return JSON.parseObject(text, JsonResult.class); - } - - public JSONObject getAsJson() { - return JSON.parseObject(text); - } - - public T getAsObject(TypeReference typeReference) { - if (isJsonResult) { - return JSON.parseObject(text, typeReference); - } - if (isXmlResult) { - @SuppressWarnings("unchecked") - Class clazz = (Class) typeReference.getType(); - return XmlStream.get(text, clazz); - } - return null; - } - - public XmlResult getAsXmlResult() { - return XmlStream.get(text, XmlResult.class); - } - - public void setText(String text) { - this.text = text; - } - - public int getStatusCode() { - return statusCode; - } - - public void setStatusCode(int statusCode) { - this.statusCode = statusCode; - } - - public String getStatusText() { - return statusText; - } - - public void setStatusText(String statusText) { - this.statusText = statusText; - } - - public byte[] getBody() { - return (byte[]) body.clone(); - } - - /** - * May expose internal representation by incorporating reference to mutable - * object - */ - public void setBody(byte[] body) { - this.body = (byte[]) body.clone(); - } - - public InputStream getStream() { - return stream; - } - - public void setStream(InputStream stream) { - this.stream = stream; - } - - @Override - public String toString() { - return "Response [text=" + text + ", statusCode=" + statusCode - + ", statusText=" + statusText + ", stream=" + stream - + ", isJsonResult=" + isJsonResult + ", isXmlResult=" - + isXmlResult + "]"; - } -} 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 new file mode 100644 index 00000000..8a568dbe --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ResponseHandler.java @@ -0,0 +1,16 @@ +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/SSLHttpRequest.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SSLHttpRequest.java deleted file mode 100644 index a4e4c40c..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SSLHttpRequest.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.foxinmy.weixin4j.http; - -import java.io.InputStream; -import java.security.KeyStore; - -import javax.net.ssl.SSLContext; - -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeSocketFactory; -import org.apache.http.conn.ssl.SSLSocketFactory; - -/** - * ssl请求 - * - * @className SSLHttpRequest - * @author jy - * @date 2014年11月6日 - * @since JDK 1.7 - * @see - */ -public class SSLHttpRequest extends HttpRequest { - - public SSLHttpRequest(String password, InputStream inputStream) { - super(); - try { - KeyStore trustStore = KeyStore - .getInstance(com.foxinmy.weixin4j.model.Consts.PKCS12); - trustStore.load(inputStream, password.toCharArray()); - SSLSocketFactory socketFactory = new SSLSocketFactory(trustStore, - password); - client.getConnectionManager().getSchemeRegistry() - .register(new Scheme("https", 443, socketFactory)); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - if (inputStream != null) { - inputStream.close(); - } - } catch (Exception ignore) { - ; - } - } - } - - public SSLHttpRequest(SSLContext sslContext) { - super(); - SchemeSocketFactory socketFactory = new SSLSocketFactory(sslContext); - client.getConnectionManager().getSchemeRegistry() - .register(new Scheme("https", 443, socketFactory)); - } -} 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 new file mode 100644 index 00000000..fc9c85a8 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java @@ -0,0 +1,200 @@ +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.URI; +import java.net.URL; +import java.security.GeneralSecurityException; +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; +import java.util.Map; +import java.util.Map.Entry; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.X509TrustManager; + +import com.foxinmy.weixin4j.http.entity.HttpEntity; +import com.foxinmy.weixin4j.model.Consts; + +/** + * HTTP 简单实现 + * + * @className SimpleHttpClient + * @author jy + * @date 2015年5月29日 + * @since JDK 1.7 + * @see + */ +public class SimpleHttpClient implements HttpClient { + + protected HostnameVerifier createHostnameVerifier() { + return new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + } + + protected X509TrustManager createX509TrustManager() { + return new X509TrustManager() { + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + @Override + public void checkServerTrusted( + X509Certificate[] paramArrayOfX509Certificate, + String paramString) throws CertificateException { + } + + @Override + public void checkClientTrusted( + X509Certificate[] paramArrayOfX509Certificate, + String paramString) throws CertificateException { + } + }; + } + + protected HttpURLConnection createHttpConnection(URI uri) + throws IOException { + URL url = uri.toURL(); + 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); + } + connection.setSSLSocketFactory(sslContext.getSocketFactory()); + connection.setHostnameVerifier(createHostnameVerifier()); + return connection; + } else { + return (HttpURLConnection) url.openConnection(); + } + } + + 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; + } + + @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); + connection.setFixedLengthStreamingMode(httpEntity + .getContentLength()); + connection.setRequestProperty("Content-Type", httpEntity + .getContentType().getMimeType()); + } + 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(); + } + 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); + } else { + response.setHttpVersion(new HttpVersion(httpVersion, true)); + } + } + 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/StatusLine.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/StatusLine.java new file mode 100644 index 00000000..a6517380 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/StatusLine.java @@ -0,0 +1,38 @@ +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/UrlEncodeParameter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/UrlEncodeParameter.java new file mode 100644 index 00000000..8802d5b8 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/UrlEncodeParameter.java @@ -0,0 +1,39 @@ +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/AbstractContentBody.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/AbstractContentBody.java new file mode 100644 index 00000000..2a3dec29 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/AbstractContentBody.java @@ -0,0 +1,68 @@ +/* + * ==================================================================== + * 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; + +/** + * + * @since 4.0 + */ +public abstract class AbstractContentBody implements ContentBody { + + private final String mimeType; + private final String mediaType; + private final String subType; + + public AbstractContentBody(final String mimeType) { + super(); + if (mimeType == null) { + throw new IllegalArgumentException("MIME type may not be null"); + } + this.mimeType = mimeType; + int i = mimeType.indexOf('/'); + if (i != -1) { + this.mediaType = mimeType.substring(0, i); + this.subType = mimeType.substring(i + 1); + } else { + this.mediaType = mimeType; + this.subType = null; + } + } + + public String getMimeType() { + return this.mimeType; + } + + public String getMediaType() { + return this.mediaType; + } + + public String getSubType() { + return this.subType; + } + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ByteArrayBody.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ByteArrayBody.java new file mode 100644 index 00000000..c778695f --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ByteArrayBody.java @@ -0,0 +1,95 @@ +/* + * ==================================================================== + * 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; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Body part that is built using a byte array containing a file. + * + * @since 4.1 + */ +public class ByteArrayBody extends AbstractContentBody { + + /** + * The contents of the file contained in this part. + */ + private final byte[] data; + + /** + * The name of the file contained in this part. + */ + private final String filename; + + /** + * Creates a new ByteArrayBody. + * + * @param data The contents of the file contained in this part. + * @param mimeType The mime type of the file contained in this part. + * @param filename The name of the file contained in this part. + */ + public ByteArrayBody(final byte[] data, final String mimeType, final String filename) { + super(mimeType); + if (data == null) { + throw new IllegalArgumentException("byte[] may not be null"); + } + this.data = data; + this.filename = filename; + } + + /** + * Creates a new ByteArrayBody. + * + * @param data The contents of the file contained in this part. + * @param filename The name of the file contained in this part. + */ + public ByteArrayBody(final byte[] data, final String filename) { + this(data, "application/octet-stream", filename); + } + + public String getFilename() { + return filename; + } + + public void writeTo(final OutputStream out) throws IOException { + out.write(data); + } + + public String getCharset() { + return null; + } + + public String getTransferEncoding() { + return MIME.ENC_BINARY; + } + + public long getContentLength() { + return data.length; + } + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ByteArrayBuffer.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ByteArrayBuffer.java new file mode 100644 index 00000000..e33116ed --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ByteArrayBuffer.java @@ -0,0 +1,345 @@ +/* + * ==================================================================== + * 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; + +import java.io.Serializable; + + +/** + * A resizable byte array. + * + * @since 4.0 + */ +public final class ByteArrayBuffer implements Serializable { + + private static final long serialVersionUID = 4359112959524048036L; + + private byte[] buffer; + private int len; + + /** + * Creates an instance of {@link ByteArrayBuffer} with the given initial + * capacity. + * + * @param capacity the capacity + */ + public ByteArrayBuffer(int capacity) { + super(); + if (capacity < 0) { + throw new IllegalArgumentException("Buffer capacity may not be negative"); + } + this.buffer = new byte[capacity]; + } + + private void expand(int newlen) { + byte newbuffer[] = new byte[Math.max(this.buffer.length << 1, newlen)]; + System.arraycopy(this.buffer, 0, newbuffer, 0, this.len); + this.buffer = newbuffer; + } + + /** + * Appends len bytes to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased, if necessary, to accommodate all len bytes. + * + * @param b the bytes to be appended. + * @param off the index of the first byte to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off if out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final byte[] b, int off, int len) { + if (b == null) { + return; + } + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) < 0) || ((off + len) > b.length)) { + throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); + } + if (len == 0) { + return; + } + int newlen = this.len + len; + if (newlen > this.buffer.length) { + expand(newlen); + } + System.arraycopy(b, off, this.buffer, this.len, len); + this.len = newlen; + } + + /** + * Appends b byte to this buffer. The capacity of the buffer + * is increased, if necessary, to accommodate the additional byte. + * + * @param b the byte to be appended. + */ + public void append(int b) { + int newlen = this.len + 1; + if (newlen > this.buffer.length) { + expand(newlen); + } + this.buffer[this.len] = (byte)b; + this.len = newlen; + } + + /** + * Appends len chars to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased if necessary to accommodate all len chars. + *

+ * The chars are converted to bytes using simple cast. + * + * @param b the chars to be appended. + * @param off the index of the first char to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off if out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final char[] b, int off, int len) { + if (b == null) { + return; + } + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) < 0) || ((off + len) > b.length)) { + throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); + } + if (len == 0) { + return; + } + int oldlen = this.len; + int newlen = oldlen + len; + if (newlen > this.buffer.length) { + expand(newlen); + } + for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) { + this.buffer[i2] = (byte) b[i1]; + } + this.len = newlen; + } + + /** + * Appends len chars to this buffer from the given source + * char array buffer starting at index off. The capacity + * of the buffer is increased if necessary to accommodate all + * len chars. + *

+ * The chars are converted to bytes using simple cast. + * + * @param b the chars to be appended. + * @param off the index of the first char to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off if out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final CharArrayBuffer b, int off, int len) { + if (b == null) { + return; + } + append(b.buffer(), off, len); + } + + /** + * Clears content of the buffer. The underlying byte array is not resized. + */ + public void clear() { + this.len = 0; + } + + /** + * Converts the content of this buffer to an array of bytes. + * + * @return byte array + */ + public byte[] toByteArray() { + byte[] b = new byte[this.len]; + if (this.len > 0) { + System.arraycopy(this.buffer, 0, b, 0, this.len); + } + return b; + } + + /** + * Returns the byte value in this buffer at the specified + * index. The index argument must be greater than or equal to + * 0, and less than the length of this buffer. + * + * @param i the index of the desired byte value. + * @return the byte value at the specified index. + * @throws IndexOutOfBoundsException if index is + * negative or greater than or equal to {@link #length()}. + */ + public int byteAt(int i) { + return this.buffer[i]; + } + + /** + * Returns the current capacity. The capacity is the amount of storage + * available for newly appended bytes, beyond which an allocation + * will occur. + * + * @return the current capacity + */ + public int capacity() { + return this.buffer.length; + } + + /** + * Returns the length of the buffer (byte count). + * + * @return the length of the buffer + */ + public int length() { + return this.len; + } + + /** + * Ensures that the capacity is at least equal to the specified minimum. + * If the current capacity is less than the argument, then a new internal + * array is allocated with greater capacity. If the required + * argument is non-positive, this method takes no action. + * + * @param required the minimum required capacity. + * + * @since 4.1 + */ + public void ensureCapacity(int required) { + if (required <= 0) { + return; + } + int available = this.buffer.length - this.len; + if (required > available) { + expand(this.len + required); + } + } + + /** + * Returns reference to the underlying byte array. + * + * @return the byte array. + */ + public byte[] buffer() { + return this.buffer; + } + + /** + * Sets the length of the buffer. The new length value is expected to be + * less than the current capacity and greater than or equal to + * 0. + * + * @param len the new length + * @throws IndexOutOfBoundsException if the + * len argument is greater than the current + * capacity of the buffer or less than 0. + */ + public void setLength(int len) { + if (len < 0 || len > this.buffer.length) { + throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length); + } + this.len = len; + } + + /** + * Returns true if this buffer is empty, that is, its + * {@link #length()} is equal to 0. + * @return true if this buffer is empty, false + * otherwise. + */ + public boolean isEmpty() { + return this.len == 0; + } + + /** + * Returns true if this buffer is full, that is, its + * {@link #length()} is equal to its {@link #capacity()}. + * @return true if this buffer is full, false + * otherwise. + */ + public boolean isFull() { + return this.len == this.buffer.length; + } + + /** + * Returns the index within this buffer of the first occurrence of the + * specified byte, starting the search at the specified + * beginIndex and finishing at endIndex. + * If no such byte occurs in this buffer within the specified bounds, + * -1 is returned. + *

+ * There is no restriction on the value of beginIndex and + * endIndex. If beginIndex is negative, + * it has the same effect as if it were zero. If endIndex is + * greater than {@link #length()}, it has the same effect as if it were + * {@link #length()}. If the beginIndex is greater than + * the endIndex, -1 is returned. + * + * @param b the byte to search for. + * @param beginIndex the index to start the search from. + * @param endIndex the index to finish the search at. + * @return the index of the first occurrence of the byte in the buffer + * within the given bounds, or -1 if the byte does + * not occur. + * + * @since 4.1 + */ + public int indexOf(byte b, int beginIndex, int endIndex) { + if (beginIndex < 0) { + beginIndex = 0; + } + if (endIndex > this.len) { + endIndex = this.len; + } + if (beginIndex > endIndex) { + return -1; + } + for (int i = beginIndex; i < endIndex; i++) { + if (this.buffer[i] == b) { + return i; + } + } + return -1; + } + + /** + * Returns the index within this buffer of the first occurrence of the + * specified byte, starting the search at 0 and finishing + * at {@link #length()}. If no such byte occurs in this buffer within + * those bounds, -1 is returned. + * + * @param b the byte to search for. + * @return the index of the first occurrence of the byte in the + * buffer, or -1 if the byte does not occur. + * + * @since 4.1 + */ + public int indexOf(byte b) { + return indexOf(b, 0, this.len); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/CharArrayBuffer.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/CharArrayBuffer.java new file mode 100644 index 00000000..ce746828 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/CharArrayBuffer.java @@ -0,0 +1,460 @@ +/* + * ==================================================================== + * 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; + +import java.io.Serializable; + +/** + * A resizable char array. + * + * @since 4.0 + */ +public final class CharArrayBuffer implements Serializable { + + private static final long serialVersionUID = -6208952725094867135L; + + private char[] buffer; + private int len; + + /** + * Creates an instance of {@link CharArrayBuffer} with the given initial + * capacity. + * + * @param capacity the capacity + */ + public CharArrayBuffer(int capacity) { + super(); + if (capacity < 0) { + throw new IllegalArgumentException("Buffer capacity may not be negative"); + } + this.buffer = new char[capacity]; + } + + private void expand(int newlen) { + char newbuffer[] = new char[Math.max(this.buffer.length << 1, newlen)]; + System.arraycopy(this.buffer, 0, newbuffer, 0, this.len); + this.buffer = newbuffer; + } + + /** + * Appends len chars to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased, if necessary, to accommodate all len chars. + * + * @param b the chars to be appended. + * @param off the index of the first char to append. + * @param len the number of chars to append. + * @throws IndexOutOfBoundsException if off is out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final char[] b, int off, int len) { + if (b == null) { + return; + } + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) < 0) || ((off + len) > b.length)) { + throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); + } + if (len == 0) { + return; + } + int newlen = this.len + len; + if (newlen > this.buffer.length) { + expand(newlen); + } + System.arraycopy(b, off, this.buffer, this.len, len); + this.len = newlen; + } + + /** + * Appends chars of the given string to this buffer. The capacity of the + * buffer is increased, if necessary, to accommodate all chars. + * + * @param str the string. + */ + public void append(String str) { + if (str == null) { + str = "null"; + } + int strlen = str.length(); + int newlen = this.len + strlen; + if (newlen > this.buffer.length) { + expand(newlen); + } + str.getChars(0, strlen, this.buffer, this.len); + this.len = newlen; + } + + /** + * Appends len chars to this buffer from the given source + * buffer starting at index off. The capacity of the + * destination buffer is increased, if necessary, to accommodate all + * len chars. + * + * @param b the source buffer to be appended. + * @param off the index of the first char to append. + * @param len the number of chars to append. + * @throws IndexOutOfBoundsException if off is out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final CharArrayBuffer b, int off, int len) { + if (b == null) { + return; + } + append(b.buffer, off, len); + } + + /** + * Appends all chars to this buffer from the given source buffer starting + * at index 0. The capacity of the destination buffer is + * increased, if necessary, to accommodate all {@link #length()} chars. + * + * @param b the source buffer to be appended. + */ + public void append(final CharArrayBuffer b) { + if (b == null) { + return; + } + append(b.buffer,0, b.len); + } + + /** + * Appends ch char to this buffer. The capacity of the buffer + * is increased, if necessary, to accommodate the additional char. + * + * @param ch the char to be appended. + */ + public void append(char ch) { + int newlen = this.len + 1; + if (newlen > this.buffer.length) { + expand(newlen); + } + this.buffer[this.len] = ch; + this.len = newlen; + } + + /** + * Appends len bytes to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased, if necessary, to accommodate all len bytes. + *

+ * The bytes are converted to chars using simple cast. + * + * @param b the bytes to be appended. + * @param off the index of the first byte to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off is out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final byte[] b, int off, int len) { + if (b == null) { + return; + } + if ((off < 0) || (off > b.length) || (len < 0) || + ((off + len) < 0) || ((off + len) > b.length)) { + throw new IndexOutOfBoundsException("off: "+off+" len: "+len+" b.length: "+b.length); + } + if (len == 0) { + return; + } + int oldlen = this.len; + int newlen = oldlen + len; + if (newlen > this.buffer.length) { + expand(newlen); + } + for (int i1 = off, i2 = oldlen; i2 < newlen; i1++, i2++) { + this.buffer[i2] = (char) (b[i1] & 0xff); + } + this.len = newlen; + } + + /** + * Appends len bytes to this buffer from the given source + * array starting at index off. The capacity of the buffer + * is increased, if necessary, to accommodate all len bytes. + *

+ * The bytes are converted to chars using simple cast. + * + * @param b the bytes to be appended. + * @param off the index of the first byte to append. + * @param len the number of bytes to append. + * @throws IndexOutOfBoundsException if off is out of + * range, len is negative, or + * off + len is out of range. + */ + public void append(final ByteArrayBuffer b, int off, int len) { + if (b == null) { + return; + } + append(b.buffer(), off, len); + } + + /** + * Appends chars of the textual representation of the given object to this + * buffer. The capacity of the buffer is increased, if necessary, to + * accommodate all chars. + * + * @param obj the object. + */ + public void append(final Object obj) { + append(String.valueOf(obj)); + } + + /** + * Clears content of the buffer. The underlying char array is not resized. + */ + public void clear() { + this.len = 0; + } + + /** + * Converts the content of this buffer to an array of chars. + * + * @return char array + */ + public char[] toCharArray() { + char[] b = new char[this.len]; + if (this.len > 0) { + System.arraycopy(this.buffer, 0, b, 0, this.len); + } + return b; + } + + /** + * Returns the char value in this buffer at the specified + * index. The index argument must be greater than or equal to + * 0, and less than the length of this buffer. + * + * @param i the index of the desired char value. + * @return the char value at the specified index. + * @throws IndexOutOfBoundsException if index is + * negative or greater than or equal to {@link #length()}. + */ + public char charAt(int i) { + return this.buffer[i]; + } + + /** + * Returns reference to the underlying char array. + * + * @return the char array. + */ + public char[] buffer() { + return this.buffer; + } + + /** + * Returns the current capacity. The capacity is the amount of storage + * available for newly appended chars, beyond which an allocation will + * occur. + * + * @return the current capacity + */ + public int capacity() { + return this.buffer.length; + } + + /** + * Returns the length of the buffer (char count). + * + * @return the length of the buffer + */ + public int length() { + return this.len; + } + + /** + * Ensures that the capacity is at least equal to the specified minimum. + * If the current capacity is less than the argument, then a new internal + * array is allocated with greater capacity. If the required + * argument is non-positive, this method takes no action. + * + * @param required the minimum required capacity. + */ + public void ensureCapacity(int required) { + if (required <= 0) { + return; + } + int available = this.buffer.length - this.len; + if (required > available) { + expand(this.len + required); + } + } + + /** + * Sets the length of the buffer. The new length value is expected to be + * less than the current capacity and greater than or equal to + * 0. + * + * @param len the new length + * @throws IndexOutOfBoundsException if the + * len argument is greater than the current + * capacity of the buffer or less than 0. + */ + public void setLength(int len) { + if (len < 0 || len > this.buffer.length) { + throw new IndexOutOfBoundsException("len: "+len+" < 0 or > buffer len: "+this.buffer.length); + } + this.len = len; + } + + /** + * Returns true if this buffer is empty, that is, its + * {@link #length()} is equal to 0. + * @return true if this buffer is empty, false + * otherwise. + */ + public boolean isEmpty() { + return this.len == 0; + } + + /** + * Returns true if this buffer is full, that is, its + * {@link #length()} is equal to its {@link #capacity()}. + * @return true if this buffer is full, false + * otherwise. + */ + public boolean isFull() { + return this.len == this.buffer.length; + } + + /** + * Returns the index within this buffer of the first occurrence of the + * specified character, starting the search at the specified + * beginIndex and finishing at endIndex. + * If no such character occurs in this buffer within the specified bounds, + * -1 is returned. + *

+ * There is no restriction on the value of beginIndex and + * endIndex. If beginIndex is negative, + * it has the same effect as if it were zero. If endIndex is + * greater than {@link #length()}, it has the same effect as if it were + * {@link #length()}. If the beginIndex is greater than + * the endIndex, -1 is returned. + * + * @param ch the char to search for. + * @param beginIndex the index to start the search from. + * @param endIndex the index to finish the search at. + * @return the index of the first occurrence of the character in the buffer + * within the given bounds, or -1 if the character does + * not occur. + */ + public int indexOf(int ch, int beginIndex, int endIndex) { + if (beginIndex < 0) { + beginIndex = 0; + } + if (endIndex > this.len) { + endIndex = this.len; + } + if (beginIndex > endIndex) { + return -1; + } + for (int i = beginIndex; i < endIndex; i++) { + if (this.buffer[i] == ch) { + return i; + } + } + return -1; + } + + /** + * Returns the index within this buffer of the first occurrence of the + * specified character, starting the search at 0 and finishing + * at {@link #length()}. If no such character occurs in this buffer within + * those bounds, -1 is returned. + * + * @param ch the char to search for. + * @return the index of the first occurrence of the character in the + * buffer, or -1 if the character does not occur. + */ + public int indexOf(int ch) { + return indexOf(ch, 0, this.len); + } + + /** + * Returns a substring of this buffer. The substring begins at the specified + * beginIndex and extends to the character at index + * endIndex - 1. + * + * @param beginIndex the beginning index, inclusive. + * @param endIndex the ending index, exclusive. + * @return the specified substring. + * @exception StringIndexOutOfBoundsException if the + * beginIndex is negative, or + * endIndex is larger than the length of this + * buffer, or beginIndex is larger than + * endIndex. + */ + public String substring(int beginIndex, int endIndex) { + return new String(this.buffer, beginIndex, endIndex - beginIndex); + } + + /** + * Returns a substring of this buffer with leading and trailing whitespace + * omitted. The substring begins with the first non-whitespace character + * from beginIndex and extends to the last + * non-whitespace character with the index lesser than + * endIndex. + * + * @param beginIndex the beginning index, inclusive. + * @param endIndex the ending index, exclusive. + * @return the specified substring. + * @exception IndexOutOfBoundsException if the + * beginIndex is negative, or + * endIndex is larger than the length of this + * buffer, or beginIndex is larger than + * endIndex. + */ + public String substringTrimmed(int beginIndex, int endIndex) { + if (beginIndex < 0) { + throw new IndexOutOfBoundsException("Negative beginIndex: "+beginIndex); + } + if (endIndex > this.len) { + throw new IndexOutOfBoundsException("endIndex: "+endIndex+" > length: "+this.len); + } + if (beginIndex > endIndex) { + throw new IndexOutOfBoundsException("beginIndex: "+beginIndex+" > endIndex: "+endIndex); + } + while (beginIndex < endIndex && HTTP.isWhitespace(this.buffer[beginIndex])) { + beginIndex++; + } + while (endIndex > beginIndex && HTTP.isWhitespace(this.buffer[endIndex - 1])) { + endIndex--; + } + return new String(this.buffer, beginIndex, endIndex - beginIndex); + } + + @Override + public String toString() { + return new String(this.buffer, 0, this.len); + } + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ContentBody.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ContentBody.java new file mode 100644 index 00000000..636be946 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ContentBody.java @@ -0,0 +1,43 @@ +/* + * ==================================================================== + * 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; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * + * @since 4.0 + */ +public interface ContentBody extends ContentDescriptor { + + String getFilename(); + + void writeTo(OutputStream out) throws IOException; + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ContentDescriptor.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ContentDescriptor.java new file mode 100644 index 00000000..aa647f89 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/ContentDescriptor.java @@ -0,0 +1,89 @@ +/* + * ==================================================================== + * 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; + +/** + * Represents common content properties. + */ +public interface ContentDescriptor { + + /** + * Returns the body descriptors MIME type. + * @see #getMediaType() + * @see #getSubType() + * @return The MIME type, which has been parsed from the + * content-type definition. Must not be null, but + * "text/plain", if no content-type was specified. + */ + String getMimeType(); + + /** + * Gets the defaulted MIME media type for this content. + * For example TEXT, IMAGE, MULTIPART + * @see #getMimeType() + * @return the MIME media type when content-type specified, + * otherwise the correct default (TEXT) + */ + String getMediaType(); + + /** + * Gets the defaulted MIME sub type for this content. + * @see #getMimeType() + * @return the MIME media type when content-type is specified, + * otherwise the correct default (PLAIN) + */ + String getSubType(); + + /** + *

The body descriptors character set, defaulted appropriately for the MIME type.

+ *

+ * For TEXT types, this will be defaulted to us-ascii. + * For other types, when the charset parameter is missing this property will be null. + *

+ * @return Character set, which has been parsed from the + * content-type definition. Not null for TEXT types, when unset will + * be set to default us-ascii. For other types, when unset, + * null will be returned. + */ + String getCharset(); + + /** + * Returns the body descriptors transfer encoding. + * @return The transfer encoding. Must not be null, but "7bit", + * if no transfer-encoding was specified. + */ + String getTransferEncoding(); + + /** + * Returns the body descriptors content-length. + * @return Content length, if known, or -1, to indicate the absence of a + * content-length header. + */ + long getContentLength(); + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FileBody.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FileBody.java new file mode 100644 index 00000000..269d7eb7 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FileBody.java @@ -0,0 +1,123 @@ +/* + * ==================================================================== + * 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; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * + * @since 4.0 + */ +public class FileBody extends AbstractContentBody { + + private final File file; + private final String filename; + private final String charset; + + /** + * @since 4.1 + */ + public FileBody(final File file, + final String filename, + final String mimeType, + final String charset) { + super(mimeType); + if (file == null) { + throw new IllegalArgumentException("File may not be null"); + } + this.file = file; + if (filename != null) + this.filename = filename; + else + this.filename = file.getName(); + this.charset = charset; + } + + /** + * @since 4.1 + */ + public FileBody(final File file, + final String mimeType, + final String charset) { + this(file, null, mimeType, charset); + } + + public FileBody(final File file, final String mimeType) { + this(file, mimeType, null); + } + + public FileBody(final File file) { + this(file, "application/octet-stream"); + } + + public InputStream getInputStream() throws IOException { + return new FileInputStream(this.file); + } + + public void writeTo(final OutputStream out) throws IOException { + if (out == null) { + throw new IllegalArgumentException("Output stream may not be null"); + } + InputStream in = new FileInputStream(this.file); + try { + byte[] tmp = new byte[4096]; + int l; + while ((l = in.read(tmp)) != -1) { + out.write(tmp, 0, l); + } + out.flush(); + } finally { + in.close(); + } + } + + public String getTransferEncoding() { + return MIME.ENC_BINARY; + } + + public String getCharset() { + return charset; + } + + public long getContentLength() { + return this.file.length(); + } + + public String getFilename() { + return filename; + } + + public File getFile() { + return this.file; + } + +} 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 new file mode 100644 index 00000000..aeb0584d --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/FormBodyPart.java @@ -0,0 +1,110 @@ +/* + * ==================================================================== + * 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; + +import com.foxinmy.weixin4j.http.NameValue; + + +/** + * FormBodyPart class represents a content body that can be used as a part of multipart encoded + * entities. This class automatically populates the header with standard fields based on + * the content description of the enclosed body. + * + * @since 4.0 + */ +public class FormBodyPart { + + private final String name; + private final Header header; + + private final ContentBody body; + + public FormBodyPart(final String name, final ContentBody body) { + super(); + if (name == null) { + throw new IllegalArgumentException("Name may not be null"); + } + if (body == null) { + throw new IllegalArgumentException("Body may not be null"); + } + this.name = name; + this.body = body; + this.header = new Header(); + + generateContentDisp(body); + generateContentType(body); + generateTransferEncoding(body); + } + + public String getName() { + return this.name; + } + + public ContentBody getBody() { + return this.body; + } + + public Header getHeader() { + return this.header; + } + + public void addField(final String name, final String value) { + if (name == null) { + throw new IllegalArgumentException("Field name may not be null"); + } + this.header.addField(new NameValue(name, value)); + } + + protected void generateContentDisp(final ContentBody body) { + StringBuilder buffer = new StringBuilder(); + buffer.append("form-data; name=\""); + buffer.append(getName()); + buffer.append("\""); + if (body.getFilename() != null) { + buffer.append("; filename=\""); + buffer.append(body.getFilename()); + buffer.append("\""); + } + addField(MIME.CONTENT_DISPOSITION, buffer.toString()); + } + + protected void generateContentType(final ContentBody body) { + StringBuilder buffer = new StringBuilder(); + buffer.append(body.getMimeType()); // MimeType cannot be null + if (body.getCharset() != null) { // charset may legitimately be null + buffer.append("; charset="); + buffer.append(body.getCharset()); + } + addField(MIME.CONTENT_TYPE, buffer.toString()); + } + + protected void generateTransferEncoding(final ContentBody body) { + addField(MIME.CONTENT_TRANSFER_ENC, body.getTransferEncoding()); // TE cannot be null + } + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HTTP.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HTTP.java new file mode 100644 index 00000000..21232a46 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HTTP.java @@ -0,0 +1,73 @@ +/* + * ==================================================================== + * 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 and static helpers related to the HTTP protocol. + * + * @since 4.0 + */ +public final class HTTP { + + public static final int CR = 13; // + public static final int LF = 10; // + public static final int SP = 32; // + public static final int HT = 9; // + + /** HTTP header definitions */ + public static final String TRANSFER_ENCODING = "Transfer-Encoding"; + public static final String CONTENT_LEN = "Content-Length"; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String CONTENT_ENCODING = "Content-Encoding"; + public static final String EXPECT_DIRECTIVE = "Expect"; + public static final String CONN_DIRECTIVE = "Connection"; + public static final String TARGET_HOST = "Host"; + public static final String USER_AGENT = "User-Agent"; + public static final String DATE_HEADER = "Date"; + public static final String SERVER_HEADER = "Server"; + + /** HTTP expectations */ + public static final String EXPECT_CONTINUE = "100-continue"; + + /** HTTP connection control */ + public static final String CONN_CLOSE = "Close"; + public static final String CONN_KEEP_ALIVE = "Keep-Alive"; + + /** Transfer encoding definitions */ + public static final String CHUNK_CODING = "chunked"; + public static final String IDENTITY_CODING = "identity"; + + public static boolean isWhitespace(char ch) { + return ch == SP || ch == HT || ch == CR || ch == LF; + } + + private HTTP() { + } + +} 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 new file mode 100644 index 00000000..74077d81 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/Header.java @@ -0,0 +1,146 @@ +/* + * ==================================================================== + * 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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import com.foxinmy.weixin4j.http.NameValue; + +/** + * The header of an entity (see RFC 2045). + */ +public class Header implements Iterable { + + private final List fields; + private final Map> fieldMap; + + public Header() { + super(); + this.fields = new LinkedList(); + this.fieldMap = new HashMap>(); + } + + public void addField(final NameValue field) { + if (field == null) { + return; + } + String key = field.getName().toLowerCase(Locale.US); + List values = this.fieldMap.get(key); + if (values == null) { + values = new LinkedList(); + this.fieldMap.put(key, values); + } + values.add(field); + this.fields.add(field); + } + + public List getFields() { + return new ArrayList(this.fields); + } + + public NameValue getField(final String name) { + if (name == null) { + return null; + } + String key = name.toLowerCase(Locale.US); + List list = this.fieldMap.get(key); + if (list != null && !list.isEmpty()) { + return list.get(0); + } + return null; + } + + public List getFields(final String name) { + if (name == null) { + return null; + } + String key = name.toLowerCase(Locale.US); + List list = this.fieldMap.get(key); + if (list == null || list.isEmpty()) { + return Collections.emptyList(); + } else { + return new ArrayList(list); + } + } + + public int removeFields(final String name) { + if (name == null) { + return 0; + } + String key = name.toLowerCase(Locale.US); + List removed = fieldMap.remove(key); + if (removed == null || removed.isEmpty()) { + return 0; + } + this.fields.removeAll(removed); + return removed.size(); + } + + public void setField(final NameValue field) { + if (field == null) { + return; + } + String key = field.getName().toLowerCase(Locale.US); + List list = fieldMap.get(key); + if (list == null || list.isEmpty()) { + addField(field); + return; + } + list.clear(); + list.add(field); + int firstOccurrence = -1; + int index = 0; + for (Iterator it = this.fields.iterator(); it.hasNext(); index++) { + NameValue f = it.next(); + if (f.getName().equalsIgnoreCase(field.getName())) { + it.remove(); + if (firstOccurrence == -1) { + firstOccurrence = index; + } + } + } + this.fields.add(firstOccurrence, field); + } + + public Iterator iterator() { + return Collections.unmodifiableList(fields).iterator(); + } + + @Override + public String toString() { + return this.fields.toString(); + } + +} 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 new file mode 100644 index 00000000..57202640 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpHeaders.java @@ -0,0 +1,206 @@ +/* + * ==================================================================== + * 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 new file mode 100644 index 00000000..cc3c4d12 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpMultipart.java @@ -0,0 +1,278 @@ +/* + * ==================================================================== + * 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; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +import com.foxinmy.weixin4j.http.NameValue; + +/** + * HttpMultipart represents a collection of MIME multipart encoded content + * bodies. This class is capable of operating either in the strict (RFC 822, RFC + * 2045, RFC 2046 compliant) or the browser compatible modes. + * + * @since 4.0 + */ +public class HttpMultipart { + + private static ByteArrayBuffer encode(final Charset charset, + final String string) { + ByteBuffer encoded = charset.encode(CharBuffer.wrap(string)); + ByteArrayBuffer bab = new ByteArrayBuffer(encoded.remaining()); + bab.append(encoded.array(), encoded.position(), encoded.remaining()); + return bab; + } + + private static void writeBytes(final ByteArrayBuffer b, + final OutputStream out) throws IOException { + out.write(b.buffer(), 0, b.length()); + } + + private static void writeBytes(final String s, final Charset charset, + final OutputStream out) throws IOException { + ByteArrayBuffer b = encode(charset, s); + writeBytes(b, out); + } + + private static void writeBytes(final String s, final OutputStream out) + throws IOException { + ByteArrayBuffer b = encode(MIME.DEFAULT_CHARSET, s); + writeBytes(b, out); + } + + private static void writeField(final NameValue field, final OutputStream out) + throws IOException { + writeBytes(field.getName(), out); + writeBytes(FIELD_SEP, out); + writeBytes(field.getValue(), out); + writeBytes(CR_LF, out); + } + + private static void writeField(final NameValue field, final Charset charset, + final OutputStream out) throws IOException { + writeBytes(field.getName(), charset, out); + writeBytes(FIELD_SEP, out); + writeBytes(field.getValue(), charset, out); + writeBytes(CR_LF, out); + } + + private static final ByteArrayBuffer FIELD_SEP = encode( + MIME.DEFAULT_CHARSET, ": "); + private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, + "\r\n"); + private static final ByteArrayBuffer TWO_DASHES = encode( + MIME.DEFAULT_CHARSET, "--"); + + private final String subType; + private final Charset charset; + private final String boundary; + private final List parts; + + private final HttpMultipartMode mode; + + /** + * Creates an instance with the specified settings. + * + * @param subType + * mime subtype - must not be {@code null} + * @param charset + * the character set to use. May be {@code null}, in which case + * {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used. + * @param boundary + * to use - must not be {@code null} + * @param mode + * the mode to use + * @throws IllegalArgumentException + * if charset is null or boundary is null + */ + public HttpMultipart(final String subType, final Charset charset, + final String boundary, HttpMultipartMode mode) { + super(); + if (subType == null) { + throw new IllegalArgumentException( + "Multipart subtype may not be null"); + } + if (boundary == null) { + throw new IllegalArgumentException( + "Multipart boundary may not be null"); + } + this.subType = subType; + this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET; + this.boundary = boundary; + this.parts = new ArrayList(); + this.mode = mode; + } + + /** + * Creates an instance with the specified settings. Mode is set to + * {@link HttpMultipartMode#STRICT} + * + * @param subType + * mime subtype - must not be {@code null} + * @param charset + * the character set to use. May be {@code null}, in which case + * {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used. + * @param boundary + * to use - must not be {@code null} + * @throws IllegalArgumentException + * if charset is null or boundary is null + */ + public HttpMultipart(final String subType, final Charset charset, + final String boundary) { + this(subType, charset, boundary, HttpMultipartMode.STRICT); + } + + public HttpMultipart(final String subType, final String boundary) { + this(subType, null, boundary); + } + + public String getSubType() { + return this.subType; + } + + public Charset getCharset() { + return this.charset; + } + + public HttpMultipartMode getMode() { + return this.mode; + } + + public List getBodyParts() { + return this.parts; + } + + public void addBodyPart(final FormBodyPart part) { + if (part == null) { + return; + } + this.parts.add(part); + } + + public String getBoundary() { + return this.boundary; + } + + private void doWriteTo(final HttpMultipartMode mode, + final OutputStream out, boolean writeContent) throws IOException { + + ByteArrayBuffer boundary = encode(this.charset, getBoundary()); + for (FormBodyPart part : this.parts) { + writeBytes(TWO_DASHES, out); + writeBytes(boundary, out); + writeBytes(CR_LF, out); + + Header header = part.getHeader(); + + switch (mode) { + case STRICT: + for (NameValue field : header) { + writeField(field, out); + } + break; + case BROWSER_COMPATIBLE: + // Only write Content-Disposition + // Use content charset + NameValue cd = part.getHeader().getField( + MIME.CONTENT_DISPOSITION); + writeField(cd, this.charset, out); + String filename = part.getBody().getFilename(); + if (filename != null) { + NameValue ct = part.getHeader().getField(MIME.CONTENT_TYPE); + writeField(ct, this.charset, out); + } + break; + } + writeBytes(CR_LF, out); + + if (writeContent) { + part.getBody().writeTo(out); + } + writeBytes(CR_LF, out); + } + writeBytes(TWO_DASHES, out); + writeBytes(boundary, out); + writeBytes(TWO_DASHES, out); + writeBytes(CR_LF, out); + } + + /** + * Writes out the content in the multipart/form encoding. This method + * produces slightly different formatting depending on its compatibility + * mode. + * + * @see #getMode() + */ + public void writeTo(final OutputStream out) throws IOException { + doWriteTo(this.mode, out, true); + } + + /** + * Determines the total length of the multipart content (content length of + * individual parts plus that of extra elements required to delimit the + * parts from one another). If any of the @{link BodyPart}s contained in + * this object is of a streaming entity of unknown length the total length + * is also unknown. + *

+ * This method buffers only a small amount of data in order to determine the + * total length of the entire entity. The content of individual parts is not + * buffered. + * + * @return total length of the multipart entity if known, -1 + * otherwise. + */ + public long getTotalLength() { + long contentLen = 0; + for (FormBodyPart part : this.parts) { + ContentBody body = part.getBody(); + long len = body.getContentLength(); + if (len >= 0) { + contentLen += len; + } else { + return -1; + } + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + doWriteTo(this.mode, out, false); + byte[] extra = out.toByteArray(); + return contentLen + extra.length; + } catch (IOException ex) { + // Should never happen + return -1; + } + } + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpMultipartMode.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpMultipartMode.java new file mode 100644 index 00000000..18e75bf7 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/HttpMultipartMode.java @@ -0,0 +1,12 @@ +package com.foxinmy.weixin4j.http.apache; + +public enum HttpMultipartMode { + + /** RFC 822, RFC 2045, RFC 2046 compliant */ + STRICT, + /** + * browser-compatible mode, i.e. only write Content-Disposition; use content + * charset + */ + BROWSER_COMPATIBLE +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/InputStreamBody.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/InputStreamBody.java new file mode 100644 index 00000000..b01c235f --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/InputStreamBody.java @@ -0,0 +1,92 @@ +/* + * ==================================================================== + * 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; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * + * @since 4.0 + */ +public class InputStreamBody extends AbstractContentBody { + + private final InputStream in; + private final String filename; + + public InputStreamBody(final InputStream in, final String mimeType, final String filename) { + super(mimeType); + if (in == null) { + throw new IllegalArgumentException("Input stream may not be null"); + } + this.in = in; + this.filename = filename; + } + + public InputStreamBody(final InputStream in, final String filename) { + this(in, "application/octet-stream", filename); + } + + public InputStream getInputStream() { + return this.in; + } + + public void writeTo(final OutputStream out) throws IOException { + if (out == null) { + throw new IllegalArgumentException("Output stream may not be null"); + } + try { + byte[] tmp = new byte[4096]; + int l; + while ((l = this.in.read(tmp)) != -1) { + out.write(tmp, 0, l); + } + out.flush(); + } finally { + this.in.close(); + } + } + + public String getTransferEncoding() { + return MIME.ENC_BINARY; + } + + public String getCharset() { + return null; + } + + public long getContentLength() { + return -1; + } + + public String getFilename() { + return this.filename; + } + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/MIME.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/MIME.java new file mode 100644 index 00000000..d2d8004c --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/MIME.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * 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; + +import java.nio.charset.Charset; + +/** + * + * @since 4.0 + */ +public final class MIME { + + public static final String CONTENT_TYPE = "Content-Type"; + public static final String CONTENT_TRANSFER_ENC = "Content-Transfer-Encoding"; + public static final String CONTENT_DISPOSITION = "Content-Disposition"; + + public static final String ENC_8BIT = "8bit"; + public static final String ENC_BINARY = "binary"; + + /** The default character set to be used, i.e. "US-ASCII" */ + public static final Charset DEFAULT_CHARSET = Charset.forName("US-ASCII"); + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/MultipartEntity.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/MultipartEntity.java new file mode 100644 index 00000000..b8992218 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/MultipartEntity.java @@ -0,0 +1,169 @@ +/* + * ==================================================================== + * 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; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.Random; + +import com.foxinmy.weixin4j.http.ContentType; +import com.foxinmy.weixin4j.http.entity.HttpEntity; + +/** + * Multipart/form coded HTTP entity consisting of multiple body parts. + * + * @since 4.0 + */ +public class MultipartEntity implements HttpEntity { + + /** + * The pool of ASCII chars to be used for generating a multipart boundary. + */ + private final static char[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + .toCharArray(); + + private final HttpMultipart multipart; + + // @GuardedBy("dirty") // we always read dirty before accessing length + private long length; + private volatile boolean dirty; // used to decide whether to recalculate + // length + + /** + * Creates an instance using the specified parameters + * + * @param mode + * the mode to use, may be {@code null}, in which case + * {@link HttpMultipartMode#STRICT} is used + * @param boundary + * the boundary string, may be {@code null}, in which case + * {@link #generateBoundary()} is invoked to create the string + * @param charset + * the character set to use, may be {@code null}, in which case + * {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used. + */ + public MultipartEntity(HttpMultipartMode mode, String boundary, + Charset charset) { + super(); + if (boundary == null) { + boundary = generateBoundary(); + } + if (mode == null) { + mode = HttpMultipartMode.STRICT; + } + this.multipart = new HttpMultipart("form-data", charset, boundary, mode); + this.dirty = true; + } + + /** + * Creates an instance using the specified {@link HttpMultipartMode} mode. + * Boundary and charset are set to {@code null}. + * + * @param mode + * the desired mode + */ + public MultipartEntity(final HttpMultipartMode mode) { + this(mode, null, null); + } + + /** + * Creates an instance using mode {@link HttpMultipartMode#STRICT} + */ + public MultipartEntity() { + this(HttpMultipartMode.STRICT, null, null); + } + + protected String generateBoundary() { + StringBuilder buffer = new StringBuilder(); + Random rand = new Random(); + int count = rand.nextInt(11) + 30; // a random size from 30 to 40 + for (int i = 0; i < count; i++) { + buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]); + } + return buffer.toString(); + } + + public void addPart(final FormBodyPart bodyPart) { + this.multipart.addBodyPart(bodyPart); + this.dirty = true; + } + + public void addPart(final String name, final ContentBody contentBody) { + addPart(new FormBodyPart(name, contentBody)); + } + + public boolean isRepeatable() { + for (FormBodyPart part : this.multipart.getBodyParts()) { + ContentBody body = part.getBody(); + if (body.getContentLength() < 0) { + return false; + } + } + return true; + } + + public boolean isChunked() { + return !isRepeatable(); + } + + public boolean isStreaming() { + return !isRepeatable(); + } + + public long getContentLength() { + if (this.dirty) { + this.length = this.multipart.getTotalLength(); + this.dirty = false; + } + return this.length; + } + + public ContentType getContentType() { + return ContentType.MULTIPART_FORM_DATA; + } + + public void consumeContent() throws IOException, + UnsupportedOperationException { + if (isStreaming()) { + throw new UnsupportedOperationException( + "Streaming entity does not implement #consumeContent()"); + } + } + + public InputStream getContent() throws IOException, + UnsupportedOperationException { + throw new UnsupportedOperationException( + "Multipart form entity does not implement #getContent()"); + } + + public void writeTo(final OutputStream outstream) throws IOException { + this.multipart.writeTo(outstream); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/StringBody.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/StringBody.java new file mode 100644 index 00000000..06df2b00 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/apache/StringBody.java @@ -0,0 +1,162 @@ +/* + * ==================================================================== + * 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; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +/** + * + * @since 4.0 + */ +public class StringBody extends AbstractContentBody { + + private final byte[] content; + private final Charset charset; + + /** + * @since 4.1 + */ + public static StringBody create( + final String text, + final String mimeType, + final Charset charset) throws IllegalArgumentException { + try { + return new StringBody(text, mimeType, charset); + } catch (UnsupportedEncodingException ex) { + throw new IllegalArgumentException("Charset " + charset + " is not supported", ex); + } + } + + /** + * @since 4.1 + */ + public static StringBody create( + final String text, final Charset charset) throws IllegalArgumentException { + return create(text, null, charset); + } + + /** + * @since 4.1 + */ + public static StringBody create(final String text) throws IllegalArgumentException { + return create(text, null, null); + } + + /** + * Create a StringBody from the specified text, mime type and character set. + * + * @param text to be used for the body, not {@code null} + * @param mimeType the mime type, not {@code null} + * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used + * @throws UnsupportedEncodingException + * @throws IllegalArgumentException if the {@code text} parameter is null + */ + public StringBody( + final String text, + final String mimeType, + Charset charset) throws UnsupportedEncodingException { + super(mimeType); + if (text == null) { + throw new IllegalArgumentException("Text may not be null"); + } + if (charset == null) { + charset = Charset.forName("US-ASCII"); + } + this.content = text.getBytes(charset.name()); + this.charset = charset; + } + + /** + * Create a StringBody from the specified text and character set. + * The mime type is set to "text/plain". + * + * @param text to be used for the body, not {@code null} + * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used + * @throws UnsupportedEncodingException + * @throws IllegalArgumentException if the {@code text} parameter is null + */ + public StringBody(final String text, final Charset charset) throws UnsupportedEncodingException { + this(text, "text/plain", charset); + } + + /** + * Create a StringBody from the specified text. + * The mime type is set to "text/plain". + * The hosts default charset is used. + * + * @param text to be used for the body, not {@code null} + * @throws UnsupportedEncodingException + * @throws IllegalArgumentException if the {@code text} parameter is null + */ + public StringBody(final String text) throws UnsupportedEncodingException { + this(text, "text/plain", null); + } + + public Reader getReader() { + return new InputStreamReader( + new ByteArrayInputStream(this.content), + this.charset); + } + + public void writeTo(final OutputStream out) throws IOException { + if (out == null) { + throw new IllegalArgumentException("Output stream may not be null"); + } + InputStream in = new ByteArrayInputStream(this.content); + byte[] tmp = new byte[4096]; + int l; + while ((l = in.read(tmp)) != -1) { + out.write(tmp, 0, l); + } + out.flush(); + } + + public String getTransferEncoding() { + return MIME.ENC_8BIT; + } + + public String getCharset() { + return this.charset.name(); + } + + public long getContentLength() { + return this.content.length; + } + + public String getFilename() { + return null; + } + +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/ByteArrayEntity.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/ByteArrayEntity.java new file mode 100644 index 00000000..e33b5074 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/ByteArrayEntity.java @@ -0,0 +1,52 @@ +package com.foxinmy.weixin4j.http.entity; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.foxinmy.weixin4j.http.ContentType; + +public class ByteArrayEntity implements HttpEntity { + + private final ContentType contentType; + private final byte[] content; + private final int off, len; + + public ByteArrayEntity(byte[] content) { + this(content, ContentType.DEFAULT_BINARY); + } + + public ByteArrayEntity(byte[] content, ContentType contentType) { + this(content, 0, content.length, contentType); + } + + public ByteArrayEntity(byte[] content, int off, int len, + ContentType contentType) { + this.content = content; + this.off = off; + this.len = len; + this.contentType = contentType; + } + + @Override + public ContentType getContentType() { + return contentType; + } + + @Override + public long getContentLength() { + return len; + } + + @Override + public InputStream getContent() throws IOException { + return new ByteArrayInputStream(this.content, this.off, this.len); + } + + @Override + public void writeTo(OutputStream outstream) throws IOException { + outstream.write(this.content, this.off, this.len); + outstream.flush(); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FileEntity.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FileEntity.java new file mode 100644 index 00000000..1ebb947b --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FileEntity.java @@ -0,0 +1,54 @@ +package com.foxinmy.weixin4j.http.entity; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.foxinmy.weixin4j.http.ContentType; + +public class FileEntity implements HttpEntity { + + private final File file; + private final ContentType contentType; + + public FileEntity(File file) { + this(file, ContentType.DEFAULT_BINARY); + } + + public FileEntity(File file, ContentType contentType) { + this.file = file; + this.contentType = contentType; + } + + @Override + public ContentType getContentType() { + return contentType; + } + + @Override + public long getContentLength() { + return this.file.length(); + } + + @Override + public InputStream getContent() throws IOException { + return new FileInputStream(this.file); + } + + @Override + public void writeTo(OutputStream outstream) throws IOException { + InputStream instream = new FileInputStream(this.file); + try { + byte[] tmp = new byte[4096]; + int l; + while ((l = instream.read(tmp)) != -1) { + outstream.write(tmp, 0, l); + } + outstream.flush(); + } finally { + instream.close(); + } + } +} 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 new file mode 100644 index 00000000..a7f5d34a --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/FormUrlEntity.java @@ -0,0 +1,27 @@ +package com.foxinmy.weixin4j.http.entity; + +import java.util.List; + +import com.foxinmy.weixin4j.http.ContentType; +import com.foxinmy.weixin4j.http.UrlEncodeParameter; + +public class FormUrlEntity extends StringEntity { + private static final String PARAMETER_SEPARATOR = "&"; + + 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()); + for (int i = 1; i < parameters.size(); i++) { + body.append(PARAMETER_SEPARATOR); + parameter = parameters.get(i); + body.append(parameter.encodingParameter()); + } + return body.toString(); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/HttpEntity.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/HttpEntity.java new file mode 100644 index 00000000..5d3f278a --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/HttpEntity.java @@ -0,0 +1,18 @@ +package com.foxinmy.weixin4j.http.entity; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.foxinmy.weixin4j.http.ContentType; + +public interface HttpEntity { + + ContentType getContentType(); + + long getContentLength(); + + InputStream getContent() throws IOException; + + void writeTo(OutputStream outstream) throws IOException; +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/StringEntity.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/StringEntity.java new file mode 100644 index 00000000..fe767991 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/entity/StringEntity.java @@ -0,0 +1,43 @@ +package com.foxinmy.weixin4j.http.entity; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.foxinmy.weixin4j.http.ContentType; + +public class StringEntity implements HttpEntity { + private final byte[] content; + private final ContentType contentType; + + public StringEntity(String body) { + this(body, ContentType.DEFAULT_TEXT); + } + + public StringEntity(String body, ContentType contentType) { + this.content = body.getBytes(); + this.contentType = contentType; + } + + @Override + public ContentType getContentType() { + return contentType; + } + + @Override + public long getContentLength() { + return this.content.length; + } + + @Override + public InputStream getContent() throws IOException { + return new ByteArrayInputStream(this.content); + } + + @Override + public void writeTo(OutputStream outstream) throws IOException { + outstream.write(this.content); + outstream.flush(); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/JsonResult.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/JsonResult.java similarity index 92% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/JsonResult.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/JsonResult.java index b3db5d73..039d84de 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/JsonResult.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/JsonResult.java @@ -1,4 +1,4 @@ -package com.foxinmy.weixin4j.http; +package com.foxinmy.weixin4j.http.weixin; import java.io.Serializable; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/README.md b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/README.md similarity index 100% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/README.md rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/README.md 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/SSLHttpClinet.java new file mode 100644 index 00000000..404d09db --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/SSLHttpClinet.java @@ -0,0 +1,60 @@ +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; + } +} 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 new file mode 100644 index 00000000..2c2c02f1 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinHttpClient.java @@ -0,0 +1,213 @@ +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.util.ErrorUtil; +import com.foxinmy.weixin4j.util.MapUtil; +import com.foxinmy.weixin4j.util.StringUtil; +import com.foxinmy.weixin4j.xml.XmlStream; +import com.thoughtworks.xstream.mapper.CannotResolveClassException; + +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 (CannotResolveClassException 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 (CannotResolveClassException ex) { + // + String newXml = response.getAsString() + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", "") + .replaceFirst("", ""); + xmlResult = XmlStream.get(newXml, XmlResult.class); + } + 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/WeixinResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java new file mode 100644 index 00000000..0a3a9c79 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java @@ -0,0 +1,54 @@ +package com.foxinmy.weixin4j.http.weixin; + +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.util.StringUtil; +import com.foxinmy.weixin4j.xml.XmlStream; + +public class WeixinResponse extends HttpResponse { + + private boolean isJsonResult; + private boolean isXmlResult; + private String text; + + public void setJsonResult(boolean isJsonResult) { + this.isJsonResult = isJsonResult; + } + + public void setXmlResult(boolean isXmlResult) { + this.isXmlResult = isXmlResult; + } + + public String getAsString() { + if (text == null) { + text = StringUtil.newStringUtf8(getContent()); + } + return text; + } + + public JsonResult getAsJsonResult() { + return JSON.parseObject(getAsString(), JsonResult.class); + } + + public JSONObject getAsJson() { + return JSON.parseObject(getAsString()); + } + + public T getAsObject(TypeReference typeReference) { + if (isJsonResult) { + return JSON.parseObject(getAsString(), typeReference); + } + if (isXmlResult) { + @SuppressWarnings("unchecked") + Class clazz = (Class) typeReference.getType(); + return XmlStream.get(getAsString(), clazz); + } + return null; + } + + public XmlResult getAsXmlResult() { + return XmlStream.get(getAsString(), XmlResult.class); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/XmlResult.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/XmlResult.java similarity index 94% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/XmlResult.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/XmlResult.java index 94cd31a0..153b69df 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/XmlResult.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/XmlResult.java @@ -1,4 +1,4 @@ -package com.foxinmy.weixin4j.http; +package com.foxinmy.weixin4j.http.weixin; import java.io.Serializable; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/error.xml b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml similarity index 95% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/error.xml rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml index f2965858..6c3b8883 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/error.xml +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/error.xml @@ -529,7 +529,7 @@ 45008 article size out of limit - article参数超过限制 + 图文消息的文章数量不能超过10条 45009 @@ -861,6 +861,10 @@ 82003 不合法的TagID列表长度 + + 82004 + 微信版本号过低 + 7000000 diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Pageable.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/page/Pageable.java similarity index 91% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Pageable.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/page/Pageable.java index 94262597..3fb29e4f 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Pageable.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/page/Pageable.java @@ -1,15 +1,15 @@ -package com.foxinmy.weixin4j.util; +package com.foxinmy.weixin4j.page; import java.io.Serializable; -import com.foxinmy.weixin4j.util.Sort.Direction; +import com.foxinmy.weixin4j.page.Sort.Direction; /** * @className Pageable * @author jy * @date 2014年12月27日 * @since JDK 1.7 - * @see org.springframework.data.domain.Pageable + * @see com.foxinmy.weixin4j.page.springframework.data.domain.Pageable */ public class Pageable implements Serializable { diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Pagedata.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/page/Pagedata.java similarity index 97% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Pagedata.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/page/Pagedata.java index 482eaaaa..d6ff144c 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Pagedata.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/page/Pagedata.java @@ -1,4 +1,4 @@ -package com.foxinmy.weixin4j.util; +package com.foxinmy.weixin4j.page; import java.io.Serializable; import java.util.Iterator; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Sort.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/page/Sort.java similarity index 97% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Sort.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/page/Sort.java index dbb2cfab..b79e3608 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/Sort.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/page/Sort.java @@ -1,4 +1,4 @@ -package com.foxinmy.weixin4j.util; +package com.foxinmy.weixin4j.page; import java.io.Serializable; import java.util.ArrayList; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/FileTokenHolder.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/FileTokenHolder.java index a1f4766a..fdfcbf42 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/FileTokenHolder.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/FileTokenHolder.java @@ -19,7 +19,6 @@ import com.thoughtworks.xstream.XStream; * @date 2015年1月9日 * @since JDK 1.7 * @see com.foxinmy.weixin4j.token.TokenCreator - * @see com.foxinmy.weixin4j.mp.token.WeixinTokenCreator */ public class FileTokenHolder implements TokenHolder { private final XStream xstream; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/RedisTokenHolder.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/RedisTokenHolder.java index 4b8560fa..5f7bbd29 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/RedisTokenHolder.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/token/RedisTokenHolder.java @@ -17,7 +17,6 @@ import com.foxinmy.weixin4j.util.StringUtil; * @date 2015年1月9日 * @since JDK 1.7 * @see com.foxinmy.weixin4j.token.TokenCreator - * @see com.foxinmy.weixin4j.mp.token.WeixinTokenCreator */ public class RedisTokenHolder implements TokenHolder { diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ErrorUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ErrorUtil.java index ed038dd7..09a46fb2 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ErrorUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/ErrorUtil.java @@ -12,7 +12,7 @@ import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; -import com.foxinmy.weixin4j.http.Response; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; /** * 接口调用错误获取 @@ -29,7 +29,7 @@ public final class ErrorUtil { static { errorCacheMap = new HashMap(); try { - errorXmlByteArray = IOUtil.toByteArray(Response.class + errorXmlByteArray = IOUtil.toByteArray(WeixinResponse.class .getResourceAsStream("error.xml")); } catch (IOException e) { ; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MapUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MapUtil.java index 59ca71c3..e7879a53 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MapUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/MapUtil.java @@ -7,10 +7,9 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; -import org.apache.http.Consts; - import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; +import com.foxinmy.weixin4j.model.Consts; /** * 签名工具类 diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/PKCS7Encoder.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/PKCS7Encoder.java index 95dcb17a..5f2beb20 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/PKCS7Encoder.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/PKCS7Encoder.java @@ -10,7 +10,7 @@ package com.foxinmy.weixin4j.util; import java.util.Arrays; -import org.apache.http.Consts; +import com.foxinmy.weixin4j.model.Consts; /** * 提供基于PKCS7算法的加解密接口
diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java index 743a488b..ce7b2a02 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinPayProxy.java @@ -5,8 +5,8 @@ import java.util.Date; import com.alibaba.fastjson.JSON; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.JsonResult; -import com.foxinmy.weixin4j.http.XmlResult; +import com.foxinmy.weixin4j.http.weixin.JsonResult; +import com.foxinmy.weixin4j.http.weixin.XmlResult; import com.foxinmy.weixin4j.mp.api.CashApi; import com.foxinmy.weixin4j.mp.api.CouponApi; import com.foxinmy.weixin4j.mp.api.Pay2Api; diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java index bbe000d7..616066ac 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/WeixinProxy.java @@ -6,7 +6,7 @@ import java.util.Date; import java.util.List; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.JsonResult; +import com.foxinmy.weixin4j.http.weixin.JsonResult; import com.foxinmy.weixin4j.model.Button; import com.foxinmy.weixin4j.mp.api.CustomApi; import com.foxinmy.weixin4j.mp.api.DataApi; diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CashApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CashApi.java index 9c0b4412..a29a2c6c 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CashApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CashApi.java @@ -10,8 +10,8 @@ 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.Response; -import com.foxinmy.weixin4j.http.SSLHttpRequest; +import com.foxinmy.weixin4j.http.weixin.SSLHttpClinet; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.mp.model.WeixinMpAccount; import com.foxinmy.weixin4j.mp.payment.PayUtil; import com.foxinmy.weixin4j.mp.payment.v3.MPPayment; @@ -66,11 +66,11 @@ public class CashApi extends MpApi { obj.put("sign", sign); String param = map2xml(new HashMap(obj)); String redpack_send_uri = getRequestUri("redpack_send_uri"); - Response response = null; + WeixinResponse response = null; InputStream ca = null; try { ca = new FileInputStream(caFile); - SSLHttpRequest request = new SSLHttpRequest( + SSLHttpClinet request = new SSLHttpClinet( weixinAccount.getMchId(), ca); response = request.post(redpack_send_uri, param); } catch (WeixinException e) { @@ -116,11 +116,11 @@ public class CashApi extends MpApi { obj.put("sign", sign); String param = map2xml(new HashMap(obj)); String mp_payment_uri = getRequestUri("mp_payment_uri"); - Response response = null; + WeixinResponse response = null; InputStream ca = null; try { ca = new FileInputStream(caFile); - SSLHttpRequest request = new SSLHttpRequest( + SSLHttpClinet request = new SSLHttpClinet( weixinAccount.getMchId(), ca); response = request.post(mp_payment_uri, param); } catch (WeixinException e) { diff --git a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CouponApi.java b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CouponApi.java index 3441aff7..bd506f39 100644 --- a/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CouponApi.java +++ b/weixin4j-mp/src/main/java/com/foxinmy/weixin4j/mp/api/CouponApi.java @@ -9,8 +9,8 @@ import java.util.Map; import com.alibaba.fastjson.TypeReference; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.Response; -import com.foxinmy.weixin4j.http.SSLHttpRequest; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; +import com.foxinmy.weixin4j.http.weixin.SSLHttpClinet; import com.foxinmy.weixin4j.mp.model.WeixinMpAccount; import com.foxinmy.weixin4j.mp.payment.PayUtil; import com.foxinmy.weixin4j.mp.payment.coupon.CouponDetail; @@ -75,11 +75,11 @@ public class CouponApi extends MpApi { map.put("sign", sign); String param = map2xml(map); String coupon_send_uri = getRequestUri("coupon_send_uri"); - Response response = null; + WeixinResponse response = null; InputStream ca = null; try { ca = new FileInputStream(caFile); - SSLHttpRequest request = new SSLHttpRequest( + SSLHttpClinet request = new SSLHttpClinet( weixinAccount.getMchId(), ca); response = request.post(coupon_send_uri, param); } catch (WeixinException e) { @@ -118,7 +118,7 @@ public class CouponApi extends MpApi { map.put("sign", sign); String param = map2xml(map); String couponstock_query_uri = getRequestUri("couponstock_query_uri"); - Response response = request.post(couponstock_query_uri, param); + WeixinResponse response = weixinClient.post(couponstock_query_uri, param); return response.getAsObject(new TypeReference() { }); } @@ -142,7 +142,7 @@ public class CouponApi extends MpApi { map.put("sign", sign); String param = map2xml(map); String coupondetail_query_uri = getRequestUri("coupondetail_query_uri"); - Response response = request.post(coupondetail_query_uri, param); + WeixinResponse response = weixinClient.post(coupondetail_query_uri, param); return response.getAsObject(new TypeReference() { }); } 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 33c5a42b..c31688be 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 @@ -6,15 +6,14 @@ import java.io.IOException; import java.util.Date; import java.util.List; -import org.apache.http.entity.mime.content.ByteArrayBody; - 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.JsonResult; -import com.foxinmy.weixin4j.http.PartParameter; -import com.foxinmy.weixin4j.http.Response; +import com.foxinmy.weixin4j.http.apache.ByteArrayBody; +import com.foxinmy.weixin4j.http.apache.FormBodyPart; +import com.foxinmy.weixin4j.http.weixin.JsonResult; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.mp.model.CustomRecord; import com.foxinmy.weixin4j.mp.model.KfAccount; @@ -72,7 +71,7 @@ public class CustomApi extends MpApi { obj.put("pageindex", pageindex); String custom_record_uri = getRequestUri("custom_record_uri"); Token token = tokenHolder.getToken(); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(custom_record_uri, token.getAccessToken()), obj.toJSONString()); @@ -102,12 +101,12 @@ public class CustomApi extends MpApi { String text = ""; if (isOnline) { String getonlinekflist_uri = getRequestUri("getonlinekflist_uri"); - Response response = request.post(String.format(getonlinekflist_uri, + WeixinResponse response = weixinClient.post(String.format(getonlinekflist_uri, token.getAccessToken())); text = response.getAsJson().getString("kf_online_list"); } else { String getkflist_uri = getRequestUri("getkflist_uri"); - Response response = request.post(String.format(getkflist_uri, + WeixinResponse response = weixinClient.post(String.format(getkflist_uri, token.getAccessToken())); text = response.getAsJson().getString("kf_list"); } @@ -139,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(); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(custom_add_uri, token.getAccessToken()), obj.toJSONString()); return response.getAsJsonResult(); @@ -170,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(); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(custom_update_uri, token.getAccessToken()), obj.toJSONString()); return response.getAsJsonResult(); @@ -196,10 +195,10 @@ public class CustomApi extends MpApi { Token token = tokenHolder.getToken(); String custom_uploadheadimg_uri = getRequestUri("custom_uploadheadimg_uri"); byte[] bytes = IOUtil.toByteArray(new FileInputStream(headimg)); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(custom_uploadheadimg_uri, token.getAccessToken(), id), - new PartParameter("media", new ByteArrayBody(bytes, headimg + new FormBodyPart("media", new ByteArrayBody(bytes, headimg .getName()))); return response.getAsJsonResult(); @@ -220,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"); - Response response = request.get(String.format(custom_delete_uri, + WeixinResponse response = weixinClient.get(String.format(custom_delete_uri, token.getAccessToken(), id)); return response.getAsJsonResult(); @@ -252,7 +251,7 @@ public class CustomApi extends MpApi { obj.put("openid", userOpenId); obj.put("kf_account", kfAccount); obj.put("text", text); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(kfsession_create_uri, token.getAccessToken()), obj.toJSONString()); @@ -281,7 +280,7 @@ public class CustomApi extends MpApi { obj.put("openid", userOpenId); obj.put("kf_account", kfAccount); obj.put("text", text); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(kfsession_close_uri, token.getAccessToken()), obj.toJSONString()); @@ -302,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"); - Response response = request.get(String.format(kfsession_get_uri, + WeixinResponse response = weixinClient.get(String.format(kfsession_get_uri, token.getAccessToken(), userOpenId)); KfSession session = response @@ -327,7 +326,7 @@ public class CustomApi extends MpApi { throws WeixinException { Token token = tokenHolder.getToken(); String kfsession_list_uri = getRequestUri("kfsession_list_uri"); - Response response = request.get(String.format(kfsession_list_uri, + WeixinResponse response = weixinClient.get(String.format(kfsession_list_uri, token.getAccessToken(), kfAccount)); List sessionList = JSON.parseArray(response.getAsJson() @@ -347,7 +346,7 @@ public class CustomApi extends MpApi { public List getKfSessionWaitList() throws WeixinException { Token token = tokenHolder.getToken(); String kfsession_wait_uri = getRequestUri("kfsession_wait_uri"); - Response response = request.get(String.format(kfsession_wait_uri, + WeixinResponse response = weixinClient.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 de0894b5..9b31e013 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 @@ -7,7 +7,7 @@ import java.util.List; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.Response; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.mp.type.DatacubeType; import com.foxinmy.weixin4j.token.TokenHolder; @@ -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)); - Response response = request.post(String.format(datacube_uri, + WeixinResponse response = weixinClient.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 10995e27..fcd4e822 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 @@ -5,8 +5,8 @@ import java.util.List; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.JsonResult; -import com.foxinmy.weixin4j.http.Response; +import com.foxinmy.weixin4j.http.weixin.JsonResult; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.mp.model.Group; import com.foxinmy.weixin4j.token.TokenHolder; @@ -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); - Response response = request.post( + WeixinResponse response = weixinClient.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(); - Response response = request.get(String.format(group_get_uri, + WeixinResponse response = weixinClient.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(); - Response response = request.post( + WeixinResponse response = weixinClient.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); - Response response = request.post( + WeixinResponse response = weixinClient.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(); - Response response = request.post(String.format(group_move_uri, + WeixinResponse response = weixinClient.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); - Response response = request.post( + WeixinResponse response = weixinClient.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(); - Response response = request.post( + WeixinResponse response = weixinClient.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 58fd5333..972b6ef0 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 @@ -10,7 +10,7 @@ import com.alibaba.fastjson.JSONPath; import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.parser.deserializer.ExtraProcessor; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.Response; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.model.Button; import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.mp.model.AutoReplySetting; @@ -53,7 +53,7 @@ public class HelperApi extends MpApi { JSONObject obj = new JSONObject(); obj.put("action", "long2short"); obj.put("long_url", url); - Response response = request.post( + WeixinResponse response = weixinClient.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(); - Response response = request.post( + WeixinResponse response = weixinClient.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(); - Response response = request.post(String.format(getcallbackip_uri, + WeixinResponse response = weixinClient.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(); - Response response = request.get(String.format(menu_get_selfmenu_uri, + WeixinResponse response = weixinClient.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(); - Response response = request.get(String.format( + WeixinResponse response = weixinClient.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 d660bf0b..11758034 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 @@ -6,8 +6,8 @@ import java.util.List; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.JsonResult; -import com.foxinmy.weixin4j.http.Response; +import com.foxinmy.weixin4j.http.weixin.JsonResult; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.token.TokenHolder; import com.foxinmy.weixin4j.tuple.MassTuple; @@ -53,7 +53,7 @@ public class MassApi extends MpApi { Token token = tokenHolder.getToken(); JSONObject obj = new JSONObject(); obj.put("articles", articles); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(article_upload_uri, token.getAccessToken()), obj.toJSONString()); @@ -76,7 +76,7 @@ public class MassApi extends MpApi { public String uploadVideo(Video video) throws WeixinException { String video_upload_uri = getRequestUri("video_upload_uri"); Token token = tokenHolder.getToken(); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(video_upload_uri, token.getAccessToken()), JSON.toJSONString(video)); @@ -150,7 +150,7 @@ public class MassApi extends MpApi { obj.put("msgtype", msgtype); String mass_group_uri = getRequestUri("mass_group_uri"); Token token = tokenHolder.getToken(); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(mass_group_uri, token.getAccessToken()), obj.toJSONString()); @@ -216,7 +216,7 @@ public class MassApi extends MpApi { obj.put("msgtype", msgtype); String mass_openid_uri = getRequestUri("mass_openid_uri"); Token token = tokenHolder.getToken(); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(mass_openid_uri, token.getAccessToken()), obj.toJSONString()); @@ -262,7 +262,7 @@ public class MassApi extends MpApi { obj.put("msgid", msgid); String mass_delete_uri = getRequestUri("mass_delete_uri"); Token token = tokenHolder.getToken(); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(mass_delete_uri, token.getAccessToken()), obj.toJSONString()); @@ -291,7 +291,7 @@ public class MassApi extends MpApi { obj.put("msgtype", msgtype); String mass_preview_uri = getRequestUri("mass_preview_uri"); Token token = tokenHolder.getToken(); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(mass_preview_uri, token.getAccessToken()), obj.toJSONString()); @@ -314,7 +314,7 @@ public class MassApi extends MpApi { obj.put("msg_id", msgId); String mass_get_uri = getRequestUri("mass_get_uri"); Token token = tokenHolder.getToken(); - Response response = request.post( + WeixinResponse response = weixinClient.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 7d21623e..fea32b92 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 @@ -9,17 +9,16 @@ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; -import org.apache.http.entity.mime.content.ByteArrayBody; -import org.apache.http.entity.mime.content.StringBody; - import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; import com.alibaba.fastjson.parser.deserializer.ExtraProcessor; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.JsonResult; -import com.foxinmy.weixin4j.http.PartParameter; -import com.foxinmy.weixin4j.http.Response; +import com.foxinmy.weixin4j.http.apache.ByteArrayBody; +import com.foxinmy.weixin4j.http.apache.FormBodyPart; +import com.foxinmy.weixin4j.http.apache.StringBody; +import com.foxinmy.weixin4j.http.weixin.JsonResult; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.mp.model.MediaCounter; @@ -126,22 +125,22 @@ public class MediaApi extends MpApi { "please invoke uploadMaterialVideo method"); } Token token = tokenHolder.getToken(); - Response response = null; + WeixinResponse response = null; if (isMaterial) { String material_media_upload_uri = getRequestUri("material_media_upload_uri"); try { - response = request.post(String.format( + response = weixinClient.post(String.format( material_media_upload_uri, token.getAccessToken()), - new PartParameter("media", new ByteArrayBody(bytes, - fileName)), new PartParameter("type", + new FormBodyPart("media", new ByteArrayBody(bytes, + fileName)), new FormBodyPart("type", new StringBody(mediaType, Consts.UTF_8))); } catch (UnsupportedEncodingException e) { ; // ignore } } else { String file_upload_uri = getRequestUri("file_upload_uri"); - response = request.post(String.format(file_upload_uri, - token.getAccessToken(), mediaType), new PartParameter( + response = weixinClient.post(String.format(file_upload_uri, + token.getAccessToken(), mediaType), new FormBodyPart( "media", new ByteArrayBody(bytes, fileName))); } return response.getAsJson().getString("media_id"); @@ -220,20 +219,20 @@ public class MediaApi extends MpApi { public byte[] downloadMedia(String mediaId, boolean isMaterial) throws WeixinException { Token token = tokenHolder.getToken(); - Response response = null; + WeixinResponse response = null; if (isMaterial) { JSONObject media = new JSONObject(); media.put("media_id", mediaId); String material_media_download_uri = getRequestUri("material_media_download_uri"); - response = request.post( + response = weixinClient.post( String.format(material_media_download_uri, token.getAccessToken()), media.toJSONString()); } else { String file_download_uri = getRequestUri("file_download_uri"); - response = request.get(String.format(file_download_uri, + response = weixinClient.get(String.format(file_download_uri, token.getAccessToken(), mediaId)); } - return response.getBody(); + return response.getContent(); } /** @@ -257,7 +256,7 @@ public class MediaApi extends MpApi { String material_article_upload_uri = getRequestUri("material_article_upload_uri"); JSONObject obj = new JSONObject(); obj.put("articles", articles); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(material_article_upload_uri, token.getAccessToken()), obj.toJSONString()); @@ -306,7 +305,7 @@ public class MediaApi extends MpApi { obj.put("articles", articles); obj.put("media_id", mediaId); obj.put("index", index); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(material_article_update_uri, token.getAccessToken()), obj.toJSONString()); @@ -329,7 +328,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); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(material_media_del_uri, token.getAccessToken()), obj.toJSONString()); @@ -360,14 +359,14 @@ public class MediaApi extends MpApi { description.put("title", title); description.put("introduction", introduction); byte[] bytes = IOUtil.toByteArray(new FileInputStream(file)); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(material_media_upload_uri, token.getAccessToken()), - new PartParameter("media", new ByteArrayBody(bytes, file + new FormBodyPart("media", new ByteArrayBody(bytes, file .getName())), - new PartParameter("type", new StringBody(MediaType.video + new FormBodyPart("type", new StringBody(MediaType.video .name(), Consts.UTF_8)), - new PartParameter("description", new StringBody(description + new FormBodyPart("description", new StringBody(description .toJSONString(), Consts.UTF_8))); return response.getAsJson().getString("media_id"); } catch (UnsupportedEncodingException e) { @@ -387,7 +386,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"); - Response response = request.get(String.format(material_media_count_uri, + WeixinResponse response = weixinClient.get(String.format(material_media_count_uri, token.getAccessToken())); return response.getAsObject(new TypeReference() { @@ -419,7 +418,7 @@ public class MediaApi extends MpApi { obj.put("type", mediaType.name()); obj.put("offset", offset); obj.put("count", count); - Response response = request.post( + WeixinResponse response = weixinClient.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 52014311..df0d23b7 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 @@ -11,8 +11,8 @@ import com.alibaba.fastjson.parser.deserializer.ExtraProcessor; import com.alibaba.fastjson.parser.deserializer.ParseProcess; import com.alibaba.fastjson.serializer.NameFilter; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.JsonResult; -import com.foxinmy.weixin4j.http.Response; +import com.foxinmy.weixin4j.http.weixin.JsonResult; +import com.foxinmy.weixin4j.http.weixin.WeixinResponse; import com.foxinmy.weixin4j.model.Button; import com.foxinmy.weixin4j.model.Token; import com.foxinmy.weixin4j.token.TokenHolder; @@ -50,7 +50,7 @@ public class MenuApi extends MpApi { Token token = tokenHolder.getToken(); JSONObject obj = new JSONObject(); obj.put("button", btnList); - Response response = request.post( + WeixinResponse response = weixinClient.post( String.format(menu_create_uri, token.getAccessToken()), JSON.toJSONString(obj, new NameFilter() { @Override @@ -87,7 +87,7 @@ public class MenuApi extends MpApi { public List