diff --git a/CHANGE.md b/CHANGE.md index b35ac2b1..29c7b8f2 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -727,4 +727,8 @@ * 2016-07-06 - + weixin4j-mp:新增第三方组件WeixinComponentProxy \ No newline at end of file + + weixin4j-mp:新增第三方组件WeixinComponentProxy + +* 2016-07-21 + + + 新增MessageConverter \ No newline at end of file diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/PayApi.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/PayApi.java index c427a19b..a842c201 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/PayApi.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/api/PayApi.java @@ -18,8 +18,8 @@ import java.util.Map; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.foxinmy.weixin4j.exception.WeixinException; +import com.foxinmy.weixin4j.http.message.XmlResult; import com.foxinmy.weixin4j.http.weixin.WeixinResponse; -import com.foxinmy.weixin4j.http.weixin.XmlResult; import com.foxinmy.weixin4j.model.Consts; import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.payment.mch.APPPayRequest; @@ -746,7 +746,7 @@ public class PayApi extends MchApi { String param = XmlStream.map2xml(map); WeixinResponse response = weixinExecutor.post( getRequestUri("interface_report_uri"), param); - return response.getAsXmlResult(); + return response.getAsXml(); } /** diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/FileCacheStorager.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/FileCacheStorager.java index ff611e59..fdff41b3 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/FileCacheStorager.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/FileCacheStorager.java @@ -22,24 +22,20 @@ public class FileCacheStorager implements CacheStorager private final String SEPARATOR = File.separator; public FileCacheStorager(String path) { - this.tmpdir = new File(String.format("%s%sweixin4j_token_temp", - path, SEPARATOR)); + this.tmpdir = new File(String.format("%s%s%s", path, SEPARATOR, ALLKEY)); this.tmpdir.mkdirs(); } @Override public T lookup(String key) { - File cacheFile = new File(String.format("%s%s%s", - tmpdir.getAbsolutePath(), SEPARATOR, key)); + File cacheFile = new File(String.format("%s%s%s", tmpdir.getAbsolutePath(), SEPARATOR, key)); try { if (cacheFile.exists()) { - T cache = SerializationUtils.deserialize(new FileInputStream( - cacheFile)); + T cache = SerializationUtils.deserialize(new FileInputStream(cacheFile)); if (cache.getCreateTime() < 0) { return cache; } - if ((cache.getCreateTime() + cache.getExpires() - CUTMS) > System - .currentTimeMillis()) { + if ((cache.getCreateTime() + cache.getExpires() - CUTMS) > System.currentTimeMillis()) { return cache; } } @@ -52,10 +48,8 @@ public class FileCacheStorager implements CacheStorager @Override public void caching(String key, T cache) { try { - SerializationUtils.serialize( - cache, - new FileOutputStream(new File(String.format("%s%s%s", - tmpdir.getAbsolutePath(), SEPARATOR, key)))); + SerializationUtils.serialize(cache, + new FileOutputStream(new File(String.format("%s%s%s", tmpdir.getAbsolutePath(), SEPARATOR, key)))); } catch (IOException e) { throw new RuntimeException(e); } @@ -64,12 +58,10 @@ public class FileCacheStorager implements CacheStorager @Override public T evict(String key) { T cache = null; - File cacheFile = new File(String.format("%s%s%s", - tmpdir.getAbsolutePath(), SEPARATOR, key)); + File cacheFile = new File(String.format("%s%s%s", tmpdir.getAbsolutePath(), SEPARATOR, key)); try { if (cacheFile.exists()) { - cache = SerializationUtils.deserialize(new FileInputStream( - cacheFile)); + cache = SerializationUtils.deserialize(new FileInputStream(cacheFile)); cacheFile.delete(); } } catch (IOException e) { diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemcacheCacheStorager.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemcacheCacheStorager.java index 1d2a8dd6..c23a359c 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemcacheCacheStorager.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/cache/MemcacheCacheStorager.java @@ -18,8 +18,7 @@ import com.whalin.MemCached.SockIOPool; * @since JDK 1.6 * @see */ -public class MemcacheCacheStorager implements - CacheStorager { +public class MemcacheCacheStorager implements CacheStorager { private final MemCachedClient mc; @@ -30,7 +29,13 @@ public class MemcacheCacheStorager implements public MemcacheCacheStorager(MemcachePoolConfig poolConfig) { mc = new MemCachedClient(); poolConfig.initSocketIO(); - mc.set(ALLKEY, new HashSet()); + init(); + } + + private void init() { + if (!mc.keyExists(ALLKEY)) { + mc.set(ALLKEY, new HashSet()); + } } @SuppressWarnings("unchecked") @@ -43,9 +48,7 @@ public class MemcacheCacheStorager implements @Override public void caching(String key, T cache) { if (cache.getCreateTime() > 0l) { - mc.set(key, - cache, - new Date(cache.getCreateTime() + cache.getExpires() - CUTMS)); + mc.set(key, cache, new Date(cache.getCreateTime() + cache.getExpires() - CUTMS)); } else { mc.set(key, cache); } 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 23cc1390..7c538c1c 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 @@ -10,17 +10,15 @@ import com.foxinmy.weixin4j.logging.InternalLoggerFactory; public abstract class AbstractHttpClient implements HttpClient { - protected final InternalLogger logger = InternalLoggerFactory - .getInstance(getClass()); - + protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass()); + @Override public HttpResponse get(String url) throws HttpClientException { return execute(HttpMethod.GET, url); } @Override - public HttpResponse get(String url, URLParameter... parameters) - throws HttpClientException { + public HttpResponse get(String url, URLParameter... parameters) throws HttpClientException { return execute(HttpMethod.GET, url, parameters); } @@ -30,8 +28,7 @@ public abstract class AbstractHttpClient implements HttpClient { } @Override - public HttpHeaders head(String url, URLParameter... parameters) - throws HttpClientException { + public HttpHeaders head(String url, URLParameter... parameters) throws HttpClientException { return execute(HttpMethod.HEAD, url, parameters).getHeaders(); } @@ -41,8 +38,7 @@ public abstract class AbstractHttpClient implements HttpClient { } @Override - public HttpResponse post(String url, URLParameter... parameters) - throws HttpClientException { + public HttpResponse post(String url, URLParameter... parameters) throws HttpClientException { HttpEntity entity = null; if (parameters != null && parameters.length > 0) { entity = new FormUrlEntity(Arrays.asList(parameters)); @@ -51,8 +47,7 @@ public abstract class AbstractHttpClient implements HttpClient { } @Override - public HttpResponse post(String url, HttpEntity entity) - throws HttpClientException { + public HttpResponse post(String url, HttpEntity entity) throws HttpClientException { HttpRequest request = new HttpRequest(HttpMethod.POST, url); request.setEntity(entity); return execute(request); @@ -64,8 +59,7 @@ public abstract class AbstractHttpClient implements HttpClient { } @Override - public void put(String url, URLParameter... parameters) - throws HttpClientException { + public void put(String url, URLParameter... parameters) throws HttpClientException { execute(HttpMethod.PUT, url, parameters); } @@ -75,8 +69,7 @@ public abstract class AbstractHttpClient implements HttpClient { } @Override - public void delete(String url, URLParameter... parameters) - throws HttpClientException { + public void delete(String url, URLParameter... parameters) throws HttpClientException { execute(HttpMethod.DELETE, url, parameters); } @@ -86,20 +79,17 @@ public abstract class AbstractHttpClient implements HttpClient { } @Override - public Set options(String url, URLParameter... parameters) - throws HttpClientException { - HttpHeaders headers = execute(HttpMethod.OPTIONS, url, parameters) - .getHeaders(); + public Set options(String url, URLParameter... parameters) throws HttpClientException { + HttpHeaders headers = execute(HttpMethod.OPTIONS, url, parameters).getHeaders(); return headers.getAllow(); } - protected HttpResponse execute(HttpMethod method, String url) - throws HttpClientException { + protected HttpResponse execute(HttpMethod method, String url) throws HttpClientException { return execute(new HttpRequest(method, url)); } - protected HttpResponse execute(HttpMethod method, String url, - URLParameter... parameters) throws HttpClientException { + protected HttpResponse execute(HttpMethod method, String url, URLParameter... parameters) + throws HttpClientException { StringBuilder buf = new StringBuilder(url); if (parameters != null && parameters.length > 0) { if (url.indexOf("?") < 0) { @@ -113,27 +103,20 @@ public abstract class AbstractHttpClient implements HttpClient { } protected boolean hasError(HttpStatus status) { - return (status.series() == HttpStatus.Series.CLIENT_ERROR || status - .series() == HttpStatus.Series.SERVER_ERROR); + return (status.series() == HttpStatus.Series.CLIENT_ERROR || status.series() == HttpStatus.Series.SERVER_ERROR); } - protected void handleResponse(HttpResponse response) - throws HttpClientException { + protected void handleResponse(HttpResponse response) throws HttpClientException { HttpStatus status = response.getStatus(); HttpHeaders headers = response.getHeaders(); - String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE); - boolean jsonResult = contentType != null - && contentType.contains(ContentType.APPLICATION_JSON - .getMimeType()); - if (!jsonResult && hasError(status)) { + MimeType resultType = MimeType.valueOf(headers.getFirst(HttpHeaders.CONTENT_TYPE)); + if (!MimeType.APPLICATION_JSON.includes(resultType) && hasError(status)) { switch (status.series()) { case CLIENT_ERROR: case SERVER_ERROR: - throw new HttpClientException(String.format("%d %s", - status.getStatusCode(), status.getStatusText())); + throw new HttpClientException(String.format("%d %s", status.getStatusCode(), status.getStatusText())); default: - throw new HttpClientException("Unknown status code [" + status - + "]"); + throw new HttpClientException("Unknown status code [" + status + "]"); } } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java index 31a462e2..3de279f2 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/ContentType.java @@ -2,7 +2,6 @@ package com.foxinmy.weixin4j.http; import java.io.Serializable; import java.nio.charset.Charset; -import java.nio.charset.UnsupportedCharsetException; import java.util.List; import java.util.Locale; @@ -21,48 +20,32 @@ 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 IMAGE_JPG = create("image/jpg", - Consts.UTF_8); - public static final ContentType AUDIO_MP3 = create("audio/mp3", - Consts.UTF_8); - public static final ContentType VIDEO_MPEG4 = create("video/mpeg4", - 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 MimeType mimeType; private final Charset charset; + private static final Charset DEFAULT_CHARSET = Consts.UTF_8; - ContentType(final String mimeType, final Charset charset) { + public static final ContentType APPLICATION_FORM_URLENCODED; + public static final ContentType MULTIPART_FORM_DATA; + public static final ContentType DEFAULT_BINARY; + public static final ContentType DEFAULT_TEXT; + + static { + APPLICATION_FORM_URLENCODED = new ContentType(MimeType.APPLICATION_FORM_URLENCODED); + MULTIPART_FORM_DATA = new ContentType(MimeType.MULTIPART_FORM_DATA); + DEFAULT_BINARY = new ContentType(MimeType.APPLICATION_OCTET_STREAM); + DEFAULT_TEXT = new ContentType(MimeType.TEXT_PLAIN); + } + + ContentType(final MimeType mimeType) { + this(mimeType, DEFAULT_CHARSET); + } + + ContentType(final MimeType mimeType, final Charset charset) { this.mimeType = mimeType; this.charset = charset; } - public String getMimeType() { + public MimeType getMimeType() { return this.mimeType; } @@ -73,7 +56,7 @@ public final class ContentType implements Serializable { @Override public String toString() { StringBuilder buf = new StringBuilder(); - buf.append(this.mimeType); + buf.append(this.mimeType.toString()); if (this.charset != null) { buf.append("; charset="); buf.append(this.charset.name()); @@ -102,8 +85,18 @@ public final class ContentType implements Serializable { return true; } - public static ContentType create(final String mimeType, - final Charset charset) { + public static ContentType create(final MimeType mimeType, final Charset charset) { + if (mimeType == null) { + throw new IllegalArgumentException("MIME type may not be null"); + } + return new ContentType(mimeType, charset); + } + + public static ContentType create(final String mimeType) { + return create(MimeType.valueOf(mimeType), (Charset) null); + } + + public static ContentType create(final String mimeType, final Charset charset) { if (mimeType == null) { throw new IllegalArgumentException("MIME type may not be null"); } @@ -112,21 +105,8 @@ public final class ContentType implements Serializable { throw new IllegalArgumentException("MIME type may not be empty"); } if (!valid(type)) { - throw new IllegalArgumentException( - "MIME type may not contain reserved characters"); + 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); + return new ContentType(MimeType.valueOf(type), charset); } } \ No newline at end of file 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 a758e181..63110ee3 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 @@ -25,7 +25,6 @@ public interface HttpResponse extends HttpMessage { * @return */ HttpStatus getStatus(); - /** * 响应内容 * diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/MimeType.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/MimeType.java new file mode 100644 index 00000000..3e47e7f7 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/MimeType.java @@ -0,0 +1,160 @@ +package com.foxinmy.weixin4j.http; + +import java.io.Serializable; +import java.util.Locale; + +import com.foxinmy.weixin4j.util.StringUtil; + +/** + * MIME type + * + * @className MimeType + * @author jinyu + * @date Jul 20, 2016 + * @since JDK 1.8 + */ +public class MimeType implements Serializable { + + private static final long serialVersionUID = 4430596628682058362L; + + private static final String WILDCARD_TYPE = "*"; + + private final String type; + private final String subType; + + public static final MimeType APPLICATION_FORM_URLENCODED; + public static final MimeType APPLICATION_JSON; + public static final MimeType APPLICATION_OCTET_STREAM; + public static final MimeType APPLICATION_XML; + public static final MimeType MULTIPART_FORM_DATA; + public static final MimeType TEXT_HTML; + public static final MimeType TEXT_PLAIN; + public static final MimeType IMAGE_JPG; + public static final MimeType AUDIO_MP3; + public static final MimeType VIDEO_MPEG4; + public static final MimeType TEXT_XML; + + static { + APPLICATION_FORM_URLENCODED = valueOf("application/x-www-form-urlencoded"); + APPLICATION_JSON = valueOf("application/json"); + APPLICATION_OCTET_STREAM = valueOf("application/octet-stream"); + APPLICATION_XML = valueOf("application/xml"); + MULTIPART_FORM_DATA = MimeType.valueOf("multipart/form-data"); + TEXT_HTML = valueOf("text/html"); + TEXT_PLAIN = valueOf("text/plain"); + IMAGE_JPG = valueOf("image/jpg"); + AUDIO_MP3 = valueOf("audio/mp3"); + VIDEO_MPEG4 = valueOf("video/mpeg4"); + TEXT_XML = valueOf("text/xml"); + } + + public MimeType(String type) { + this(type, WILDCARD_TYPE); + } + + public MimeType(String type, String subType) { + this.type = type.toLowerCase(Locale.ENGLISH); + this.subType = subType.toLowerCase(Locale.ENGLISH); + } + + public String getType() { + return type; + } + + public String getSubType() { + return subType; + } + + public boolean isWildcardType() { + return WILDCARD_TYPE.equals(getType()); + } + + public boolean isWildcardSubtype() { + return WILDCARD_TYPE.equals(getSubType()) || getSubType().startsWith("*+"); + } + + public static MimeType valueOf(String value) { + if (StringUtil.isBlank(value)) { + return null; + } + String mimeType = StringUtil.tokenizeToStringArray(value, ";")[0].trim().toLowerCase(Locale.ENGLISH); + if (WILDCARD_TYPE.equals(mimeType)) { + mimeType = "*/*"; + } + int subIndex = mimeType.indexOf('/'); + if (subIndex == -1) { + throw new IllegalArgumentException(mimeType + ":does not contain '/'"); + } + if (subIndex == mimeType.length() - 1) { + throw new IllegalArgumentException(mimeType + ":does not contain subtype after '/'"); + } + String type = mimeType.substring(0, subIndex); + String subType = mimeType.substring(subIndex + 1, mimeType.length()); + if (WILDCARD_TYPE.equals(type) && !WILDCARD_TYPE.equals(subType)) { + throw new IllegalArgumentException(mimeType + ":wildcard type is legal only in '*/*' (all mime types)"); + } + return new MimeType(type, subType); + } + + /** + * reference of apache Spring Web + */ + public boolean includes(MimeType other) { + if (other == null) { + return false; + } + if (this.isWildcardType()) { + // */* includes anything + return true; + } else if (getType().equals(other.getType())) { + if (getSubType().equals(other.getSubType())) { + return true; + } + if (this.isWildcardSubtype()) { + // wildcard with suffix, e.g. application/*+xml + int thisPlusIdx = getSubType().indexOf('+'); + if (thisPlusIdx == -1) { + return true; + } else { + // application/*+xml includes application/soap+xml + int otherPlusIdx = other.getSubType().indexOf('+'); + if (otherPlusIdx != -1) { + String thisSubtypeNoSuffix = getSubType().substring(0, thisPlusIdx); + String thisSubtypeSuffix = getSubType().substring(thisPlusIdx + 1); + String otherSubtypeSuffix = other.getSubType().substring(otherPlusIdx + 1); + if (thisSubtypeSuffix.equals(otherSubtypeSuffix) && WILDCARD_TYPE.equals(thisSubtypeNoSuffix)) { + return true; + } + } + } + } + } + return false; + } + + @Override + public String toString() { + return String.format("%s/%s", this.type, this.subType); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof MimeType)) { + return false; + } + MimeType otherType = (MimeType) other; + return this.type.equalsIgnoreCase(otherType.type) && this.subType.equalsIgnoreCase(otherType.subType); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((subType == null) ? 0 : subType.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + return result; + } +} 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 203d2b18..9ea2cd23 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 @@ -148,7 +148,7 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient { } if (httpEntity.getContentType() != null) { connection.setRequestProperty(HttpHeaders.CONTENT_TYPE, - httpEntity.getContentType().getMimeType()); + httpEntity.getContentType().toString()); } logger.debug("entity >> " + httpEntity.getContentType() + "(" + httpEntity.getContentLength() + "byte)"); 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 index e80130b1..5f9c831d 100644 --- 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 @@ -137,6 +137,7 @@ public class MultipartEntity implements HttpEntity { return !isRepeatable(); } + @Override public long getContentLength() { if (this.dirty) { this.length = this.multipart.getTotalLength(); @@ -145,6 +146,7 @@ public class MultipartEntity implements HttpEntity { return this.length; } + @Override public ContentType getContentType() { return ContentType.MULTIPART_FORM_DATA; } @@ -157,12 +159,14 @@ public class MultipartEntity implements HttpEntity { } } + @Override public InputStream getContent() throws IOException, UnsupportedOperationException { throw new UnsupportedOperationException( "Multipart form entity does not implement #getContent()"); } + @Override public void writeTo(final OutputStream outstream) throws IOException { this.multipart.writeTo(outstream); outstream.flush(); diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/AbstractMessageConverter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/AbstractMessageConverter.java new file mode 100644 index 00000000..1d56a243 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/AbstractMessageConverter.java @@ -0,0 +1,95 @@ +package com.foxinmy.weixin4j.http.message; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.MimeType; +import com.foxinmy.weixin4j.model.Consts; + +public abstract class AbstractMessageConverter implements MessageConverter { + + protected Charset charset = Consts.UTF_8; + + private List supportedMimeTypes; + + protected AbstractMessageConverter() { + this.supportedMimeTypes = Collections.emptyList(); + } + + protected AbstractMessageConverter(MimeType supportedMimeType) { + setSupportedMediaTypes(Collections.singletonList(supportedMimeType)); + } + + protected AbstractMessageConverter(MimeType... supportedMimeTypes) { + setSupportedMediaTypes(Arrays.asList(supportedMimeTypes)); + } + + public void setSupportedMediaTypes(List supportedMimeTypes) { + this.supportedMimeTypes = new ArrayList(supportedMimeTypes); + } + + public Charset getCharset() { + return charset; + } + + public void setCharset(Charset charset) { + this.charset = charset; + } + + @Override + public List supportedMimeTypes() { + return Collections.unmodifiableList(this.supportedMimeTypes); + } + + @Override + public boolean canConvert(Class clazz, HttpResponse response) { + MimeType mimeType = MimeType.valueOf(response.getHeaders().getContentType()); + byte[] content = response.getContent(); + return supports(clazz, mimeType) || supports(clazz, content); + } + + /** + * 满足其中一个supports + * + * @param clazz + * 转换类型 + * @param mimeType + * 媒体类型 + * @return 支持标识 + */ + protected boolean supports(Class clazz, MimeType mimeType) { + if (mimeType == null) { + return true; + } + for (MimeType supportedMediaType : supportedMimeTypes()) { + if (supportedMediaType.includes(mimeType)) { + return true; + } + } + return false; + } + + /** + * 满足其中一个supports + * + * @param clazz + * 转换类型 + * @param content + * 内容数据 + * @return 支持标识 + */ + protected abstract boolean supports(Class clazz, byte[] content); + + @Override + public T convert(Class clazz, HttpResponse response) throws IOException { + return convertInternal(clazz, response.getBody()); + } + + protected abstract T convertInternal(Class clazz, InputStream body) throws IOException; +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/ApiResult.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/ApiResult.java new file mode 100644 index 00000000..2a0955b3 --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/ApiResult.java @@ -0,0 +1,74 @@ +package com.foxinmy.weixin4j.http.message; + +import java.io.Serializable; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +import com.alibaba.fastjson.annotation.JSONField; + +/** + * 调用接口的返回 + * + * @className ApiResult + * @author jinyu(foxinmy@gmail.com) + * @date 2014年9月24日 + * @since JDK 1.6 + * @see 公众平台全局返回码说明 + * @see 企业号全局返回码说明 + */ +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ApiResult implements Serializable { + + private static final long serialVersionUID = -6185313616955051150L; + + /** + * 调用接口返回码,通信标识 + */ + @XmlElement(name = "return_code") + @JSONField(name = "errcode") + private String returnCode; + + /** + * 调用接口返回消息,如非 空,为错误原因 可能为空 + */ + @XmlElement(name = "return_msg") + @JSONField(name = "errmsg") + private String returnMsg; + + public ApiResult() { + this.returnCode = "0"; + this.returnMsg = "OK"; + } + + public ApiResult(String returnCode, String returnMsg) { + this.returnCode = returnCode; + this.returnMsg = returnMsg; + } + + public String getReturnCode() { + return returnCode; + } + + public String getReturnMsg() { + return returnMsg; + } + + public void setReturnCode(String returnCode) { + this.returnCode = returnCode; + } + + public void setReturnMsg(String returnMsg) { + this.returnMsg = returnMsg; + } + + @Override + public String toString() { + return "returnCode=" + returnCode + ", returnMsg=" + returnMsg; + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/JsonMessageConverter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/JsonMessageConverter.java new file mode 100644 index 00000000..02e2176e --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/JsonMessageConverter.java @@ -0,0 +1,55 @@ +package com.foxinmy.weixin4j.http.message; + +import java.io.IOException; +import java.io.InputStream; + +import com.alibaba.fastjson.JSON; +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.MimeType; +import com.foxinmy.weixin4j.util.FileUtil; +import com.foxinmy.weixin4j.util.IOUtil; +import com.foxinmy.weixin4j.util.RegexUtil; + +/** + * JSON 转换 + * + * @className JsonMessageConverter + * @author jinyu + * @date Jul 20, 2016 + * @since JDK 1.8 + */ +public class JsonMessageConverter extends AbstractMessageConverter { + + public static final JsonMessageConverter GLOBAL = new JsonMessageConverter(); + + private static final String JSO = "json"; + private static final int BRACE = 1 << '{'; + private static final int BRACKET = 1 << '['; + private static final int MASK = BRACE | BRACKET; + + public JsonMessageConverter() { + super(MimeType.APPLICATION_JSON, new MimeType("application", "*+json")); + } + + @Override + public boolean canConvert(Class clazz, HttpResponse response) { + if (!super.canConvert(clazz, response)) { + String disposition = response.getHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION); + String fileName = RegexUtil.regexFileNameFromContentDispositionHeader(disposition); + return (fileName != null && FileUtil.getFileExtension(fileName).equalsIgnoreCase(JSO)); + } + return true; + } + + @Override + protected boolean supports(Class clazz, byte[] content) { + return (MASK & (1 << content[0])) != 0; + } + + @Override + protected T convertInternal(Class clazz, InputStream body) throws IOException { + byte[] bytes = IOUtil.toByteArray(body); + return JSON.parseObject(bytes, 0, bytes.length, charset.newDecoder(), clazz); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/MessageConverter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/MessageConverter.java new file mode 100644 index 00000000..4fdcfb7e --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/MessageConverter.java @@ -0,0 +1,48 @@ +package com.foxinmy.weixin4j.http.message; + +import java.io.IOException; +import java.util.List; + +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.MimeType; + +/** + * 消息转换接口 + * + * @className MessageConverter + * @author jinyu + * @date Jul 20, 2016 + * @since JDK 1.8 + * @see + */ +public interface MessageConverter { + /** + * 获取可以转换的媒体类型 + * + * @return 媒体列表 + */ + public List supportedMimeTypes(); + + /** + * 是否可以转换 + * + * @param clazz + * 转换类型 + * @param response + * 响应对象 + * @return 是否标识 + */ + public boolean canConvert(Class clazz, HttpResponse response); + + /** + * 转换消息 + * + * @param clazz + * 转换类型 + * @param response + * 响应对象 + * @throws IOException + * @return 消息对象 + */ + public T convert(Class clazz, HttpResponse response) throws IOException; +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/XmlMessageConverter.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/XmlMessageConverter.java new file mode 100644 index 00000000..083aff3b --- /dev/null +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/XmlMessageConverter.java @@ -0,0 +1,51 @@ +package com.foxinmy.weixin4j.http.message; + +import java.io.IOException; +import java.io.InputStream; + +import com.foxinmy.weixin4j.http.HttpHeaders; +import com.foxinmy.weixin4j.http.HttpResponse; +import com.foxinmy.weixin4j.http.MimeType; +import com.foxinmy.weixin4j.util.FileUtil; +import com.foxinmy.weixin4j.util.RegexUtil; +import com.foxinmy.weixin4j.xml.XmlStream; + +/** + * XML 转换 + * + * @className XmlMessageConverter + * @author jinyu + * @date Jul 20, 2016 + * @since JDK 1.8 + */ +public class XmlMessageConverter extends AbstractMessageConverter { + + public static final XmlMessageConverter GLOBAL = new XmlMessageConverter(); + + private static final String XML = "xml"; + private static final int BRACKET = '<'; + + public XmlMessageConverter() { + super(MimeType.APPLICATION_XML, MimeType.TEXT_XML, new MimeType("application", "*+xml")); + } + + @Override + public boolean canConvert(Class clazz, HttpResponse response) { + if (!super.canConvert(clazz, response)) { + String disposition = response.getHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION); + String fileName = RegexUtil.regexFileNameFromContentDispositionHeader(disposition); + return (fileName != null && FileUtil.getFileExtension(fileName).equalsIgnoreCase(XML)); + } + return true; + } + + @Override + protected boolean supports(Class clazz, byte[] content) { + return BRACKET == content[0]; + } + + @Override + protected T convertInternal(Class clazz, InputStream body) throws IOException { + return XmlStream.fromXML(body, clazz); + } +} diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/XmlResult.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/XmlResult.java similarity index 56% rename from weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/XmlResult.java rename to weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/XmlResult.java index d6a69c8e..87a9775d 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/XmlResult.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/message/XmlResult.java @@ -1,6 +1,4 @@ -package com.foxinmy.weixin4j.http.weixin; - -import java.io.Serializable; +package com.foxinmy.weixin4j.http.message; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -20,22 +18,10 @@ import com.alibaba.fastjson.annotation.JSONField; */ @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) -public class XmlResult implements Serializable { +public class XmlResult extends ApiResult { private static final long serialVersionUID = -6185313616955051150L; - - /** - * 此字段是通信标识,非交易 标识,交易是否成功需要查 看 result_code 来判断非空 - */ - @XmlElement(name = "return_code") - @JSONField(name = "return_code") - private String returnCode; - /** - * 返回信息,如非 空,为错误原因 可能为空 - */ - @XmlElement(name = "return_msg") - @JSONField(name = "return_msg") - private String returnMsg; + /** * 业务结果SUCCESS/FAIL 非空 */ @@ -55,20 +41,11 @@ public class XmlResult implements Serializable { @JSONField(name = "err_code_des") private String errCodeDes; - public XmlResult() { + protected XmlResult() { } public XmlResult(String returnCode, String returnMsg) { - this.returnCode = returnCode; - this.returnMsg = returnMsg; - } - - public String getReturnCode() { - return returnCode; - } - - public String getReturnMsg() { - return returnMsg; + super(returnCode, returnMsg); } public String getResultCode() { @@ -83,14 +60,6 @@ public class XmlResult implements Serializable { return errCodeDes; } - public void setReturnCode(String returnCode) { - this.returnCode = returnCode; - } - - public void setReturnMsg(String returnMsg) { - this.returnMsg = returnMsg; - } - public void setResultCode(String resultCode) { this.resultCode = resultCode; } @@ -105,8 +74,6 @@ public class XmlResult implements Serializable { @Override public String toString() { - return "returnCode=" + returnCode + ", returnMsg=" + returnMsg - + ", resultCode=" + resultCode + ", errCode=" + errCode - + ", errCodeDes=" + errCodeDes; + return super.toString() + ", resultCode=" + resultCode + ", errCode=" + errCode + ", errCodeDes=" + errCodeDes; } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/JsonResult.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/JsonResult.java deleted file mode 100644 index c1c656fd..00000000 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/JsonResult.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.foxinmy.weixin4j.http.weixin; - -import java.io.Serializable; - -import com.alibaba.fastjson.annotation.JSONField; - -/** - * 调用接口返回JSON格式 - * - * @className JsonResult - * @author jinyu(foxinmy@gmail.com) - * @date 2014年9月24日 - * @since JDK 1.6 - * @see 公众平台全局返回码说明 - * @see 企业号全局返回码说明 - */ -public class JsonResult implements Serializable { - - private static final long serialVersionUID = -6185313616955051150L; - - @JSONField(name = "errcode") - private int code; - @JSONField(name = "errmsg") - private String desc; - private String text; - - public JsonResult() { - this.desc = ""; - this.text = ""; - } - - public JsonResult(int code, String desc, String text) { - this.code = code; - this.desc = desc; - this.text = text; - } - - public int getCode() { - return code; - } - - public void setCode(int code) { - this.code = code; - } - - public String getDesc() { - return desc; - } - - public void setDesc(String desc) { - this.desc = desc; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - - @Override - public String toString() { - return "JsonResult [code=" + code + ", desc=" + desc + ", text=" + text - + "]"; - } -} 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 3ed5762d..fdb6ffdf 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 @@ -1,13 +1,11 @@ package com.foxinmy.weixin4j.http.weixin; +import java.io.IOException; import java.util.Map; -import com.alibaba.fastjson.JSONException; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.ContentType; import com.foxinmy.weixin4j.http.HttpClient; import com.foxinmy.weixin4j.http.HttpClientException; -import com.foxinmy.weixin4j.http.HttpHeaders; import com.foxinmy.weixin4j.http.HttpMethod; import com.foxinmy.weixin4j.http.HttpParams; import com.foxinmy.weixin4j.http.HttpRequest; @@ -19,10 +17,12 @@ 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.message.ApiResult; +import com.foxinmy.weixin4j.http.message.XmlMessageConverter; +import com.foxinmy.weixin4j.http.message.XmlResult; import com.foxinmy.weixin4j.logging.InternalLogger; import com.foxinmy.weixin4j.logging.InternalLoggerFactory; import com.foxinmy.weixin4j.model.Consts; -import com.foxinmy.weixin4j.xml.XmlStream; /** * 负责微信请求的执行 @@ -35,8 +35,9 @@ import com.foxinmy.weixin4j.xml.XmlStream; */ public class WeixinRequestExecutor { - protected final InternalLogger logger = InternalLoggerFactory - .getInstance(getClass()); + protected final InternalLogger logger = InternalLoggerFactory.getInstance(getClass()); + + private static final String SUCCESS_CODE = ",0,success,"; protected final HttpClient httpClient; protected final HttpParams httpParams; @@ -55,8 +56,7 @@ public class WeixinRequestExecutor { return doRequest(request); } - public WeixinResponse get(String url, Map parameters) - throws WeixinException { + public WeixinResponse get(String url, Map parameters) throws WeixinException { StringBuilder buf = new StringBuilder(url); if (parameters != null && !parameters.isEmpty()) { if (url.indexOf("?") < 0) { @@ -81,10 +81,8 @@ public class WeixinRequestExecutor { return doRequest(request); } - public WeixinResponse post(String url, FormBodyPart... bodyParts) - throws WeixinException { - MultipartEntity entity = new MultipartEntity( - HttpMultipartMode.BROWSER_COMPATIBLE, null, Consts.UTF_8); + public WeixinResponse post(String url, FormBodyPart... bodyParts) throws WeixinException { + MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, Consts.UTF_8); for (FormBodyPart bodyPart : bodyParts) { entity.addPart(bodyPart); } @@ -93,85 +91,35 @@ public class WeixinRequestExecutor { return doRequest(request); } - protected WeixinResponse doRequest(HttpRequest request) - throws WeixinException { + protected WeixinResponse doRequest(HttpRequest request) throws WeixinException { request.setParams(httpParams); try { - logger.info("weixin request >> " + request.getMethod() + " " - + request.getURI().toString()); + logger.info("weixin request >> " + request.getMethod() + " " + request.getURI().toString()); HttpResponse httpResponse = httpClient.execute(request); - HttpHeaders headers = httpResponse.getHeaders(); WeixinResponse response = new WeixinResponse(httpResponse); - logger.info("weixin response << " + httpResponse.getProtocol() - + httpResponse.getStatus() + ":" + response.getAsString()); - String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE); - String disposition = headers - .getFirst(HttpHeaders.CONTENT_DISPOSITION); - // json - if (contentType - .contains(ContentType.APPLICATION_JSON.getMimeType()) - || (disposition != null && disposition.indexOf(".json") > 0)) { - checkJson(response); - } else if (contentType.contains(ContentType.TEXT_XML.getMimeType())) { - checkXml(response); - } else if (contentType.contains(ContentType.TEXT_PLAIN - .getMimeType()) - || contentType - .contains(ContentType.TEXT_HTML.getMimeType())) { - try { - checkJson(response); - return response; - } catch (JSONException e) { - ; - } - try { - checkXml(response); - return response; - } catch (IllegalArgumentException ex) { - ; - } - throw new WeixinException(response.getAsString()); - } + logger.info("weixin response << " + httpResponse.getProtocol() + httpResponse.getStatus() + ":" + + response.getAsString()); + handlResponse(response); return response; } catch (HttpClientException e) { throw new WeixinException(e); } } - protected void checkJson(WeixinResponse response) throws WeixinException { - JsonResult jsonResult = response.getAsJsonResult(); - response.setJsonResult(true); - if (jsonResult.getCode() != 0) { - throw new WeixinException(Integer.toString(jsonResult.getCode()), - jsonResult.getDesc()); + protected void handlResponse(WeixinResponse response) throws WeixinException { + ApiResult result = response.getAsResult(); + if (!SUCCESS_CODE.contains(String.format(",%s,", result.getReturnCode().toLowerCase()))) { + throw new WeixinException(result.getReturnCode(), result.getReturnMsg()); } - } - - protected void checkXml(WeixinResponse response) throws WeixinException { - String xmlContent = response.getAsString(); - if (xmlContent.length() != xmlContent.replaceFirst("", - "").length()) { - // - xmlContent = xmlContent.replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", "") - .replaceFirst("", ""); - } - XmlResult xmlResult = XmlStream.fromXML(xmlContent, XmlResult.class); - response.setText(xmlContent); - response.setXmlResult(true); - if ("0".equals(xmlResult.getReturnCode())) { - return; - } - if (!Consts.SUCCESS.equalsIgnoreCase(xmlResult.getReturnCode())) { - throw new WeixinException(xmlResult.getReturnCode(), - xmlResult.getReturnMsg()); - } - if (!Consts.SUCCESS.equalsIgnoreCase(xmlResult.getResultCode())) { - throw new WeixinException(xmlResult.getErrCode(), - xmlResult.getErrCodeDes()); + if (XmlMessageConverter.GLOBAL.canConvert(XmlResult.class, response)) { + try { + XmlResult xmlResult = XmlMessageConverter.GLOBAL.convert(XmlResult.class, response); + if (!SUCCESS_CODE.contains(xmlResult.getResultCode())) { + throw new WeixinException(xmlResult.getErrCode(), xmlResult.getErrCodeDes()); + } + } catch (IOException e) { + ; + } } } 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 f9d93e92..7006e7ff 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/http/weixin/WeixinResponse.java @@ -1,6 +1,9 @@ package com.foxinmy.weixin4j.http.weixin; +import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; @@ -8,73 +11,102 @@ import com.alibaba.fastjson.TypeReference; import com.foxinmy.weixin4j.http.HttpHeaders; import com.foxinmy.weixin4j.http.HttpResponse; import com.foxinmy.weixin4j.http.HttpStatus; +import com.foxinmy.weixin4j.http.HttpVersion; +import com.foxinmy.weixin4j.http.message.ApiResult; +import com.foxinmy.weixin4j.http.message.JsonMessageConverter; +import com.foxinmy.weixin4j.http.message.MessageConverter; +import com.foxinmy.weixin4j.http.message.XmlMessageConverter; +import com.foxinmy.weixin4j.http.message.XmlResult; import com.foxinmy.weixin4j.util.StringUtil; -import com.foxinmy.weixin4j.xml.XmlStream; -public class WeixinResponse { +/** + * 调用微信接口响应 + * + * @className WeixinResponse + * @author jinyu + * @date Jul 21, 2016 + * @since JDK 1.6 + */ +public class WeixinResponse implements HttpResponse { - private boolean isJsonResult; - private boolean isXmlResult; private volatile String text; - private final HttpResponse response; + private static List messageConverters = new ArrayList(); + private final TypeReference APIRESULT_CLAZZ = new TypeReference() { + }; + private final TypeReference XMLRESULT_CLAZZ = new TypeReference() { + }; + + static { + messageConverters.add(new JsonMessageConverter()); + messageConverters.add(new XmlMessageConverter()); + } public WeixinResponse(HttpResponse response) { this.response = response; } - 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(response.getContent()); + text = StringUtil.newStringUtf8(getContent()); } return text; } - public void setText(String text) { - this.text = text; - } - - public JsonResult getAsJsonResult() { - return JSON.parseObject(getAsString(), JsonResult.class); + public ApiResult getAsResult() { + return getAsObject(APIRESULT_CLAZZ); } public JSONObject getAsJson() { return JSON.parseObject(getAsString()); } + public XmlResult getAsXml() { + return getAsObject(XMLRESULT_CLAZZ); + } + + @SuppressWarnings("unchecked") public T getAsObject(TypeReference typeReference) { - if (isJsonResult) { - return JSON.parseObject(getAsString(), typeReference); + Class clazz = (Class) typeReference.getType(); + for (MessageConverter messageConverter : messageConverters) { + if (messageConverter.canConvert(clazz, response)) { + try { + return messageConverter.convert(clazz, response); + } catch (IOException e) { + throw new RuntimeException("IO error on convert to " + typeReference, e); + } + } } - if (isXmlResult) { - @SuppressWarnings("unchecked") - Class clazz = (Class) typeReference.getType(); - return XmlStream.fromXML(getAsString(), clazz); - } - return null; - } - - public XmlResult getAsXmlResult() { - return XmlStream.fromXML(getAsString(), XmlResult.class); + throw new RuntimeException("cannot convert to " + typeReference); } + @Override public HttpHeaders getHeaders() { return response.getHeaders(); } + @Override public HttpStatus getStatus() { return response.getStatus(); } + @Override + public byte[] getContent() { + return response.getContent(); + } + + @Override public InputStream getBody() { return response.getBody(); } + + @Override + public HttpVersion getProtocol() { + return response.getProtocol(); + } + + @Override + public void close() { + response.close(); + } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java index dd78b2fa..80ef4a16 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/WeixinPayProxy.java @@ -12,7 +12,7 @@ import com.foxinmy.weixin4j.api.CouponApi; import com.foxinmy.weixin4j.api.CustomsApi; import com.foxinmy.weixin4j.api.PayApi; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.weixin.XmlResult; +import com.foxinmy.weixin4j.http.message.XmlResult; import com.foxinmy.weixin4j.model.Pageable; import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.payment.coupon.CouponDetail; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/MerchantResult.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/MerchantResult.java index 102461e7..d6ce958d 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/MerchantResult.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/MerchantResult.java @@ -6,7 +6,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import com.alibaba.fastjson.annotation.JSONField; -import com.foxinmy.weixin4j.http.weixin.XmlResult; +import com.foxinmy.weixin4j.http.message.XmlResult; import com.foxinmy.weixin4j.type.SignType; /** diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/RedpacketRecord.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/RedpacketRecord.java index de1c35a7..ea5e699e 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/RedpacketRecord.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/payment/mch/RedpacketRecord.java @@ -11,7 +11,7 @@ import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import com.alibaba.fastjson.annotation.JSONField; -import com.foxinmy.weixin4j.http.weixin.XmlResult; +import com.foxinmy.weixin4j.http.message.XmlResult; import com.foxinmy.weixin4j.type.RedpacketSendType; import com.foxinmy.weixin4j.type.RedpacketStatus; import com.foxinmy.weixin4j.type.RedpacketType; diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/MediaType.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/MediaType.java index 8441d562..12344ddb 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/MediaType.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/type/MediaType.java @@ -1,19 +1,22 @@ package com.foxinmy.weixin4j.type; -import com.foxinmy.weixin4j.http.ContentType; +import com.foxinmy.weixin4j.http.MimeType; /** * 上传的媒体类型
*

- * 公众平台上传限制:
图片(image): 2MB,支持bmp/png/jpeg/jpg/gif格式
+ * 公众平台上传限制:
+ * 图片(image): 2MB,支持bmp/png/jpeg/jpg/gif格式
* 语音(voice):2MB,播放长度不超过60s,支持mp3/wma/wav/amr格式
* 视频(video):10MB,支持rm/rmvb/wmv/avi/mpg/mpeg/mp4格式
* 缩略图(thumb):64KB,支持JPG格式
*

*

- * 企业号上传限制:
图片(image):1MB,支持bmp/png/jpeg/jpg/gif格式
+ * 企业号上传限制:
+ * 图片(image):1MB,支持bmp/png/jpeg/jpg/gif格式
* 语音(voice):2MB,播放长度不超过60s,支持mp3/wma/wav/amr格式
- * 视频(video):10MB,支持rm/rmvb/wmv/avi/mpg/mpeg/mp4格式
普通文件(file):20MB
+ * 视频(video):10MB,支持rm/rmvb/wmv/avi/mpg/mpeg/mp4格式
+ * 普通文件(file):20MB
*

*

* 临时媒体文件在后台保存时间为3天,即3天后media_id失效 @@ -24,18 +27,16 @@ import com.foxinmy.weixin4j.http.ContentType; * @since JDK 1.6 */ public enum MediaType { - image(ContentType.IMAGE_JPG), voice(ContentType.AUDIO_MP3), video( - ContentType.VIDEO_MPEG4), thumb(ContentType.IMAGE_JPG), file( - ContentType.MULTIPART_FORM_DATA), news( - ContentType.MULTIPART_FORM_DATA); + image(MimeType.IMAGE_JPG), voice(MimeType.AUDIO_MP3), video(MimeType.VIDEO_MPEG4), thumb(MimeType.IMAGE_JPG), file( + MimeType.MULTIPART_FORM_DATA), news(MimeType.MULTIPART_FORM_DATA); - MediaType(ContentType contentType) { - this.contentType = contentType; + MediaType(MimeType mimeType) { + this.mimeType = mimeType; } - private ContentType contentType; + private MimeType mimeType; - public ContentType getContentType() { - return contentType; + public MimeType getMimeType() { + return mimeType; } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java index 83637076..d2c06327 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/util/StringUtil.java @@ -1,7 +1,10 @@ package com.foxinmy.weixin4j.util; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; import com.foxinmy.weixin4j.model.Consts; @@ -62,9 +65,7 @@ public final class StringUtil { return str; } - return new StringBuilder(strLen) - .append(Character.toLowerCase(firstChar)) - .append(str.substring(1)).toString(); + return new StringBuilder(strLen).append(Character.toLowerCase(firstChar)).append(str.substring(1)).toString(); } public static String capitalize(final String str) { @@ -78,13 +79,10 @@ public final class StringUtil { // already capitalized return str; } - return new StringBuilder(strLen) - .append(Character.toTitleCase(firstChar)) - .append(str.substring(1)).toString(); + return new StringBuilder(strLen).append(Character.toTitleCase(firstChar)).append(str.substring(1)).toString(); } - public static String substringBefore(final String str, - final String separator) { + public static String substringBefore(final String str, final String separator) { if (isEmpty(str) || separator == null) { return str; } @@ -119,8 +117,7 @@ public final class StringUtil { return join(array, separator, 0, array.length); } - public static String join(final Object[] array, final char separator, - final int startIndex, final int endIndex) { + public static String join(final Object[] array, final char separator, final int startIndex, final int endIndex) { if (array == null) { return null; } @@ -187,8 +184,7 @@ public final class StringUtil { return join(array, separator, 0, array.length); } - public static String join(final int[] array, final char separator, - final int startIndex, final int endIndex) { + public static String join(final int[] array, final char separator, final int startIndex, final int endIndex) { if (array == null) { return null; } @@ -234,4 +230,28 @@ public final class StringUtil { return clazz.getName(); } } + + public static String[] tokenizeToStringArray(String str, String delimiters) { + return tokenizeToStringArray(str, delimiters, true, true); + } + + public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, + boolean ignoreEmptyTokens) { + + if (str == null) { + return null; + } + StringTokenizer st = new StringTokenizer(str, delimiters); + List tokens = new ArrayList(); + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (trimTokens) { + token = token.trim(); + } + if (!ignoreEmptyTokens || token.length() > 0) { + tokens.add(token); + } + } + return tokens.toArray(new String[tokens.size()]); + } } diff --git a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/xml/XmlStream.java b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/xml/XmlStream.java index fb5edc6e..97d957d7 100644 --- a/weixin4j-base/src/main/java/com/foxinmy/weixin4j/xml/XmlStream.java +++ b/weixin4j-base/src/main/java/com/foxinmy/weixin4j/xml/XmlStream.java @@ -69,7 +69,7 @@ public final class XmlStream { unmarshaller = jaxbContext.createUnmarshaller(); messageUnmarshaller.put(clazz, unmarshaller); } catch (JAXBException e) { - throw new IllegalArgumentException(e); + throw new RuntimeException(e); } } try { @@ -83,7 +83,7 @@ public final class XmlStream { return (T) unmarshaller.unmarshal(source); } } catch (Exception e) { - throw new IllegalArgumentException(e); + throw new RuntimeException(e); } finally { if (content != null) { try { diff --git a/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/PayTest.java b/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/PayTest.java index 6adfc964..997cf3ec 100644 --- a/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/PayTest.java +++ b/weixin4j-base/src/test/java/com/foxinmy/weixin4j/base/test/PayTest.java @@ -11,7 +11,7 @@ import org.junit.Test; import com.foxinmy.weixin4j.exception.WeixinException; import com.foxinmy.weixin4j.exception.WeixinPayException; -import com.foxinmy.weixin4j.http.weixin.XmlResult; +import com.foxinmy.weixin4j.http.message.XmlResult; import com.foxinmy.weixin4j.model.WeixinPayAccount; import com.foxinmy.weixin4j.payment.WeixinPayProxy; import com.foxinmy.weixin4j.payment.mch.MchPayPackage; @@ -45,7 +45,7 @@ public class PayTest { static { ACCOUNT = new WeixinPayAccount("appid", "paysignkey", "mchid"); SIGNATURE = new WeixinPaymentSignature(ACCOUNT.getPaySignKey()); - PAY = new WeixinPayProxy(new Weixin4jSettings(ACCOUNT)); + PAY = new WeixinPayProxy(new Weixin4jSettings(ACCOUNT)); } /** * 商户证书文件 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 09465220..061665b8 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 @@ -5,7 +5,7 @@ import java.util.Date; import java.util.List; import com.foxinmy.weixin4j.exception.WeixinException; -import com.foxinmy.weixin4j.http.weixin.JsonResult; +import com.foxinmy.weixin4j.http.message.ApiResult; import com.foxinmy.weixin4j.model.Button; import com.foxinmy.weixin4j.model.MediaCounter; import com.foxinmy.weixin4j.model.MediaDownloadResult; @@ -370,7 +370,7 @@ public class WeixinProxy { * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738732&token=&lang=zh_CN"> * 更新永久图文素材 */ - public JsonResult updateMaterialArticle(String mediaId, int index, + public ApiResult updateMaterialArticle(String mediaId, int index, MpArticle article) throws WeixinException { return mediaApi.updateMaterialArticle(mediaId, index, article); } @@ -387,7 +387,7 @@ public class WeixinProxy { * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738731&token=&lang=zh_CN"> * 删除永久媒体素材 */ - public JsonResult deleteMaterialMedia(String mediaId) + public ApiResult deleteMaterialMedia(String mediaId) throws WeixinException { return mediaApi.deleteMaterialMedia(mediaId); } @@ -478,7 +478,7 @@ public class WeixinProxy { * @see {@link #sendNotify(NotifyMessage,String) } * @throws WeixinException */ - public JsonResult sendNotify(NotifyMessage notify) throws WeixinException { + public ApiResult sendNotify(NotifyMessage notify) throws WeixinException { return notifyApi.sendNotify(notify); } @@ -502,7 +502,7 @@ public class WeixinProxy { * @see com.foxinmy.weixin4j.tuple.News * @see com.foxinmy.weixin4j.mp.api.NotifyApi */ - public JsonResult sendNotify(NotifyMessage notify, String kfAccount) + public ApiResult sendNotify(NotifyMessage notify, String kfAccount) throws WeixinException { return notifyApi.sendNotify(notify, kfAccount); } @@ -568,7 +568,7 @@ public class WeixinProxy { * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN"> * 新增客服账号 */ - public JsonResult createKfAccount(String id, String name, String pwd) + public ApiResult createKfAccount(String id, String name, String pwd) throws WeixinException { return customApi.createKfAccount(id, name, pwd); } @@ -590,7 +590,7 @@ public class WeixinProxy { * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN"> * 更新客服账号 */ - public JsonResult updateKfAccount(String id, String name, String pwd) + public ApiResult updateKfAccount(String id, String name, String pwd) throws WeixinException { return customApi.updateKfAccount(id, name, pwd); } @@ -611,7 +611,7 @@ public class WeixinProxy { * >邀请绑定客服帐号 * @throws WeixinException */ - public JsonResult inviteKfAccount(String kfAccount, String inviteAccount) + public ApiResult inviteKfAccount(String kfAccount, String inviteAccount) throws WeixinException { return customApi.inviteKfAccount(kfAccount, inviteAccount); } @@ -632,7 +632,7 @@ public class WeixinProxy { * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN"> * 上传客服头像 */ - public JsonResult uploadKfAvatar(String accountId, InputStream is, + public ApiResult uploadKfAvatar(String accountId, InputStream is, String fileName) throws WeixinException { return customApi.uploadKfAvatar(accountId, is, fileName); } @@ -649,7 +649,7 @@ public class WeixinProxy { * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN"> * 删除客服账号 */ - public JsonResult deleteKfAccount(String id) throws WeixinException { + public ApiResult deleteKfAccount(String id) throws WeixinException { return customApi.deleteKfAccount(id); } @@ -673,7 +673,7 @@ public class WeixinProxy { * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044813&token=&lang=zh_CN"> * 创建会话 */ - public JsonResult createKfSession(String userOpenId, String kfAccount, + public ApiResult createKfSession(String userOpenId, String kfAccount, String text) throws WeixinException { return customApi.createKfSession(userOpenId, kfAccount, text); } @@ -694,7 +694,7 @@ public class WeixinProxy { * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1458044820&token=&lang=zh_CN"> * 关闭会话 */ - public JsonResult closeKfSession(String userOpenId, String kfAccount, + public ApiResult closeKfSession(String userOpenId, String kfAccount, String text) throws WeixinException { return customApi.closeKfSession(userOpenId, kfAccount, text); } @@ -891,7 +891,7 @@ public class WeixinProxy { * @see {@link #massByOpenIds(Tuple, String...) * */ - public JsonResult deleteMassNews(String msgid) throws WeixinException { + public ApiResult deleteMassNews(String msgid) throws WeixinException { return massApi.deleteMassNews(msgid); } @@ -912,7 +912,7 @@ public class WeixinProxy { * "https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140549&token=&lang=zh_CN"> * 预览群发消息 */ - public JsonResult previewMassNews(String toUser, String toWxName, + public ApiResult previewMassNews(String toUser, String toWxName, MassTuple tuple) throws WeixinException { return massApi.previewMassNews(toUser, toWxName, tuple); } @@ -1109,7 +1109,7 @@ public class WeixinProxy { * 设置用户备注名 * @see com.foxinmy.weixin4j.mp.api.UserApi */ - public JsonResult remarkUserName(String openId, String remark) + public ApiResult remarkUserName(String openId, String remark) throws WeixinException { return userApi.remarkUserName(openId, remark); } @@ -1178,7 +1178,7 @@ public class WeixinProxy { * @see com.foxinmy.weixin4j.mp.model.Group * @see com.foxinmy.weixin4j.mp.api.GroupApi */ - public JsonResult modifyGroup(int groupId, String name) + public ApiResult modifyGroup(int groupId, String name) throws WeixinException { return groupApi.modifyGroup(groupId, name); } @@ -1197,7 +1197,7 @@ public class WeixinProxy { * @see com.foxinmy.weixin4j.mp.model.Group * @see com.foxinmy.weixin4j.mp.api.GroupApi */ - public JsonResult moveGroup(int groupId, String openId) + public ApiResult moveGroup(int groupId, String openId) throws WeixinException { return groupApi.moveGroup(groupId, openId); } @@ -1216,7 +1216,7 @@ public class WeixinProxy { * @see com.foxinmy.weixin4j.mp.model.Group * @see com.foxinmy.weixin4j.mp.api.GroupApi */ - public JsonResult moveGroup(int groupId, String... openIds) + public ApiResult moveGroup(int groupId, String... openIds) throws WeixinException { return groupApi.moveGroup(groupId, openIds); } @@ -1233,7 +1233,7 @@ public class WeixinProxy { * @see com.foxinmy.weixin4j.mp.model.Group * @see com.foxinmy.weixin4j.mp.api.GroupApi */ - public JsonResult deleteGroup(int groupId) throws WeixinException { + public ApiResult deleteGroup(int groupId) throws WeixinException { return groupApi.deleteGroup(groupId); } @@ -1250,7 +1250,7 @@ public class WeixinProxy { * @see com.foxinmy.weixin4j.type.ButtonType * @see com.foxinmy.weixin4j.mp.api.MenuApi */ - public JsonResult createMenu(List