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 @@
xstream1.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.alibabafastjson
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 extends T> 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 extends T> 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 @@
45008article 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