diff --git a/CHANGE.md b/CHANGE.md
index e1c3cc8d..7ec34694 100644
--- a/CHANGE.md
+++ b/CHANGE.md
@@ -445,8 +445,16 @@
+ `release`: weixin4j-[mp|qy] upgrade to 1.5.3,weixin4j-server upgrade to 1.0.5
- + **weixin4j-[mp|qy]**: 媒体接口类(MediaApi)查询素材接口调整:去掉offset,count替换为Pageable类
+ + **weixin4j-[mp|qy]**: 媒体接口类(MediaApi)查询素材接口调整:去掉offset,count替换为Pageable类
* 2015-08-18
- + 比较大的改动:重构了HttpClient部分
+ + 比较大的改动:重构了HttpClient部分
+
+* 2015-09-08
+
+ + weixin4j-mp:新增批量获取用户信息接口
+
+* 2015-09-10
+
+ + 对Netty-Http-Client的支持
\ No newline at end of file
diff --git a/weixin4j-base/pom.xml b/weixin4j-base/pom.xml
index beb55685..f7f01e98 100644
--- a/weixin4j-base/pom.xml
+++ b/weixin4j-base/pom.xml
@@ -29,5 +29,11 @@
4.3true
+
+ io.netty
+ netty-all
+ 4.0.30.Final
+ true
+
\ No newline at end of file
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java
index 48cebf0d..217e3355 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpClient.java
@@ -106,4 +106,25 @@ public abstract class AbstractHttpClient implements HttpClient {
}
return execute(new HttpRequest(method, buf.toString()));
}
+
+ protected boolean hasError(HttpStatus status) {
+ return (status.series() == HttpStatus.Series.CLIENT_ERROR || status
+ .series() == HttpStatus.Series.SERVER_ERROR);
+ }
+
+ protected void handleResponse(HttpResponse response)
+ throws HttpClientException {
+ HttpStatus status = response.getStatus();
+ if (hasError(status)) {
+ switch (status.series()) {
+ case CLIENT_ERROR:
+ case SERVER_ERROR:
+ throw new HttpClientException(String.format("%d %s",
+ status.getStatusCode(), status.getStatusText()));
+ default:
+ throw new HttpClientException("Unknown status code [" + status
+ + "]");
+ }
+ }
+ }
}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpResponse.java
new file mode 100644
index 00000000..5eecfc7e
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/AbstractHttpResponse.java
@@ -0,0 +1,31 @@
+package com.foxinmy.weixin4j.http;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ *
+ * @className AbstractHttpResponse
+ * @author jy
+ * @date 2015年9月7日
+ * @since JDK 1.7
+ * @see
+ */
+public abstract class AbstractHttpResponse implements HttpResponse {
+
+ private final byte[] content;
+
+ public AbstractHttpResponse(byte[] content) {
+ this.content = content;
+ }
+
+ @Override
+ public byte[] getContent() {
+ return content;
+ }
+
+ @Override
+ public InputStream getBody() {
+ return content != null ? new ByteArrayInputStream(content) : null;
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java
index 45e1c661..a5598b52 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpResponse.java
@@ -24,14 +24,21 @@ public interface HttpResponse extends HttpMessage {
*
* @return
*/
- HttpStatus getStatus() throws HttpClientException;
+ HttpStatus getStatus();
/**
* 响应内容
*
* @return
*/
- InputStream getBody() throws HttpClientException;
+ InputStream getBody();
+
+ /**
+ * 响应内容
+ *
+ * @return
+ */
+ byte[] getContent();
/**
* 释放资源
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java
index 72dba067..5200772b 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/HttpStatus.java
@@ -31,7 +31,6 @@ 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
*/
@@ -188,6 +187,54 @@ public final class HttpStatus {
return statusText;
}
+ /**
+ * Returns the HTTP status series of this status code.
+ *
+ * @see HttpStatus.Series
+ */
+ public Series series() {
+ return Series.valueOf(this);
+ }
+
+ /**
+ * Java 5 enumeration of HTTP status series.
+ *
+ * Retrievable via {@link HttpStatus#series()}.
+ */
+ public static enum Series {
+
+ INFORMATIONAL(1), SUCCESSFUL(2), REDIRECTION(3), CLIENT_ERROR(4), SERVER_ERROR(
+ 5);
+
+ private final int value;
+
+ private Series(int value) {
+ this.value = value;
+ }
+
+ /**
+ * Return the integer value of this status series. Ranges from 1 to 5.
+ */
+ public int value() {
+ return this.value;
+ }
+
+ public static Series valueOf(int status) {
+ int seriesCode = status / 100;
+ for (Series series : values()) {
+ if (series.value == seriesCode) {
+ return series;
+ }
+ }
+ throw new IllegalArgumentException("No matching constant for ["
+ + status + "]");
+ }
+
+ public static Series valueOf(HttpStatus status) {
+ return valueOf(status.statusCode);
+ }
+ }
+
@Override
public String toString() {
return "[" + statusCode + "," + statusText + "]";
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java
index 2c4c5991..b278be92 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpClient.java
@@ -1,29 +1,24 @@
package com.foxinmy.weixin4j.http;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URI;
import java.net.URLConnection;
-import java.security.KeyManagementException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-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;
+import com.foxinmy.weixin4j.http.factory.HttpClientFactory;
+import com.foxinmy.weixin4j.util.IOUtil;
import com.foxinmy.weixin4j.util.StringUtil;
/**
@@ -46,27 +41,6 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
};
}
- 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(HttpRequest request)
throws IOException {
URI uri = request.getURI();
@@ -83,11 +57,7 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
hostnameVerifier = params.getHostnameVerifier();
}
if (sslContext == null) {
- sslContext = SSLContext.getInstance("TLS");
- sslContext
- .init(null,
- new X509TrustManager[] { createX509TrustManager() },
- new java.security.SecureRandom());
+ sslContext = HttpClientFactory.allowSSLContext();
}
if (hostnameVerifier == null) {
hostnameVerifier = createHostnameVerifier();
@@ -96,23 +66,14 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setHostnameVerifier(hostnameVerifier);
return connection;
- } catch (NoSuchAlgorithmException | KeyManagementException e) {
- throw new IOException(e.getMessage());
+ } catch (HttpClientException e) {
+ throw new IOException(e);
}
} else {
return (HttpURLConnection) urlConnection;
}
}
- protected Map createDefualtHeader() {
- Map header = new HashMap();
- header.put("User-Agent", "simple-httpclient");
- header.put("Accept", "text/xml,text/javascript");
- header.put("Accept-Charset", Consts.UTF_8.name());
- header.put("Accept-Encoding", Consts.UTF_8.name());
- return header;
- }
-
@Override
public HttpResponse execute(HttpRequest request) throws HttpClientException {
HttpResponse response = null;
@@ -141,25 +102,31 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
connection.setDoOutput(false);
}
// set headers
- for (Iterator> headerIterator = createDefualtHeader()
- .entrySet().iterator(); headerIterator.hasNext();) {
- Entry header = headerIterator.next();
- connection.setRequestProperty(header.getKey(),
- header.getValue());
- }
HttpHeaders headers = request.getHeaders();
- if (headers != null) {
- for (Iterator>> headerIterator = headers
- .entrySet().iterator(); headerIterator.hasNext();) {
- Entry> header = headerIterator.next();
- if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
- connection.setRequestProperty(header.getKey(),
- StringUtil.join(header.getValue(), ';'));
- } else {
- for (String headerValue : header.getValue()) {
- connection.addRequestProperty(header.getKey(),
- headerValue != null ? headerValue : "");
- }
+ if (headers == null) {
+ headers = new HttpHeaders();
+ if (!headers.containsKey(HttpHeaders.HOST)) {
+ headers.set(HttpHeaders.HOST, request.getURI().getHost());
+ }
+ // Add default accept headers
+ if (!headers.containsKey(HttpHeaders.ACCEPT)) {
+ headers.set(HttpHeaders.ACCEPT, "*/*");
+ }
+ // Add default user agent
+ if (!headers.containsKey(HttpHeaders.USER_AGENT)) {
+ headers.set(HttpHeaders.USER_AGENT, "jdk/httpclient");
+ }
+ }
+ for (Iterator>> headerIterator = headers
+ .entrySet().iterator(); headerIterator.hasNext();) {
+ Entry> header = headerIterator.next();
+ if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
+ connection.setRequestProperty(header.getKey(),
+ StringUtil.join(header.getValue(), ';'));
+ } else {
+ for (String headerValue : header.getValue()) {
+ connection.addRequestProperty(header.getKey(),
+ headerValue != null ? headerValue : "");
}
}
}
@@ -170,16 +137,15 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
if (httpEntity != null) {
// Read Out Exception when connection.disconnect();
/*
+ * if (httpEntity.getContentLength() > 0l) {
+ * connection.setFixedLengthStreamingMode(httpEntity
+ * .getContentLength()); } else { connection
+ * .setChunkedStreamingMode(params != null ? params
+ * .getChunkSize() : 4096); }
+ */
if (httpEntity.getContentLength() > 0l) {
- connection.setFixedLengthStreamingMode(httpEntity
- .getContentLength());
- } else {
- connection
- .setChunkedStreamingMode(params != null ? params
- .getChunkSize() : 4096);
- }*/
- if (httpEntity.getContentLength() > 0l) {
- connection.setRequestProperty(HttpHeaders.CONTENT_LENGTH,
+ connection.setRequestProperty(
+ HttpHeaders.CONTENT_LENGTH,
Long.toString(httpEntity.getContentLength()));
}
if (httpEntity.getContentType() != null) {
@@ -198,7 +164,12 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
output.close();
}
// building response
- response = new SimpleHttpResponse(connection);
+ InputStream input = connection.getErrorStream() != null ? connection
+ .getErrorStream() : connection.getInputStream();
+ byte[] content = IOUtil.toByteArray(input);
+ response = new SimpleHttpResponse(connection, content);
+ input.close();
+ handleResponse(response);
} catch (IOException e) {
throw new HttpClientException("I/O error on "
+ request.getMethod().name() + " request for \""
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpResponse.java
index fb70be55..193d881e 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpResponse.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/SimpleHttpResponse.java
@@ -1,7 +1,6 @@
package com.foxinmy.weixin4j.http;
import java.io.IOException;
-import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.Iterator;
import java.util.List;
@@ -17,7 +16,7 @@ import java.util.Map.Entry;
* @since JDK 1.7
* @see
*/
-public class SimpleHttpResponse implements HttpResponse {
+public class SimpleHttpResponse extends AbstractHttpResponse {
private final HttpURLConnection connection;
@@ -25,7 +24,8 @@ public class SimpleHttpResponse implements HttpResponse {
private HttpVersion protocol;
private HttpStatus status;
- public SimpleHttpResponse(HttpURLConnection connection) {
+ public SimpleHttpResponse(HttpURLConnection connection, byte[] content) {
+ super(content);
this.connection = connection;
}
@@ -65,28 +65,18 @@ public class SimpleHttpResponse implements HttpResponse {
}
@Override
- public HttpStatus getStatus() throws HttpClientException {
+ public HttpStatus getStatus() {
if (status == null) {
try {
status = new HttpStatus(connection.getResponseCode(),
connection.getResponseMessage());
} catch (IOException e) {
- throw new HttpClientException("I/O Error on getStatus", e);
+ throw new RuntimeException("I/O Error on getStatus", e);
}
}
return status;
}
- @Override
- public InputStream getBody() throws HttpClientException {
- try {
- return connection.getErrorStream() != null ? connection
- .getErrorStream() : connection.getInputStream();
- } catch (IOException e) {
- throw new HttpClientException("I/O Error on getBody", e);
- }
- }
-
@Override
public void close() {
connection.disconnect();
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpClientFactory.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpClientFactory.java
index c51a65ba..2d381bb0 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpClientFactory.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpClientFactory.java
@@ -1,6 +1,15 @@
package com.foxinmy.weixin4j.http.factory;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.X509TrustManager;
+
import com.foxinmy.weixin4j.http.HttpClient;
+import com.foxinmy.weixin4j.http.HttpClientException;
/**
* HttpClient工厂生产类:参考netty的InternalLoggerFactory
@@ -26,12 +35,16 @@ public abstract class HttpClientFactory {
private static HttpClientFactory newDefaultFactory() {
HttpClientFactory f;
try {
- f = new HttpComponent4Factory();
- } catch (Throwable e2) {
+ f = new Netty4HttpClientFactory();
+ } catch (Throwable e1) {
try {
- f = new HttpComponent3Factory();
- } catch (Throwable e3) {
- f = new SimpleHttpClientFactory();
+ f = new HttpComponent4Factory();
+ } catch (Throwable e2) {
+ try {
+ f = new HttpComponent3Factory();
+ } catch (Throwable e3) {
+ f = new SimpleHttpClientFactory();
+ }
}
}
return f;
@@ -73,4 +86,37 @@ public abstract class HttpClientFactory {
* @return
*/
public abstract HttpClient newInstance();
+
+ public static SSLContext allowSSLContext() throws HttpClientException {
+ try {
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null,
+ new X509TrustManager[] { createX509TrustManager() },
+ new java.security.SecureRandom());
+ return sslContext;
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ throw new HttpClientException("Create SSLContext Error:", e);
+ }
+ }
+
+ protected static 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 {
+ }
+ };
+ }
}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3.java
index f7228d0d..721b5ad7 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3.java
@@ -109,18 +109,30 @@ public class HttpComponent3 extends AbstractHttpClient {
}
com.foxinmy.weixin4j.http.HttpHeaders headers = request
.getHeaders();
- if (headers != null) {
- for (Iterator>> headerIterator = headers
- .entrySet().iterator(); headerIterator.hasNext();) {
- Entry> header = headerIterator.next();
- if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
- httpMethod.setRequestHeader(header.getKey(),
- StringUtil.join(header.getValue(), ';'));
- } else {
- for (String headerValue : header.getValue()) {
- httpMethod.addRequestHeader(header.getKey(),
- headerValue != null ? headerValue : "");
- }
+ if (headers == null) {
+ headers = new HttpHeaders();
+ }
+ if (!headers.containsKey(HttpHeaders.HOST)) {
+ headers.set(HttpHeaders.HOST, uri.getHost());
+ }
+ // Add default accept headers
+ if (!headers.containsKey(HttpHeaders.ACCEPT)) {
+ headers.set(HttpHeaders.ACCEPT, "*/*");
+ }
+ // Add default user agent
+ if (!headers.containsKey(HttpHeaders.USER_AGENT)) {
+ headers.set(HttpHeaders.USER_AGENT, "apache/httpclient3");
+ }
+ for (Iterator>> headerIterator = headers
+ .entrySet().iterator(); headerIterator.hasNext();) {
+ Entry> header = headerIterator.next();
+ if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
+ httpMethod.setRequestHeader(header.getKey(),
+ StringUtil.join(header.getValue(), ';'));
+ } else {
+ for (String headerValue : header.getValue()) {
+ httpMethod.addRequestHeader(header.getKey(),
+ headerValue != null ? headerValue : "");
}
}
}
@@ -153,6 +165,7 @@ public class HttpComponent3 extends AbstractHttpClient {
}
httpClient.executeMethod(httpMethod);
response = new HttpComponent3Response(httpMethod);
+ handleResponse(response);
} catch (IOException e) {
throw new HttpClientException("I/O error on "
+ request.getMethod().name() + " request for \""
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Response.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Response.java
index 499153cf..ece04ff6 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Response.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent3Response.java
@@ -1,16 +1,13 @@
package com.foxinmy.weixin4j.http.factory;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
-import java.io.InputStream;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.protocol.Protocol;
-import com.foxinmy.weixin4j.http.HttpClientException;
+import com.foxinmy.weixin4j.http.AbstractHttpResponse;
import com.foxinmy.weixin4j.http.HttpHeaders;
-import com.foxinmy.weixin4j.http.HttpResponse;
import com.foxinmy.weixin4j.http.HttpStatus;
import com.foxinmy.weixin4j.http.HttpVersion;
@@ -23,22 +20,17 @@ import com.foxinmy.weixin4j.http.HttpVersion;
* @since JDK 1.7
* @see
*/
-public class HttpComponent3Response implements HttpResponse {
+public class HttpComponent3Response extends AbstractHttpResponse {
private final HttpMethod httpMethod;
private HttpHeaders headers;
private HttpVersion protocol;
private HttpStatus status;
- private InputStream body;
- public HttpComponent3Response(HttpMethod httpMethod) {
+ public HttpComponent3Response(HttpMethod httpMethod) throws IOException {
+ super(httpMethod.getResponseBody());
this.httpMethod = httpMethod;
- try {
- this.body = new ByteArrayInputStream(httpMethod.getResponseBody());
- } catch (IOException e) {
- ;
- }
}
@Override
@@ -71,7 +63,7 @@ public class HttpComponent3Response implements HttpResponse {
}
@Override
- public HttpStatus getStatus() throws HttpClientException {
+ public HttpStatus getStatus() {
if (status == null) {
status = new HttpStatus(httpMethod.getStatusCode(),
httpMethod.getStatusText());
@@ -79,11 +71,6 @@ public class HttpComponent3Response implements HttpResponse {
return status;
}
- @Override
- public InputStream getBody() throws HttpClientException {
- return body;
- }
-
@Override
public void close() {
httpMethod.releaseConnection();
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4.java
index 1f9fcc0a..2d772853 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4.java
@@ -26,6 +26,7 @@ import org.apache.http.client.methods.HttpTrace;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.util.EntityUtils;
import com.foxinmy.weixin4j.http.AbstractHttpClient;
import com.foxinmy.weixin4j.http.HttpHeaders;
@@ -57,18 +58,27 @@ public abstract class HttpComponent4 extends AbstractHttpClient {
}
protected void addHeaders(HttpHeaders headers, HttpRequestBase uriRequest) {
- if (headers != null) {
- for (Iterator>> headerIterator = headers
- .entrySet().iterator(); headerIterator.hasNext();) {
- Entry> header = headerIterator.next();
- if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
+ if (headers == null) {
+ headers = new HttpHeaders();
+ }
+ // Add default accept headers
+ if (!headers.containsKey(HttpHeaders.ACCEPT)) {
+ headers.set(HttpHeaders.ACCEPT, "*/*");
+ }
+ // Add default user agent
+ if (!headers.containsKey(HttpHeaders.USER_AGENT)) {
+ headers.set(HttpHeaders.USER_AGENT, "apache/httpclient4");
+ }
+ for (Iterator>> headerIterator = headers
+ .entrySet().iterator(); headerIterator.hasNext();) {
+ Entry> header = headerIterator.next();
+ if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
+ uriRequest.addHeader(header.getKey(),
+ StringUtil.join(header.getValue(), ';'));
+ } else {
+ for (String headerValue : header.getValue()) {
uriRequest.addHeader(header.getKey(),
- StringUtil.join(header.getValue(), ';'));
- } else {
- for (String headerValue : header.getValue()) {
- uriRequest.addHeader(header.getKey(),
- headerValue != null ? headerValue : "");
- }
+ headerValue != null ? headerValue : "");
}
}
}
@@ -94,6 +104,11 @@ public abstract class HttpComponent4 extends AbstractHttpClient {
}
}
+ protected byte[] getContent(org.apache.http.HttpResponse httpResponse)
+ throws IOException {
+ return EntityUtils.toByteArray(httpResponse.getEntity());
+ }
+
protected static class CustomHostnameVerifier implements
X509HostnameVerifier {
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1.java
index ccb29d9c..8f72907b 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1.java
@@ -13,7 +13,6 @@ import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
-import com.foxinmy.weixin4j.http.HttpClient;
import com.foxinmy.weixin4j.http.HttpClientException;
import com.foxinmy.weixin4j.http.HttpHeaders;
import com.foxinmy.weixin4j.http.HttpParams;
@@ -87,7 +86,9 @@ public class HttpComponent4_1 extends HttpComponent4 {
addEntity(request.getEntity(), uriRequest);
org.apache.http.HttpResponse httpResponse = httpClient
.execute(uriRequest);
- response = new HttpComponent4_1Response(httpClient, httpResponse);
+ response = new HttpComponent4_1Response(httpResponse,
+ getContent(httpResponse));
+ handleResponse(response);
} catch (IOException e) {
throw new HttpClientException("I/O error on "
+ request.getMethod().name() + " request for \""
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1Response.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1Response.java
index c1175617..f3879e1f 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1Response.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_1Response.java
@@ -1,16 +1,13 @@
package com.foxinmy.weixin4j.http.factory;
import java.io.IOException;
-import java.io.InputStream;
import org.apache.http.Header;
-import org.apache.http.HttpEntity;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
-import com.foxinmy.weixin4j.http.HttpClientException;
+import com.foxinmy.weixin4j.http.AbstractHttpResponse;
import com.foxinmy.weixin4j.http.HttpHeaders;
-import com.foxinmy.weixin4j.http.HttpResponse;
import com.foxinmy.weixin4j.http.HttpStatus;
import com.foxinmy.weixin4j.http.HttpVersion;
@@ -23,18 +20,16 @@ import com.foxinmy.weixin4j.http.HttpVersion;
* @since JDK 1.7
* @see
*/
-public class HttpComponent4_1Response implements HttpResponse {
- private final org.apache.http.client.HttpClient httpClient;
+public class HttpComponent4_1Response extends AbstractHttpResponse {
private final org.apache.http.HttpResponse httpResponse;
private HttpHeaders headers;
private HttpVersion protocol;
private HttpStatus status;
- public HttpComponent4_1Response(
- org.apache.http.client.HttpClient httpClient,
- org.apache.http.HttpResponse httpResponse) {
- this.httpClient = httpClient;
+ public HttpComponent4_1Response(org.apache.http.HttpResponse httpResponse,
+ byte[] content) throws IOException {
+ super(content);
this.httpResponse = httpResponse;
}
@@ -64,7 +59,7 @@ public class HttpComponent4_1Response implements HttpResponse {
}
@Override
- public HttpStatus getStatus() throws HttpClientException {
+ public HttpStatus getStatus() {
if (status == null) {
StatusLine statusLine = httpResponse.getStatusLine();
status = new HttpStatus(statusLine.getStatusCode(),
@@ -74,17 +69,11 @@ public class HttpComponent4_1Response implements HttpResponse {
}
@Override
- public InputStream getBody() throws HttpClientException {
+ public void close() {
try {
- HttpEntity entity = this.httpResponse.getEntity();
- return (entity != null ? entity.getContent() : null);
+ httpResponse.getEntity().consumeContent();
} catch (IOException e) {
- throw new HttpClientException("I/O Error on getBody", e);
+ ;
}
}
-
- @Override
- public void close() {
- httpClient.getConnectionManager().closeExpiredConnections();
- }
}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2.java
index 5a0eaf07..9a7b1977 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2.java
@@ -69,7 +69,9 @@ public class HttpComponent4_2 extends HttpComponent4 {
addHeaders(request.getHeaders(), uriRequest);
addEntity(request.getEntity(), uriRequest);
CloseableHttpResponse httpResponse = httpClient.execute(uriRequest);
- response = new HttpComponent4_2Response(httpResponse);
+ response = new HttpComponent4_2Response(httpResponse,
+ getContent(httpResponse));
+ handleResponse(response);
} catch (IOException e) {
throw new HttpClientException("I/O error on "
+ request.getMethod().name() + " request for \""
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2Response.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2Response.java
index f1b681de..ea2d0921 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2Response.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/HttpComponent4_2Response.java
@@ -1,17 +1,15 @@
package com.foxinmy.weixin4j.http.factory;
import java.io.IOException;
-import java.io.InputStream;
import org.apache.http.Header;
-import org.apache.http.HttpEntity;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.util.EntityUtils;
-import com.foxinmy.weixin4j.http.HttpClientException;
+import com.foxinmy.weixin4j.http.AbstractHttpResponse;
import com.foxinmy.weixin4j.http.HttpHeaders;
-import com.foxinmy.weixin4j.http.HttpResponse;
import com.foxinmy.weixin4j.http.HttpStatus;
import com.foxinmy.weixin4j.http.HttpVersion;
@@ -24,7 +22,7 @@ import com.foxinmy.weixin4j.http.HttpVersion;
* @since JDK 1.7
* @see
*/
-public class HttpComponent4_2Response implements HttpResponse {
+public class HttpComponent4_2Response extends AbstractHttpResponse {
private final CloseableHttpResponse httpResponse;
@@ -32,7 +30,9 @@ public class HttpComponent4_2Response implements HttpResponse {
private HttpVersion protocol;
private HttpStatus status;
- public HttpComponent4_2Response(CloseableHttpResponse httpResponse) {
+ public HttpComponent4_2Response(CloseableHttpResponse httpResponse,
+ byte[] content) {
+ super(content);
this.httpResponse = httpResponse;
}
@@ -62,7 +62,7 @@ public class HttpComponent4_2Response implements HttpResponse {
}
@Override
- public HttpStatus getStatus() throws HttpClientException {
+ public HttpStatus getStatus() {
if (status == null) {
StatusLine statusLine = httpResponse.getStatusLine();
status = new HttpStatus(statusLine.getStatusCode(),
@@ -71,20 +71,10 @@ public class HttpComponent4_2Response implements HttpResponse {
return status;
}
- @Override
- public InputStream getBody() throws HttpClientException {
- try {
- HttpEntity entity = this.httpResponse.getEntity();
- return (entity != null ? entity.getContent() : null);
- } catch (IOException e) {
- throw new HttpClientException("I/O Error on getBody", e);
- }
- }
-
@Override
public void close() {
try {
- // EntityUtils.consume(httpResponse.getEntity());
+ EntityUtils.consume(httpResponse.getEntity());
httpResponse.close();
} catch (IOException ex) {
;
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/Netty4HttpClient.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/Netty4HttpClient.java
new file mode 100644
index 00000000..bd51d9cf
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/Netty4HttpClient.java
@@ -0,0 +1,223 @@
+package com.foxinmy.weixin4j.http.factory;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.ByteBufOutputStream;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelFutureListener;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.DefaultHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.ssl.SslHandler;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import com.foxinmy.weixin4j.http.AbstractHttpClient;
+import com.foxinmy.weixin4j.http.HttpClientException;
+import com.foxinmy.weixin4j.http.HttpHeaders;
+import com.foxinmy.weixin4j.http.HttpParams;
+import com.foxinmy.weixin4j.http.HttpRequest;
+import com.foxinmy.weixin4j.http.HttpResponse;
+import com.foxinmy.weixin4j.http.entity.HttpEntity;
+import com.foxinmy.weixin4j.util.SettableFuture;
+import com.foxinmy.weixin4j.util.StringUtil;
+
+/**
+ * Netty 4.x
+ *
+ * @className Netty4HttpClient
+ * @author jy
+ * @date 2015年8月30日
+ * @since JDK 1.7
+ * @see
+ */
+public class Netty4HttpClient extends AbstractHttpClient {
+
+ private final Bootstrap bootstrap;
+
+ public Netty4HttpClient(Bootstrap bootstrap) {
+ this.bootstrap = bootstrap;
+ }
+
+ @Override
+ public HttpResponse execute(HttpRequest request) throws HttpClientException {
+ HttpResponse response = null;
+ try {
+ final URI uri = request.getURI();
+ final HttpParams params = request.getParams();
+ if (params != null) {
+ bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
+ params.getConnectTimeout());
+ }
+ final boolean useProxy = params != null
+ && params.getProxy() != null;
+ final boolean useSSL = "https".equals(uri.getScheme()) && !useProxy;
+ final DefaultHttpRequest uriRequest = createRequest(request);
+ final SettableFuture future = new SettableFuture();
+ ChannelFutureListener listener = new ChannelFutureListener() {
+ @Override
+ public void operationComplete(ChannelFuture channelFuture)
+ throws Exception {
+ if (channelFuture.isSuccess()) {
+ Channel channel = channelFuture.channel();
+ // ssl
+ SSLContext sslContext = null;
+ if (useSSL) {
+ if (params != null
+ && params.getSSLContext() != null) {
+ sslContext = params.getSSLContext();
+ } else {
+ sslContext = HttpClientFactory
+ .allowSSLContext();
+ }
+ SSLEngine sslEngine = sslContext.createSSLEngine();
+ sslEngine.setUseClientMode(true);
+ channel.pipeline().addFirst(
+ new SslHandler(sslEngine));
+ }
+ channel.pipeline().addLast(new RequestHandler(future));
+ channel.writeAndFlush(uriRequest);
+ } else {
+ future.setException(channelFuture.cause());
+ }
+ }
+ };
+ InetSocketAddress address = new InetSocketAddress(
+ InetAddress.getByName(uri.getHost()), getPort(uri));
+ if (useProxy) {
+ address = (InetSocketAddress) params.getProxy().address();
+ }
+ bootstrap.connect(address).syncUninterruptibly()
+ .addListener(listener);
+ response = future.get();
+ handleResponse(response);
+ } catch (IOException | InterruptedException | ExecutionException e) {
+ throw new HttpClientException("I/O error on "
+ + request.getMethod().name() + " request for \""
+ + request.getURI().toString() + "\":" + e.getMessage(), e);
+ } finally {
+ if (response != null) {
+ response.close();
+ }
+ }
+ return response;
+ }
+
+ private DefaultHttpRequest createRequest(HttpRequest request)
+ throws IOException {
+ HttpMethod method = HttpMethod.valueOf(request.getMethod().name());
+ URI uri = request.getURI();
+ String url = StringUtil.isBlank(uri.getRawPath()) ? "/" : uri
+ .getRawPath();
+ if (StringUtil.isNotBlank(uri.getRawQuery())) {
+ url += "?" + uri.getRawQuery();
+ }
+ DefaultHttpRequest uriRequest = new DefaultHttpRequest(
+ HttpVersion.HTTP_1_1, method, url);
+ // entity
+ HttpEntity entity = request.getEntity();
+ if (entity != null) {
+ ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
+ try (ByteBufOutputStream out = new ByteBufOutputStream(byteBuf)) {
+ entity.writeTo(out);
+ }
+ uriRequest = new DefaultFullHttpRequest(
+ uriRequest.getProtocolVersion(), uriRequest.getMethod(),
+ uriRequest.getUri(), byteBuf);
+ if (entity.getContentType() != null) {
+ uriRequest.headers().add(HttpHeaders.CONTENT_TYPE,
+ entity.getContentType().toString());
+ }
+ if (entity.getContentLength() < 0) {
+ uriRequest.headers().add(HttpHeaders.TRANSFER_ENCODING,
+ io.netty.handler.codec.http.HttpHeaders.Values.CHUNKED);
+ } else {
+ uriRequest.headers().add(HttpHeaders.CONTENT_LENGTH,
+ entity.getContentLength());
+ }
+ }
+ // header
+ HttpHeaders headers = request.getHeaders();
+ if (headers == null) {
+ headers = new HttpHeaders();
+ }
+ if (!headers.containsKey(HttpHeaders.HOST)) {
+ headers.set(HttpHeaders.HOST, uri.getHost());
+ }
+ // Add default accept headers
+ if (!headers.containsKey(HttpHeaders.ACCEPT)) {
+ headers.set(HttpHeaders.ACCEPT, "*/*");
+ }
+ // Add default user agent
+ if (!headers.containsKey(HttpHeaders.USER_AGENT)) {
+ headers.set(HttpHeaders.USER_AGENT, "netty/httpclient");
+ }
+ for (Iterator>> headerIterator = headers
+ .entrySet().iterator(); headerIterator.hasNext();) {
+ Entry> header = headerIterator.next();
+ uriRequest.headers().add(header.getKey(), header.getValue());
+ }
+ uriRequest.headers().set(HttpHeaders.CONNECTION,
+ io.netty.handler.codec.http.HttpHeaders.Values.CLOSE);
+ return uriRequest;
+ }
+
+ private int getPort(URI uri) {
+ int port = uri.getPort();
+ if (port == -1) {
+ if ("http".equalsIgnoreCase(uri.getScheme())) {
+ port = 80;
+ } else if ("https".equalsIgnoreCase(uri.getScheme())) {
+ port = 443;
+ }
+ }
+ return port;
+ }
+
+ private static class RequestHandler extends
+ SimpleChannelInboundHandler {
+
+ private final SettableFuture future;
+
+ public RequestHandler(SettableFuture future) {
+ this.future = future;
+ }
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext context,
+ FullHttpResponse response) throws Exception {
+ byte[] content = null;
+ ByteBuf byteBuf = response.content();
+ if (byteBuf.hasArray()) {
+ content = byteBuf.array();
+ } else {
+ content = new byte[byteBuf.readableBytes()];
+ byteBuf.readBytes(content);
+ }
+ future.set(new Netty4HttpResponse(context, response, content));
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext context,
+ Throwable cause) throws Exception {
+ future.setException(cause);
+ }
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/Netty4HttpClientFactory.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/Netty4HttpClientFactory.java
new file mode 100644
index 00000000..cb89eef8
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/Netty4HttpClientFactory.java
@@ -0,0 +1,64 @@
+package com.foxinmy.weixin4j.http.factory;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpContentDecompressor;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpResponseDecoder;
+import io.netty.handler.stream.ChunkedWriteHandler;
+
+import com.foxinmy.weixin4j.http.HttpClient;
+
+/**
+ * 使用Netty
+ *
+ * @className Netty4HttpClientFactory
+ * @author jy
+ * @date 2015年8月30日
+ * @since JDK 1.7
+ * @see
+ */
+public class Netty4HttpClientFactory extends HttpClientFactory {
+ /**
+ * worker线程数,默认设置为cpu的核数 * 4
+ */
+ private final int workerThreads;
+
+ public Netty4HttpClientFactory() {
+ this(Runtime.getRuntime().availableProcessors() * 4);
+ }
+
+ public Netty4HttpClientFactory(int workerThreads) {
+ this.workerThreads = workerThreads;
+ }
+
+ @Override
+ public HttpClient newInstance() {
+ Bootstrap b = new Bootstrap();
+ b.option(ChannelOption.SO_KEEPALIVE, true).option(
+ ChannelOption.TCP_NODELAY, true);
+ EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
+ b.group(workerGroup).channel(NioSocketChannel.class)
+ .handler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel channel)
+ throws Exception {
+ ChannelPipeline pipeline = channel.pipeline();
+ pipeline.addLast(new HttpClientCodec());
+ pipeline.addLast(new HttpContentDecompressor());
+ pipeline.addLast(new ChunkedWriteHandler());
+ pipeline.addLast(new HttpResponseDecoder());
+ pipeline.addLast(new HttpObjectAggregator(
+ Integer.MAX_VALUE));
+ }
+ });
+ return new Netty4HttpClient(b);
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/Netty4HttpResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/Netty4HttpResponse.java
new file mode 100644
index 00000000..78092bfb
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/factory/Netty4HttpResponse.java
@@ -0,0 +1,78 @@
+package com.foxinmy.weixin4j.http.factory;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpResponseStatus;
+
+import java.util.Map;
+
+import com.foxinmy.weixin4j.http.AbstractHttpResponse;
+import com.foxinmy.weixin4j.http.HttpHeaders;
+import com.foxinmy.weixin4j.http.HttpStatus;
+import com.foxinmy.weixin4j.http.HttpVersion;
+
+/**
+ * Netty Respone::Requires Netty 4.x or higher
+ *
+ * @className Netty4HttpResponse
+ * @author jy
+ * @date 2015年8月30日
+ * @since JDK 1.7
+ * @see
+ */
+public class Netty4HttpResponse extends AbstractHttpResponse {
+
+ private final ChannelHandlerContext context;
+ private final FullHttpResponse response;
+
+ private HttpVersion protocol;
+ private HttpStatus status;
+ private volatile HttpHeaders headers;
+
+ public Netty4HttpResponse(ChannelHandlerContext context,
+ FullHttpResponse response, byte[] content) {
+ super(content);
+ this.context = context;
+ this.response = response;
+ this.response.retain();
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ if (this.headers == null) {
+ HttpHeaders headers = new HttpHeaders();
+ for (Map.Entry entry : this.response.headers()) {
+ headers.add(entry.getKey(), entry.getValue());
+ }
+ this.headers = headers;
+ }
+ return this.headers;
+ }
+
+ @Override
+ public HttpVersion getProtocol() {
+ if (protocol == null) {
+ io.netty.handler.codec.http.HttpVersion version = response
+ .getProtocolVersion();
+ this.protocol = new HttpVersion(version.protocolName(),
+ version.majorVersion(), version.majorVersion(),
+ version.isKeepAliveDefault());
+ }
+ return protocol;
+ }
+
+ @Override
+ public HttpStatus getStatus() {
+ if (status == null) {
+ HttpResponseStatus status = response.getStatus();
+ this.status = new HttpStatus(status.code(), status.reasonPhrase());
+ }
+ return status;
+ }
+
+ @Override
+ public void close() {
+ this.response.release();
+ this.context.close();
+ }
+}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java
index 9af976e5..e61ecedf 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinRequestExecutor.java
@@ -20,6 +20,7 @@ import com.foxinmy.weixin4j.http.entity.FormUrlEntity;
import com.foxinmy.weixin4j.http.entity.HttpEntity;
import com.foxinmy.weixin4j.http.entity.StringEntity;
import com.foxinmy.weixin4j.http.factory.HttpClientFactory;
+import com.foxinmy.weixin4j.http.factory.Netty4HttpClientFactory;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.util.StringUtil;
import com.foxinmy.weixin4j.util.WeixinErrorUtil;
@@ -44,6 +45,7 @@ public class WeixinRequestExecutor {
}
public WeixinRequestExecutor(HttpParams params) {
+ HttpClientFactory.setDefaultFactory(new Netty4HttpClientFactory());
this.httpClient = HttpClientFactory.getInstance();
this.params = params;
}
@@ -97,10 +99,6 @@ public class WeixinRequestExecutor {
HttpResponse httpResponse = httpClient.execute(request);
HttpStatus status = httpResponse.getStatus();
HttpHeaders headers = httpResponse.getHeaders();
- if (status.getStatusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
- throw new WeixinException(String.format("request fail : %d-%s",
- status.getStatusCode(), status.getStatusText()));
- }
WeixinResponse response = new WeixinResponse(headers, status,
httpResponse.getBody());
String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE);
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java
index 1e17d5b6..55b06cc0 100644
--- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java
@@ -42,6 +42,7 @@ public class WeixinResponse {
try {
text = StringUtil.newStringUtf8(IOUtil.toByteArray(body));
} catch (IOException e) {
+ e.printStackTrace();
;
}
}
diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/SettableFuture.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/SettableFuture.java
new file mode 100644
index 00000000..f4d3b2f3
--- /dev/null
+++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/SettableFuture.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2002-2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.foxinmy.weixin4j.util;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A {@link org.springframework.util.concurrent.ListenableFuture
+ * ListenableFuture} whose value can be set via {@link #set(Object)} or
+ * {@link #setException(Throwable)}. It may also be cancelled.
+ *
+ *
+ * Inspired by {@code com.google.common.util.concurrent.SettableFuture}.
+ *
+ * @author Mattias Severson
+ * @author Rossen Stoyanchev
+ * @since 4.1
+ */
+public class SettableFuture implements Future {
+
+ private final SettableTask settableTask;
+
+ private final FutureTask futureTask;
+
+ public SettableFuture() {
+ this.settableTask = new SettableTask();
+ this.futureTask = new FutureTask(this.settableTask);
+ }
+
+ /**
+ * Set the value of this future. This method will return {@code true} if the
+ * value was set successfully, or {@code false} if the future has already
+ * been set or cancelled.
+ *
+ * @param value
+ * the value that will be set.
+ * @return {@code true} if the value was successfully set, else
+ * {@code false}.
+ */
+ public boolean set(T value) {
+ boolean success = this.settableTask.setValue(value);
+ if (success) {
+ this.futureTask.run();
+ }
+ return success;
+ }
+
+ /**
+ * Set the exception of this future. This method will return {@code true} if
+ * the exception was set successfully, or {@code false} if the future has
+ * already been set or cancelled.
+ *
+ * @param exception
+ * the value that will be set.
+ * @return {@code true} if the exception was successfully set, else
+ * {@code false}.
+ */
+ public boolean setException(Throwable exception) {
+ boolean success = this.settableTask.setException(exception);
+ if (success) {
+ this.futureTask.run();
+ }
+ return success;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ this.settableTask.setCancelled();
+ boolean cancelled = this.futureTask.cancel(mayInterruptIfRunning);
+ if (cancelled && mayInterruptIfRunning) {
+ interruptTask();
+ }
+ return cancelled;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.futureTask.isCancelled();
+ }
+
+ @Override
+ public boolean isDone() {
+ return this.futureTask.isDone();
+ }
+
+ /**
+ * Retrieve the value.
+ *
+ * Will return the value if it has been set via {@link #set(Object)}, throw
+ * an {@link java.util.concurrent.ExecutionException} if it has been set via
+ * {@link #setException(Throwable)} or throw a
+ * {@link java.util.concurrent.CancellationException} if it has been
+ * cancelled.
+ *
+ * @return The value associated with this future.
+ */
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ return this.futureTask.get();
+ }
+
+ /**
+ * Retrieve the value.
+ *
+ * Will return the value if it has been set via {@link #set(Object)}, throw
+ * an {@link java.util.concurrent.ExecutionException} if it has been set via
+ * {@link #setException(Throwable)} or throw a
+ * {@link java.util.concurrent.CancellationException} if it has been
+ * cancelled.
+ *
+ * @param timeout
+ * the maximum time to wait.
+ * @param unit
+ * the time unit of the timeout argument.
+ * @return The value associated with this future.
+ */
+ @Override
+ public T get(long timeout, TimeUnit unit) throws InterruptedException,
+ ExecutionException, TimeoutException {
+ return this.futureTask.get(timeout, unit);
+ }
+
+ /**
+ * Subclasses can override this method to implement interruption of the
+ * future's computation. The method is invoked automatically by a successful
+ * call to {@link #cancel(boolean) cancel(true)}.
+ *
+ * The default implementation does nothing.
+ */
+ protected void interruptTask() {
+ }
+
+ private static class SettableTask implements Callable {
+
+ private static final String NO_VALUE = SettableFuture.class
+ .getName() + ".NO_VALUE";
+
+ private final AtomicReference